##// END OF EJS Templates
Only ignore existing target for symlink if it is already a symlink
Thomas Kluyver -
Show More
@@ -1,592 +1,592
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.lib.tests' : ['*.wav'],
154 'IPython.lib.tests' : ['*.wav'],
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_entry_points():
314 def find_entry_points():
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 ep = [
325 ep = [
326 'ipython%s = IPython:start_ipython',
326 'ipython%s = IPython:start_ipython',
327 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
327 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
328 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
328 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
329 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
329 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
330 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
330 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
331 'iptest%s = IPython.testing.iptestcontroller:main',
331 'iptest%s = IPython.testing.iptestcontroller:main',
332 'irunner%s = IPython.lib.irunner:main',
332 'irunner%s = IPython.lib.irunner:main',
333 ]
333 ]
334 suffix = str(sys.version_info[0])
334 suffix = str(sys.version_info[0])
335 return [e % '' for e in ep] + [e % suffix for e in ep]
335 return [e % '' for e in ep] + [e % suffix for e in ep]
336
336
337 script_src = """#!{executable}
337 script_src = """#!{executable}
338 # This script was automatically generated by setup.py
338 # This script was automatically generated by setup.py
339 from {mod} import {func}
339 from {mod} import {func}
340 {func}()
340 {func}()
341 """
341 """
342
342
343 class build_scripts_entrypt(build_scripts):
343 class build_scripts_entrypt(build_scripts):
344 def run(self):
344 def run(self):
345 self.mkpath(self.build_dir)
345 self.mkpath(self.build_dir)
346 outfiles = []
346 outfiles = []
347 for script in find_entry_points():
347 for script in find_entry_points():
348 name, entrypt = script.split('=')
348 name, entrypt = script.split('=')
349 name = name.strip()
349 name = name.strip()
350 entrypt = entrypt.strip()
350 entrypt = entrypt.strip()
351 outfile = os.path.join(self.build_dir, name)
351 outfile = os.path.join(self.build_dir, name)
352 outfiles.append(outfile)
352 outfiles.append(outfile)
353 print('Writing script to', outfile)
353 print('Writing script to', outfile)
354
354
355 mod, func = entrypt.split(':')
355 mod, func = entrypt.split(':')
356 with open(outfile, 'w') as f:
356 with open(outfile, 'w') as f:
357 f.write(script_src.format(executable=sys.executable,
357 f.write(script_src.format(executable=sys.executable,
358 mod=mod, func=func))
358 mod=mod, func=func))
359
359
360 return outfiles, outfiles
360 return outfiles, outfiles
361
361
362 class install_lib_symlink(Command):
362 class install_lib_symlink(Command):
363 user_options = [
363 user_options = [
364 ('install-dir=', 'd', "directory to install to"),
364 ('install-dir=', 'd', "directory to install to"),
365 ]
365 ]
366
366
367 def initialize_options(self):
367 def initialize_options(self):
368 self.install_dir = None
368 self.install_dir = None
369
369
370 def finalize_options(self):
370 def finalize_options(self):
371 self.set_undefined_options('symlink',
371 self.set_undefined_options('symlink',
372 ('install_lib', 'install_dir'),
372 ('install_lib', 'install_dir'),
373 )
373 )
374
374
375 def run(self):
375 def run(self):
376 if sys.platform == 'win32':
376 if sys.platform == 'win32':
377 raise Exception("This doesn't work on Windows.")
377 raise Exception("This doesn't work on Windows.")
378 pkg = os.path.join(os.getcwd(), 'IPython')
378 pkg = os.path.join(os.getcwd(), 'IPython')
379 dest = os.path.join(self.install_dir, 'IPython')
379 dest = os.path.join(self.install_dir, 'IPython')
380 print('symlinking %s -> %s' % (pkg, dest))
380 print('symlinking %s -> %s' % (pkg, dest))
381 try:
381 try:
382 os.symlink(pkg, dest)
382 os.symlink(pkg, dest)
383 except OSError as e:
383 except OSError as e:
384 if e.errno == errno.EEXIST:
384 if e.errno == errno.EEXIST and os.path.islink(dest):
385 print('ALREADY EXISTS')
385 print('Symlink already exists')
386 else:
386 else:
387 raise
387 raise
388
388
389 class install_symlinked(install):
389 class install_symlinked(install):
390 def run(self):
390 def run(self):
391 if sys.platform == 'win32':
391 if sys.platform == 'win32':
392 raise Exception("This doesn't work on Windows.")
392 raise Exception("This doesn't work on Windows.")
393
393
394 # Run all sub-commands (at least those that need to be run)
394 # Run all sub-commands (at least those that need to be run)
395 for cmd_name in self.get_sub_commands():
395 for cmd_name in self.get_sub_commands():
396 self.run_command(cmd_name)
396 self.run_command(cmd_name)
397
397
398 # 'sub_commands': a list of commands this command might have to run to
398 # 'sub_commands': a list of commands this command might have to run to
399 # get its work done. See cmd.py for more info.
399 # get its work done. See cmd.py for more info.
400 sub_commands = [('install_lib_symlink', lambda self:True),
400 sub_commands = [('install_lib_symlink', lambda self:True),
401 ('install_scripts_sym', lambda self:True),
401 ('install_scripts_sym', lambda self:True),
402 ]
402 ]
403
403
404 class install_scripts_for_symlink(install_scripts):
404 class install_scripts_for_symlink(install_scripts):
405 """Redefined to get options from 'symlink' instead of 'install'.
405 """Redefined to get options from 'symlink' instead of 'install'.
406
406
407 I love distutils almost as much as I love setuptools.
407 I love distutils almost as much as I love setuptools.
408 """
408 """
409 def finalize_options(self):
409 def finalize_options(self):
410 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
410 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
411 self.set_undefined_options('symlink',
411 self.set_undefined_options('symlink',
412 ('install_scripts', 'install_dir'),
412 ('install_scripts', 'install_dir'),
413 ('force', 'force'),
413 ('force', 'force'),
414 ('skip_build', 'skip_build'),
414 ('skip_build', 'skip_build'),
415 )
415 )
416
416
417 #---------------------------------------------------------------------------
417 #---------------------------------------------------------------------------
418 # Verify all dependencies
418 # Verify all dependencies
419 #---------------------------------------------------------------------------
419 #---------------------------------------------------------------------------
420
420
421 def check_for_dependencies():
421 def check_for_dependencies():
422 """Check for IPython's dependencies.
422 """Check for IPython's dependencies.
423
423
424 This function should NOT be called if running under setuptools!
424 This function should NOT be called if running under setuptools!
425 """
425 """
426 from setupext.setupext import (
426 from setupext.setupext import (
427 print_line, print_raw, print_status,
427 print_line, print_raw, print_status,
428 check_for_sphinx, check_for_pygments,
428 check_for_sphinx, check_for_pygments,
429 check_for_nose, check_for_pexpect,
429 check_for_nose, check_for_pexpect,
430 check_for_pyzmq, check_for_readline,
430 check_for_pyzmq, check_for_readline,
431 check_for_jinja2, check_for_tornado
431 check_for_jinja2, check_for_tornado
432 )
432 )
433 print_line()
433 print_line()
434 print_raw("BUILDING IPYTHON")
434 print_raw("BUILDING IPYTHON")
435 print_status('python', sys.version)
435 print_status('python', sys.version)
436 print_status('platform', sys.platform)
436 print_status('platform', sys.platform)
437 if sys.platform == 'win32':
437 if sys.platform == 'win32':
438 print_status('Windows version', sys.getwindowsversion())
438 print_status('Windows version', sys.getwindowsversion())
439
439
440 print_raw("")
440 print_raw("")
441 print_raw("OPTIONAL DEPENDENCIES")
441 print_raw("OPTIONAL DEPENDENCIES")
442
442
443 check_for_sphinx()
443 check_for_sphinx()
444 check_for_pygments()
444 check_for_pygments()
445 check_for_nose()
445 check_for_nose()
446 check_for_pexpect()
446 check_for_pexpect()
447 check_for_pyzmq()
447 check_for_pyzmq()
448 check_for_tornado()
448 check_for_tornado()
449 check_for_readline()
449 check_for_readline()
450 check_for_jinja2()
450 check_for_jinja2()
451
451
452 #---------------------------------------------------------------------------
452 #---------------------------------------------------------------------------
453 # VCS related
453 # VCS related
454 #---------------------------------------------------------------------------
454 #---------------------------------------------------------------------------
455
455
456 # utils.submodule has checks for submodule status
456 # utils.submodule has checks for submodule status
457 execfile(pjoin('IPython','utils','submodule.py'), globals())
457 execfile(pjoin('IPython','utils','submodule.py'), globals())
458
458
459 class UpdateSubmodules(Command):
459 class UpdateSubmodules(Command):
460 """Update git submodules
460 """Update git submodules
461
461
462 IPython's external javascript dependencies live in a separate repo.
462 IPython's external javascript dependencies live in a separate repo.
463 """
463 """
464 description = "Update git submodules"
464 description = "Update git submodules"
465 user_options = []
465 user_options = []
466
466
467 def initialize_options(self):
467 def initialize_options(self):
468 pass
468 pass
469
469
470 def finalize_options(self):
470 def finalize_options(self):
471 pass
471 pass
472
472
473 def run(self):
473 def run(self):
474 failure = False
474 failure = False
475 try:
475 try:
476 self.spawn('git submodule init'.split())
476 self.spawn('git submodule init'.split())
477 self.spawn('git submodule update --recursive'.split())
477 self.spawn('git submodule update --recursive'.split())
478 except Exception as e:
478 except Exception as e:
479 failure = e
479 failure = e
480 print(e)
480 print(e)
481
481
482 if not check_submodule_status(repo_root) == 'clean':
482 if not check_submodule_status(repo_root) == 'clean':
483 print("submodules could not be checked out")
483 print("submodules could not be checked out")
484 sys.exit(1)
484 sys.exit(1)
485
485
486
486
487 def git_prebuild(pkg_dir, build_cmd=build_py):
487 def git_prebuild(pkg_dir, build_cmd=build_py):
488 """Return extended build or sdist command class for recording commit
488 """Return extended build or sdist command class for recording commit
489
489
490 records git commit in IPython.utils._sysinfo.commit
490 records git commit in IPython.utils._sysinfo.commit
491
491
492 for use in IPython.utils.sysinfo.sys_info() calls after installation.
492 for use in IPython.utils.sysinfo.sys_info() calls after installation.
493
493
494 Also ensures that submodules exist prior to running
494 Also ensures that submodules exist prior to running
495 """
495 """
496
496
497 class MyBuildPy(build_cmd):
497 class MyBuildPy(build_cmd):
498 ''' Subclass to write commit data into installation tree '''
498 ''' Subclass to write commit data into installation tree '''
499 def run(self):
499 def run(self):
500 build_cmd.run(self)
500 build_cmd.run(self)
501 # this one will only fire for build commands
501 # this one will only fire for build commands
502 if hasattr(self, 'build_lib'):
502 if hasattr(self, 'build_lib'):
503 self._record_commit(self.build_lib)
503 self._record_commit(self.build_lib)
504
504
505 def make_release_tree(self, base_dir, files):
505 def make_release_tree(self, base_dir, files):
506 # this one will fire for sdist
506 # this one will fire for sdist
507 build_cmd.make_release_tree(self, base_dir, files)
507 build_cmd.make_release_tree(self, base_dir, files)
508 self._record_commit(base_dir)
508 self._record_commit(base_dir)
509
509
510 def _record_commit(self, base_dir):
510 def _record_commit(self, base_dir):
511 import subprocess
511 import subprocess
512 proc = subprocess.Popen('git rev-parse --short HEAD',
512 proc = subprocess.Popen('git rev-parse --short HEAD',
513 stdout=subprocess.PIPE,
513 stdout=subprocess.PIPE,
514 stderr=subprocess.PIPE,
514 stderr=subprocess.PIPE,
515 shell=True)
515 shell=True)
516 repo_commit, _ = proc.communicate()
516 repo_commit, _ = proc.communicate()
517 repo_commit = repo_commit.strip().decode("ascii")
517 repo_commit = repo_commit.strip().decode("ascii")
518
518
519 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
519 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
520 if os.path.isfile(out_pth) and not repo_commit:
520 if os.path.isfile(out_pth) and not repo_commit:
521 # nothing to write, don't clobber
521 # nothing to write, don't clobber
522 return
522 return
523
523
524 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
524 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
525
525
526 # remove to avoid overwriting original via hard link
526 # remove to avoid overwriting original via hard link
527 try:
527 try:
528 os.remove(out_pth)
528 os.remove(out_pth)
529 except (IOError, OSError):
529 except (IOError, OSError):
530 pass
530 pass
531 with open(out_pth, 'w') as out_file:
531 with open(out_pth, 'w') as out_file:
532 out_file.writelines([
532 out_file.writelines([
533 '# GENERATED BY setup.py\n',
533 '# GENERATED BY setup.py\n',
534 'commit = "%s"\n' % repo_commit,
534 'commit = "%s"\n' % repo_commit,
535 ])
535 ])
536 return require_submodules(MyBuildPy)
536 return require_submodules(MyBuildPy)
537
537
538
538
539 def require_submodules(command):
539 def require_submodules(command):
540 """decorator for instructing a command to check for submodules before running"""
540 """decorator for instructing a command to check for submodules before running"""
541 class DecoratedCommand(command):
541 class DecoratedCommand(command):
542 def run(self):
542 def run(self):
543 if not check_submodule_status(repo_root) == 'clean':
543 if not check_submodule_status(repo_root) == 'clean':
544 print("submodules missing! Run `setup.py submodule` and try again")
544 print("submodules missing! Run `setup.py submodule` and try again")
545 sys.exit(1)
545 sys.exit(1)
546 command.run(self)
546 command.run(self)
547 return DecoratedCommand
547 return DecoratedCommand
548
548
549 #---------------------------------------------------------------------------
549 #---------------------------------------------------------------------------
550 # Notebook related
550 # Notebook related
551 #---------------------------------------------------------------------------
551 #---------------------------------------------------------------------------
552
552
553 class CompileCSS(Command):
553 class CompileCSS(Command):
554 """Recompile Notebook CSS
554 """Recompile Notebook CSS
555
555
556 Regenerate the compiled CSS from LESS sources.
556 Regenerate the compiled CSS from LESS sources.
557
557
558 Requires various dev dependencies, such as fabric and lessc.
558 Requires various dev dependencies, such as fabric and lessc.
559 """
559 """
560 description = "Recompile Notebook CSS"
560 description = "Recompile Notebook CSS"
561 user_options = []
561 user_options = []
562
562
563 def initialize_options(self):
563 def initialize_options(self):
564 pass
564 pass
565
565
566 def finalize_options(self):
566 def finalize_options(self):
567 pass
567 pass
568
568
569 def run(self):
569 def run(self):
570 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
570 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
571
571
572 class JavascriptVersion(Command):
572 class JavascriptVersion(Command):
573 """write the javascript version to notebook javascript"""
573 """write the javascript version to notebook javascript"""
574 description = "Write IPython version to javascript"
574 description = "Write IPython version to javascript"
575 user_options = []
575 user_options = []
576
576
577 def initialize_options(self):
577 def initialize_options(self):
578 pass
578 pass
579
579
580 def finalize_options(self):
580 def finalize_options(self):
581 pass
581 pass
582
582
583 def run(self):
583 def run(self):
584 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
584 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
585 with open(nsfile) as f:
585 with open(nsfile) as f:
586 lines = f.readlines()
586 lines = f.readlines()
587 with open(nsfile, 'w') as f:
587 with open(nsfile, 'w') as f:
588 for line in lines:
588 for line in lines:
589 if line.startswith("IPython.version"):
589 if line.startswith("IPython.version"):
590 line = 'IPython.version = "{0}";\n'.format(version)
590 line = 'IPython.version = "{0}";\n'.format(version)
591 f.write(line)
591 f.write(line)
592
592
General Comments 0
You need to be logged in to leave comments. Login now