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        return "gcc" in compiler_name or "g++" in compiler_name
219
220    def runtime_library_dir_option(self, dir):
221        # XXX Hackish, at the very least.  See Python bug #445902:
222        # http://sourceforge.net/tracker/index.php
223        #   ?func=detail&aid=445902&group_id=5470&atid=105470
224        # Linkers on different platforms need different options to
225        # specify that directories need to be added to the list of
226        # directories searched for dependencies when a dynamic library
227        # is sought.  GCC on GNU systems (Linux, FreeBSD, ...) has to
228        # be told to pass the -R option through to the linker, whereas
229        # other compilers and gcc on other systems just know this.
230        # Other compilers may need something slightly different.  At
231        # this time, there's no way to determine this information from
232        # the configuration data stored in the Python installation, so
233        # we use this hack.
234        compiler = os.path.basename(sysconfig.get_config_var("CC"))
235        if sys.platform[:6] == "darwin":
236            # MacOSX's linker doesn't understand the -R flag at all
237            return "-L" + dir
238        elif sys.platform[:7] == "freebsd":
239            return "-Wl,-rpath=" + dir
240        elif sys.platform[:5] == "hp-ux":
241            if self._is_gcc(compiler):
242                return ["-Wl,+s", "-L" + dir]
243            return ["+s", "-L" + dir]
244        else:
245            if self._is_gcc(compiler):
246                # gcc on non-GNU systems does not need -Wl, but can
247                # use it anyway.  Since distutils has always passed in
248                # -Wl whenever gcc was used in the past it is probably
249                # safest to keep doing so.
250                if sysconfig.get_config_var("GNULD") == "yes":
251                    # GNU ld needs an extra option to get a RUNPATH
252                    # instead of just an RPATH.
253                    return "-Wl,--enable-new-dtags,-R" + dir
254                else:
255                    return "-Wl,-R" + dir
256            else:
257                # No idea how --enable-new-dtags would be passed on to
258                # ld if this system was using GNU ld.  Don't know if a
259                # system like this even exists.
260                return "-R" + dir
261
262    def library_option(self, lib):
263        return "-l" + lib
264
265    def find_library_file(self, dirs, lib, debug=0):
266        shared_f = self.library_filename(lib, lib_type='shared')
267        dylib_f = self.library_filename(lib, lib_type='dylib')
268        xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub')
269        static_f = self.library_filename(lib, lib_type='static')
270
271        if sys.platform == 'darwin':
272            # On OSX users can specify an alternate SDK using
273            # '-isysroot', calculate the SDK root if it is specified
274            # (and use it further on)
275            #
276            # Note that, as of Xcode 7, Apple SDKs may contain textual stub
277            # libraries with .tbd extensions rather than the normal .dylib
278            # shared libraries installed in /.  The Apple compiler tool
279            # chain handles this transparently but it can cause problems
280            # for programs that are being built with an SDK and searching
281            # for specific libraries.  Callers of find_library_file need to
282            # keep in mind that the base filename of the returned SDK library
283            # file might have a different extension from that of the library
284            # file installed on the running system, for example:
285            #   /Applications/Xcode.app/Contents/Developer/Platforms/
286            #       MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
287            #       usr/lib/libedit.tbd
288            # vs
289            #   /usr/lib/libedit.dylib
290            cflags = sysconfig.get_config_var('CFLAGS')
291            m = re.search(r'-isysroot\s*(\S+)', cflags)
292            if m is None:
293                sysroot = '/'
294            else:
295                sysroot = m.group(1)
296
297
298
299        for dir in dirs:
300            shared = os.path.join(dir, shared_f)
301            dylib = os.path.join(dir, dylib_f)
302            static = os.path.join(dir, static_f)
303            xcode_stub = os.path.join(dir, xcode_stub_f)
304
305            if sys.platform == 'darwin' and (
306                dir.startswith('/System/') or (
307                dir.startswith('/usr/') and not dir.startswith('/usr/local/'))):
308
309                shared = os.path.join(sysroot, dir[1:], shared_f)
310                dylib = os.path.join(sysroot, dir[1:], dylib_f)
311                static = os.path.join(sysroot, dir[1:], static_f)
312                xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f)
313
314            # We're second-guessing the linker here, with not much hard
315            # data to go on: GCC seems to prefer the shared library, so I'm
316            # assuming that *all* Unix C compilers do.  And of course I'm
317            # ignoring even GCC's "-static" option.  So sue me.
318            if os.path.exists(dylib):
319                return dylib
320            elif os.path.exists(xcode_stub):
321                return xcode_stub
322            elif os.path.exists(shared):
323                return shared
324            elif os.path.exists(static):
325                return static
326
327        # Oops, didn't find it in *any* of 'dirs'
328        return None
329