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