~inkscape.dev/inkscape-devlibs/trunk

« back to all changes in this revision

Viewing changes to python/Lib/distutils/command/build_py.py

  • Committer: Eduard Braun
  • Date: 2016-10-22 16:54:41 UTC
  • Revision ID: eduard.braun2@gmx.de-20161022165441-gfp6agtut9nh4p22
Update Python to version 2.7.12

Included modules:
  coverage 4.2
  lxml 3.6.4
  numpy 1.11.2
  scour 0.35
  six 1.10.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
                and os.path.isfile(fn)])
133
 
        return files
134
 
 
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,
142
 
                               preserve_mode=False)
143
 
 
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)."""
148
 
 
149
 
        path = package.split('.')
150
 
 
151
 
        if not self.package_dir:
152
 
            if path:
153
 
                return os.path.join(*path)
154
 
            else:
155
 
                return ''
156
 
        else:
157
 
            tail = []
158
 
            while path:
159
 
                try:
160
 
                    pdir = self.package_dir['.'.join(path)]
161
 
                except KeyError:
162
 
                    tail.insert(0, path[-1])
163
 
                    del path[-1]
164
 
                else:
165
 
                    tail.insert(0, pdir)
166
 
                    return os.path.join(*tail)
167
 
            else:
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('')
176
 
                if pdir is not None:
177
 
                    tail.insert(0, pdir)
178
 
 
179
 
                if tail:
180
 
                    return os.path.join(*tail)
181
 
                else:
182
 
                    return ''
183
 
 
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
188
 
        # circumvent them.
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)
197
 
 
198
 
        # Require __init__.py for all but the "root package"
199
 
        if package:
200
 
            init_py = os.path.join(package_dir, "__init__.py")
201
 
            if os.path.isfile(init_py):
202
 
                return init_py
203
 
            else:
204
 
                log.warn(("package init file '%s' not found " +
205
 
                          "(or not a regular file)"), init_py)
206
 
 
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.
209
 
        return None
210
 
 
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)
214
 
            return False
215
 
        else:
216
 
            return True
217
 
 
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"))
221
 
        modules = []
222
 
        setup_script = os.path.abspath(self.distribution.script_name)
223
 
 
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))
229
 
            else:
230
 
                self.debug_print("excluding %s" % setup_script)
231
 
        return modules
232
 
 
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
240
 
        module.
241
 
        """
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
245
 
        #   this package
246
 
        # checked - true if we have checked that the package directory
247
 
        #   is valid (exists, contains __init__.py, ... ?)
248
 
        packages = {}
249
 
 
250
 
        # List of (package, module, filename) tuples to return
251
 
        modules = []
252
 
 
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]
261
 
 
262
 
            try:
263
 
                (package_dir, checked) = packages[package]
264
 
            except KeyError:
265
 
                package_dir = self.get_package_dir(package)
266
 
                checked = 0
267
 
 
268
 
            if not checked:
269
 
                init_py = self.check_package(package, package_dir)
270
 
                packages[package] = (package_dir, 1)
271
 
                if init_py:
272
 
                    modules.append((package, "__init__", init_py))
273
 
 
274
 
            # XXX perhaps we should also check for just .pyc files
275
 
            # (so greedy closed-source bastards can distribute Python
276
 
            # modules too)
277
 
            module_file = os.path.join(package_dir, module_base + ".py")
278
 
            if not self.check_module(module, module_file):
279
 
                continue
280
 
 
281
 
            modules.append((package, module_base, module_file))
282
 
 
283
 
        return modules
284
 
 
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."""
291
 
        modules = []
292
 
        if self.py_modules:
293
 
            modules.extend(self.find_modules())
294
 
        if self.packages:
295
 
            for package in self.packages:
296
 
                package_dir = self.get_package_dir(package)
297
 
                m = self.find_package_modules(package, package_dir)
298
 
                modules.extend(m)
299
 
        return modules
300
 
 
301
 
    def get_source_files(self):
302
 
        return [module[-1] for module in self.find_all_modules()]
303
 
 
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)
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 = package.split('.')
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
 
        outputs += [
322
 
            os.path.join(build_dir, filename)
323
 
            for package, src_dir, build_dir, filenames in self.data_files
324
 
            for filename in filenames
325
 
            ]
326
 
 
327
 
        return outputs
328
 
 
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)):
333
 
            raise TypeError(
334
 
                  "'package' must be a string (dot-separated), list, or tuple")
335
 
 
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)
341
 
        self.mkpath(dir)
342
 
        return self.copy_file(module_file, outfile, preserve_mode=0)
343
 
 
344
 
    def build_modules(self):
345
 
        modules = self.find_modules()
346
 
        for (package, module, module_file) in modules:
347
 
 
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)
353
 
 
354
 
    def build_packages(self):
355
 
        for package in self.packages:
356
 
 
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)
368
 
 
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)
374
 
 
375
 
    def byte_compile(self, files):
376
 
        if sys.dont_write_bytecode:
377
 
            self.warn('byte-compiling is disabled, skipping.')
378
 
            return
379
 
 
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)
 
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
                and os.path.isfile(fn)])
 
133
        return files
 
134
 
 
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,
 
142
                               preserve_mode=False)
 
143
 
 
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)."""
 
