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