]> git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/command/build_py.py
unicode compilation fix
[wxWidgets.git] / wxPython / distutils / command / build_py.py
1 """distutils.command.build_py
2
3 Implements the Distutils 'build_py' command."""
4
5 # This module should be kept compatible with Python 1.5.2.
6
7 __revision__ = "$Id$"
8
9 import sys, string, os
10 from types import *
11 from glob import glob
12
13 from distutils.core import Command
14 from distutils.errors import *
15 from distutils.util import convert_path
16 from distutils import log
17
18 class build_py (Command):
19
20 description = "\"build\" pure Python modules (copy to build directory)"
21
22 user_options = [
23 ('build-lib=', 'd', "directory to \"build\" (copy) to"),
24 ('compile', 'c', "compile .py to .pyc"),
25 ('no-compile', None, "don't compile .py files [default]"),
26 ('optimize=', 'O',
27 "also compile with optimization: -O1 for \"python -O\", "
28 "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
29 ('force', 'f', "forcibly build everything (ignore file timestamps)"),
30 ]
31
32 boolean_options = ['compile', 'force']
33 negative_opt = {'no-compile' : 'compile'}
34
35
36 def initialize_options (self):
37 self.build_lib = None
38 self.py_modules = None
39 self.package = None
40 self.package_dir = None
41 self.compile = 0
42 self.optimize = 0
43 self.force = None
44
45 def finalize_options (self):
46 self.set_undefined_options('build',
47 ('build_lib', 'build_lib'),
48 ('force', 'force'))
49
50 # Get the distribution options that are aliases for build_py
51 # options -- list of packages and list of modules.
52 self.packages = self.distribution.packages
53 self.py_modules = self.distribution.py_modules
54 self.package_dir = {}
55 if self.distribution.package_dir:
56 for name, path in self.distribution.package_dir.items():
57 self.package_dir[name] = convert_path(path)
58
59 # Ick, copied straight from install_lib.py (fancy_getopt needs a
60 # type system! Hell, *everything* needs a type system!!!)
61 if type(self.optimize) is not IntType:
62 try:
63 self.optimize = int(self.optimize)
64 assert 0 <= self.optimize <= 2
65 except (ValueError, AssertionError):
66 raise DistutilsOptionError, "optimize must be 0, 1, or 2"
67
68 def run (self):
69
70 # XXX copy_file by default preserves atime and mtime. IMHO this is
71 # the right thing to do, but perhaps it should be an option -- in
72 # particular, a site administrator might want installed files to
73 # reflect the time of installation rather than the last
74 # modification time before the installed release.
75
76 # XXX copy_file by default preserves mode, which appears to be the
77 # wrong thing to do: if a file is read-only in the working
78 # directory, we want it to be installed read/write so that the next
79 # installation of the same module distribution can overwrite it
80 # without problems. (This might be a Unix-specific issue.) Thus
81 # we turn off 'preserve_mode' when copying to the build directory,
82 # since the build directory is supposed to be exactly what the
83 # installation will look like (ie. we preserve mode when
84 # installing).
85
86 # Two options control which modules will be installed: 'packages'
87 # and 'py_modules'. The former lets us work with whole packages, not
88 # specifying individual modules at all; the latter is for
89 # specifying modules one-at-a-time. Currently they are mutually
90 # exclusive: you can define one or the other (or neither), but not
91 # both. It remains to be seen how limiting this is.
92
93 # Dispose of the two "unusual" cases first: no pure Python modules
94 # at all (no problem, just return silently), and over-specified
95 # 'packages' and 'py_modules' options.
96
97 if not self.py_modules and not self.packages:
98 return
99 if self.py_modules and self.packages:
100 raise DistutilsOptionError, \
101 "build_py: supplying both 'packages' and 'py_modules' " + \
102 "options is not allowed"
103
104 # Now we're down to two cases: 'py_modules' only and 'packages' only.
105 if self.py_modules:
106 self.build_modules()
107 else:
108 self.build_packages()
109
110 self.byte_compile(self.get_outputs(include_bytecode=0))
111
112 # run ()
113
114
115 def get_package_dir (self, package):
116 """Return the directory, relative to the top of the source
117 distribution, where package 'package' should be found
118 (at least according to the 'package_dir' option, if any)."""
119
120 path = string.split(package, '.')
121
122 if not self.package_dir:
123 if path:
124 return apply(os.path.join, path)
125 else:
126 return ''
127 else:
128 tail = []
129 while path:
130 try:
131 pdir = self.package_dir[string.join(path, '.')]
132 except KeyError:
133 tail.insert(0, path[-1])
134 del path[-1]
135 else:
136 tail.insert(0, pdir)
137 return apply(os.path.join, tail)
138 else:
139 # Oops, got all the way through 'path' without finding a
140 # match in package_dir. If package_dir defines a directory
141 # for the root (nameless) package, then fallback on it;
142 # otherwise, we might as well have not consulted
143 # package_dir at all, as we just use the directory implied
144 # by 'tail' (which should be the same as the original value
145 # of 'path' at this point).
146 pdir = self.package_dir.get('')
147 if pdir is not None:
148 tail.insert(0, pdir)
149
150 if tail:
151 return apply(os.path.join, tail)
152 else:
153 return ''
154
155 # get_package_dir ()
156
157
158 def check_package (self, package, package_dir):
159
160 # Empty dir name means current directory, which we can probably
161 # assume exists. Also, os.path.exists and isdir don't know about
162 # my "empty string means current dir" convention, so we have to
163 # circumvent them.
164 if package_dir != "":
165 if not os.path.exists(package_dir):
166 raise DistutilsFileError, \
167 "package directory '%s' does not exist" % package_dir
168 if not os.path.isdir(package_dir):
169 raise DistutilsFileError, \
170 ("supposed package directory '%s' exists, " +
171 "but is not a directory") % package_dir
172
173 # Require __init__.py for all but the "root package"
174 if package:
175 init_py = os.path.join(package_dir, "__init__.py")
176 if os.path.isfile(init_py):
177 return init_py
178 else:
179 log.warn(("package init file '%s' not found " +
180 "(or not a regular file)"), init_py)
181
182 # Either not in a package at all (__init__.py not expected), or
183 # __init__.py doesn't exist -- so don't return the filename.
184 return None
185
186 # check_package ()
187
188
189 def check_module (self, module, module_file):
190 if not os.path.isfile(module_file):
191 log.warn("file %s (for module %s) not found", module_file, module)
192 return 0
193 else:
194 return 1
195
196 # check_module ()
197
198
199 def find_package_modules (self, package, package_dir):
200 self.check_package(package, package_dir)
201 module_files = glob(os.path.join(package_dir, "*.py"))
202 modules = []
203 setup_script = os.path.abspath(self.distribution.script_name)
204
205 for f in module_files:
206 abs_f = os.path.abspath(f)
207 if abs_f != setup_script:
208 module = os.path.splitext(os.path.basename(f))[0]
209 modules.append((package, module, f))
210 else:
211 self.debug_print("excluding %s" % setup_script)
212 return modules
213
214
215 def find_modules (self):
216 """Finds individually-specified Python modules, ie. those listed by
217 module name in 'self.py_modules'. Returns a list of tuples (package,
218 module_base, filename): 'package' is a tuple of the path through
219 package-space to the module; 'module_base' is the bare (no
220 packages, no dots) module name, and 'filename' is the path to the
221 ".py" file (relative to the distribution root) that implements the
222 module.
223 """
224
225 # Map package names to tuples of useful info about the package:
226 # (package_dir, checked)
227 # package_dir - the directory where we'll find source files for
228 # this package
229 # checked - true if we have checked that the package directory
230 # is valid (exists, contains __init__.py, ... ?)
231 packages = {}
232
233 # List of (package, module, filename) tuples to return
234 modules = []
235
236 # We treat modules-in-packages almost the same as toplevel modules,
237 # just the "package" for a toplevel is empty (either an empty
238 # string or empty list, depending on context). Differences:
239 # - don't check for __init__.py in directory for empty package
240
241 for module in self.py_modules:
242 path = string.split(module, '.')
243 package = string.join(path[0:-1], '.')
244 module_base = path[-1]
245
246 try:
247 (package_dir, checked) = packages[package]
248 except KeyError:
249 package_dir = self.get_package_dir(package)
250 checked = 0
251
252 if not checked:
253 init_py = self.check_package(package, package_dir)
254 packages[package] = (package_dir, 1)
255 if init_py:
256 modules.append((package, "__init__", init_py))
257
258 # XXX perhaps we should also check for just .pyc files
259 # (so greedy closed-source bastards can distribute Python
260 # modules too)
261 module_file = os.path.join(package_dir, module_base + ".py")
262 if not self.check_module(module, module_file):
263 continue
264
265 modules.append((package, module_base, module_file))
266
267 return modules
268
269 # find_modules ()
270
271
272 def find_all_modules (self):
273 """Compute the list of all modules that will be built, whether
274 they are specified one-module-at-a-time ('self.py_modules') or
275 by whole packages ('self.packages'). Return a list of tuples
276 (package, module, module_file), just like 'find_modules()' and
277 'find_package_modules()' do."""
278
279 if self.py_modules:
280 modules = self.find_modules()
281 else:
282 modules = []
283 for package in self.packages:
284 package_dir = self.get_package_dir(package)
285 m = self.find_package_modules(package, package_dir)
286 modules.extend(m)
287
288 return modules
289
290 # find_all_modules ()
291
292
293 def get_source_files (self):
294
295 modules = self.find_all_modules()
296 filenames = []
297 for module in modules:
298 filenames.append(module[-1])
299
300 return filenames
301
302
303 def get_module_outfile (self, build_dir, package, module):
304 outfile_path = [build_dir] + list(package) + [module + ".py"]
305 return apply(os.path.join, outfile_path)
306
307
308 def get_outputs (self, include_bytecode=1):
309 modules = self.find_all_modules()
310 outputs = []
311 for (package, module, module_file) in modules:
312 package = string.split(package, '.')
313 filename = self.get_module_outfile(self.build_lib, package, module)
314 outputs.append(filename)
315 if include_bytecode:
316 if self.compile:
317 outputs.append(filename + "c")
318 if self.optimize > 0:
319 outputs.append(filename + "o")
320
321 return outputs
322
323
324 def build_module (self, module, module_file, package):
325 if type(package) is StringType:
326 package = string.split(package, '.')
327 elif type(package) not in (ListType, TupleType):
328 raise TypeError, \
329 "'package' must be a string (dot-separated), list, or tuple"
330
331 # Now put the module source file into the "build" area -- this is
332 # easy, we just copy it somewhere under self.build_lib (the build
333 # directory for Python source).
334 outfile = self.get_module_outfile(self.build_lib, package, module)
335 dir = os.path.dirname(outfile)
336 self.mkpath(dir)
337 return self.copy_file(module_file, outfile, preserve_mode=0)
338
339
340 def build_modules (self):
341
342 modules = self.find_modules()
343 for (package, module, module_file) in modules:
344
345 # Now "build" the module -- ie. copy the source file to
346 # self.build_lib (the build directory for Python source).
347 # (Actually, it gets copied to the directory for this package
348 # under self.build_lib.)
349 self.build_module(module, module_file, package)
350
351 # build_modules ()
352
353
354 def build_packages (self):
355
356 for package in self.packages:
357
358 # Get list of (package, module, module_file) tuples based on
359 # scanning the package directory. 'package' is only included
360 # in the tuple so that 'find_modules()' and
361 # 'find_package_tuples()' have a consistent interface; it's
362 # ignored here (apart from a sanity check). Also, 'module' is
363 # the *unqualified* module name (ie. no dots, no package -- we
364 # already know its package!), and 'module_file' is the path to
365 # the .py file, relative to the current directory
366 # (ie. including 'package_dir').
367 package_dir = self.get_package_dir(package)
368 modules = self.find_package_modules(package, package_dir)
369
370 # Now loop over the modules we found, "building" each one (just
371 # copy it to self.build_lib).
372 for (package_, module, module_file) in modules:
373 assert package == package_
374 self.build_module(module, module_file, package)
375
376 # build_packages ()
377
378
379 def byte_compile (self, files):
380 from distutils.util import byte_compile
381 prefix = self.build_lib
382 if prefix[-1] != os.sep:
383 prefix = prefix + os.sep
384
385 # XXX this code is essentially the same as the 'byte_compile()
386 # method of the "install_lib" command, except for the determination
387 # of the 'prefix' string. Hmmm.
388
389 if self.compile:
390 byte_compile(files, optimize=0,
391 force=self.force, prefix=prefix, dry_run=self.dry_run)
392 if self.optimize > 0:
393 byte_compile(files, optimize=self.optimize,
394 force=self.force, prefix=prefix, dry_run=self.dry_run)
395
396 # class build_py