1 """distutils.command.install_lib
3 Implements the Distutils 'install_lib' command
4 (install all Python modules)."""
10 from ..core import Command
11 from ..errors import DistutilsOptionError
14 # Extension for Python source files.
15 PYTHON_SOURCE_EXTENSION = ".py"
18 class install_lib(Command):
20 description = "install all Python modules (extensions and pure Python)"
22 # The byte-compilation options are a tad confusing. Here are the
24 # 1) no compilation at all (--no-compile --no-optimize)
25 # 2) compile .pyc only (--compile --no-optimize; default)
26 # 3) compile .pyc and "opt-1" .pyc (--compile --optimize)
27 # 4) compile "opt-1" .pyc only (--no-compile --optimize)
28 # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more)
29 # 6) compile "opt-2" .pyc only (--no-compile --optimize-more)
31 # The UI for this is two options, 'compile' and 'optimize'.
32 # 'compile' is strictly boolean, and only decides whether to
33 # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and
34 # decides both whether to generate .pyc files and what level of
35 # optimization to use.
38 ('install-dir=', 'd', "directory to install to"),
39 ('build-dir=', 'b', "build directory (where to install from)"),
40 ('force', 'f', "force installation (overwrite existing files)"),
41 ('compile', 'c', "compile .py to .pyc [default]"),
42 ('no-compile', None, "don't compile .py files"),
46 "also compile with optimization: -O1 for \"python -O\", "
47 "-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
49 ('skip-build', None, "skip the build steps"),
52 boolean_options = ['force', 'compile', 'skip-build']
53 negative_opt = {'no-compile': 'compile'}
55 def initialize_options(self):
56 # let the 'install' command dictate our installation directory
57 self.install_dir = None
62 self.skip_build = None
64 def finalize_options(self):
65 # Get all the information we need to install pure Python modules
66 # from the umbrella 'install' command -- build (source) directory,
67 # install (target) directory, and whether to compile .py files.
68 self.set_undefined_options(
70 ('build_lib', 'build_dir'),
71 ('install_lib', 'install_dir'),
73 ('compile', 'compile'),
74 ('optimize', 'optimize'),
75 ('skip_build', 'skip_build'),
78 if self.compile is None:
80 if self.optimize is None:
83 if not isinstance(self.optimize, int):
85 self.optimize = int(self.optimize)
86 if self.optimize not in (0, 1, 2):
88 except (ValueError, AssertionError):
89 raise DistutilsOptionError("optimize must be 0, 1, or 2")
92 # Make sure we have built everything we need first
95 # Install everything: simply dump the entire contents of the build
96 # directory to the installation directory (that's the beauty of
97 # having a build directory!)
98 outfiles = self.install()
100 # (Optionally) compile .py to .pyc
101 if outfiles is not None and self.distribution.has_pure_modules():
102 self.byte_compile(outfiles)
104 # -- Top-level worker functions ------------------------------------
105 # (called from 'run()')
108 if not self.skip_build:
109 if self.distribution.has_pure_modules():
110 self.run_command('build_py')
111 if self.distribution.has_ext_modules():
112 self.run_command('build_ext')
115 if os.path.isdir(self.build_dir):
116 outfiles = self.copy_tree(self.build_dir, self.install_dir)
119 "'%s' does not exist -- no Python modules to install" % self.build_dir
124 def byte_compile(self, files):
125 if sys.dont_write_bytecode:
126 self.warn('byte-compiling is disabled, skipping.')
129 from ..util import byte_compile
131 # Get the "--root" directory supplied to the "install" command,
132 # and use it as a prefix to strip off the purported filename
133 # encoded in bytecode files. This is far from complete, but it
134 # should at least generate usable bytecode in RPM distributions.
135 install_root = self.get_finalized_command('install').root
143 dry_run=self.dry_run,
145 if self.optimize > 0:
148 optimize=self.optimize,
151 verbose=self.verbose,
152 dry_run=self.dry_run,
155 # -- Utility methods -----------------------------------------------
157 def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir):
161 build_cmd = self.get_finalized_command(build_cmd)
162 build_files = build_cmd.get_outputs()
163 build_dir = getattr(build_cmd, cmd_option)
165 prefix_len = len(build_dir) + len(os.sep)
167 for file in build_files:
168 outputs.append(os.path.join(output_dir, file[prefix_len:]))
172 def _bytecode_filenames(self, py_filenames):
174 for py_file in py_filenames:
175 # Since build_py handles package data installation, the
176 # list of outputs can contain more than just .py files.
177 # Make sure we only report bytecode for the .py files.
178 ext = os.path.splitext(os.path.normcase(py_file))[1]
179 if ext != PYTHON_SOURCE_EXTENSION:
182 bytecode_files.append(
183 importlib.util.cache_from_source(py_file, optimization='')
185 if self.optimize > 0:
186 bytecode_files.append(
187 importlib.util.cache_from_source(
188 py_file, optimization=self.optimize
192 return bytecode_files
194 # -- External interface --------------------------------------------
195 # (called by outsiders)
197 def get_outputs(self):
198 """Return the list of files that would be installed if this command
199 were actually run. Not affected by the "dry-run" flag or whether
200 modules have actually been built yet.
202 pure_outputs = self._mutate_outputs(
203 self.distribution.has_pure_modules(),
209 bytecode_outputs = self._bytecode_filenames(pure_outputs)
211 bytecode_outputs = []
213 ext_outputs = self._mutate_outputs(
214 self.distribution.has_ext_modules(),
220 return pure_outputs + bytecode_outputs + ext_outputs
222 def get_inputs(self):
223 """Get the list of files that are input to this command, ie. the
224 files that get installed as they are named in the build tree.
225 The files in this list correspond one-to-one to the output
226 filenames returned by 'get_outputs()'.
230 if self.distribution.has_pure_modules():
231 build_py = self.get_finalized_command('build_py')
232 inputs.extend(build_py.get_outputs())
234 if self.distribution.has_ext_modules():
235 build_ext = self.get_finalized_command('build_ext')
236 inputs.extend(build_ext.get_outputs())