• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""distutils.unixccompiler
2
3Contains the UnixCCompiler class, a subclass of CCompiler that handles
4the "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
16import os, sys, re
17
18from distutils import sysconfig
19from distutils.dep_util import newer
20from distutils.ccompiler import \
21     CCompiler, gen_preprocess_options, gen_lib_options
22from distutils.errors import \
23     DistutilsExecError, CompileError, LibError, LinkError
24from distutils import log
25
26if sys.platform == 'darwin':
27    import _osx_support
28
29# XXX Things not currently handled:
30#   * optimization/debug/warning flags; we just use whatever's in Python's
31#     Makefile and live with it.  Is this adequate?  If not, we might
32#     have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
33#     SunCCompiler, and I suspect down that road lies madness.
34#   * even if we don't know a warning flag from an optimization flag,
35#     we need some way for outsiders to feed preprocessor/compiler/linker
36#     flags in to us -- eg. a sysadmin might want to mandate certain flags
37#     via a site config file, or a user might want to set something for
38#     compiling this module distribution only via the setup.py command
39#     line, whatever.  As long as these options come from something on the
40#     current system, they can be as system-dependent as they like, and we
41#     should just happily stuff them into the preprocessor/compiler/linker
42#     options and carry on.
43
44
45class UnixCCompiler(CCompiler):
46
47    compiler_type = 'unix'
48
49    # These are used by CCompiler in two places: the constructor sets
50    # instance attributes 'preprocessor', 'compiler', etc. from them, and
51    # 'set_executable()' allows any of these to be set.  The defaults here
52    # are pretty generic; they will probably have to be set by an outsider
53    # (eg. using information discovered by the sysconfig about building
54    # Python extensions).
55    executables = {'preprocessor' : None,
56                   'compiler'     : ["cc"],
57                   'compiler_so'  : ["cc"],
58                   'compiler_cxx' : ["cc"],
59                   'linker_so'    : ["cc", "-shared"],
60                   'linker_exe'   : ["cc"],
61                   'archiver'     : ["ar", "-cr"],
62                   'ranlib'       : None,
63                  }
64
65    if sys.platform[:6] == "darwin":
66        executables['ranlib'] = ["ranlib"]
67
68    # Needed for the filename generation methods provided by the base
69    # class, CCompiler.  NB. whoever instantiates/uses a particular
70    # UnixCCompiler instance should set 'shared_lib_ext' -- we set a
71    # reasonable common default here, but it's not necessarily used on all
72    # Unices!
73
74    src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"]
75    obj_extension = ".o"
76    static_lib_extension = ".a"
77    shared_lib_extension = ".so"
78    dylib_lib_extension = ".dylib"
79    xcode_stub_lib_extension = ".tbd"
80    static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
81    xcode_stub_lib_format = dylib_lib_format
82    if sys.platform == "cygwin":
83        exe_extension = ".exe"
84
85    def preprocess(self, source, output_file=None, macros=None,
86                   include_dirs=None, extra_preargs=None, extra_postargs=None):
87        fixed_args = self._fix_compile_args(None, macros, include_dirs)
88        ignore, macros, include_dirs = fixed_args
89        pp_opts = gen_preprocess_options(macros, include_dirs)
90        pp_args = self.preprocessor + pp_opts
91        if output_file:
92            pp_args.extend(['-o', output_file])
93        if extra_preargs:
94            pp_args[:0] = extra_preargs
95        if extra_postargs:
96            pp_args.extend(extra_postargs)
97        pp_args.append(source)
98
99        # We need to preprocess: either we're being forced to, or we're
100        # generating output to stdout, or there's a target output file and
101        # the source file is newer than the target (or the target doesn't
102        # exist).
103        if self.force or output_file is None or newer(source, output_file):
104            if output_file:
105                self.mkpath(os.path.dirname(output_file))
106            try:
107                self.spawn(pp_args)
108            except DistutilsExecError as msg:
109                raise CompileError(msg)
110
111    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
112        compiler_so = self.compiler_so
113        if sys.platform == 'darwin':
114            compiler_so = _osx_support.compiler_fixup(compiler_so,
115                                                    cc_args + extra_postargs)
116        try:
117            self.spawn(compiler_so + cc_args + [src, '-o', obj] +
118                       extra_postargs)
119        except DistutilsExecError as msg:
120            raise CompileError(msg)
121
122    def create_static_lib(self, objects, output_libname,
123                          output_dir=None, debug=0, target_lang=None):
124        objects, output_dir = self._fix_object_args(objects, output_dir)
125
126        output_filename = \
127            self.library_filename(output_libname, output_dir=output_dir)
128
129        if self._need_link(objects, output_filename):
130            self.mkpath(os.path.dirname(output_filename))
131            self.spawn(self.archiver +
132                       [output_filename] +
133                       objects + self.objects)
134
135            # Not many Unices required ranlib anymore -- SunOS 4.x is, I
136            # think the only major Unix that does.  Maybe we need some
137            # platform intelligence here to skip ranlib if it's not
138            # needed -- or maybe Python's configure script took care of
139            # it for us, hence the check for leading colon.
140            if self.ranlib:
141                try:
142                    self.spawn(self.ranlib + [output_filename])
143                except DistutilsExecError as msg:
144                    raise LibError(msg)
145        else:
146            log.debug("skipping %s (up-to-date)", output_filename)
147
148    def link(self, target_desc, objects,
149             output_filename, output_dir=None, libraries=None,
150             library_dirs=None, runtime_library_dirs=None,
151             export_symbols=None, debug=0, extra_preargs=None,
152             extra_postargs=None, build_temp=None, target_lang=None):
153        objects, output_dir = self._fix_object_args(objects, output_dir)
154        fixed_args = self._fix_lib_args(libraries, library_dirs,
155                                        runtime_library_dirs)
156        libraries, library_dirs, runtime_library_dirs = fixed_args
157
158        lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
159                                   libraries)
160        if not isinstance(output_dir, (str, type(None))):
161            raise TypeError("'output_dir' must be a string or None")
162        if output_dir is not None:
163            output_filename = os.path.join(output_dir, output_filename)
164
165        if self._need_link(objects, output_filename):
166            ld_args = (objects + self.objects +
167                       lib_opts + ['-o', output_filename])
168            if debug:
169                ld_args[:0] = ['-g']
170            if extra_preargs:
171                ld_args[:0] = extra_preargs
172            if extra_postargs:
173                ld_args.extend(extra_postargs)
174            self.mkpath(os.path.dirname(output_filename))
175            try:
176                if target_desc == CCompiler.EXECUTABLE:
177                    linker = self.linker_exe[:]
178                else:
179                    linker = self.linker_so[:]
180                if target_lang == "c++" and self.compiler_cxx:
181                    # skip over environment variable settings if /usr/bin/env
182                    # is used to set up the linker's environment.
183                    # This is needed on OSX. Note: this assumes that the
184                    # normal and C++ compiler have the same environment
185                    # settings.
186                    i = 0
187                    if os.path.basename(linker[0]) == "env":
188                        i = 1
189                        while '=' in linker[i]:
190                            i += 1
191
192                    if os.path.basename(linker[i]) == 'ld_so_aix':
193                        # AIX platforms prefix the compiler with the ld_so_aix
194                        # script, so we need to adjust our linker index
195                        offset = 1
196                    else:
197                        offset = 0
198
199                    linker[i+offset] = self.compiler_cxx[i]
200
201                if sys.platform == 'darwin':
202                    linker = _osx_support.compiler_fixup(linker, ld_args)
203
204                self.spawn(linker + ld_args)
205            except DistutilsExecError as msg:
206                raise LinkError(msg)
207        else:
208            log.debug("skipping %s (up-to-date)", output_filename)
209
210    # -- Miscellaneous methods -----------------------------------------
211    # These are all used by the 'gen_lib_options() function, in
212    # ccompiler.py.
213
214    def library_dir_option(self, dir):
215        return "-L" + dir
216
217    def _is_gcc(self, compiler_name):
218        # clang uses same syntax for rpath as gcc
219        return any(name in compiler_name for name in ("gcc", "g++", "clang"))
220
221    def runtime_library_dir_option(self, dir):
222        # XXX Hackish, at the very least.  See Python bug #445902:
223        # http://sourceforge.net/tracker/index.php
224        #   ?func=detail&aid=445902&group_id=5470&atid=105470
225        # Linkers on different platforms need different options to
226        # specify that directories need to be added to the list of
227        # directories searched for dependencies when a dynamic library
228        # is sought.  GCC on GNU systems (Linux, FreeBSD, ...) has to
229        # be told to pass the -R option through to the linker, whereas
230        # other compilers and gcc on other systems just know this.
231        # Other compilers may need something slightly different.  At
232        # this time, there's no way to determine this information from
233        # the configuration data stored in the Python installation, so
234        # we use this hack.
235        compiler = os.path.basename(sysconfig.get_config_var("CC"))
236        if sys.platform[:6] == "darwin":
237            # MacOSX's linker doesn't understand the -R flag at all
238            return "-L" + dir
239        elif sys.platform[:7] == "freebsd":
240            return "-Wl,-rpath=" + dir
241        elif sys.platform[:5] == "hp-ux":
242            if self._is_gcc(compiler):
243                return ["-Wl,+s", "-L" + dir]
244            return ["+s", "-L" + dir]
245        else:
246            if self._is_gcc(compiler):
247                # gcc on non-GNU systems does not need -Wl, but can
248                # use it anyway.  Since distutils has always passed in
249                # -Wl whenever gcc was used in the past it is probably
250                # safest to keep doing so.
251                if sysconfig.get_config_var("GNULD") == "yes":
252                    # GNU ld needs an extra option to get a RUNPATH
253                    # instead of just an RPATH.
254                    return "-Wl,--enable-new-dtags,-R" + dir
255                else:
256                    return "-Wl,-R" + dir
257            else:
258                # No idea how --enable-new-dtags would be passed on to
259                # ld if this system was using GNU ld.  Don't know if a
260                # system like this even exists.
261                return "-R" + dir
262
263    def library_option(self, lib):
264        return "-l" + lib
265
266    def find_library_file(self, dirs, lib, debug=0):
267        shared_f = self.library_filename(lib, lib_type='shared')
268        dylib_f = self.library_filename(lib, lib_type='dylib')
269        xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub')
270        static_f = self.library_filename(lib, lib_type='static')
271
272        if sys.platform == 'darwin':
273            # On OSX users can specify an alternate SDK using
274            # '-isysroot', calculate the SDK root if it is specified
275            # (and use it further on)
276            #
277            # Note that, as of Xcode 7, Apple SDKs may contain textual stub
278            # libraries with .tbd extensions rather than the normal .dylib
279            # shared libraries installed in /.  The Apple compiler tool
280            # chain handles this transparently but it can cause problems
281            # for programs that are being built with an SDK and searching
282            # for specific libraries.  Callers of find_library_file need to
283            # keep in mind that the base filename of the returned SDK library
284            # file might have a different extension from that of the library
285            # file installed on the running system, for example:
286            #   /Applications/Xcode.app/Contents/Developer/Platforms/
287            #       MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
288            #       usr/lib/libedit.tbd
289            # vs
290            #   /usr/lib/libedit.dylib
291            cflags = sysconfig.get_config_var('CFLAGS')
292            m = re.search(r'-isysroot\s*(\S+)', cflags)
293            if m is None:
294                sysroot = _osx_support._default_sysroot(sysconfig.get_config_var('CC'))
295            else:
296                sysroot = m.group(1)
297
298
299
300        for dir in dirs:
301            shared = os.path.join(dir, shared_f)
302            dylib = os.path.join(dir, dylib_f)
303            static = os.path.join(dir, static_f)
304            xcode_stub = os.path.join(dir, xcode_stub_f)
305
306            if sys.platform == 'darwin' and (
307                dir.startswith('/System/') or (
308                dir.startswith('/usr/') and not dir.startswith('/usr/local/'))):
309
310                shared = os.path.join(sysroot, dir[1:], shared_f)
311                dylib = os.path.join(sysroot, dir[1:], dylib_f)
312                static = os.path.join(sysroot, dir[1:], static_f)
313                xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f)
314
315            # We're second-guessing the linker here, with not much hard
316            # data to go on: GCC seems to prefer the shared library, so I'm
317            # assuming that *all* Unix C compilers do.  And of course I'm
318            # ignoring even GCC's "-static" option.  So sue me.
319            if os.path.exists(dylib):
320                return dylib
321            elif os.path.exists(xcode_stub):
322                return xcode_stub
323            elif os.path.exists(shared):
324                return shared
325            elif os.path.exists(static):
326                return static
327
328        # Oops, didn't find it in *any* of 'dirs'
329        return None
330