unixccompiler.py 13.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
"""distutils.unixccompiler

Contains the UnixCCompiler class, a subclass of CCompiler that handles
the "typical" Unix-style command-line C compiler:
  * macros defined with -Dname[=value]
  * macros undefined with -Uname
  * include search directories specified with -Idir
  * libraries specified with -lllib
  * library search directories specified with -Ldir
  * compile handled by 'cc' (or similar) executable with -c option:
    compiles .c to .o
  * link static library handled by 'ar' command (possibly with 'ranlib')
  * link shared library handled by 'cc -shared'
"""

16
__revision__ = "$Id$"
17

18
import os, sys
19 20
from types import StringType, NoneType

21
from distutils import sysconfig
22
from distutils.dep_util import newer
23
from distutils.ccompiler import \
24 25 26
     CCompiler, gen_preprocess_options, gen_lib_options
from distutils.errors import \
     DistutilsExecError, CompileError, LibError, LinkError
27
from distutils import log
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

# XXX Things not currently handled:
#   * optimization/debug/warning flags; we just use whatever's in Python's
#     Makefile and live with it.  Is this adequate?  If not, we might
#     have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
#     SunCCompiler, and I suspect down that road lies madness.
#   * even if we don't know a warning flag from an optimization flag,
#     we need some way for outsiders to feed preprocessor/compiler/linker
#     flags in to us -- eg. a sysadmin might want to mandate certain flags
#     via a site config file, or a user might want to set something for
#     compiling this module distribution only via the setup.py command
#     line, whatever.  As long as these options come from something on the
#     current system, they can be as system-dependent as they like, and we
#     should just happily stuff them into the preprocessor/compiler/linker
#     options and carry on.

Ronald Oussoren's avatar
Ronald Oussoren committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
def _darwin_compiler_fixup(compiler_so, cc_args):
    """
    This function will strip '-isysroot PATH' and '-arch ARCH' from the
    compile flags if the user has specified one them in extra_compile_flags.

    This is needed because '-arch ARCH' adds another architecture to the
    build, without a way to remove an architecture. Furthermore GCC will
    barf if multiple '-isysroot' arguments are present.
    """
    stripArch = stripSysroot = 0

    compiler_so = list(compiler_so)
    kernel_version = os.uname()[2] # 8.4.3
    major_version = int(kernel_version.split('.')[0])

    if major_version < 8:
        # OSX before 10.4.0, these don't support -arch and -isysroot at
        # all.
        stripArch = stripSysroot = True
    else:
        stripArch = '-arch' in cc_args
        stripSysroot = '-isysroot' in cc_args

67
    if stripArch or 'ARCHFLAGS' in os.environ:
Ronald Oussoren's avatar
Ronald Oussoren committed
68 69 70 71 72 73 74 75
        while 1:
            try:
                index = compiler_so.index('-arch')
                # Strip this argument and the next one:
                del compiler_so[index:index+2]
            except ValueError:
                break

76 77
    if 'ARCHFLAGS' in os.environ and not stripArch:
        # User specified different -arch flags in the environ,
78
        # see also distutils.sysconfig
79
        compiler_so = compiler_so + os.environ['ARCHFLAGS'].split()
80

Ronald Oussoren's avatar
Ronald Oussoren committed
81 82 83 84
    if stripSysroot:
        try:
            index = compiler_so.index('-isysroot')
            # Strip this argument and the next one:
85
            del compiler_so[index:index+2]
Ronald Oussoren's avatar
Ronald Oussoren committed
86 87 88
        except ValueError:
            pass

Tim Peters's avatar
Tim Peters committed
89
    # Check if the SDK that is used during compilation actually exists,
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    # the universal build requires the usage of a universal SDK and not all
    # users have that installed by default.
    sysroot = None
    if '-isysroot' in cc_args:
        idx = cc_args.index('-isysroot')
        sysroot = cc_args[idx+1]
    elif '-isysroot' in compiler_so:
        idx = compiler_so.index('-isysroot')
        sysroot = compiler_so[idx+1]

    if sysroot and not os.path.isdir(sysroot):
        log.warn("Compiling with an SDK that doesn't seem to exist: %s",
                sysroot)
        log.warn("Please check your Xcode installation")

Ronald Oussoren's avatar
Ronald Oussoren committed
105 106
    return compiler_so

107
class UnixCCompiler(CCompiler):
108

109 110
    compiler_type = 'unix'

