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