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