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