diff options
Diffstat (limited to 'Lib/distutils/ccompiler.py')
-rw-r--r-- | Lib/distutils/ccompiler.py | 242 |
1 files changed, 168 insertions, 74 deletions
diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py index 4c8b881..50246b8 100644 --- a/Lib/distutils/ccompiler.py +++ b/Lib/distutils/ccompiler.py @@ -160,7 +160,6 @@ class CCompiler: setattr(self, key, value) - def _find_macro (self, name): i = 0 for defn in self.macros: @@ -321,6 +320,100 @@ class CCompiler: # -- Private utility methods -------------------------------------- # (here for the convenience of subclasses) + # Helper method to prep compiler in subclass compile() methods + + def _setup_compile(self, outdir, macros, incdirs, sources, depends, + extra): + """Process arguments and decide which source files to compile. + + Merges _fix_compile_args() and _prep_compile(). + """ + if outdir is None: + outdir = self.output_dir + elif type(outdir) is not StringType: + raise TypeError, "'output_dir' must be a string or None" + + if macros is None: + macros = self.macros + elif type(macros) is ListType: + macros = macros + (self.macros or []) + else: + raise TypeError, "'macros' (if supplied) must be a list of tuples" + + if incdirs is None: + incdirs = self.include_dirs + elif type(incdirs) in (ListType, TupleType): + incdirs = list(incdirs) + (self.include_dirs or []) + else: + raise TypeError, \ + "'include_dirs' (if supplied) must be a list of strings" + + if extra is None: + extra = [] + + # Get the list of expected output (object) files + objects = self.object_filenames(sources, 1, outdir) + assert len(objects) == len(sources) + + # XXX should redo this code to eliminate skip_source entirely. + # XXX instead create build and issue skip messages inline + + if self.force: + skip_source = {} # rebuild everything + for source in sources: + skip_source[source] = 0 + elif depends is None: + # If depends is None, figure out which source files we + # have to recompile according to a simplistic check. We + # just compare the source and object file, no deep + # dependency checking involving header files. + skip_source = {} # rebuild everything + for source in sources: # no wait, rebuild nothing + skip_source[source] = 1 + + n_sources, n_objects = newer_pairwise(sources, objects) + for source in n_sources: # no really, only rebuild what's + skip_source[source] = 0 # out-of-date + else: + # If depends is a list of files, then do a different + # simplistic check. Assume that each object depends on + # its source and all files in the depends list. + skip_source = {} + # L contains all the depends plus a spot at the end for a + # particular source file + L = depends[:] + [None] + for i in range(len(objects)): + source = sources[i] + L[-1] = source + if newer_group(L, objects[i]): + skip_source[source] = 0 + else: + skip_source[source] = 1 + + pp_opts = gen_preprocess_options(macros, incdirs) + + build = {} + for i in range(len(sources)): + src = sources[i] + obj = objects[i] + ext = os.path.splitext(src)[1] + self.mkpath(os.path.dirname(obj)) + if skip_source[src]: + log.debug("skipping %s (%s up-to-date)", src, obj) + else: + build[obj] = src, ext + + return macros, objects, extra, pp_opts, build + + def _get_cc_args(self, pp_opts, debug, before): + # works for unixccompiler, emxccompiler, cygwinccompiler + cc_args = pp_opts + ['-c'] + if debug: + cc_args[:0] = ['-g'] + if before: + cc_args[:0] = before + return cc_args + def _fix_compile_args (self, output_dir, macros, include_dirs): """Typecheck and fix-up some of the arguments to the 'compile()' method, and return fixed-up values. Specifically: if 'output_dir' @@ -341,8 +434,7 @@ class CCompiler: elif type (macros) is ListType: macros = macros + (self.macros or []) else: - raise TypeError, \ - "'macros' (if supplied) must be a list of tuples" + raise TypeError, "'macros' (if supplied) must be a list of tuples" if include_dirs is None: include_dirs = self.include_dirs @@ -352,40 +444,57 @@ class CCompiler: raise TypeError, \ "'include_dirs' (if supplied) must be a list of strings" - return (output_dir, macros, include_dirs) + return output_dir, macros, include_dirs # _fix_compile_args () - def _prep_compile (self, sources, output_dir): - """Determine the list of object files corresponding to 'sources', - and figure out which ones really need to be recompiled. Return a - list of all object files and a dictionary telling which source - files can be skipped. + def _prep_compile(self, sources, output_dir, depends=None): + """Decide which souce files must be recompiled. + + Determine the list of object files corresponding to 'sources', + and figure out which ones really need to be recompiled. + Return a list of all object files and a dictionary telling + which source files can be skipped. """ # Get the list of expected output (object) files - objects = self.object_filenames (sources, - strip_dir=1, - output_dir=output_dir) + objects = self.object_filenames(sources, strip_dir=1, + output_dir=output_dir) + assert len(objects) == len(sources) if self.force: skip_source = {} # rebuild everything for source in sources: skip_source[source] = 0 - else: - # Figure out which source files we have to recompile according - # to a simplistic check -- we just compare the source and - # object file, no deep dependency checking involving header - # files. + elif depends is None: + # If depends is None, figure out which source files we + # have to recompile according to a simplistic check. We + # just compare the source and object file, no deep + # dependency checking involving header files. skip_source = {} # rebuild everything for source in sources: # no wait, rebuild nothing skip_source[source] = 1 - (n_sources, n_objects) = newer_pairwise (sources, objects) + n_sources, n_objects = newer_pairwise(sources, objects) for source in n_sources: # no really, only rebuild what's skip_source[source] = 0 # out-of-date - - return (objects, skip_source) + else: + # If depends is a list of files, then do a different + # simplistic check. Assume that each object depends on + # its source and all files in the depends list. + skip_source = {} + # L contains all the depends plus a spot at the end for a + # particular source file + L = depends[:] + [None] + for i in range(len(objects)): + source = sources[i] + L[-1] = source + if newer_group(L, objects[i]): + skip_source[source] = 0 + else: + skip_source[source] = 1 + + return objects, skip_source # _prep_compile () @@ -484,22 +593,19 @@ class CCompiler: """ pass - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - """Compile one or more source files. 'sources' must be a list of - filenames, most likely C/C++ files, but in reality anything that - can be handled by a particular compiler and compiler class - (eg. MSVCCompiler can handle resource files in 'sources'). Return - a list of object filenames, one per source filename in 'sources'. - Depending on the implementation, not all source files will - necessarily be compiled, but all corresponding object filenames - will be returned. + def compile(self, sources, output_dir=None, macros=None, + include_dirs=None, debug=0, extra_preargs=None, + extra_postargs=None, depends=None): + """Compile one or more source files. + + 'sources' must be a list of filenames, most likely C/C++ + files, but in reality anything that can be handled by a + particular compiler and compiler class (eg. MSVCCompiler can + handle resource files in 'sources'). Return a list of object + filenames, one per source filename in 'sources'. Depending on + the implementation, not all source files will necessarily be + compiled, but all corresponding object filenames will be + returned. If 'output_dir' is given, object files will be put under it, while retaining their original path component. That is, "foo/bar.c" @@ -530,6 +636,12 @@ class CCompiler: for those occasions when the abstract compiler framework doesn't cut the mustard. + 'depends', if given, is a list of filenames that all targets + depend on. If a source file is older than any file in + depends, then the source file will be recompiled. This + supports dependency tracking, but only at a coarse + granularity. + Raises CompileError on failure. """ pass @@ -710,7 +822,6 @@ class CCompiler: """ raise NotImplementedError - # -- Filename generation methods ----------------------------------- # The default implementation of the filename generating methods are @@ -745,63 +856,46 @@ class CCompiler: # * exe_extension - # extension for executable files, eg. '' or '.exe' - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + assert output_dir is not None obj_names = [] for src_name in source_filenames: - (base, ext) = os.path.splitext (src_name) + base, ext = os.path.splitext(src_name) if ext not in self.src_extensions: raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % \ - (ext, src_name) + "unknown file type '%s' (from '%s')" % (ext, src_name) if strip_dir: - base = os.path.basename (base) - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) + base = os.path.basename(base) + obj_names.append(os.path.join(output_dir, + base + self.obj_extension)) return obj_names - # object_filenames () - - - def shared_object_filename (self, - basename, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def shared_object_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None if strip_dir: basename = os.path.basename (basename) - return os.path.join (output_dir, basename + self.shared_lib_extension) + return os.path.join(output_dir, basename + self.shared_lib_extension) - def executable_filename (self, - basename, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def executable_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None if strip_dir: basename = os.path.basename (basename) return os.path.join(output_dir, basename + (self.exe_extension or '')) - def library_filename (self, - libname, - lib_type='static', # or 'shared' - strip_dir=0, - output_dir=''): - - if output_dir is None: output_dir = '' - if lib_type not in ("static","shared","dylib"): + def library_filename(self, libname, lib_type='static', # or 'shared' + strip_dir=0, output_dir=''): + assert output_dir is not None + if lib_type not in ("static", "shared", "dylib"): raise ValueError, "'lib_type' must be \"static\", \"shared\" or \"dylib\"" - fmt = getattr (self, lib_type + "_lib_format") - ext = getattr (self, lib_type + "_lib_extension") + fmt = getattr(self, lib_type + "_lib_format") + ext = getattr(self, lib_type + "_lib_extension") - (dir, base) = os.path.split (libname) + dir, base = os.path.split (libname) filename = fmt % (base, ext) if strip_dir: dir = '' - return os.path.join (output_dir, dir, filename) + return os.path.join(output_dir, dir, filename) # -- Utility methods ----------------------------------------------- |