PageRenderTime 296ms CodeModel.GetById 120ms app.highlight 53ms RepoModel.GetById 119ms app.codeStats 0ms

/Lib/distutils/emxccompiler.py

http://unladen-swallow.googlecode.com/
Python | 315 lines | 197 code | 43 blank | 75 comment | 22 complexity | 470791f9d824cba9b3f10d14887e2c73 MD5 | raw file
  1"""distutils.emxccompiler
  2
  3Provides the EMXCCompiler class, a subclass of UnixCCompiler that
  4handles the EMX port of the GNU C compiler to OS/2.
  5"""
  6
  7# issues:
  8#
  9# * OS/2 insists that DLLs can have names no longer than 8 characters
 10#   We put export_symbols in a def-file, as though the DLL can have
 11#   an arbitrary length name, but truncate the output filename.
 12#
 13# * only use OMF objects and use LINK386 as the linker (-Zomf)
 14#
 15# * always build for multithreading (-Zmt) as the accompanying OS/2 port
 16#   of Python is only distributed with threads enabled.
 17#
 18# tested configurations:
 19#
 20# * EMX gcc 2.81/EMX 0.9d fix03
 21
 22__revision__ = "$Id: emxccompiler.py 34786 2003-12-02 12:17:59Z aimacintyre $"
 23
 24import os,sys,copy
 25from distutils.ccompiler import gen_preprocess_options, gen_lib_options
 26from distutils.unixccompiler import UnixCCompiler
 27from distutils.file_util import write_file
 28from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
 29from distutils import log
 30
 31class EMXCCompiler (UnixCCompiler):
 32
 33    compiler_type = 'emx'
 34    obj_extension = ".obj"
 35    static_lib_extension = ".lib"
 36    shared_lib_extension = ".dll"
 37    static_lib_format = "%s%s"
 38    shared_lib_format = "%s%s"
 39    res_extension = ".res"      # compiled resource file
 40    exe_extension = ".exe"
 41
 42    def __init__ (self,
 43                  verbose=0,
 44                  dry_run=0,
 45                  force=0):
 46
 47        UnixCCompiler.__init__ (self, verbose, dry_run, force)
 48
 49        (status, details) = check_config_h()
 50        self.debug_print("Python's GCC status: %s (details: %s)" %
 51                         (status, details))
 52        if status is not CONFIG_H_OK:
 53            self.warn(
 54                "Python's pyconfig.h doesn't seem to support your compiler.  " +
 55                ("Reason: %s." % details) +
 56                "Compiling may fail because of undefined preprocessor macros.")
 57
 58        (self.gcc_version, self.ld_version) = \
 59            get_versions()
 60        self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" %
 61                         (self.gcc_version,
 62                          self.ld_version) )
 63
 64        # Hard-code GCC because that's what this is all about.
 65        # XXX optimization, warnings etc. should be customizable.
 66        self.set_executables(compiler='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall',
 67                             compiler_so='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall',
 68                             linker_exe='gcc -Zomf -Zmt -Zcrtdll',
 69                             linker_so='gcc -Zomf -Zmt -Zcrtdll -Zdll')
 70
 71        # want the gcc library statically linked (so that we don't have
 72        # to distribute a version dependent on the compiler we have)
 73        self.dll_libraries=["gcc"]
 74
 75    # __init__ ()
 76
 77    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
 78        if ext == '.rc':
 79            # gcc requires '.rc' compiled to binary ('.res') files !!!
 80            try:
 81                self.spawn(["rc", "-r", src])
 82            except DistutilsExecError, msg:
 83                raise CompileError, msg
 84        else: # for other files use the C-compiler
 85            try:
 86                self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
 87                           extra_postargs)
 88            except DistutilsExecError, msg:
 89                raise CompileError, msg
 90
 91    def link (self,
 92              target_desc,
 93              objects,
 94              output_filename,
 95              output_dir=None,
 96              libraries=None,
 97              library_dirs=None,
 98              runtime_library_dirs=None,
 99              export_symbols=None,
100              debug=0,
101              extra_preargs=None,
102              extra_postargs=None,
103              build_temp=None,
104              target_lang=None):
105
106        # use separate copies, so we can modify the lists
107        extra_preargs = copy.copy(extra_preargs or [])
108        libraries = copy.copy(libraries or [])
109        objects = copy.copy(objects or [])
110
111        # Additional libraries
112        libraries.extend(self.dll_libraries)
113
114        # handle export symbols by creating a def-file
115        # with executables this only works with gcc/ld as linker
116        if ((export_symbols is not None) and
117            (target_desc != self.EXECUTABLE)):
118            # (The linker doesn't do anything if output is up-to-date.
119            # So it would probably better to check if we really need this,
120            # but for this we had to insert some unchanged parts of
121            # UnixCCompiler, and this is not what we want.)
122
123            # we want to put some files in the same directory as the
124            # object files are, build_temp doesn't help much
125            # where are the object files
126            temp_dir = os.path.dirname(objects[0])
127            # name of dll to give the helper files the same base name
128            (dll_name, dll_extension) = os.path.splitext(
129                os.path.basename(output_filename))
130
131            # generate the filenames for these files
132            def_file = os.path.join(temp_dir, dll_name + ".def")
133
134            # Generate .def file
135            contents = [
136                "LIBRARY %s INITINSTANCE TERMINSTANCE" % \
137                os.path.splitext(os.path.basename(output_filename))[0],
138                "DATA MULTIPLE NONSHARED",
139                "EXPORTS"]
140            for sym in export_symbols:
141                contents.append('  "%s"' % sym)
142            self.execute(write_file, (def_file, contents),
143                         "writing %s" % def_file)
144
145            # next add options for def-file and to creating import libraries
146            # for gcc/ld the def-file is specified as any other object files
147            objects.append(def_file)
148
149        #end: if ((export_symbols is not None) and
150        #        (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
151
152        # who wants symbols and a many times larger output file
153        # should explicitly switch the debug mode on
154        # otherwise we let dllwrap/ld strip the output file
155        # (On my machine: 10KB < stripped_file < ??100KB
156        #   unstripped_file = stripped_file + XXX KB
157        #  ( XXX=254 for a typical python extension))
158        if not debug:
159            extra_preargs.append("-s")
160
161        UnixCCompiler.link(self,
162                           target_desc,
163                           objects,
164                           output_filename,
165                           output_dir,
166                           libraries,
167                           library_dirs,
168                           runtime_library_dirs,
169                           None, # export_symbols, we do this in our def-file
170                           debug,
171                           extra_preargs,
172                           extra_postargs,
173                           build_temp,
174                           target_lang)
175
176    # link ()
177
178    # -- Miscellaneous methods -----------------------------------------
179
180    # override the object_filenames method from CCompiler to
181    # support rc and res-files
182    def object_filenames (self,
183                          source_filenames,
184                          strip_dir=0,
185                          output_dir=''):
186        if output_dir is None: output_dir = ''
187        obj_names = []
188        for src_name in source_filenames:
189            # use normcase to make sure '.rc' is really '.rc' and not '.RC'
190            (base, ext) = os.path.splitext (os.path.normcase(src_name))
191            if ext not in (self.src_extensions + ['.rc']):
192                raise UnknownFileError, \
193                      "unknown file type '%s' (from '%s')" % \
194                      (ext, src_name)
195            if strip_dir:
196                base = os.path.basename (base)
197            if ext == '.rc':
198                # these need to be compiled to object files
199                obj_names.append (os.path.join (output_dir,
200                                            base + self.res_extension))
201            else:
202                obj_names.append (os.path.join (output_dir,
203                                            base + self.obj_extension))
204        return obj_names
205
206    # object_filenames ()
207
208    # override the find_library_file method from UnixCCompiler
209    # to deal with file naming/searching differences
210    def find_library_file(self, dirs, lib, debug=0):
211        shortlib = '%s.lib' % lib
212        longlib = 'lib%s.lib' % lib    # this form very rare
213
214        # get EMX's default library directory search path
215        try:
216            emx_dirs = os.environ['LIBRARY_PATH'].split(';')
217        except KeyError:
218            emx_dirs = []
219
220        for dir in dirs + emx_dirs:
221            shortlibp = os.path.join(dir, shortlib)
222            longlibp = os.path.join(dir, longlib)
223            if os.path.exists(shortlibp):
224                return shortlibp
225            elif os.path.exists(longlibp):
226                return longlibp
227
228        # Oops, didn't find it in *any* of 'dirs'
229        return None
230
231# class EMXCCompiler
232
233
234# Because these compilers aren't configured in Python's pyconfig.h file by
235# default, we should at least warn the user if he is using a unmodified
236# version.
237
238CONFIG_H_OK = "ok"
239CONFIG_H_NOTOK = "not ok"
240CONFIG_H_UNCERTAIN = "uncertain"
241
242def check_config_h():
243
244    """Check if the current Python installation (specifically, pyconfig.h)
245    appears amenable to building extensions with GCC.  Returns a tuple
246    (status, details), where 'status' is one of the following constants:
247      CONFIG_H_OK
248        all is well, go ahead and compile
249      CONFIG_H_NOTOK
250        doesn't look good
251      CONFIG_H_UNCERTAIN
252        not sure -- unable to read pyconfig.h
253    'details' is a human-readable string explaining the situation.
254
255    Note there are two ways to conclude "OK": either 'sys.version' contains
256    the string "GCC" (implying that this Python was built with GCC), or the
257    installed "pyconfig.h" contains the string "__GNUC__".
258    """
259
260    # XXX since this function also checks sys.version, it's not strictly a
261    # "pyconfig.h" check -- should probably be renamed...
262
263    from distutils import sysconfig
264    import string
265    # if sys.version contains GCC then python was compiled with
266    # GCC, and the pyconfig.h file should be OK
267    if string.find(sys.version,"GCC") >= 0:
268        return (CONFIG_H_OK, "sys.version mentions 'GCC'")
269
270    fn = sysconfig.get_config_h_filename()
271    try:
272        # It would probably better to read single lines to search.
273        # But we do this only once, and it is fast enough
274        f = open(fn)
275        s = f.read()
276        f.close()
277
278    except IOError, exc:
279        # if we can't read this file, we cannot say it is wrong
280        # the compiler will complain later about this file as missing
281        return (CONFIG_H_UNCERTAIN,
282                "couldn't read '%s': %s" % (fn, exc.strerror))
283
284    else:
285        # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
286        if string.find(s,"__GNUC__") >= 0:
287            return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
288        else:
289            return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
290
291
292def get_versions():
293    """ Try to find out the versions of gcc and ld.
294        If not possible it returns None for it.
295    """
296    from distutils.version import StrictVersion
297    from distutils.spawn import find_executable
298    import re
299
300    gcc_exe = find_executable('gcc')
301    if gcc_exe:
302        out = os.popen(gcc_exe + ' -dumpversion','r')
303        out_string = out.read()
304        out.close()
305        result = re.search('(\d+\.\d+\.\d+)',out_string)
306        if result:
307            gcc_version = StrictVersion(result.group(1))
308        else:
309            gcc_version = None
310    else:
311        gcc_version = None
312    # EMX ld has no way of reporting version number, and we use GCC
313    # anyway - so we can link OMF DLLs
314    ld_version = None
315    return (gcc_version, ld_version)