##// END OF EJS Templates
we only install man pages to usr/share...
MinRK -
Show More
@@ -1,676 +1,659 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 Most of these are docs.
239 Just man pages at this point.
240 """
240 """
241
241
242 docdirbase = pjoin('share', 'doc', 'ipython')
243 manpagebase = pjoin('share', 'man', 'man1')
242 manpagebase = pjoin('share', 'man', 'man1')
244
243
245 # Simple file lists can be made by hand
244 # Simple file lists can be made by hand
246 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)]
247 if not manpages:
246 if not manpages:
248 # When running from a source tree, the manpages aren't gzipped
247 # When running from a source tree, the manpages aren't gzipped
249 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)]
250
249
251 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
252
253 # For nested structures, use the utility above
254 example_files = make_dir_struct(
255 'data',
256 pjoin('docs','examples'),
257 pjoin(docdirbase,'examples')
258 )
259 manual_files = make_dir_struct(
260 'data',
261 pjoin('docs','html'),
262 pjoin(docdirbase,'manual')
263 )
264
265 # And assemble the entire output list
250 # And assemble the entire output list
266 data_files = [ (manpagebase, manpages),
251 data_files = [ (manpagebase, manpages) ]
267 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
268 ] + manual_files + example_files
269
252
270 return data_files
253 return data_files
271
254
272
255
273 def make_man_update_target(manpage):
256 def make_man_update_target(manpage):
274 """Return a target_update-compliant tuple for the given manpage.
257 """Return a target_update-compliant tuple for the given manpage.
275
258
276 Parameters
259 Parameters
277 ----------
260 ----------
278 manpage : string
261 manpage : string
279 Name of the manpage, must include the section number (trailing number).
262 Name of the manpage, must include the section number (trailing number).
280
263
281 Example
264 Example
282 -------
265 -------
283
266
284 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
267 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
285 ('docs/man/ipython.1.gz',
268 ('docs/man/ipython.1.gz',
286 ['docs/man/ipython.1'],
269 ['docs/man/ipython.1'],
287 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
270 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
288 """
271 """
289 man_dir = pjoin('docs', 'man')
272 man_dir = pjoin('docs', 'man')
290 manpage_gz = manpage + '.gz'
273 manpage_gz = manpage + '.gz'
291 manpath = pjoin(man_dir, manpage)
274 manpath = pjoin(man_dir, manpage)
292 manpath_gz = pjoin(man_dir, manpage_gz)
275 manpath_gz = pjoin(man_dir, manpage_gz)
293 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" %
294 locals() )
277 locals() )
295 return (manpath_gz, [manpath], gz_cmd)
278 return (manpath_gz, [manpath], gz_cmd)
296
279
297 # 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
298 # to import IPython during setup, which fails on Python 3.
281 # to import IPython during setup, which fails on Python 3.
299
282
300 def target_outdated(target,deps):
283 def target_outdated(target,deps):
301 """Determine whether a target is out of date.
284 """Determine whether a target is out of date.
302
285
303 target_outdated(target,deps) -> 1/0
286 target_outdated(target,deps) -> 1/0
304
287
305 deps: list of filenames which MUST exist.
288 deps: list of filenames which MUST exist.
306 target: single filename which may or may not exist.
289 target: single filename which may or may not exist.
307
290
308 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
309 true, otherwise return false.
292 true, otherwise return false.
310 """
293 """
311 try:
294 try:
312 target_time = os.path.getmtime(target)
295 target_time = os.path.getmtime(target)
313 except os.error:
296 except os.error:
314 return 1
297 return 1
315 for dep in deps:
298 for dep in deps:
316 dep_time = os.path.getmtime(dep)
299 dep_time = os.path.getmtime(dep)
317 if dep_time > target_time:
300 if dep_time > target_time:
318 #print "For target",target,"Dep failed:",dep # dbg
301 #print "For target",target,"Dep failed:",dep # dbg
319 #print "times (dep,tar):",dep_time,target_time # dbg
302 #print "times (dep,tar):",dep_time,target_time # dbg
320 return 1
303 return 1
321 return 0
304 return 0
322
305
323
306
324 def target_update(target,deps,cmd):
307 def target_update(target,deps,cmd):
325 """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.
326
309
327 target_update(target,deps,cmd) -> runs cmd if target is outdated.
310 target_update(target,deps,cmd) -> runs cmd if target is outdated.
328
311
329 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
330 command if target is outdated."""
313 command if target is outdated."""
331
314
332 if target_outdated(target,deps):
315 if target_outdated(target,deps):
333 os.system(cmd)
316 os.system(cmd)
334
317
335 #---------------------------------------------------------------------------
318 #---------------------------------------------------------------------------
336 # Find scripts
319 # Find scripts
337 #---------------------------------------------------------------------------
320 #---------------------------------------------------------------------------
338
321
339 def find_entry_points():
322 def find_entry_points():
340 """Find IPython's scripts.
323 """Find IPython's scripts.
341
324
342 if entry_points is True:
325 if entry_points is True:
343 return setuptools entry_point-style definitions
326 return setuptools entry_point-style definitions
344 else:
327 else:
345 return file paths of plain scripts [default]
328 return file paths of plain scripts [default]
346
329
347 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
348 Python 3 scripts get named "ipython3" etc.
331 Python 3 scripts get named "ipython3" etc.
349 """
332 """
350 ep = [
333 ep = [
351 'ipython%s = IPython:start_ipython',
334 'ipython%s = IPython:start_ipython',
352 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
335 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
353 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
336 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
354 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
337 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
355 'iptest%s = IPython.testing.iptestcontroller:main',
338 'iptest%s = IPython.testing.iptestcontroller:main',
356 ]
339 ]
357 suffix = str(sys.version_info[0])
340 suffix = str(sys.version_info[0])
358 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]
359
342
360 script_src = """#!{executable}
343 script_src = """#!{executable}
361 # This script was automatically generated by setup.py
344 # This script was automatically generated by setup.py
362 if __name__ == '__main__':
345 if __name__ == '__main__':
363 from {mod} import {func}
346 from {mod} import {func}
364 {func}()
347 {func}()
365 """
348 """
366
349
367 class build_scripts_entrypt(build_scripts):
350 class build_scripts_entrypt(build_scripts):
368 def run(self):
351 def run(self):
369 self.mkpath(self.build_dir)
352 self.mkpath(self.build_dir)
370 outfiles = []
353 outfiles = []
371 for script in find_entry_points():
354 for script in find_entry_points():
372 name, entrypt = script.split('=')
355 name, entrypt = script.split('=')
373 name = name.strip()
356 name = name.strip()
374 entrypt = entrypt.strip()
357 entrypt = entrypt.strip()
375 outfile = os.path.join(self.build_dir, name)
358 outfile = os.path.join(self.build_dir, name)
376 outfiles.append(outfile)
359 outfiles.append(outfile)
377 print('Writing script to', outfile)
360 print('Writing script to', outfile)
378
361
379 mod, func = entrypt.split(':')
362 mod, func = entrypt.split(':')
380 with open(outfile, 'w') as f:
363 with open(outfile, 'w') as f:
381 f.write(script_src.format(executable=sys.executable,
364 f.write(script_src.format(executable=sys.executable,
382 mod=mod, func=func))
365 mod=mod, func=func))
383
366
384 return outfiles, outfiles
367 return outfiles, outfiles
385
368
386 class install_lib_symlink(Command):
369 class install_lib_symlink(Command):
387 user_options = [
370 user_options = [
388 ('install-dir=', 'd', "directory to install to"),
371 ('install-dir=', 'd', "directory to install to"),
389 ]
372 ]
390
373
391 def initialize_options(self):
374 def initialize_options(self):
392 self.install_dir = None
375 self.install_dir = None
393
376
394 def finalize_options(self):
377 def finalize_options(self):
395 self.set_undefined_options('symlink',
378 self.set_undefined_options('symlink',
396 ('install_lib', 'install_dir'),
379 ('install_lib', 'install_dir'),
397 )
380 )
398
381
399 def run(self):
382 def run(self):
400 if sys.platform == 'win32':
383 if sys.platform == 'win32':
401 raise Exception("This doesn't work on Windows.")
384 raise Exception("This doesn't work on Windows.")
402 pkg = os.path.join(os.getcwd(), 'IPython')
385 pkg = os.path.join(os.getcwd(), 'IPython')
403 dest = os.path.join(self.install_dir, 'IPython')
386 dest = os.path.join(self.install_dir, 'IPython')
404 if os.path.islink(dest):
387 if os.path.islink(dest):
405 print('removing existing symlink at %s' % dest)
388 print('removing existing symlink at %s' % dest)
406 os.unlink(dest)
389 os.unlink(dest)
407 print('symlinking %s -> %s' % (pkg, dest))
390 print('symlinking %s -> %s' % (pkg, dest))
408 os.symlink(pkg, dest)
391 os.symlink(pkg, dest)
409
392
410 class unsymlink(install):
393 class unsymlink(install):
411 def run(self):
394 def run(self):
412 dest = os.path.join(self.install_lib, 'IPython')
395 dest = os.path.join(self.install_lib, 'IPython')
413 if os.path.islink(dest):
396 if os.path.islink(dest):
414 print('removing symlink at %s' % dest)
397 print('removing symlink at %s' % dest)
415 os.unlink(dest)
398 os.unlink(dest)
416 else:
399 else:
417 print('No symlink exists at %s' % dest)
400 print('No symlink exists at %s' % dest)
418
401
419 class install_symlinked(install):
402 class install_symlinked(install):
420 def run(self):
403 def run(self):
421 if sys.platform == 'win32':
404 if sys.platform == 'win32':
422 raise Exception("This doesn't work on Windows.")
405 raise Exception("This doesn't work on Windows.")
423
406
424 # 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)
425 for cmd_name in self.get_sub_commands():
408 for cmd_name in self.get_sub_commands():
426 self.run_command(cmd_name)
409 self.run_command(cmd_name)
427
410
428 # '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
429 # get its work done. See cmd.py for more info.
412 # get its work done. See cmd.py for more info.
430 sub_commands = [('install_lib_symlink', lambda self:True),
413 sub_commands = [('install_lib_symlink', lambda self:True),
431 ('install_scripts_sym', lambda self:True),
414 ('install_scripts_sym', lambda self:True),
432 ]
415 ]
433
416
434 class install_scripts_for_symlink(install_scripts):
417 class install_scripts_for_symlink(install_scripts):
435 """Redefined to get options from 'symlink' instead of 'install'.
418 """Redefined to get options from 'symlink' instead of 'install'.
436
419
437 I love distutils almost as much as I love setuptools.
420 I love distutils almost as much as I love setuptools.
438 """
421 """
439 def finalize_options(self):
422 def finalize_options(self):
440 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
423 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
441 self.set_undefined_options('symlink',
424 self.set_undefined_options('symlink',
442 ('install_scripts', 'install_dir'),
425 ('install_scripts', 'install_dir'),
443 ('force', 'force'),
426 ('force', 'force'),
444 ('skip_build', 'skip_build'),
427 ('skip_build', 'skip_build'),
445 )
428 )
446
429
447 #---------------------------------------------------------------------------
430 #---------------------------------------------------------------------------
448 # Verify all dependencies
431 # Verify all dependencies
449 #---------------------------------------------------------------------------
432 #---------------------------------------------------------------------------
450
433
451 def check_for_dependencies():
434 def check_for_dependencies():
452 """Check for IPython's dependencies.
435 """Check for IPython's dependencies.
453
436
454 This function should NOT be called if running under setuptools!
437 This function should NOT be called if running under setuptools!
455 """
438 """
456 from setupext.setupext import (
439 from setupext.setupext import (
457 print_line, print_raw, print_status,
440 print_line, print_raw, print_status,
458 check_for_sphinx, check_for_pygments,
441 check_for_sphinx, check_for_pygments,
459 check_for_nose, check_for_pexpect,
442 check_for_nose, check_for_pexpect,
460 check_for_pyzmq, check_for_readline,
443 check_for_pyzmq, check_for_readline,
461 check_for_jinja2, check_for_tornado
444 check_for_jinja2, check_for_tornado
462 )
445 )
463 print_line()
446 print_line()
464 print_raw("BUILDING IPYTHON")
447 print_raw("BUILDING IPYTHON")
465 print_status('python', sys.version)
448 print_status('python', sys.version)
466 print_status('platform', sys.platform)
449 print_status('platform', sys.platform)
467 if sys.platform == 'win32':
450 if sys.platform == 'win32':
468 print_status('Windows version', sys.getwindowsversion())
451 print_status('Windows version', sys.getwindowsversion())
469
452
470 print_raw("")
453 print_raw("")
471 print_raw("OPTIONAL DEPENDENCIES")
454 print_raw("OPTIONAL DEPENDENCIES")
472
455
473 check_for_sphinx()
456 check_for_sphinx()
474 check_for_pygments()
457 check_for_pygments()
475 check_for_nose()
458 check_for_nose()
476 if os.name == 'posix':
459 if os.name == 'posix':
477 check_for_pexpect()
460 check_for_pexpect()
478 check_for_pyzmq()
461 check_for_pyzmq()
479 check_for_tornado()
462 check_for_tornado()
480 check_for_readline()
463 check_for_readline()
481 check_for_jinja2()
464 check_for_jinja2()
482
465
483 #---------------------------------------------------------------------------
466 #---------------------------------------------------------------------------
484 # VCS related
467 # VCS related
485 #---------------------------------------------------------------------------
468 #---------------------------------------------------------------------------
486
469
487 # utils.submodule has checks for submodule status
470 # utils.submodule has checks for submodule status
488 execfile(pjoin('IPython','utils','submodule.py'), globals())
471 execfile(pjoin('IPython','utils','submodule.py'), globals())
489
472
490 class UpdateSubmodules(Command):
473 class UpdateSubmodules(Command):
491 """Update git submodules
474 """Update git submodules
492
475
493 IPython's external javascript dependencies live in a separate repo.
476 IPython's external javascript dependencies live in a separate repo.
494 """
477 """
495 description = "Update git submodules"
478 description = "Update git submodules"
496 user_options = []
479 user_options = []
497
480
498 def initialize_options(self):
481 def initialize_options(self):
499 pass
482 pass
500
483
501 def finalize_options(self):
484 def finalize_options(self):
502 pass
485 pass
503
486
504 def run(self):
487 def run(self):
505 failure = False
488 failure = False
506 try:
489 try:
507 self.spawn('git submodule init'.split())
490 self.spawn('git submodule init'.split())
508 self.spawn('git submodule update --recursive'.split())
491 self.spawn('git submodule update --recursive'.split())
509 except Exception as e:
492 except Exception as e:
510 failure = e
493 failure = e
511 print(e)
494 print(e)
512
495
513 if not check_submodule_status(repo_root) == 'clean':
496 if not check_submodule_status(repo_root) == 'clean':
514 print("submodules could not be checked out")
497 print("submodules could not be checked out")
515 sys.exit(1)
498 sys.exit(1)
516
499
517
500
518 def git_prebuild(pkg_dir, build_cmd=build_py):
501 def git_prebuild(pkg_dir, build_cmd=build_py):
519 """Return extended build or sdist command class for recording commit
502 """Return extended build or sdist command class for recording commit
520
503
521 records git commit in IPython.utils._sysinfo.commit
504 records git commit in IPython.utils._sysinfo.commit
522
505
523 for use in IPython.utils.sysinfo.sys_info() calls after installation.
506 for use in IPython.utils.sysinfo.sys_info() calls after installation.
524
507
525 Also ensures that submodules exist prior to running
508 Also ensures that submodules exist prior to running
526 """
509 """
527
510
528 class MyBuildPy(build_cmd):
511 class MyBuildPy(build_cmd):
529 ''' Subclass to write commit data into installation tree '''
512 ''' Subclass to write commit data into installation tree '''
530 def run(self):
513 def run(self):
531 build_cmd.run(self)
514 build_cmd.run(self)
532 # this one will only fire for build commands
515 # this one will only fire for build commands
533 if hasattr(self, 'build_lib'):
516 if hasattr(self, 'build_lib'):
534 self._record_commit(self.build_lib)
517 self._record_commit(self.build_lib)
535
518
536 def make_release_tree(self, base_dir, files):
519 def make_release_tree(self, base_dir, files):
537 # this one will fire for sdist
520 # this one will fire for sdist
538 build_cmd.make_release_tree(self, base_dir, files)
521 build_cmd.make_release_tree(self, base_dir, files)
539 self._record_commit(base_dir)
522 self._record_commit(base_dir)
540
523
541 def _record_commit(self, base_dir):
524 def _record_commit(self, base_dir):
542 import subprocess
525 import subprocess
543 proc = subprocess.Popen('git rev-parse --short HEAD',
526 proc = subprocess.Popen('git rev-parse --short HEAD',
544 stdout=subprocess.PIPE,
527 stdout=subprocess.PIPE,
545 stderr=subprocess.PIPE,
528 stderr=subprocess.PIPE,
546 shell=True)
529 shell=True)
547 repo_commit, _ = proc.communicate()
530 repo_commit, _ = proc.communicate()
548 repo_commit = repo_commit.strip().decode("ascii")
531 repo_commit = repo_commit.strip().decode("ascii")
549
532
550 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
533 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
551 if os.path.isfile(out_pth) and not repo_commit:
534 if os.path.isfile(out_pth) and not repo_commit:
552 # nothing to write, don't clobber
535 # nothing to write, don't clobber
553 return
536 return
554
537
555 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
538 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
556
539
557 # remove to avoid overwriting original via hard link
540 # remove to avoid overwriting original via hard link
558 try:
541 try:
559 os.remove(out_pth)
542 os.remove(out_pth)
560 except (IOError, OSError):
543 except (IOError, OSError):
561 pass
544 pass
562 with open(out_pth, 'w') as out_file:
545 with open(out_pth, 'w') as out_file:
563 out_file.writelines([
546 out_file.writelines([
564 '# GENERATED BY setup.py\n',
547 '# GENERATED BY setup.py\n',
565 'commit = "%s"\n' % repo_commit,
548 'commit = "%s"\n' % repo_commit,
566 ])
549 ])
567 return require_submodules(MyBuildPy)
550 return require_submodules(MyBuildPy)
568
551
569
552
570 def require_submodules(command):
553 def require_submodules(command):
571 """decorator for instructing a command to check for submodules before running"""
554 """decorator for instructing a command to check for submodules before running"""
572 class DecoratedCommand(command):
555 class DecoratedCommand(command):
573 def run(self):
556 def run(self):
574 if not check_submodule_status(repo_root) == 'clean':
557 if not check_submodule_status(repo_root) == 'clean':
575 print("submodules missing! Run `setup.py submodule` and try again")
558 print("submodules missing! Run `setup.py submodule` and try again")
576 sys.exit(1)
559 sys.exit(1)
577 command.run(self)
560 command.run(self)
578 return DecoratedCommand
561 return DecoratedCommand
579
562
580 #---------------------------------------------------------------------------
563 #---------------------------------------------------------------------------
581 # bdist related
564 # bdist related
582 #---------------------------------------------------------------------------
565 #---------------------------------------------------------------------------
583
566
584 def get_bdist_wheel():
567 def get_bdist_wheel():
585 """Construct bdist_wheel command for building wheels
568 """Construct bdist_wheel command for building wheels
586
569
587 Constructs py2-none-any tag, instead of py2.7-none-any
570 Constructs py2-none-any tag, instead of py2.7-none-any
588 """
571 """
589 class RequiresWheel(Command):
572 class RequiresWheel(Command):
590 def run(self):
573 def run(self):
591 print("bdist_wheel requires the wheel package")
574 print("bdist_wheel requires the wheel package")
592 if 'setuptools' not in sys.modules:
575 if 'setuptools' not in sys.modules:
593 return RequiresWheel
576 return RequiresWheel
594 else:
577 else:
595 try:
578 try:
596 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
579 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
597 except ImportError:
580 except ImportError:
598 return RequiresWheel
581 return RequiresWheel
599
582
600 class bdist_wheel_tag(bdist_wheel):
583 class bdist_wheel_tag(bdist_wheel):
601
584
602 def get_tag(self):
585 def get_tag(self):
603 return ('py%i' % sys.version_info[0], 'none', 'any')
586 return ('py%i' % sys.version_info[0], 'none', 'any')
604
587
605 def add_requirements(self, metadata_path):
588 def add_requirements(self, metadata_path):
606 """transform platform-dependent requirements"""
589 """transform platform-dependent requirements"""
607 pkg_info = read_pkg_info(metadata_path)
590 pkg_info = read_pkg_info(metadata_path)
608 # pkg_info is an email.Message object (?!)
591 # pkg_info is an email.Message object (?!)
609 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
592 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
610 # and transform them to conditionals
593 # and transform them to conditionals
611 requires = pkg_info.get_all('Requires-Dist')
594 requires = pkg_info.get_all('Requires-Dist')
612 del pkg_info['Requires-Dist']
595 del pkg_info['Requires-Dist']
613 def _remove_startswith(lis, prefix):
596 def _remove_startswith(lis, prefix):
614 """like list.remove, but with startswith instead of =="""
597 """like list.remove, but with startswith instead of =="""
615 found = False
598 found = False
616 for idx, item in enumerate(lis):
599 for idx, item in enumerate(lis):
617 if item.startswith(prefix):
600 if item.startswith(prefix):
618 found = True
601 found = True
619 break
602 break
620 if found:
603 if found:
621 lis.pop(idx)
604 lis.pop(idx)
622
605
623 for pkg in ("readline", "pyreadline"):
606 for pkg in ("readline", "pyreadline"):
624 _remove_startswith(requires, pkg)
607 _remove_startswith(requires, pkg)
625 requires.append("readline; sys.platform == 'darwin'")
608 requires.append("readline; sys.platform == 'darwin'")
626 requires.append("pyreadline (>=2.0); sys.platform == 'win32'")
609 requires.append("pyreadline (>=2.0); sys.platform == 'win32'")
627 for r in requires:
610 for r in requires:
628 pkg_info['Requires-Dist'] = r
611 pkg_info['Requires-Dist'] = r
629 write_pkg_info(metadata_path, pkg_info)
612 write_pkg_info(metadata_path, pkg_info)
630
613
631 return bdist_wheel_tag
614 return bdist_wheel_tag
632
615
633 #---------------------------------------------------------------------------
616 #---------------------------------------------------------------------------
634 # Notebook related
617 # Notebook related
635 #---------------------------------------------------------------------------
618 #---------------------------------------------------------------------------
636
619
637 class CompileCSS(Command):
620 class CompileCSS(Command):
638 """Recompile Notebook CSS
621 """Recompile Notebook CSS
639
622
640 Regenerate the compiled CSS from LESS sources.
623 Regenerate the compiled CSS from LESS sources.
641
624
642 Requires various dev dependencies, such as fabric and lessc.
625 Requires various dev dependencies, such as fabric and lessc.
643 """
626 """
644 description = "Recompile Notebook CSS"
627 description = "Recompile Notebook CSS"
645 user_options = []
628 user_options = []
646
629
647 def initialize_options(self):
630 def initialize_options(self):
648 pass
631 pass
649
632
650 def finalize_options(self):
633 def finalize_options(self):
651 pass
634 pass
652
635
653 def run(self):
636 def run(self):
654 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
637 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
655
638
656 class JavascriptVersion(Command):
639 class JavascriptVersion(Command):
657 """write the javascript version to notebook javascript"""
640 """write the javascript version to notebook javascript"""
658 description = "Write IPython version to javascript"
641 description = "Write IPython version to javascript"
659 user_options = []
642 user_options = []
660
643
661 def initialize_options(self):
644 def initialize_options(self):
662 pass
645 pass
663
646
664 def finalize_options(self):
647 def finalize_options(self):
665 pass
648 pass
666
649
667 def run(self):
650 def run(self):
668 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
651 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
669 with open(nsfile) as f:
652 with open(nsfile) as f:
670 lines = f.readlines()
653 lines = f.readlines()
671 with open(nsfile, 'w') as f:
654 with open(nsfile, 'w') as f:
672 for line in lines:
655 for line in lines:
673 if line.startswith("IPython.version"):
656 if line.startswith("IPython.version"):
674 line = 'IPython.version = "{0}";\n'.format(version)
657 line = 'IPython.version = "{0}";\n'.format(version)
675 f.write(line)
658 f.write(line)
676
659
General Comments 0
You need to be logged in to leave comments. Login now