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