##// END OF EJS Templates
Generate package list automatically in find_packages...
MinRK -
Show More
@@ -1,388 +1,351 b''
1 1 # encoding: utf-8
2 2 """
3 3 This module defines the things that are used in setup.py for building IPython
4 4
5 5 This includes:
6 6
7 7 * The basic arguments to setup
8 8 * Functions for finding things like packages, package data, etc.
9 9 * A function for checking dependencies.
10 10 """
11 11 from __future__ import print_function
12 12
13 13 #-------------------------------------------------------------------------------
14 14 # Copyright (C) 2008 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
18 18 #-------------------------------------------------------------------------------
19 19
20 20 #-------------------------------------------------------------------------------
21 21 # Imports
22 22 #-------------------------------------------------------------------------------
23 23 import os
24 24 import sys
25 25
26 26 from ConfigParser import ConfigParser
27 27 from distutils.command.build_py import build_py
28 28 from glob import glob
29 29
30 30 from setupext import install_data_ext
31 31
32 32 #-------------------------------------------------------------------------------
33 33 # Useful globals and utility functions
34 34 #-------------------------------------------------------------------------------
35 35
36 36 # A few handy globals
37 37 isfile = os.path.isfile
38 38 pjoin = os.path.join
39 39
40 40 def oscmd(s):
41 41 print(">", s)
42 42 os.system(s)
43 43
44 44 # A little utility we'll need below, since glob() does NOT allow you to do
45 45 # exclusion on multiple endings!
46 46 def file_doesnt_endwith(test,endings):
47 47 """Return true if test is a file and its name does NOT end with any
48 48 of the strings listed in endings."""
49 49 if not isfile(test):
50 50 return False
51 51 for e in endings:
52 52 if test.endswith(e):
53 53 return False
54 54 return True
55 55
56 56 #---------------------------------------------------------------------------
57 57 # Basic project information
58 58 #---------------------------------------------------------------------------
59 59
60 60 # release.py contains version, authors, license, url, keywords, etc.
61 61 execfile(pjoin('IPython','core','release.py'))
62 62
63 63 # Create a dict with the basic information
64 64 # This dict is eventually passed to setup after additional keys are added.
65 65 setup_args = dict(
66 66 name = name,
67 67 version = version,
68 68 description = description,
69 69 long_description = long_description,
70 70 author = author,
71 71 author_email = author_email,
72 72 url = url,
73 73 download_url = download_url,
74 74 license = license,
75 75 platforms = platforms,
76 76 keywords = keywords,
77 77 cmdclass = {'install_data': install_data_ext},
78 78 )
79 79
80 80
81 81 #---------------------------------------------------------------------------
82 82 # Find packages
83 83 #---------------------------------------------------------------------------
84 84
85 def add_package(packages,pname,config=False,tests=False,scripts=False,
86 others=None):
87 """
88 Add a package to the list of packages, including certain subpackages.
89 """
90 packages.append('.'.join(['IPython',pname]))
91 if config:
92 packages.append('.'.join(['IPython',pname,'config']))
93 if tests:
94 packages.append('.'.join(['IPython',pname,'tests']))
95 if scripts:
96 packages.append('.'.join(['IPython',pname,'scripts']))
97 if others is not None:
98 for o in others:
99 packages.append('.'.join(['IPython',pname,o]))
100
101 85 def find_packages():
102 86 """
103 87 Find all of IPython's packages.
104 88 """
105 packages = ['IPython']
106 add_package(packages, 'config', tests=True, others=['profile'])
107 add_package(packages, 'core', tests=True)
108 add_package(packages, 'extensions')
109 add_package(packages, 'external')
110 add_package(packages, 'external.argparse')
111 add_package(packages, 'external.decorator')
112 add_package(packages, 'external.decorators')
113 add_package(packages, 'external.guid')
114 add_package(packages, 'external.Itpl')
115 add_package(packages, 'external.mglob')
116 add_package(packages, 'external.path')
117 add_package(packages, 'external.pexpect')
118 add_package(packages, 'external.pyparsing')
119 add_package(packages, 'external.simplegeneric')
120 add_package(packages, 'external.ssh')
121 add_package(packages, 'kernel')
122 add_package(packages, 'frontend')
123 add_package(packages, 'frontend.qt')
124 add_package(packages, 'frontend.qt.console', tests=True)
125 add_package(packages, 'frontend.terminal', tests=True)
126 add_package(packages, 'lib', tests=True)
127 add_package(packages, 'parallel', tests=True, scripts=True,
128 others=['apps','engine','client','controller'])
129 add_package(packages, 'quarantine', tests=True)
130 add_package(packages, 'scripts')
131 add_package(packages, 'testing', tests=True)
132 add_package(packages, 'testing.plugin', tests=False)
133 add_package(packages, 'utils', tests=True)
134 add_package(packages, 'zmq')
135 add_package(packages, 'zmq.pylab')
136 add_package(packages, 'zmq.gui')
89 excludes = ['deathrow']
90 packages = []
91 for dir,subdirs,files in os.walk('IPython'):
92 package = dir.replace(os.path.sep, '.')
93 if any([ package.startswith('IPython.'+exc) for exc in excludes ]):
94 # package is to be excluded (e.g. deathrow)
95 continue
96 if '__init__.py' not in files:
97 # not a package
98 continue
99 packages.append(package)
137 100 return packages
138 101
139 102 #---------------------------------------------------------------------------
140 103 # Find package data
141 104 #---------------------------------------------------------------------------
142 105
143 106 def find_package_data():
144 107 """
145 108 Find IPython's package_data.
146 109 """
147 110 # This is not enough for these things to appear in an sdist.
148 111 # We need to muck with the MANIFEST to get this to work
149 112 package_data = {
150 113 'IPython.config.profile' : ['README', '*/*.py'],
151 114 'IPython.testing' : ['*.txt'],
152 115 }
153 116 return package_data
154 117
155 118
156 119 #---------------------------------------------------------------------------
157 120 # Find data files
158 121 #---------------------------------------------------------------------------
159 122
160 123 def make_dir_struct(tag,base,out_base):
161 124 """Make the directory structure of all files below a starting dir.
162 125
163 126 This is just a convenience routine to help build a nested directory
164 127 hierarchy because distutils is too stupid to do this by itself.
165 128
166 129 XXX - this needs a proper docstring!
167 130 """
168 131
169 132 # we'll use these a lot below
170 133 lbase = len(base)
171 134 pathsep = os.path.sep
172 135 lpathsep = len(pathsep)
173 136
174 137 out = []
175 138 for (dirpath,dirnames,filenames) in os.walk(base):
176 139 # we need to strip out the dirpath from the base to map it to the
177 140 # output (installation) path. This requires possibly stripping the
178 141 # path separator, because otherwise pjoin will not work correctly
179 142 # (pjoin('foo/','/bar') returns '/bar').
180 143
181 144 dp_eff = dirpath[lbase:]
182 145 if dp_eff.startswith(pathsep):
183 146 dp_eff = dp_eff[lpathsep:]
184 147 # The output path must be anchored at the out_base marker
185 148 out_path = pjoin(out_base,dp_eff)
186 149 # Now we can generate the final filenames. Since os.walk only produces
187 150 # filenames, we must join back with the dirpath to get full valid file
188 151 # paths:
189 152 pfiles = [pjoin(dirpath,f) for f in filenames]
190 153 # Finally, generate the entry we need, which is a pari of (output
191 154 # path, files) for use as a data_files parameter in install_data.
192 155 out.append((out_path, pfiles))
193 156
194 157 return out
195 158
196 159
197 160 def find_data_files():
198 161 """
199 162 Find IPython's data_files.
200 163
201 164 Most of these are docs.
202 165 """
203 166
204 167 docdirbase = pjoin('share', 'doc', 'ipython')
205 168 manpagebase = pjoin('share', 'man', 'man1')
206 169
207 170 # Simple file lists can be made by hand
208 171 manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz')))
209 172 if not manpages:
210 173 # When running from a source tree, the manpages aren't gzipped
211 174 manpages = filter(isfile, glob(pjoin('docs','man','*.1')))
212 175 igridhelpfiles = filter(isfile,
213 176 glob(pjoin('IPython','extensions','igrid_help.*')))
214 177
215 178 # For nested structures, use the utility above
216 179 example_files = make_dir_struct(
217 180 'data',
218 181 pjoin('docs','examples'),
219 182 pjoin(docdirbase,'examples')
220 183 )
221 184 manual_files = make_dir_struct(
222 185 'data',
223 186 pjoin('docs','html'),
224 187 pjoin(docdirbase,'manual')
225 188 )
226 189
227 190 # And assemble the entire output list
228 191 data_files = [ (manpagebase, manpages),
229 192 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
230 193 ] + manual_files + example_files
231 194
232 195 return data_files
233 196
234 197
235 198 def make_man_update_target(manpage):
236 199 """Return a target_update-compliant tuple for the given manpage.
237 200
238 201 Parameters
239 202 ----------
240 203 manpage : string
241 204 Name of the manpage, must include the section number (trailing number).
242 205
243 206 Example
244 207 -------
245 208
246 209 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
247 210 ('docs/man/ipython.1.gz',
248 211 ['docs/man/ipython.1'],
249 212 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
250 213 """
251 214 man_dir = pjoin('docs', 'man')
252 215 manpage_gz = manpage + '.gz'
253 216 manpath = pjoin(man_dir, manpage)
254 217 manpath_gz = pjoin(man_dir, manpage_gz)
255 218 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
256 219 locals() )
257 220 return (manpath_gz, [manpath], gz_cmd)
258 221
259 222 #---------------------------------------------------------------------------
260 223 # Find scripts
261 224 #---------------------------------------------------------------------------
262 225
263 226 def find_scripts(entry_points=False):
264 227 """Find IPython's scripts.
265 228
266 229 if entry_points is True:
267 230 return setuptools entry_point-style definitions
268 231 else:
269 232 return file paths of plain scripts [default]
270 233 """
271 234 if entry_points:
272 235 console_scripts = [
273 236 'ipython = IPython.frontend.terminal.ipapp:launch_new_instance',
274 237 'pycolor = IPython.utils.PyColorize:main',
275 238 'ipcontroller = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
276 239 'ipengine = IPython.parallel.apps.ipengineapp:launch_new_instance',
277 240 'iplogger = IPython.parallel.apps.iploggerapp:launch_new_instance',
278 241 'ipcluster = IPython.parallel.apps.ipclusterapp:launch_new_instance',
279 242 'iptest = IPython.testing.iptest:main',
280 243 'irunner = IPython.lib.irunner:main'
281 244 ]
282 245 gui_scripts = [
283 246 'ipython-qtconsole = IPython.frontend.qt.console.qtconsoleapp:main',
284 247 ]
285 248 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
286 249 else:
287 250 parallel_scripts = pjoin('IPython','parallel','scripts')
288 251 main_scripts = pjoin('IPython','scripts')
289 252 scripts = [
290 253 pjoin(parallel_scripts, 'ipengine'),
291 254 pjoin(parallel_scripts, 'ipcontroller'),
292 255 pjoin(parallel_scripts, 'ipcluster'),
293 256 pjoin(parallel_scripts, 'iplogger'),
294 257 pjoin(main_scripts, 'ipython'),
295 258 pjoin(main_scripts, 'pycolor'),
296 259 pjoin(main_scripts, 'irunner'),
297 260 pjoin(main_scripts, 'iptest')
298 261 ]
299 262 return scripts
300 263
301 264 #---------------------------------------------------------------------------
302 265 # Verify all dependencies
303 266 #---------------------------------------------------------------------------
304 267
305 268 def check_for_dependencies():
306 269 """Check for IPython's dependencies.
307 270
308 271 This function should NOT be called if running under setuptools!
309 272 """
310 273 from setupext.setupext import (
311 274 print_line, print_raw, print_status,
312 275 check_for_sphinx, check_for_pygments,
313 276 check_for_nose, check_for_pexpect,
314 277 check_for_pyzmq, check_for_readline
315 278 )
316 279 print_line()
317 280 print_raw("BUILDING IPYTHON")
318 281 print_status('python', sys.version)
319 282 print_status('platform', sys.platform)
320 283 if sys.platform == 'win32':
321 284 print_status('Windows version', sys.getwindowsversion())
322 285
323 286 print_raw("")
324 287 print_raw("OPTIONAL DEPENDENCIES")
325 288
326 289 check_for_sphinx()
327 290 check_for_pygments()
328 291 check_for_nose()
329 292 check_for_pexpect()
330 293 check_for_pyzmq()
331 294 check_for_readline()
332 295
333 296 def record_commit_info(pkg_dir, build_cmd=build_py):
334 297 """ Return extended build command class for recording commit
335 298
336 299 The extended command tries to run git to find the current commit, getting
337 300 the empty string if it fails. It then writes the commit hash into a file
338 301 in the `pkg_dir` path, named ``.git_commit_info.ini``.
339 302
340 303 In due course this information can be used by the package after it is
341 304 installed, to tell you what commit it was installed from if known.
342 305
343 306 To make use of this system, you need a package with a .git_commit_info.ini
344 307 file - e.g. ``myproject/.git_commit_info.ini`` - that might well look like
345 308 this::
346 309
347 310 # This is an ini file that may contain information about the code state
348 311 [commit hash]
349 312 # The line below may contain a valid hash if it has been substituted
350 313 # during 'git archive'
351 314 archive_subst_hash=$Format:%h$
352 315 # This line may be modified by the install process
353 316 install_hash=
354 317
355 318 The .git_commit_info file above is also designed to be used with git
356 319 substitution - so you probably also want a ``.gitattributes`` file in the
357 320 root directory of your working tree that contains something like this::
358 321
359 322 myproject/.git_commit_info.ini export-subst
360 323
361 324 That will cause the ``.git_commit_info.ini`` file to get filled in by ``git
362 325 archive`` - useful in case someone makes such an archive - for example with
363 326 via the github 'download source' button.
364 327
365 328 Although all the above will work as is, you might consider having something
366 329 like a ``get_info()`` function in your package to display the commit
367 330 information at the terminal. See the ``pkg_info.py`` module in the nipy
368 331 package for an example.
369 332 """
370 333 class MyBuildPy(build_cmd):
371 334 ''' Subclass to write commit data into installation tree '''
372 335 def run(self):
373 336 build_py.run(self)
374 337 import subprocess
375 338 proc = subprocess.Popen('git rev-parse --short HEAD',
376 339 stdout=subprocess.PIPE,
377 340 stderr=subprocess.PIPE,
378 341 shell=True)
379 342 repo_commit, _ = proc.communicate()
380 343 # We write the installation commit even if it's empty
381 344 cfg_parser = ConfigParser()
382 345 cfg_parser.read(pjoin(pkg_dir, '.git_commit_info.ini'))
383 346 cfg_parser.set('commit hash', 'install_hash', repo_commit)
384 347 out_pth = pjoin(self.build_lib, pkg_dir, '.git_commit_info.ini')
385 348 out_file = open(out_pth, 'wt')
386 349 cfg_parser.write(out_file)
387 350 out_file.close()
388 351 return MyBuildPy
General Comments 0
You need to be logged in to leave comments. Login now