##// END OF EJS Templates
Fix a bug that caused man pages to not be installed in Python 3 when running from the source tree.
Thomas Robitaille -
Show More
@@ -1,418 +1,418 b''
1 1 # encoding: utf-8
2 2 """
3 3 This module defines the things that are used in setup.py for building IPython
4 4
5 5 This includes:
6 6
7 7 * The basic arguments to setup
8 8 * Functions for finding things like packages, package data, etc.
9 9 * A function for checking dependencies.
10 10 """
11 11 from __future__ import print_function
12 12
13 13 #-------------------------------------------------------------------------------
14 14 # Copyright (C) 2008 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
18 18 #-------------------------------------------------------------------------------
19 19
20 20 #-------------------------------------------------------------------------------
21 21 # Imports
22 22 #-------------------------------------------------------------------------------
23 23 import os
24 24 import sys
25 25
26 26 try:
27 27 from configparser import ConfigParser
28 28 except:
29 29 from ConfigParser import ConfigParser
30 30 from distutils.command.build_py import build_py
31 31 from glob import glob
32 32
33 33 from setupext import install_data_ext
34 34
35 35 #-------------------------------------------------------------------------------
36 36 # Useful globals and utility functions
37 37 #-------------------------------------------------------------------------------
38 38
39 39 # A few handy globals
40 40 isfile = os.path.isfile
41 41 pjoin = os.path.join
42 42
43 43 def oscmd(s):
44 44 print(">", s)
45 45 os.system(s)
46 46
47 47 # Py3 compatibility hacks, without assuming IPython itself is installed with
48 48 # the full py3compat machinery.
49 49
50 50 try:
51 51 execfile
52 52 except NameError:
53 53 def execfile(fname, globs, locs=None):
54 54 locs = locs or globs
55 55 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
56 56
57 57 # A little utility we'll need below, since glob() does NOT allow you to do
58 58 # exclusion on multiple endings!
59 59 def file_doesnt_endwith(test,endings):
60 60 """Return true if test is a file and its name does NOT end with any
61 61 of the strings listed in endings."""
62 62 if not isfile(test):
63 63 return False
64 64 for e in endings:
65 65 if test.endswith(e):
66 66 return False
67 67 return True
68 68
69 69 #---------------------------------------------------------------------------
70 70 # Basic project information
71 71 #---------------------------------------------------------------------------
72 72
73 73 # release.py contains version, authors, license, url, keywords, etc.
74 74 execfile(pjoin('IPython','core','release.py'), globals())
75 75
76 76 # Create a dict with the basic information
77 77 # This dict is eventually passed to setup after additional keys are added.
78 78 setup_args = dict(
79 79 name = name,
80 80 version = version,
81 81 description = description,
82 82 long_description = long_description,
83 83 author = author,
84 84 author_email = author_email,
85 85 url = url,
86 86 download_url = download_url,
87 87 license = license,
88 88 platforms = platforms,
89 89 keywords = keywords,
90 90 classifiers = classifiers,
91 91 cmdclass = {'install_data': install_data_ext},
92 92 )
93 93
94 94
95 95 #---------------------------------------------------------------------------
96 96 # Find packages
97 97 #---------------------------------------------------------------------------
98 98
99 99 def find_packages():
100 100 """
101 101 Find all of IPython's packages.
102 102 """
103 103 excludes = ['deathrow', 'quarantine']
104 104 packages = []
105 105 for dir,subdirs,files in os.walk('IPython'):
106 106 package = dir.replace(os.path.sep, '.')
107 107 if any(package.startswith('IPython.'+exc) for exc in excludes):
108 108 # package is to be excluded (e.g. deathrow)
109 109 continue
110 110 if '__init__.py' not in files:
111 111 # not a package
112 112 continue
113 113 packages.append(package)
114 114 return packages
115 115
116 116 #---------------------------------------------------------------------------
117 117 # Find package data
118 118 #---------------------------------------------------------------------------
119 119
120 120 def find_package_data():
121 121 """
122 122 Find IPython's package_data.
123 123 """
124 124 # This is not enough for these things to appear in an sdist.
125 125 # We need to muck with the MANIFEST to get this to work
126 126
127 127 # exclude static things that we don't ship (e.g. mathjax)
128 128 excludes = ['mathjax']
129 129
130 130 # add 'static/' prefix to exclusions, and tuplify for use in startswith
131 131 excludes = tuple([os.path.join('static', ex) for ex in excludes])
132 132
133 133 # walk notebook resources:
134 134 cwd = os.getcwd()
135 135 os.chdir(os.path.join('IPython', 'frontend', 'html', 'notebook'))
136 136 static_walk = list(os.walk('static'))
137 137 os.chdir(cwd)
138 138 static_data = []
139 139 for parent, dirs, files in static_walk:
140 140 if parent.startswith(excludes):
141 141 continue
142 142 for f in files:
143 143 static_data.append(os.path.join(parent, f))
144 144
145 145 package_data = {
146 146 'IPython.config.profile' : ['README*', '*/*.py'],
147 147 'IPython.testing' : ['*.txt'],
148 148 'IPython.testing.plugin' : ['*.txt'],
149 149 'IPython.frontend.html.notebook' : ['templates/*'] + static_data,
150 150 'IPython.frontend.qt.console' : ['resources/icon/*.svg'],
151 151 }
152 152 return package_data
153 153
154 154
155 155 #---------------------------------------------------------------------------
156 156 # Find data files
157 157 #---------------------------------------------------------------------------
158 158
159 159 def make_dir_struct(tag,base,out_base):
160 160 """Make the directory structure of all files below a starting dir.
161 161
162 162 This is just a convenience routine to help build a nested directory
163 163 hierarchy because distutils is too stupid to do this by itself.
164 164
165 165 XXX - this needs a proper docstring!
166 166 """
167 167
168 168 # we'll use these a lot below
169 169 lbase = len(base)
170 170 pathsep = os.path.sep
171 171 lpathsep = len(pathsep)
172 172
173 173 out = []
174 174 for (dirpath,dirnames,filenames) in os.walk(base):
175 175 # we need to strip out the dirpath from the base to map it to the
176 176 # output (installation) path. This requires possibly stripping the
177 177 # path separator, because otherwise pjoin will not work correctly
178 178 # (pjoin('foo/','/bar') returns '/bar').
179 179
180 180 dp_eff = dirpath[lbase:]
181 181 if dp_eff.startswith(pathsep):
182 182 dp_eff = dp_eff[lpathsep:]
183 183 # The output path must be anchored at the out_base marker
184 184 out_path = pjoin(out_base,dp_eff)
185 185 # Now we can generate the final filenames. Since os.walk only produces
186 186 # filenames, we must join back with the dirpath to get full valid file
187 187 # paths:
188 188 pfiles = [pjoin(dirpath,f) for f in filenames]
189 189 # Finally, generate the entry we need, which is a pari of (output
190 190 # path, files) for use as a data_files parameter in install_data.
191 191 out.append((out_path, pfiles))
192 192
193 193 return out
194 194
195 195
196 196 def find_data_files():
197 197 """
198 198 Find IPython's data_files.
199 199
200 200 Most of these are docs.
201 201 """
202 202
203 203 docdirbase = pjoin('share', 'doc', 'ipython')
204 204 manpagebase = pjoin('share', 'man', 'man1')
205 205
206 206 # Simple file lists can be made by hand
207 manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz')))
207 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
208 208 if not manpages:
209 209 # When running from a source tree, the manpages aren't gzipped
210 manpages = filter(isfile, glob(pjoin('docs','man','*.1')))
211 igridhelpfiles = filter(isfile,
212 glob(pjoin('IPython','extensions','igrid_help.*')))
210 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
211
212 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
213 213
214 214 # For nested structures, use the utility above
215 215 example_files = make_dir_struct(
216 216 'data',
217 217 pjoin('docs','examples'),
218 218 pjoin(docdirbase,'examples')
219 219 )
220 220 manual_files = make_dir_struct(
221 221 'data',
222 222 pjoin('docs','html'),
223 223 pjoin(docdirbase,'manual')
224 224 )
225 225
226 226 # And assemble the entire output list
227 227 data_files = [ (manpagebase, manpages),
228 228 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
229 229 ] + manual_files + example_files
230 230
231 231 return data_files
232 232
233 233
234 234 def make_man_update_target(manpage):
235 235 """Return a target_update-compliant tuple for the given manpage.
236 236
237 237 Parameters
238 238 ----------
239 239 manpage : string
240 240 Name of the manpage, must include the section number (trailing number).
241 241
242 242 Example
243 243 -------
244 244
245 245 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
246 246 ('docs/man/ipython.1.gz',
247 247 ['docs/man/ipython.1'],
248 248 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
249 249 """
250 250 man_dir = pjoin('docs', 'man')
251 251 manpage_gz = manpage + '.gz'
252 252 manpath = pjoin(man_dir, manpage)
253 253 manpath_gz = pjoin(man_dir, manpage_gz)
254 254 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
255 255 locals() )
256 256 return (manpath_gz, [manpath], gz_cmd)
257 257
258 258 # The two functions below are copied from IPython.utils.path, so we don't need
259 259 # to import IPython during setup, which fails on Python 3.
260 260
261 261 def target_outdated(target,deps):
262 262 """Determine whether a target is out of date.
263 263
264 264 target_outdated(target,deps) -> 1/0
265 265
266 266 deps: list of filenames which MUST exist.
267 267 target: single filename which may or may not exist.
268 268
269 269 If target doesn't exist or is older than any file listed in deps, return
270 270 true, otherwise return false.
271 271 """
272 272 try:
273 273 target_time = os.path.getmtime(target)
274 274 except os.error:
275 275 return 1
276 276 for dep in deps:
277 277 dep_time = os.path.getmtime(dep)
278 278 if dep_time > target_time:
279 279 #print "For target",target,"Dep failed:",dep # dbg
280 280 #print "times (dep,tar):",dep_time,target_time # dbg
281 281 return 1
282 282 return 0
283 283
284 284
285 285 def target_update(target,deps,cmd):
286 286 """Update a target with a given command given a list of dependencies.
287 287
288 288 target_update(target,deps,cmd) -> runs cmd if target is outdated.
289 289
290 290 This is just a wrapper around target_outdated() which calls the given
291 291 command if target is outdated."""
292 292
293 293 if target_outdated(target,deps):
294 294 os.system(cmd)
295 295
296 296 #---------------------------------------------------------------------------
297 297 # Find scripts
298 298 #---------------------------------------------------------------------------
299 299
300 300 def find_scripts(entry_points=False, suffix=''):
301 301 """Find IPython's scripts.
302 302
303 303 if entry_points is True:
304 304 return setuptools entry_point-style definitions
305 305 else:
306 306 return file paths of plain scripts [default]
307 307
308 308 suffix is appended to script names if entry_points is True, so that the
309 309 Python 3 scripts get named "ipython3" etc.
310 310 """
311 311 if entry_points:
312 312 console_scripts = [s % suffix for s in [
313 313 'ipython%s = IPython.frontend.terminal.ipapp:launch_new_instance',
314 314 'pycolor%s = IPython.utils.PyColorize:main',
315 315 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
316 316 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
317 317 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
318 318 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
319 319 'iptest%s = IPython.testing.iptest:main',
320 320 'irunner%s = IPython.lib.irunner:main'
321 321 ]]
322 322 gui_scripts = []
323 323 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
324 324 else:
325 325 parallel_scripts = pjoin('IPython','parallel','scripts')
326 326 main_scripts = pjoin('IPython','scripts')
327 327 scripts = [
328 328 pjoin(parallel_scripts, 'ipengine'),
329 329 pjoin(parallel_scripts, 'ipcontroller'),
330 330 pjoin(parallel_scripts, 'ipcluster'),
331 331 pjoin(parallel_scripts, 'iplogger'),
332 332 pjoin(main_scripts, 'ipython'),
333 333 pjoin(main_scripts, 'pycolor'),
334 334 pjoin(main_scripts, 'irunner'),
335 335 pjoin(main_scripts, 'iptest')
336 336 ]
337 337 return scripts
338 338
339 339 #---------------------------------------------------------------------------
340 340 # Verify all dependencies
341 341 #---------------------------------------------------------------------------
342 342
343 343 def check_for_dependencies():
344 344 """Check for IPython's dependencies.
345 345
346 346 This function should NOT be called if running under setuptools!
347 347 """
348 348 from setupext.setupext import (
349 349 print_line, print_raw, print_status,
350 350 check_for_sphinx, check_for_pygments,
351 351 check_for_nose, check_for_pexpect,
352 352 check_for_pyzmq, check_for_readline
353 353 )
354 354 print_line()
355 355 print_raw("BUILDING IPYTHON")
356 356 print_status('python', sys.version)
357 357 print_status('platform', sys.platform)
358 358 if sys.platform == 'win32':
359 359 print_status('Windows version', sys.getwindowsversion())
360 360
361 361 print_raw("")
362 362 print_raw("OPTIONAL DEPENDENCIES")
363 363
364 364 check_for_sphinx()
365 365 check_for_pygments()
366 366 check_for_nose()
367 367 check_for_pexpect()
368 368 check_for_pyzmq()
369 369 check_for_readline()
370 370
371 371 def record_commit_info(pkg_dir, build_cmd=build_py):
372 372 """ Return extended build or sdist command class for recording commit
373 373
374 374 records git commit in IPython.utils._sysinfo.commit
375 375
376 376 for use in IPython.utils.sysinfo.sys_info() calls after installation.
377 377 """
378 378
379 379 class MyBuildPy(build_cmd):
380 380 ''' Subclass to write commit data into installation tree '''
381 381 def run(self):
382 382 build_cmd.run(self)
383 383 # this one will only fire for build commands
384 384 if hasattr(self, 'build_lib'):
385 385 self._record_commit(self.build_lib)
386 386
387 387 def make_release_tree(self, base_dir, files):
388 388 # this one will fire for sdist
389 389 build_cmd.make_release_tree(self, base_dir, files)
390 390 self._record_commit(base_dir)
391 391
392 392 def _record_commit(self, base_dir):
393 393 import subprocess
394 394 proc = subprocess.Popen('git rev-parse --short HEAD',
395 395 stdout=subprocess.PIPE,
396 396 stderr=subprocess.PIPE,
397 397 shell=True)
398 398 repo_commit, _ = proc.communicate()
399 399 repo_commit = repo_commit.strip().decode("ascii")
400 400
401 401 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
402 402 if os.path.isfile(out_pth) and not repo_commit:
403 403 # nothing to write, don't clobber
404 404 return
405 405
406 406 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
407 407
408 408 # remove to avoid overwriting original via hard link
409 409 try:
410 410 os.remove(out_pth)
411 411 except (IOError, OSError):
412 412 pass
413 413 with open(out_pth, 'w') as out_file:
414 414 out_file.writelines([
415 415 '# GENERATED BY setup.py\n',
416 416 'commit = "%s"\n' % repo_commit,
417 417 ])
418 418 return MyBuildPy
General Comments 0
You need to be logged in to leave comments. Login now