111 112 113 114 115 116 117 118 119
    # These are used by CCompiler in two places: the constructor sets
    # instance attributes 'preprocessor', 'compiler', etc. from them, and
    # 'set_executable()' allows any of these to be set.  The defaults here
    # are pretty generic; they will probably have to be set by an outsider
    # (eg. using information discovered by the sysconfig about building
    # Python extensions).
    executables = {'preprocessor' : None,
                   'compiler'     : ["cc"],
                   'compiler_so'  : ["cc"],
120
                   'compiler_cxx' : ["cc"],
121 122 123 124 125 126
                   'linker_so'    : ["cc", "-shared"],
                   'linker_exe'   : ["cc"],
                   'archiver'     : ["ar", "-cr"],
                   'ranlib'       : None,
                  }

127 128 129
    if sys.platform[:6] == "darwin":
        executables['ranlib'] = ["ranlib"]

130 131 132 133 134 135
    # Needed for the filename generation methods provided by the base
    # class, CCompiler.  NB. whoever instantiates/uses a particular
    # UnixCCompiler instance should set 'shared_lib_ext' -- we set a
    # reasonable common default here, but it's not necessarily used on all
    # Unices!

136
    src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"]
137 138
    obj_extension = ".o"
    static_lib_extension = ".a"
139
    shared_lib_extension = ".so"
140 141
    dylib_lib_extension = ".dylib"
    static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
142 143
    if sys.platform == "cygwin":
        exe_extension = ".exe"
144

145 146 147 148
    def preprocess(self, source,
                   output_file=None, macros=None, include_dirs=None,
                   extra_preargs=None, extra_postargs=None):
        ignore, macros, include_dirs = \
149 150
            self._fix_compile_args(None, macros, include_dirs)
        pp_opts = gen_preprocess_options(macros, include_dirs)
151
        pp_args = self.preprocessor + pp_opts
152
        if output_file:
153
            pp_args.extend(['-o', output_file])
154
        if extra_preargs:
155
            pp_args[:0] = extra_preargs
156
        if extra_postargs:
157
            pp_args.extend(extra_postargs)
158
        pp_args.append(source)
159

160
        # We need to preprocess: either we're being forced to, or we're
Fred Drake's avatar
Fred Drake committed
161 162
        # generating output to stdout, or there's a target output file and
        # the source file is newer than the target (or the target doesn't
163
        # exist).
164
        if self.force or output_file is None or newer(source, output_file):
165 166 167
            if output_file:
                self.mkpath(os.path.dirname(output_file))
            try:
168
                self.spawn(pp_args)
169 170 171
            except DistutilsExecError, msg:
                raise CompileError, msg

172
    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
Ronald Oussoren's avatar
Ronald Oussoren committed
173 174 175
        compiler_so = self.compiler_so
        if sys.platform == 'darwin':
            compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs)
176
        try:
Ronald Oussoren's avatar
Ronald Oussoren committed
177
            self.spawn(compiler_so + cc_args + [src, '-o', obj] +
178 179 180
                       extra_postargs)
        except DistutilsExecError, msg:
            raise CompileError, msg
181

182
    def create_static_lib(self, objects, output_libname,
183
                          output_dir=None, debug=0, target_lang=None):
184
        objects, output_dir = self._fix_object_args(objects, output_dir)
185

186
        output_filename = \
187
            self.library_filename(output_libname, output_dir=output_dir)
188

189 190 191 192 193
        if self._need_link(objects, output_filename):
            self.mkpath(os.path.dirname(output_filename))
            self.spawn(self.archiver +
                       [output_filename] +
                       objects + self.objects)
194

195 196 197 198 199
            # Not many Unices required ranlib anymore -- SunOS 4.x is, I
            # think the only major Unix that does.  Maybe we need some
            # platform intelligence here to skip ranlib if it's not
            # needed -- or maybe Python's configure script took care of
            # it for us, hence the check for leading colon.
200
            if self.ranlib:
201
                try:
202
                    self.spawn(self.ranlib + [output_filename])
203 204
                except DistutilsExecError, msg:
                    raise LibError, msg
205
        else:
206
            log.debug("skipping %s (up-to-date)", output_filename)
207

208 209 210 211
    def link(self, target_desc, objects,
             output_filename, output_dir=None, libraries=None,
             library_dirs=None, runtime_library_dirs=None,
             export_symbols=None, debug=0, extra_preargs=None,
212
             extra_postargs=None, build_temp=None, target_lang=None):
213 214
        objects, output_dir = self._fix_object_args(objects, output_dir)
        libraries, library_dirs, runtime_library_dirs = \
215
            self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
216

217
        lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
218 219
                                   libraries)
        if type(output_dir) not in (StringType, NoneType):
220
            raise TypeError, "'output_dir' must be a string or None"
221
        if output_dir is not None:
