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