##// END OF EJS Templates
Fix bug in our specification of data_files....
Fernando Perez -
Show More
@@ -1,374 +1,375 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 from ConfigParser import ConfigParser
27 27 from distutils.command.build_py import build_py
28 28 from glob import glob
29 29
30 30 from setupext import install_data_ext
31 31
32 32 #-------------------------------------------------------------------------------
33 33 # Useful globals and utility functions
34 34 #-------------------------------------------------------------------------------
35 35
36 36 # A few handy globals
37 37 isfile = os.path.isfile
38 38 pjoin = os.path.join
39 39
40 40 def oscmd(s):
41 41 print(">", s)
42 42 os.system(s)
43 43
44 44 # A little utility we'll need below, since glob() does NOT allow you to do
45 45 # exclusion on multiple endings!
46 46 def file_doesnt_endwith(test,endings):
47 47 """Return true if test is a file and its name does NOT end with any
48 48 of the strings listed in endings."""
49 49 if not isfile(test):
50 50 return False
51 51 for e in endings:
52 52 if test.endswith(e):
53 53 return False
54 54 return True
55 55
56 56 #---------------------------------------------------------------------------
57 57 # Basic project information
58 58 #---------------------------------------------------------------------------
59 59
60 60 # release.py contains version, authors, license, url, keywords, etc.
61 61 execfile(pjoin('IPython','core','release.py'))
62 62
63 63 # Create a dict with the basic information
64 64 # This dict is eventually passed to setup after additional keys are added.
65 65 setup_args = dict(
66 66 name = name,
67 67 version = version,
68 68 description = description,
69 69 long_description = long_description,
70 70 author = author,
71 71 author_email = author_email,
72 72 url = url,
73 73 download_url = download_url,
74 74 license = license,
75 75 platforms = platforms,
76 76 keywords = keywords,
77 77 cmdclass = {'install_data': install_data_ext},
78 78 )
79 79
80 80
81 81 #---------------------------------------------------------------------------
82 82 # Find packages
83 83 #---------------------------------------------------------------------------
84 84
85 85 def add_package(packages,pname,config=False,tests=False,scripts=False,
86 86 others=None):
87 87 """
88 88 Add a package to the list of packages, including certain subpackages.
89 89 """
90 90 packages.append('.'.join(['IPython',pname]))
91 91 if config:
92 92 packages.append('.'.join(['IPython',pname,'config']))
93 93 if tests:
94 94 packages.append('.'.join(['IPython',pname,'tests']))
95 95 if scripts:
96 96 packages.append('.'.join(['IPython',pname,'scripts']))
97 97 if others is not None:
98 98 for o in others:
99 99 packages.append('.'.join(['IPython',pname,o]))
100 100
101 101 def find_packages():
102 102 """
103 103 Find all of IPython's packages.
104 104 """
105 105 packages = ['IPython']
106 106 add_package(packages, 'config', tests=True, others=['default','profile'])
107 107 add_package(packages, 'core', tests=True)
108 108 add_package(packages, 'deathrow', tests=True)
109 109 add_package(packages, 'extensions')
110 110 add_package(packages, 'external')
111 111 add_package(packages, 'frontend')
112 112 add_package(packages, 'frontend.qt')
113 113 add_package(packages, 'frontend.qt.console', tests=True)
114 114 add_package(packages, 'frontend.terminal', tests=True)
115 115 add_package(packages, 'kernel', config=False, tests=True, scripts=True)
116 116 add_package(packages, 'kernel.core', config=False, tests=True)
117 117 add_package(packages, 'lib', tests=True)
118 118 add_package(packages, 'quarantine', tests=True)
119 119 add_package(packages, 'scripts')
120 120 add_package(packages, 'testing', tests=True)
121 121 add_package(packages, 'testing.plugin', tests=False)
122 122 add_package(packages, 'utils', tests=True)
123 123 add_package(packages, 'zmq')
124 124 add_package(packages, 'zmq.pylab')
125 125 return packages
126 126
127 127 #---------------------------------------------------------------------------
128 128 # Find package data
129 129 #---------------------------------------------------------------------------
130 130
131 131 def find_package_data():
132 132 """
133 133 Find IPython's package_data.
134 134 """
135 135 # This is not enough for these things to appear in an sdist.
136 136 # We need to muck with the MANIFEST to get this to work
137 137 package_data = {
138 138 'IPython.config.userconfig' : ['*'],
139 139 'IPython.testing' : ['*.txt']
140 140 }
141 141 return package_data
142 142
143 143
144 144 #---------------------------------------------------------------------------
145 145 # Find data files
146 146 #---------------------------------------------------------------------------
147 147
148 148 def make_dir_struct(tag,base,out_base):
149 149 """Make the directory structure of all files below a starting dir.
150 150
151 151 This is just a convenience routine to help build a nested directory
152 152 hierarchy because distutils is too stupid to do this by itself.
153 153
154 154 XXX - this needs a proper docstring!
155 155 """
156 156
157 157 # we'll use these a lot below
158 158 lbase = len(base)
159 159 pathsep = os.path.sep
160 160 lpathsep = len(pathsep)
161 161
162 162 out = []
163 163 for (dirpath,dirnames,filenames) in os.walk(base):
164 164 # we need to strip out the dirpath from the base to map it to the
165 165 # output (installation) path. This requires possibly stripping the
166 166 # path separator, because otherwise pjoin will not work correctly
167 167 # (pjoin('foo/','/bar') returns '/bar').
168 168
169 169 dp_eff = dirpath[lbase:]
170 170 if dp_eff.startswith(pathsep):
171 171 dp_eff = dp_eff[lpathsep:]
172 172 # The output path must be anchored at the out_base marker
173 173 out_path = pjoin(out_base,dp_eff)
174 174 # Now we can generate the final filenames. Since os.walk only produces
175 175 # filenames, we must join back with the dirpath to get full valid file
176 176 # paths:
177 177 pfiles = [pjoin(dirpath,f) for f in filenames]
178 # Finally, generate the entry we need, which is a triple of (tag,output
178 # Finally, generate the entry we need, which is a pari of (output
179 179 # path, files) for use as a data_files parameter in install_data.
180 out.append((tag,out_path,pfiles))
180 out.append((out_path, pfiles))
181 181
182 182 return out
183 183
184 184
185 185 def find_data_files():
186 186 """
187 187 Find IPython's data_files.
188 188
189 189 Most of these are docs.
190 190 """
191 191
192 192 docdirbase = pjoin('share', 'doc', 'ipython')
193 193 manpagebase = pjoin('share', 'man', 'man1')
194 194
195 195 # Simple file lists can be made by hand
196 196 manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz')))
197 igridhelpfiles = filter(isfile, glob(pjoin('IPython','extensions','igrid_help.*')))
197 igridhelpfiles = filter(isfile,
198 glob(pjoin('IPython','extensions','igrid_help.*')))
198 199
199 200 # For nested structures, use the utility above
200 201 example_files = make_dir_struct(
201 202 'data',
202 203 pjoin('docs','examples'),
203 204 pjoin(docdirbase,'examples')
204 205 )
205 206 manual_files = make_dir_struct(
206 207 'data',
207 208 pjoin('docs','dist'),
208 209 pjoin(docdirbase,'manual')
209 210 )
210 211
211 212 # And assemble the entire output list
212 data_files = [ ('data',manpagebase, manpages),
213 ('data',pjoin(docdirbase,'extensions'),igridhelpfiles),
213 data_files = [ (manpagebase, manpages),
214 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
214 215 ] + manual_files + example_files
215 216
216 217 ## import pprint # dbg
217 218 ## print('*'*80)
218 219 ## print('data files')
219 220 ## pprint.pprint(data_files)
220 221 ## print('*'*80)
221 222
222 223 return data_files
223 224
224 225
225 226 def make_man_update_target(manpage):
226 227 """Return a target_update-compliant tuple for the given manpage.
227 228
228 229 Parameters
229 230 ----------
230 231 manpage : string
231 232 Name of the manpage, must include the section number (trailing number).
232 233
233 234 Example
234 235 -------
235 236
236 237 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
237 238 ('docs/man/ipython.1.gz',
238 239 ['docs/man/ipython.1'],
239 240 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
240 241 """
241 242 man_dir = pjoin('docs', 'man')
242 243 manpage_gz = manpage + '.gz'
243 244 manpath = pjoin(man_dir, manpage)
244 245 manpath_gz = pjoin(man_dir, manpage_gz)
245 246 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
246 247 locals() )
247 248 return (manpath_gz, [manpath], gz_cmd)
248 249
249 250 #---------------------------------------------------------------------------
250 251 # Find scripts
251 252 #---------------------------------------------------------------------------
252 253
253 254 def find_scripts():
254 255 """
255 256 Find IPython's scripts.
256 257 """
257 258 kernel_scripts = pjoin('IPython','kernel','scripts')
258 259 main_scripts = pjoin('IPython','scripts')
259 260 scripts = [pjoin(kernel_scripts, 'ipengine'),
260 261 pjoin(kernel_scripts, 'ipcontroller'),
261 262 pjoin(kernel_scripts, 'ipcluster'),
262 263 pjoin(main_scripts, 'ipython'),
263 264 pjoin(main_scripts, 'ipython-qtconsole'),
264 265 pjoin(main_scripts, 'pycolor'),
265 266 pjoin(main_scripts, 'irunner'),
266 267 pjoin(main_scripts, 'iptest')
267 268 ]
268 269
269 270 # Script to be run by the windows binary installer after the default setup
270 271 # routine, to add shortcuts and similar windows-only things. Windows
271 272 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
272 273 # doesn't find them.
273 274 if 'bdist_wininst' in sys.argv:
274 275 if len(sys.argv) > 2 and \
275 276 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
276 277 print("ERROR: bdist_wininst must be run alone. Exiting.",
277 278 file=sys.stderr)
278 279 sys.exit(1)
279 280 scripts.append(pjoin('scripts','ipython_win_post_install.py'))
280 281
281 282 return scripts
282 283
283 284 #---------------------------------------------------------------------------
284 285 # Verify all dependencies
285 286 #---------------------------------------------------------------------------
286 287
287 288 def check_for_dependencies():
288 289 """Check for IPython's dependencies.
289 290
290 291 This function should NOT be called if running under setuptools!
291 292 """
292 293 from setupext.setupext import (
293 294 print_line, print_raw, print_status,
294 295 check_for_zopeinterface, check_for_twisted,
295 296 check_for_foolscap, check_for_pyopenssl,
296 297 check_for_sphinx, check_for_pygments,
297 298 check_for_nose, check_for_pexpect
298 299 )
299 300 print_line()
300 301 print_raw("BUILDING IPYTHON")
301 302 print_status('python', sys.version)
302 303 print_status('platform', sys.platform)
303 304 if sys.platform == 'win32':
304 305 print_status('Windows version', sys.getwindowsversion())
305 306
306 307 print_raw("")
307 308 print_raw("OPTIONAL DEPENDENCIES")
308 309
309 310 check_for_zopeinterface()
310 311 check_for_twisted()
311 312 check_for_foolscap()
312 313 check_for_pyopenssl()
313 314 check_for_sphinx()
314 315 check_for_pygments()
315 316 check_for_nose()
316 317 check_for_pexpect()
317 318
318 319
319 320 def record_commit_info(pkg_dir, build_cmd=build_py):
320 321 """ Return extended build command class for recording commit
321 322
322 323 The extended command tries to run git to find the current commit, getting
323 324 the empty string if it fails. It then writes the commit hash into a file
324 325 in the `pkg_dir` path, named ``.git_commit_info.ini``.
325 326
326 327 In due course this information can be used by the package after it is
327 328 installed, to tell you what commit it was installed from if known.
328 329
329 330 To make use of this system, you need a package with a .git_commit_info.ini
330 331 file - e.g. ``myproject/.git_commit_info.ini`` - that might well look like
331 332 this::
332 333
333 334 # This is an ini file that may contain information about the code state
334 335 [commit hash]
335 336 # The line below may contain a valid hash if it has been substituted
336 337 # during 'git archive'
337 338 archive_subst_hash=$Format:%h$
338 339 # This line may be modified by the install process
339 340 install_hash=
340 341
341 342 The .git_commit_info file above is also designed to be used with git
342 343 substitution - so you probably also want a ``.gitattributes`` file in the
343 344 root directory of your working tree that contains something like this::
344 345
345 346 myproject/.git_commit_info.ini export-subst
346 347
347 348 That will cause the ``.git_commit_info.ini`` file to get filled in by ``git
348 349 archive`` - useful in case someone makes such an archive - for example with
349 350 via the github 'download source' button.
350 351
351 352 Although all the above will work as is, you might consider having something
352 353 like a ``get_info()`` function in your package to display the commit
353 354 information at the terminal. See the ``pkg_info.py`` module in the nipy
354 355 package for an example.
355 356 """
356 357 class MyBuildPy(build_cmd):
357 358 ''' Subclass to write commit data into installation tree '''
358 359 def run(self):
359 360 build_py.run(self)
360 361 import subprocess
361 362 proc = subprocess.Popen('git rev-parse --short HEAD',
362 363 stdout=subprocess.PIPE,
363 364 stderr=subprocess.PIPE,
364 365 shell=True)
365 366 repo_commit, _ = proc.communicate()
366 367 # We write the installation commit even if it's empty
367 368 cfg_parser = ConfigParser()
368 369 cfg_parser.read(pjoin(pkg_dir, '.git_commit_info.ini'))
369 370 cfg_parser.set('commit hash', 'install_hash', repo_commit)
370 371 out_pth = pjoin(self.build_lib, pkg_dir, '.git_commit_info.ini')
371 372 out_file = open(out_pth, 'wt')
372 373 cfg_parser.write(out_file)
373 374 out_file.close()
374 375 return MyBuildPy
General Comments 0
You need to be logged in to leave comments. Login now