| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 1 | """distutils.unixccompiler | 
|  | 2 |  | 
|  | 3 | Contains the UnixCCompiler class, a subclass of CCompiler that handles | 
|  | 4 | the "typical" Unix-style command-line C compiler: | 
|  | 5 | * macros defined with -Dname[=value] | 
|  | 6 | * macros undefined with -Uname | 
|  | 7 | * include search directories specified with -Idir | 
|  | 8 | * libraries specified with -lllib | 
|  | 9 | * library search directories specified with -Ldir | 
|  | 10 | * compile handled by 'cc' (or similar) executable with -c option: | 
|  | 11 | compiles .c to .o | 
|  | 12 | * link static library handled by 'ar' command (possibly with 'ranlib') | 
|  | 13 | * link shared library handled by 'cc -shared' | 
|  | 14 | """ | 
|  | 15 |  | 
|  | 16 | # created 1999/07/05, Greg Ward | 
|  | 17 |  | 
|  | 18 | __rcsid__ = "$Id$" | 
|  | 19 |  | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 20 | import string, re | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 21 | from types import * | 
|  | 22 | from sysconfig import \ | 
|  | 23 | CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO | 
|  | 24 | from ccompiler import CCompiler | 
|  | 25 |  | 
|  | 26 |  | 
|  | 27 | # XXX Things not currently handled: | 
|  | 28 | #   * optimization/debug/warning flags; we just use whatever's in Python's | 
|  | 29 | #     Makefile and live with it.  Is this adequate?  If not, we might | 
|  | 30 | #     have to have a bunch of subclasses GNUCCompiler, SGICCompiler, | 
|  | 31 | #     SunCCompiler, and I suspect down that road lies madness. | 
|  | 32 | #   * even if we don't know a warning flag from an optimization flag, | 
|  | 33 | #     we need some way for outsiders to feed preprocessor/compiler/linker | 
|  | 34 | #     flags in to us -- eg. a sysadmin might want to mandate certain flags | 
|  | 35 | #     via a site config file, or a user might want to set something for | 
|  | 36 | #     compiling this module distribution only via the setup.py command | 
|  | 37 | #     line, whatever.  As long as these options come from something on the | 
|  | 38 | #     current system, they can be as system-dependent as they like, and we | 
|  | 39 | #     should just happily stuff them into the preprocessor/compiler/linker | 
|  | 40 | #     options and carry on. | 
|  | 41 |  | 
|  | 42 |  | 
|  | 43 | class UnixCCompiler (CCompiler): | 
|  | 44 |  | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 45 | # XXX perhaps there should really be *three* kinds of include | 
|  | 46 | # directories: those built in to the preprocessor, those from Python's | 
|  | 47 | # Makefiles, and those supplied to {add,set}_include_dirs().  Currently | 
|  | 48 | # we make no distinction between the latter two at this point; it's all | 
|  | 49 | # up to the client class to select the include directories to use above | 
|  | 50 | # and beyond the compiler's defaults.  That is, both the Python include | 
|  | 51 | # directories and any module- or package-specific include directories | 
|  | 52 | # are specified via {add,set}_include_dirs(), and there's no way to | 
|  | 53 | # distinguish them.  This might be a bug. | 
|  | 54 |  | 
|  | 55 | def __init__ (self, | 
|  | 56 | verbose=0, | 
|  | 57 | dry_run=0): | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 58 |  | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 59 | CCompiler.__init__ (self, verbose, dry_run) | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 60 |  | 
|  | 61 | self.preprocess_options = None | 
|  | 62 | self.compile_options = None | 
|  | 63 |  | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 64 | # Munge CC and OPT together in case there are flags stuck in CC. | 
|  | 65 | # Note that using these variables from sysconfig immediately makes | 
|  | 66 | # this module specific to building Python extensions and | 
|  | 67 | # inappropriate as a general-purpose C compiler front-end.  So sue | 
|  | 68 | # me.  Note also that we use OPT rather than CFLAGS, because CFLAGS | 
|  | 69 | # is the flags used to compile Python itself -- not only are there | 
|  | 70 | # -I options in there, they are the *wrong* -I options.  We'll | 
|  | 71 | # leave selection of include directories up to the class using | 
|  | 72 | # UnixCCompiler! | 
|  | 73 |  | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 74 | (self.cc, self.ccflags) = \ | 
|  | 75 | _split_command (CC + ' ' + OPT) | 
|  | 76 | self.ccflags_shared = string.split (CCSHARED) | 
|  | 77 |  | 
|  | 78 | (self.ld_shared, self.ldflags_shared) = \ | 
|  | 79 | _split_command (LDSHARED) | 
|  | 80 |  | 
|  | 81 |  | 
|  | 82 | def compile (self, | 
|  | 83 | sources, | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 84 | macros=None, | 
|  | 85 | includes=None): | 
|  | 86 |  | 
|  | 87 | if macros is None: | 
|  | 88 | macros = [] | 
|  | 89 | if includes is None: | 
|  | 90 | includes = [] | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 91 |  | 
|  | 92 | if type (macros) is not ListType: | 
|  | 93 | raise TypeError, \ | 
|  | 94 | "'macros' (if supplied) must be a list of tuples" | 
|  | 95 | if type (includes) is not ListType: | 
|  | 96 | raise TypeError, \ | 
|  | 97 | "'includes' (if supplied) must be a list of strings" | 
|  | 98 |  | 
|  | 99 | pp_opts = _gen_preprocess_options (self.macros + macros, | 
|  | 100 | self.include_dirs + includes) | 
|  | 101 |  | 
|  | 102 | # use of ccflags_shared means we're blithely assuming that we're | 
|  | 103 | # compiling for inclusion in a shared object! (will have to fix | 
|  | 104 | # this when I add the ability to build a new Python) | 
|  | 105 | cc_args = ['-c'] + pp_opts + \ | 
|  | 106 | self.ccflags + self.ccflags_shared + \ | 
|  | 107 | sources | 
|  | 108 |  | 
|  | 109 | # this will change to 'spawn' when I have it! | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 110 | #print string.join ([self.cc] + cc_args, ' ') | 
|  | 111 | self.spawn ([self.cc] + cc_args) | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 112 |  | 
|  | 113 |  | 
|  | 114 | # XXX punting on 'link_static_lib()' for now -- it might be better for | 
|  | 115 | # CCompiler to mandate just 'link_binary()' or some such to build a new | 
|  | 116 | # Python binary; it would then take care of linking in everything | 
|  | 117 | # needed for the new Python without messing with an intermediate static | 
|  | 118 | # library. | 
|  | 119 |  | 
|  | 120 | def link_shared_lib (self, | 
|  | 121 | objects, | 
|  | 122 | output_libname, | 
|  | 123 | libraries=None, | 
|  | 124 | library_dirs=None): | 
|  | 125 | # XXX should we sanity check the library name? (eg. no | 
|  | 126 | # slashes) | 
|  | 127 | self.link_shared_object (objects, "lib%s%s" % (output_libname, SO)) | 
|  | 128 |  | 
|  | 129 |  | 
|  | 130 | def link_shared_object (self, | 
|  | 131 | objects, | 
|  | 132 | output_filename, | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 133 | libraries=None, | 
|  | 134 | library_dirs=None): | 
|  | 135 |  | 
|  | 136 | if libraries is None: | 
|  | 137 | libraries = [] | 
|  | 138 | if library_dirs is None: | 
|  | 139 | library_dirs = [] | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 140 |  | 
|  | 141 | lib_opts = _gen_lib_options (self.libraries + libraries, | 
|  | 142 | self.library_dirs + library_dirs) | 
|  | 143 | ld_args = self.ldflags_shared + lib_opts + \ | 
|  | 144 | objects + ['-o', output_filename] | 
|  | 145 |  | 
| Greg Ward | 5e71744 | 1999-08-14 23:53:53 +0000 | [diff] [blame^] | 146 | #print string.join ([self.ld_shared] + ld_args, ' ') | 
|  | 147 | self.spawn ([self.ld_shared] + ld_args) | 
|  | 148 |  | 
|  | 149 |  | 
|  | 150 | def object_filenames (self, source_filenames): | 
|  | 151 | outnames = [] | 
|  | 152 | for inname in source_filenames: | 
|  | 153 | outnames.append (re.sub (r'\.(c|C|cc|cxx)$', '.o', inname)) | 
|  | 154 | return outnames | 
|  | 155 |  | 
|  | 156 | def shared_object_filename (self, source_filename): | 
|  | 157 | return re.sub (r'\.(c|C|cc|cxx)$', SO) | 
|  | 158 |  | 
|  | 159 | def library_filename (self, libname): | 
|  | 160 | return "lib%s.a" % libname | 
|  | 161 |  | 
|  | 162 | def shared_library_filename (self, libname): | 
|  | 163 | return "lib%s.so" % libname | 
|  | 164 |  | 
|  | 165 |  | 
| Greg Ward | 170bdc0 | 1999-07-10 02:04:22 +0000 | [diff] [blame] | 166 | # class UnixCCompiler | 
|  | 167 |  | 
|  | 168 |  | 
|  | 169 | def _split_command (cmd): | 
|  | 170 | """Split a command string up into the progam to run (a string) and | 
|  | 171 | the list of arguments; return them as (cmd, arglist).""" | 
|  | 172 | args = string.split (cmd) | 
|  | 173 | return (args[0], args[1:]) | 
|  | 174 |  | 
|  | 175 |  | 
|  | 176 | def _gen_preprocess_options (macros, includes): | 
|  | 177 |  | 
|  | 178 | # XXX it would be nice (mainly aesthetic, and so we don't generate | 
|  | 179 | # stupid-looking command lines) to go over 'macros' and eliminate | 
|  | 180 | # redundant definitions/undefinitions (ie. ensure that only the | 
|  | 181 | # latest mention of a particular macro winds up on the command | 
|  | 182 | # line).  I don't think it's essential, though, since most (all?) | 
|  | 183 | # Unix C compilers only pay attention to the latest -D or -U | 
|  | 184 | # mention of a macro on their command line.  Similar situation for | 
|  | 185 | # 'includes'.  I'm punting on both for now.  Anyways, weeding out | 
|  | 186 | # redundancies like this should probably be the province of | 
|  | 187 | # CCompiler, since the data structures used are inherited from it | 
|  | 188 | # and therefore common to all CCompiler classes. | 
|  | 189 |  | 
|  | 190 |  | 
|  | 191 | pp_opts = [] | 
|  | 192 | for macro in macros: | 
|  | 193 | if len (macro) == 1:        # undefine this macro | 
|  | 194 | pp_opts.append ("-U%s" % macro[0]) | 
|  | 195 | elif len (macro) == 2: | 
|  | 196 | if macro[1] is None:    # define with no explicit value | 
|  | 197 | pp_opts.append ("-D%s" % macro[0]) | 
|  | 198 | else: | 
|  | 199 | # XXX *don't* need to be clever about quoting the | 
|  | 200 | # macro value here, because we're going to avoid the | 
|  | 201 | # shell at all costs when we spawn the command! | 
|  | 202 | pp_opts.append ("-D%s=%s" % macro) | 
|  | 203 |  | 
|  | 204 | for dir in includes: | 
|  | 205 | pp_opts.append ("-I%s" % dir) | 
|  | 206 |  | 
|  | 207 | return pp_opts | 
|  | 208 |  | 
|  | 209 | # _gen_preprocess_options () | 
|  | 210 |  | 
|  | 211 |  | 
|  | 212 | def _gen_lib_options (libraries, library_dirs): | 
|  | 213 |  | 
|  | 214 | lib_opts = [] | 
|  | 215 |  | 
|  | 216 | for dir in library_dirs: | 
|  | 217 | lib_opts.append ("-L%s" % dir) | 
|  | 218 |  | 
|  | 219 | # XXX it's important that we *not* remove redundant library mentions! | 
|  | 220 | # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to | 
|  | 221 | # resolve all symbols.  I just hope we never have to say "-lfoo obj.o | 
|  | 222 | # -lbar" to get things to work -- that's certainly a possibility, but a | 
|  | 223 | # pretty nasty way to arrange your C code. | 
|  | 224 |  | 
|  | 225 | for lib in libraries: | 
|  | 226 | lib_opts.append ("-l%s" % lib) | 
|  | 227 |  | 
|  | 228 | return lib_opts | 
|  | 229 |  | 
|  | 230 | # _gen_lib_options () |