148
 
 
149
        path = package.split('.')
 
150
 
 
151
        if not self.package_dir:
 
152
            if path:
 
153
                return os.path.join(*path)
 
154
            else:
 
155
                return ''
 
156
        else:
 
157
            tail = []
 
158
            while path:
 
159
                try:
 
160
                    pdir = self.package_dir['.'.join(path)]
 
161
                except KeyError:
 
162
                    tail.insert(0, path[-1])
 
163
                    del path[-1]
 
164
                else:
 
165
                    tail.insert(0, pdir)
 
166
                    return os.path.join(*tail)
 
167
            else:
 
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('')
 
176
                if pdir is not None:
 
177
                    tail.insert(0, pdir)
 
178
 
 
179
                if tail:
 
180
                    return os.path.join(*tail)
 
181
                else:
 
182
                    return ''
 
183
 
 
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
 
188
        # circumvent them.
 
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)
 
197
 
 
198
        # Require __init__.py for all but the "root package"
 
199
        if package:
 
200
            init_py = os.path.join(package_dir, "__init__.py")
 
201
            if os.path.isfile(init_py):
 
202
                return init_py
 
203
            else:
 
204
                log.warn(("package init file '%s' not found " +
 
205
                          "(or not a regular file)"), init_py)
 
206
 
 
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.
 
209
        return None
 
210
 
 
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)
 
214
            return False
 
215
        else:
 
216
            return True
 
217
 
 
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"))
 
221
        modules = []
 
222
        setup_script = os.path.abspath(self.distribution.script_name)
 
223
 
 
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))
 
229
            else:
 
230
                self.debug_print("excluding %s" % setup_script)
 
231
        return modules
 
232
 
 
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
 
240
        module.
 
241
        """
 
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
 
245
        #   this package
 
246
        # checked - true if we have checked that the package directory
 
247
        #   is valid (exists, contains __init__.py, ... ?)
 
248
        packages = {}
 
249
 
 
250
        # List of (package, module, filename) tuples to return
 
251
        modules = []
 
252
 
 
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]
 
261
 
 
262
            try:
 
263
                (package_dir, checked) = packages[package]
 
264
            except KeyError:
 
265
                package_dir = self.get_package_dir(package)
 
266
                checked = 0
 
267
 
 
268
            if not checked:
 
269
                init_py = self.check_package(package, package_dir)
 
270
                packages[package] = (package_dir, 1)
 
271
                if init_py:
 
272
                    modules.append((package, "__init__", init_py))
 
273
 
 
274
            # XXX perhaps we should also check for just .pyc files
 
275
            # (so greedy closed-source bastards can distribute Python
 
276
            # modules too)
 
277
            module_file = os.path.join(package_dir, module_base + ".py")
 
278
            if not self.check_module(module, module_file):
 
279
                continue
 
280
 
 
281
            modules.append((package, module_base, module_file))
 
282
 
 
283
        return modules
 
284
 
 
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."""
 
291
        modules = []
 
292
        if self.py_modules:
 
293
            modules.extend(self.find_modules())
 
294
        if self.packages:
 
295
            for package in self.packages:
 
296
                package_dir = self.get_package_dir(package)
 
297
                m = self.find_package_modules(package, package_dir)
 
298
                modules.extend(m)
 
299
        return modules
 
300
 
 
301
    def get_source_files(self):
 
302
        return [module[-1] for module in self.find_all_modules()]
 
303
 
 
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)
 
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 = package.split('.')
 
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
        outputs += [
 
322
            os.path.join(build_dir, filename)
 
323
            for package, src_dir, build_dir, filenames in self.data_files
 
324
            for filename in filenames
 
325
            ]
 
326
 
 
327
        return outputs
 
328
 
 
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)):
 
333
            raise TypeError(
 
334
                  "'package' must be a string (dot-separated), list, or tuple")
 
335
 
 
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)
 
341
        self.mkpath(dir)
 
342
        return self.copy_file(module_file, outfile, preserve_mode=0)
 
343
 
 
344
    def build_modules(self):
 
345
        modules = self.find_modules()
 
346
        for (package, module, module_file) in modules:
 
347
 
 
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)
 
353
 
 
354
    def build_packages(self):
 
355
        for package in self.packages:
 
356
 
 
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)
 
368
 
 
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)
 
374
 
 
375
    def byte_compile(self, files):
 
376
        if sys.dont_write_bytecode:
 
377
            self.warn('byte-compiling is disabled, skipping.')
 
378
            return
 
379
 
 
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)