blob: ae133f9c2a6c855976d559493bf78ed84a368cb0 [file] [log] [blame]
Greg Ward13ae1c81999-03-22 14:55:25 +00001"""distutils.command.build_py
2
3Implements the Distutils 'build_py' command."""
4
5# created 1999/03/08, Greg Ward
6
7__rcsid__ = "$Id$"
8
9import string, os
10from distutils.core import Command
11from distutils.errors import *
12from distutils.util import mkpath, newer, make_file, copy_file
13
14
15class BuildPy (Command):
16
17 options = [('dir=', 'd', "directory for platform-shared files"),
18 ('compile', 'c', "compile .py to .pyc"),
19 ('optimize', 'o', "compile .py to .pyo (optimized)"),
20 ]
21
22
23 def set_default_options (self):
24 self.dir = None
25 self.compile = 1
26 self.optimize = 1
27
28 def set_final_options (self):
29 self.set_undefined_options ('build',
30 ('libdir', 'dir'),
31 ('compile_py', 'compile'),
32 ('optimize_py', 'optimize'))
33
34
35 def run (self):
36
37 # XXX copy_file by default preserves all stat info -- mode, atime,
38 # and mtime. IMHO this is the right thing to do, but perhaps it
39 # should be an option -- in particular, a site administrator might
40 # want installed files to reflect the time of installation rather
41 # than the last modification time before the installed release.
42
43 # XXX copy_file does *not* preserve MacOS-specific file metadata.
44 # If this is a problem for building/installing Python modules, then
45 # we'll have to fix copy_file. (And what about installing scripts,
46 # when the time comes for that -- does MacOS use its special
47 # metadata to know that a file is meant to be interpreted by
48 # Python?)
49
50 self.set_final_options ()
51
52 (modules, package) = \
53 self.distribution.get_options ('py_modules', 'package')
54 package = package or ''
55
56 infiles = []
57 outfiles = []
58 missing = []
59
60 # Loop over the list of "pure Python" modules, deriving
61 # input and output filenames and checking for missing
62 # input files.
63
64 # XXX we should allow for wildcards, so eg. the Distutils setup.py
65 # file would just have to say
66 # py_modules = ['distutils.*', 'distutils.command.*']
67 # without having to list each one explicitly.
68 for m in modules:
69 fn = apply (os.path.join, tuple (string.split (m, '.'))) + '.py'
70 if not os.path.exists (fn):
71 missing.append (fn)
72 else:
73 infiles.append (fn)
74 outfiles.append (os.path.join (self.dir, package, fn))
75
76 # Blow up if any input files were not found.
77 if missing:
78 raise DistutilsFileError, \
79 "missing files: " + string.join (missing, ' ')
80
81 # Loop over the list of input files, copying them to their
82 # temporary (build) destination.
83 created = {}
84 for i in range (len (infiles)):
85 outdir = os.path.split (outfiles[i])[0]
86 if not created.get(outdir):
87 mkpath (outdir, verbose=self.distribution.verbose)
88 created[outdir] = 1
89
90 copy_file (infiles[i], outfiles[i],
91 update=1, verbose=self.distribution.verbose)
92
93 # (Optionally) compile .py to .pyc
94 # XXX hey! we can't control whether we optimize or not; that's up
95 # to the invocation of the current Python interpreter (at least
96 # according to the py_compile docs). That sucks.
97
98 if self.compile:
99 from py_compile import compile
100
101 for f in outfiles:
102 # XXX can't assume this filename mapping!
103 out_fn = string.replace (f, '.py', '.pyc')
104
105 make_file (f, out_fn, compile, (f,),
106 verbose=self.distribution.verbose,
107 update_message="compiling %s" % f)
108
109 # XXX ignore self.optimize for now, since we don't really know if
110 # we're compiling optimally or not, and couldn't pick what to do
111 # even if we did know. ;-(
112
113# end class BuildPy