1
"""distutils.command.build_py
3
Implements the Distutils 'build_py' command."""
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
16
class build_py(Command):
18
description = "\"build\" pure Python modules (copy to build directory)"
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]"),
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)"),
30
boolean_options = ['compile', 'force']
31
negative_opt = {'no-compile' : 'compile'}
33
def initialize_options(self):
35
self.py_modules = None
37
self.package_data = None
38
self.package_dir = None
43
def finalize_options(self):
44
self.set_undefined_options('build',
45
('build_lib', 'build_lib'),
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
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()
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):
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")
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.
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
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.
94
self.build_package_data()
96
self.byte_compile(self.get_outputs(include_bytecode=0))
98
def get_data_files(self):
99
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
101
if not self.packages:
103
for package in self.packages:
104
# Locate package source directory
105
src_dir = self.get_package_dir(package)
107
# Compute package build directory
108
build_dir = os.path.join(*([self.build_lib] + package.split('.')))
110
# Length of path to strip from found files
113
plen = len(src_dir)+1
115
# Strip directory from globbed filenames
117
file[plen:] for file in self.find_data_files(package, src_dir)
119
data.append((package, src_dir, build_dir, filenames))
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, []))
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
and os.path.isfile(fn)])
135
def build_package_data(self):
136
"""Copy data files into build directory"""
137
for package, src_dir, build_dir, filenames in self.data_files:
138
for filename in filenames:
139
target = os.path.join(build_dir, filename)
140
self.mkpath(os.path.dirname(target))
141
self.copy_file(os.path.join(src_dir, filename), target,
144
def get_package_dir(self, package):
145
"""Return the directory, relative to the top of the source
146
distribution, where package 'package' should be found
147
(at least according to the 'package_dir' option, if any)."""
149
path = package.split('.')
151
if not self.package_dir:
153
return os.path.join(*path)
160
pdir = self.package_dir['.'.join(path)]
162
tail.insert(0, path[-1])
166
return os.path.join(*tail)
168
# Oops, got all the way through 'path' without finding a
169
# match in package_dir. If package_dir defines a directory
170
# for the root (nameless) package, then fallback on it;
171
# otherwise, we might as well have not consulted
172
# package_dir at all, as we just use the directory implied
173
# by 'tail' (which should be the same as the original value
174
# of 'path' at this point).
175
pdir = self.package_dir.get('')
180
return os.path.join(*tail)
184
def check_package(self, package, package_dir):
185
# Empty dir name means current directory, which we can probably
186
# assume exists. Also, os.path.exists and isdir don't know about
187
# my "empty string means current dir" convention, so we have to
189
if package_dir != "":
190
if not os.path.exists(package_dir):
191
raise DistutilsFileError(
192
"package directory '%s' does not exist" % package_dir)
193
if not os.path.isdir(package_dir):
194
raise DistutilsFileError(
195
"supposed package directory '%s' exists, "
196
"but is not a directory" % package_dir)
198
# Require __init__.py for all but the "root package"
200
init_py = os.path.join(package_dir, "__init__.py")
201
if os.path.isfile(init_py):
204
log.warn(("package init file '%s' not found " +
205
"(or not a regular file)"), init_py)
207
# Either not in a package at all (__init__.py not expected), or
208
# __init__.py doesn't exist -- so don't return the filename.
211
def check_module(self, module, module_file):
212
if not os.path.isfile(module_file):
213
log.warn("file %s (for module %s) not found", module_file, module)
218
def find_package_modules(self, package, package_dir):
219
self.check_package(package, package_dir)
220
module_files = glob(os.path.join(package_dir, "*.py"))
222
setup_script = os.path.abspath(self.distribution.script_name)
224
for f in module_files:
225
abs_f = os.path.abspath(f)
226
if abs_f != setup_script:
227
module = os.path.splitext(os.path.basename(f))[0]
228
modules.append((package, module, f))
230
self.debug_print("excluding %s" % setup_script)
233
def find_modules(self):
234
"""Finds individually-specified Python modules, ie. those listed by
235
module name in 'self.py_modules'. Returns a list of tuples (package,
236
module_base, filename): 'package' is a tuple of the path through
237
package-space to the module; 'module_base' is the bare (no
238
packages, no dots) module name, and 'filename' is the path to the
239
".py" file (relative to the distribution root) that implements the
242
# Map package names to tuples of useful info about the package:
243
# (package_dir, checked)
244
# package_dir - the directory where we'll find source files for
246
# checked - true if we have checked that the package directory
247
# is valid (exists, contains __init__.py, ... ?)
250
# List of (package, module, filename) tuples to return
253
# We treat modules-in-packages almost the same as toplevel modules,
254
# just the "package" for a toplevel is empty (either an empty
255
# string or empty list, depending on context). Differences:
256
# - don't check for __init__.py in directory for empty package
257
for module in self.py_modules:
258
path = module.split('.')
259
package = '.'.join(path[0:-1])
260
module_base = path[-1]
263
(package_dir, checked) = packages[package]
265
package_dir = self.get_package_dir(package)
269
init_py = self.check_package(package, package_dir)
270
packages[package] = (package_dir, 1)
272
modules.append((package, "__init__", init_py))
274
# XXX perhaps we should also check for just .pyc files
275
# (so greedy closed-source bastards can distribute Python
277
module_file = os.path.join(package_dir, module_base + ".py")
278
if not self.check_module(module, module_file):
281
modules.append((package, module_base, module_file))
285
def find_all_modules(self):
286
"""Compute the list of all modules that will be built, whether
287
they are specified one-module-at-a-time ('self.py_modules') or
288
by whole packages ('self.packages'). Return a list of tuples
289
(package, module, module_file), just like 'find_modules()' and
290
'find_package_modules()' do."""
293
modules.extend(self.find_modules())
295
for package in self.packages:
296
package_dir = self.get_package_dir(package)
297
m = self.find_package_modules(package, package_dir)
301
def get_source_files(self):
302
return [module[-1] for module in self.find_all_modules()]
304
def get_module_outfile(self, build_dir, package, module):
305
outfile_path = [build_dir] + list(package) + [module + ".py"]
306
return os.path.join(*outfile_path)
308
def get_outputs(self, include_bytecode=1):
309
modules = self.find_all_modules()
311
for (package, module, module_file) in modules:
312
package = package.split('.')
313
filename = self.get_module_outfile(self.build_lib, package, module)
314
outputs.append(filename)
317
outputs.append(filename + "c")
318
if self.optimize > 0:
319
outputs.append(filename + "o")
322
os.path.join(build_dir, filename)
323
for package, src_dir, build_dir, filenames in self.data_files
324
for filename in filenames
329
def build_module(self, module, module_file, package):
330
if isinstance(package, str):
331
package = package.split('.')
332
elif not isinstance(package, (list, tuple)):
334
"'package' must be a string (dot-separated), list, or tuple")
336
# Now put the module source file into the "build" area -- this is
337
# easy, we just copy it somewhere under self.build_lib (the build
338
# directory for Python source).
339
outfile = self.get_module_outfile(self.build_lib, package, module)
340
dir = os.path.dirname(outfile)
342
return self.copy_file(module_file, outfile, preserve_mode=0)
344
def build_modules(self):
345
modules = self.find_modules()
346
for (package, module, module_file) in modules:
348
# Now "build" the module -- ie. copy the source file to
349
# self.build_lib (the build directory for Python source).
350
# (Actually, it gets copied to the directory for this package
351
# under self.build_lib.)
352
self.build_module(module, module_file, package)
354
def build_packages(self):
355
for package in self.packages:
357
# Get list of (package, module, module_file) tuples based on
358
# scanning the package directory. 'package' is only included
359
# in the tuple so that 'find_modules()' and
360
# 'find_package_tuples()' have a consistent interface; it's
361
# ignored here (apart from a sanity check). Also, 'module' is
362
# the *unqualified* module name (ie. no dots, no package -- we
363
# already know its package!), and 'module_file' is the path to
364
# the .py file, relative to the current directory
365
# (ie. including 'package_dir').
366
package_dir = self.get_package_dir(package)
367
modules = self.find_package_modules(package, package_dir)
369
# Now loop over the modules we found, "building" each one (just
370
# copy it to self.build_lib).
371
for (package_, module, module_file) in modules:
372
assert package == package_
373
self.build_module(module, module_file, package)
375
def byte_compile(self, files):
376
if sys.dont_write_bytecode:
377
self.warn('byte-compiling is disabled, skipping.')
380
from distutils.util import byte_compile
381
prefix = self.build_lib
382
if prefix[-1] != os.sep:
383
prefix = prefix + os.sep
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.
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)
1
"""distutils.command.build_py
3
Implements the Distutils 'build_py' command."""
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
16
class build_py(Command):
18
description = "\"build\" pure Python modules (copy to build directory)"
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]"),
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)"),
30
boolean_options = ['compile', 'force']
31
negative_opt = {'no-compile' : 'compile'}
33
def initialize_options(self):
35
self.py_modules = None
37
self.package_data = None
38
self.package_dir = None
43
def finalize_options(self):
44
self.set_undefined_options('build',
45
('build_lib', 'build_lib'),
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
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()
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):
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")
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.
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
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.
94
self.build_package_data()
96
self.byte_compile(self.get_outputs(include_bytecode=0))
98
def get_data_files(self):
99
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
101
if not self.packages:
103
for package in self.packages:
104
# Locate package source directory
105
src_dir = self.get_package_dir(package)
107
# Compute package build directory
108
build_dir = os.path.join(*([self.build_lib] + package.split('.')))
110
# Length of path to strip from found files
113
plen = len(src_dir)+1
115
# Strip directory from globbed filenames
117
file[plen:] for file in self.find_data_files(package, src_dir)
119
data.append((package, src_dir, build_dir, filenames))
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, []))
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
and os.path.isfile(fn)])
135
def build_package_data(self):
136
"""Copy data files into build directory"""
137
for package, src_dir, build_dir, filenames in self.data_files:
138
for filename in filenames:
139
target = os.path.join(build_dir, filename)
140
self.mkpath(os.path.dirname(target))
141
self.copy_file(os.path.join(src_dir, filename), target,
144
def get_package_dir(self, package):
145
"""Return the directory, relative to the top of the source
146
distribution, where package 'package' should be found
147
(at least according to the 'package_dir' option, if any)."""
149
path = package.split('.')
151
if not self.package_dir:
153
return os.path.join(*path)
160
pdir = self.package_dir['.'.join(path)]
162
tail.insert(0, path[-1])
166
return os.path.join(*tail)
168
# Oops, got all the way through 'path' without finding a
169
# match in package_dir. If package_dir defines a directory
170
# for the root (nameless) package, then fallback on it;
171
# otherwise, we might as well have not consulted
172
# package_dir at all, as we just use the directory implied
173
# by 'tail' (which should be the same as the original value
174
# of 'path' at this point).
175
pdir = self.package_dir.get('')
180
return os.path.join(*tail)
184
def check_package(self, package, package_dir):
185
# Empty dir name means current directory, which we can probably
186
# assume exists. Also, os.path.exists and isdir don't know about
187
# my "empty string means current dir" convention, so we have to
189
if package_dir != "":
190
if not os.path.exists(package_dir):
191
raise DistutilsFileError(
192
"package directory '%s' does not exist" % package_dir)
193
if not os.path.isdir(package_dir):
194
raise DistutilsFileError(
195
"supposed package directory '%s' exists, "
196
"but is not a directory" % package_dir)
198
# Require __init__.py for all but the "root package"
200
init_py = os.path.join(package_dir, "__init__.py")
201
if os.path.isfile(init_py):
204
log.warn(("package init file '%s' not found " +
205
"(or not a regular file)"), init_py)
207
# Either not in a package at all (__init__.py not expected), or
208
# __init__.py doesn't exist -- so don't return the filename.
211
def check_module(self, module, module_file):
212
if not os.path.isfile(module_file):
213
log.warn("file %s (for module %s) not found", module_file, module)
218
def find_package_modules(self, package, package_dir):
219
self.check_package(package, package_dir)
220
module_files = glob(os.path.join(package_dir, "*.py"))
222
setup_script = os.path.abspath(self.distribution.script_name)
224
for f in module_files:
225
abs_f = os.path.abspath(f)
226
if abs_f != setup_script:
227
module = os.path.splitext(os.path.basename(f))[0]
228
modules.append((package, module, f))
230
self.debug_print("excluding %s" % setup_script)
233
def find_modules(self):
234
"""Finds individually-specified Python modules, ie. those listed by
235
module name in 'self.py_modules'. Returns a list of tuples (package,
236
module_base, filename): 'package' is a tuple of the path through
237
package-space to the module; 'module_base' is the bare (no
238
packages, no dots) module name, and 'filename' is the path to the
239
".py" file (relative to the distribution root) that implements the
242
# Map package names to tuples of useful info about the package:
243
# (package_dir, checked)
244
# package_dir - the directory where we'll find source files for
246
# checked - true if we have checked that the package directory
247
# is valid (exists, contains __init__.py, ... ?)
250
# List of (package, module, filename) tuples to return
253
# We treat modules-in-packages almost the same as toplevel modules,
254
# just the "package" for a toplevel is empty (either an empty
255
# string or empty list, depending on context). Differences:
256
# - don't check for __init__.py in directory for empty package
257
for module in self.py_modules:
258
path = module.split('.')
259
package = '.'.join(path[0:-1])
260
module_base = path[-1]
263
(package_dir, checked) = packages[package]
265
package_dir = self.get_package_dir(package)
269
init_py = self.check_package(package, package_dir)
270
packages[package] = (package_dir, 1)
272
modules.append((package, "__init__", init_py))
274
# XXX perhaps we should also check for just .pyc files
275
# (so greedy closed-source bastards can distribute Python
277
module_file = os.path.join(package_dir, module_base + ".py")
278
if not self.check_module(module, module_file):
281
modules.append((package, module_base, module_file))
285
def find_all_modules(self):
286
"""Compute the list of all modules that will be built, whether
287
they are specified one-module-at-a-time ('self.py_modules') or
288
by whole packages ('self.packages'). Return a list of tuples
289
(package, module, module_file), just like 'find_modules()' and
290
'find_package_modules()' do."""
293
modules.extend(self.find_modules())
295
for package in self.packages:
296
package_dir = self.get_package_dir(package)
297
m = self.find_package_modules(package, package_dir)
301
def get_source_files(self):
302
return [module[-1] for module in self.find_all_modules()]
304
def get_module_outfile(self, build_dir, package, module):
305
outfile_path = [build_dir] + list(package) + [module + ".py"]
306
return os.path.join(*outfile_path)
308
def get_outputs(self, include_bytecode=1):
309
modules = self.find_all_modules()
311
for (package, module, module_file) in modules:
312
package = package.split('.')
313
filename = self.get_module_outfile(self.build_lib, package, module)
314
outputs.append(filename)
317
outputs.append(filename + "c")
318
if self.optimize > 0:
319
outputs.append(filename + "o")
322
os.path.join(build_dir, filename)
323
for package, src_dir, build_dir, filenames in self.data_files
324
for filename in filenames
329
def build_module(self, module, module_file, package):
330
if isinstance(package, str):
331
package = package.split('.')
332
elif not isinstance(package, (list, tuple)):
334
"'package' must be a string (dot-separated), list, or tuple")
336
# Now put the module source file into the "build" area -- this is
337
# easy, we just copy it somewhere under self.build_lib (the build
338
# directory for Python source).
339
outfile = self.get_module_outfile(self.build_lib, package, module)
340
dir = os.path.dirname(outfile)
342
return self.copy_file(module_file, outfile, preserve_mode=0)
344
def build_modules(self):
345
modules = self.find_modules()
346
for (package, module, module_file) in modules:
348
# Now "build" the module -- ie. copy the source file to
349
# self.build_lib (the build directory for Python source).
350
# (Actually, it gets copied to the directory for this package
351
# under self.build_lib.)
352
self.build_module(module, module_file, package)
354
def build_packages(self):
355
for package in self.packages:
357
# Get list of (package, module, module_file) tuples based on
358
# scanning the package directory. 'package' is only included
359
# in the tuple so that 'find_modules()' and
360
# 'find_package_tuples()' have a consistent interface; it's
361
# ignored here (apart from a sanity check). Also, 'module' is
362
# the *unqualified* module name (ie. no dots, no package -- we
363
# already know its package!), and 'module_file' is the path to
364
# the .py file, relative to the current directory
365
# (ie. including 'package_dir').
366
package_dir = self.get_package_dir(package)
367
modules = self.find_package_modules(package, package_dir)
369
# Now loop over the modules we found, "building" each one (just
370
# copy it to self.build_lib).
371
for (package_, module, module_file) in modules:
372
assert package == package_
373
self.build_module(module, module_file, package)
375
def byte_compile(self, files):
376
if sys.dont_write_bytecode:
377
self.warn('byte-compiling is disabled, skipping.')
380
from distutils.util import byte_compile
381
prefix = self.build_lib
382
if prefix[-1] != os.sep:
383
prefix = prefix + os.sep
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.
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)