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