##// END OF EJS Templates
make sure test files are installed
MinRK -
Show More
@@ -1,468 +1,469 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 try:
27 27 from configparser import ConfigParser
28 28 except:
29 29 from ConfigParser import ConfigParser
30 30 from distutils.command.build_py import build_py
31 31 from distutils.cmd import Command
32 32 from glob import glob
33 33
34 34 from setupext import install_data_ext
35 35
36 36 #-------------------------------------------------------------------------------
37 37 # Useful globals and utility functions
38 38 #-------------------------------------------------------------------------------
39 39
40 40 # A few handy globals
41 41 isfile = os.path.isfile
42 42 pjoin = os.path.join
43 43 repo_root = os.path.dirname(os.path.abspath(__file__))
44 44
45 45 def oscmd(s):
46 46 print(">", s)
47 47 os.system(s)
48 48
49 49 # Py3 compatibility hacks, without assuming IPython itself is installed with
50 50 # the full py3compat machinery.
51 51
52 52 try:
53 53 execfile
54 54 except NameError:
55 55 def execfile(fname, globs, locs=None):
56 56 locs = locs or globs
57 57 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
58 58
59 59 # A little utility we'll need below, since glob() does NOT allow you to do
60 60 # exclusion on multiple endings!
61 61 def file_doesnt_endwith(test,endings):
62 62 """Return true if test is a file and its name does NOT end with any
63 63 of the strings listed in endings."""
64 64 if not isfile(test):
65 65 return False
66 66 for e in endings:
67 67 if test.endswith(e):
68 68 return False
69 69 return True
70 70
71 71 #---------------------------------------------------------------------------
72 72 # Basic project information
73 73 #---------------------------------------------------------------------------
74 74
75 75 # release.py contains version, authors, license, url, keywords, etc.
76 76 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
77 77
78 78 # Create a dict with the basic information
79 79 # This dict is eventually passed to setup after additional keys are added.
80 80 setup_args = dict(
81 81 name = name,
82 82 version = version,
83 83 description = description,
84 84 long_description = long_description,
85 85 author = author,
86 86 author_email = author_email,
87 87 url = url,
88 88 download_url = download_url,
89 89 license = license,
90 90 platforms = platforms,
91 91 keywords = keywords,
92 92 classifiers = classifiers,
93 93 cmdclass = {'install_data': install_data_ext},
94 94 )
95 95
96 96
97 97 #---------------------------------------------------------------------------
98 98 # Find packages
99 99 #---------------------------------------------------------------------------
100 100
101 101 def find_packages():
102 102 """
103 103 Find all of IPython's packages.
104 104 """
105 105 excludes = ['deathrow', 'quarantine']
106 106 packages = []
107 107 for dir,subdirs,files in os.walk('IPython'):
108 108 package = dir.replace(os.path.sep, '.')
109 109 if any(package.startswith('IPython.'+exc) for exc in excludes):
110 110 # package is to be excluded (e.g. deathrow)
111 111 continue
112 112 if '__init__.py' not in files:
113 113 # not a package
114 114 continue
115 115 packages.append(package)
116 116 return packages
117 117
118 118 #---------------------------------------------------------------------------
119 119 # Find package data
120 120 #---------------------------------------------------------------------------
121 121
122 122 def find_package_data():
123 123 """
124 124 Find IPython's package_data.
125 125 """
126 126 # This is not enough for these things to appear in an sdist.
127 127 # We need to muck with the MANIFEST to get this to work
128 128
129 129 # exclude static things that we don't ship (e.g. mathjax)
130 130 excludes = ['mathjax']
131 131
132 132 # add 'static/' prefix to exclusions, and tuplify for use in startswith
133 133 excludes = tuple([os.path.join('static', ex) for ex in excludes])
134 134
135 135 # walk notebook resources:
136 136 cwd = os.getcwd()
137 137 os.chdir(os.path.join('IPython', 'frontend', 'html', 'notebook'))
138 138 static_walk = list(os.walk('static'))
139 139 os.chdir(cwd)
140 140 static_data = []
141 141 for parent, dirs, files in static_walk:
142 142 if parent.startswith(excludes):
143 143 continue
144 144 for f in files:
145 145 static_data.append(os.path.join(parent, f))
146 146
147 147 package_data = {
148 148 'IPython.config.profile' : ['README*', '*/*.py'],
149 'IPython.core.tests' : ['*.png', '*.jpg'],
149 150 'IPython.testing' : ['*.txt'],
150 151 'IPython.testing.plugin' : ['*.txt'],
151 152 'IPython.frontend.html.notebook' : ['templates/*'] + static_data,
152 153 'IPython.frontend.qt.console' : ['resources/icon/*.svg'],
153 154 }
154 155 return package_data
155 156
156 157
157 158 #---------------------------------------------------------------------------
158 159 # Find data files
159 160 #---------------------------------------------------------------------------
160 161
161 162 def make_dir_struct(tag,base,out_base):
162 163 """Make the directory structure of all files below a starting dir.
163 164
164 165 This is just a convenience routine to help build a nested directory
165 166 hierarchy because distutils is too stupid to do this by itself.
166 167
167 168 XXX - this needs a proper docstring!
168 169 """
169 170
170 171 # we'll use these a lot below
171 172 lbase = len(base)
172 173 pathsep = os.path.sep
173 174 lpathsep = len(pathsep)
174 175
175 176 out = []
176 177 for (dirpath,dirnames,filenames) in os.walk(base):
177 178 # we need to strip out the dirpath from the base to map it to the
178 179 # output (installation) path. This requires possibly stripping the
179 180 # path separator, because otherwise pjoin will not work correctly
180 181 # (pjoin('foo/','/bar') returns '/bar').
181 182
182 183 dp_eff = dirpath[lbase:]
183 184 if dp_eff.startswith(pathsep):
184 185 dp_eff = dp_eff[lpathsep:]
185 186 # The output path must be anchored at the out_base marker
186 187 out_path = pjoin(out_base,dp_eff)
187 188 # Now we can generate the final filenames. Since os.walk only produces
188 189 # filenames, we must join back with the dirpath to get full valid file
189 190 # paths:
190 191 pfiles = [pjoin(dirpath,f) for f in filenames]
191 192 # Finally, generate the entry we need, which is a pari of (output
192 193 # path, files) for use as a data_files parameter in install_data.
193 194 out.append((out_path, pfiles))
194 195
195 196 return out
196 197
197 198
198 199 def find_data_files():
199 200 """
200 201 Find IPython's data_files.
201 202
202 203 Most of these are docs.
203 204 """
204 205
205 206 docdirbase = pjoin('share', 'doc', 'ipython')
206 207 manpagebase = pjoin('share', 'man', 'man1')
207 208
208 209 # Simple file lists can be made by hand
209 210 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
210 211 if not manpages:
211 212 # When running from a source tree, the manpages aren't gzipped
212 213 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
213 214
214 215 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
215 216
216 217 # For nested structures, use the utility above
217 218 example_files = make_dir_struct(
218 219 'data',
219 220 pjoin('docs','examples'),
220 221 pjoin(docdirbase,'examples')
221 222 )
222 223 manual_files = make_dir_struct(
223 224 'data',
224 225 pjoin('docs','html'),
225 226 pjoin(docdirbase,'manual')
226 227 )
227 228
228 229 # And assemble the entire output list
229 230 data_files = [ (manpagebase, manpages),
230 231 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
231 232 ] + manual_files + example_files
232 233
233 234 return data_files
234 235
235 236
236 237 def make_man_update_target(manpage):
237 238 """Return a target_update-compliant tuple for the given manpage.
238 239
239 240 Parameters
240 241 ----------
241 242 manpage : string
242 243 Name of the manpage, must include the section number (trailing number).
243 244
244 245 Example
245 246 -------
246 247
247 248 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
248 249 ('docs/man/ipython.1.gz',
249 250 ['docs/man/ipython.1'],
250 251 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
251 252 """
252 253 man_dir = pjoin('docs', 'man')
253 254 manpage_gz = manpage + '.gz'
254 255 manpath = pjoin(man_dir, manpage)
255 256 manpath_gz = pjoin(man_dir, manpage_gz)
256 257 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
257 258 locals() )
258 259 return (manpath_gz, [manpath], gz_cmd)
259 260
260 261 # The two functions below are copied from IPython.utils.path, so we don't need
261 262 # to import IPython during setup, which fails on Python 3.
262 263
263 264 def target_outdated(target,deps):
264 265 """Determine whether a target is out of date.
265 266
266 267 target_outdated(target,deps) -> 1/0
267 268
268 269 deps: list of filenames which MUST exist.
269 270 target: single filename which may or may not exist.
270 271
271 272 If target doesn't exist or is older than any file listed in deps, return
272 273 true, otherwise return false.
273 274 """
274 275 try:
275 276 target_time = os.path.getmtime(target)
276 277 except os.error:
277 278 return 1
278 279 for dep in deps:
279 280 dep_time = os.path.getmtime(dep)
280 281 if dep_time > target_time:
281 282 #print "For target",target,"Dep failed:",dep # dbg
282 283 #print "times (dep,tar):",dep_time,target_time # dbg
283 284 return 1
284 285 return 0
285 286
286 287
287 288 def target_update(target,deps,cmd):
288 289 """Update a target with a given command given a list of dependencies.
289 290
290 291 target_update(target,deps,cmd) -> runs cmd if target is outdated.
291 292
292 293 This is just a wrapper around target_outdated() which calls the given
293 294 command if target is outdated."""
294 295
295 296 if target_outdated(target,deps):
296 297 os.system(cmd)
297 298
298 299 #---------------------------------------------------------------------------
299 300 # Find scripts
300 301 #---------------------------------------------------------------------------
301 302
302 303 def find_scripts(entry_points=False, suffix=''):
303 304 """Find IPython's scripts.
304 305
305 306 if entry_points is True:
306 307 return setuptools entry_point-style definitions
307 308 else:
308 309 return file paths of plain scripts [default]
309 310
310 311 suffix is appended to script names if entry_points is True, so that the
311 312 Python 3 scripts get named "ipython3" etc.
312 313 """
313 314 if entry_points:
314 315 console_scripts = [s % suffix for s in [
315 316 'ipython%s = IPython.frontend.terminal.ipapp:launch_new_instance',
316 317 'pycolor%s = IPython.utils.PyColorize:main',
317 318 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
318 319 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
319 320 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
320 321 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
321 322 'iptest%s = IPython.testing.iptest:main',
322 323 'irunner%s = IPython.lib.irunner:main'
323 324 ]]
324 325 gui_scripts = []
325 326 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
326 327 else:
327 328 parallel_scripts = pjoin('IPython','parallel','scripts')
328 329 main_scripts = pjoin('IPython','scripts')
329 330 scripts = [
330 331 pjoin(parallel_scripts, 'ipengine'),
331 332 pjoin(parallel_scripts, 'ipcontroller'),
332 333 pjoin(parallel_scripts, 'ipcluster'),
333 334 pjoin(parallel_scripts, 'iplogger'),
334 335 pjoin(main_scripts, 'ipython'),
335 336 pjoin(main_scripts, 'pycolor'),
336 337 pjoin(main_scripts, 'irunner'),
337 338 pjoin(main_scripts, 'iptest')
338 339 ]
339 340 return scripts
340 341
341 342 #---------------------------------------------------------------------------
342 343 # Verify all dependencies
343 344 #---------------------------------------------------------------------------
344 345
345 346 def check_for_dependencies():
346 347 """Check for IPython's dependencies.
347 348
348 349 This function should NOT be called if running under setuptools!
349 350 """
350 351 from setupext.setupext import (
351 352 print_line, print_raw, print_status,
352 353 check_for_sphinx, check_for_pygments,
353 354 check_for_nose, check_for_pexpect,
354 355 check_for_pyzmq, check_for_readline
355 356 )
356 357 print_line()
357 358 print_raw("BUILDING IPYTHON")
358 359 print_status('python', sys.version)
359 360 print_status('platform', sys.platform)
360 361 if sys.platform == 'win32':
361 362 print_status('Windows version', sys.getwindowsversion())
362 363
363 364 print_raw("")
364 365 print_raw("OPTIONAL DEPENDENCIES")
365 366
366 367 check_for_sphinx()
367 368 check_for_pygments()
368 369 check_for_nose()
369 370 check_for_pexpect()
370 371 check_for_pyzmq()
371 372 check_for_readline()
372 373
373 374 #---------------------------------------------------------------------------
374 375 # VCS related
375 376 #---------------------------------------------------------------------------
376 377
377 378 # utils.submodule has checks for submodule status
378 379 execfile(pjoin('IPython','utils','submodule.py'), globals())
379 380
380 381 class UpdateSubmodules(Command):
381 382 """Update git submodules
382 383
383 384 IPython's external javascript dependencies live in a separate repo.
384 385 """
385 386 description = "Update git submodules"
386 387 user_options = []
387 388
388 389 def initialize_options(self):
389 390 pass
390 391
391 392 def finalize_options(self):
392 393 pass
393 394
394 395 def run(self):
395 396 failure = False
396 397 try:
397 398 self.spawn('git submodule init'.split())
398 399 self.spawn('git submodule update --recursive'.split())
399 400 except Exception as e:
400 401 failure = e
401 402 print(e)
402 403
403 404 if not check_submodule_status(repo_root) == 'clean':
404 405 print("submodules could not be checked out")
405 406 sys.exit(1)
406 407
407 408
408 409 def git_prebuild(pkg_dir, build_cmd=build_py):
409 410 """Return extended build or sdist command class for recording commit
410 411
411 412 records git commit in IPython.utils._sysinfo.commit
412 413
413 414 for use in IPython.utils.sysinfo.sys_info() calls after installation.
414 415
415 416 Also ensures that submodules exist prior to running
416 417 """
417 418
418 419 class MyBuildPy(build_cmd):
419 420 ''' Subclass to write commit data into installation tree '''
420 421 def run(self):
421 422 build_cmd.run(self)
422 423 # this one will only fire for build commands
423 424 if hasattr(self, 'build_lib'):
424 425 self._record_commit(self.build_lib)
425 426
426 427 def make_release_tree(self, base_dir, files):
427 428 # this one will fire for sdist
428 429 build_cmd.make_release_tree(self, base_dir, files)
429 430 self._record_commit(base_dir)
430 431
431 432 def _record_commit(self, base_dir):
432 433 import subprocess
433 434 proc = subprocess.Popen('git rev-parse --short HEAD',
434 435 stdout=subprocess.PIPE,
435 436 stderr=subprocess.PIPE,
436 437 shell=True)
437 438 repo_commit, _ = proc.communicate()
438 439 repo_commit = repo_commit.strip().decode("ascii")
439 440
440 441 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
441 442 if os.path.isfile(out_pth) and not repo_commit:
442 443 # nothing to write, don't clobber
443 444 return
444 445
445 446 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
446 447
447 448 # remove to avoid overwriting original via hard link
448 449 try:
449 450 os.remove(out_pth)
450 451 except (IOError, OSError):
451 452 pass
452 453 with open(out_pth, 'w') as out_file:
453 454 out_file.writelines([
454 455 '# GENERATED BY setup.py\n',
455 456 'commit = "%s"\n' % repo_commit,
456 457 ])
457 458 return require_submodules(MyBuildPy)
458 459
459 460
460 461 def require_submodules(command):
461 462 """decorator for instructing a command to check for submodules before running"""
462 463 class DecoratedCommand(command):
463 464 def run(self):
464 465 if not check_submodule_status(repo_root) == 'clean':
465 466 print("submodules missing! Run `setup.py submodule` and try again")
466 467 sys.exit(1)
467 468 command.run(self)
468 469 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now