222
            output_filename = os.path.join(output_dir, output_filename)
223

224
        if self._need_link(objects, output_filename):
Fred Drake's avatar
Fred Drake committed
225
            ld_args = (objects + self.objects +
226
                       lib_opts + ['-o', output_filename])
227 228
            if debug:
                ld_args[:0] = ['-g']
229 230 231
            if extra_preargs:
                ld_args[:0] = extra_preargs
            if extra_postargs:
232 233
                ld_args.extend(extra_postargs)
            self.mkpath(os.path.dirname(output_filename))
234
            try:
Fred Drake's avatar
Fred Drake committed
235
                if target_desc == CCompiler.EXECUTABLE:
236
                    linker = self.linker_exe[:]
237
                else:
238 239
                    linker = self.linker_so[:]
                if target_lang == "c++" and self.compiler_cxx:
Ronald Oussoren's avatar
Ronald Oussoren committed
240 241 242
                    # skip over environment variable settings if /usr/bin/env
                    # is used to set up the linker's environment.
                    # This is needed on OSX. Note: this assumes that the
Tim Peters's avatar
Tim Peters committed
243
                    # normal and C++ compiler have the same environment
Ronald Oussoren's avatar
Ronald Oussoren committed
244 245 246 247 248 249 250 251 252 253 254 255
                    # settings.
                    i = 0
                    if os.path.basename(linker[0]) == "env":
                        i = 1
                        while '=' in linker[i]:
                            i = i + 1

                    linker[i] = self.compiler_cxx[i]

                if sys.platform == 'darwin':
                    linker = _darwin_compiler_fixup(linker, ld_args)

256
                self.spawn(linker + ld_args)
257 258
            except DistutilsExecError, msg:
                raise LinkError, msg
259
        else:
260
            log.debug("skipping %s (up-to-date)", output_filename)
261

262 263 264
    # -- Miscellaneous methods -----------------------------------------
    # These are all used by the 'gen_lib_options() function, in
    # ccompiler.py.
Fred Drake's avatar
Fred Drake committed
265

266
    def library_dir_option(self, dir):
267 268
        return "-L" + dir

269 270 271
    def _is_gcc(self, compiler_name):
        return "gcc" in compiler_name or "g++" in compiler_name

272
    def runtime_library_dir_option(self, dir):
273 274 275 276 277 278
        # XXX Hackish, at the very least.  See Python bug #445902:
        # http://sourceforge.net/tracker/index.php
        #   ?func=detail&aid=445902&group_id=5470&atid=105470
        # Linkers on different platforms need different options to
        # specify that directories need to be added to the list of
        # directories searched for dependencies when a dynamic library
279 280
        # is sought.  GCC has to be told to pass the -R option through
        # to the linker, whereas other compilers just know this.
281 282 283 284
        # Other compilers may need something slightly different.  At
        # this time, there's no way to determine this information from
        # the configuration data stored in the Python installation, so
        # we use this hack.
285
        compiler = os.path.basename(sysconfig.get_config_var("CC"))
286 287 288
        if sys.platform[:6] == "darwin":
            # MacOSX's linker doesn't understand the -R flag at all
            return "-L" + dir
289
        elif sys.platform[:5] == "hp-ux":
290
            if self._is_gcc(compiler):
291 292
                return ["-Wl,+s", "-L" + dir]
            return ["+s", "-L" + dir]
293 294
        elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5":
            return ["-rpath", dir]
295
        elif self._is_gcc(compiler):
296
            return "-Wl,-R" + dir
297 298
        else:
            return "-R" + dir
299

300
    def library_option(self, lib):
301 302
        return "-l" + lib

303
    def find_library_file(self, dirs, lib, debug=0):
304 305 306
        shared_f = self.library_filename(lib, lib_type='shared')
        dylib_f = self.library_filename(lib, lib_type='dylib')
        static_f = self.library_filename(lib, lib_type='static')
307

308
        for dir in dirs:
309 310 311
            shared = os.path.join(dir, shared_f)
            dylib = os.path.join(dir, dylib_f)
            static = os.path.join(dir, static_f)
312 313 314 315
            # We're second-guessing the linker here, with not much hard
            # data to go on: GCC seems to prefer the shared library, so I'm
            # assuming that *all* Unix C compilers do.  And of course I'm
            # ignoring even GCC's "-static" option.  So sue me.
316 317 318
            if os.path.exists(dylib):
                return dylib
            elif os.path.exists(shared):
319
                return shared
320
            elif os.path.exists(static):
321
                return static
322

323 324
        # Oops, didn't find it in *any* of 'dirs'
        return None