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$" 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 try: 276 s = f.read() 277 finally: 278 f.close() 279 280 except IOError, exc: 281 # if we can't read this file, we cannot say it is wrong 282 # the compiler will complain later about this file as missing 283 return (CONFIG_H_UNCERTAIN, 284 "couldn't read '%s': %s" % (fn, exc.strerror)) 285 286 else: 287 # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar 288 if string.find(s,"__GNUC__") >= 0: 289 return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) 290 else: 291 return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) 292 293 294def get_versions(): 295 """ Try to find out the versions of gcc and ld. 296 If not possible it returns None for it. 297 """ 298 from distutils.version import StrictVersion 299 from distutils.spawn import find_executable 300 import re 301 302 gcc_exe = find_executable('gcc') 303 if gcc_exe: 304 out = os.popen(gcc_exe + ' -dumpversion','r') 305 try: 306 out_string = out.read() 307 finally: 308 out.close() 309 result = re.search('(\d+\.\d+\.\d+)',out_string) 310 if result: 311 gcc_version = StrictVersion(result.group(1)) 312 else: 313 gcc_version = None 314 else: 315 gcc_version = None 316 # EMX ld has no way of reporting version number, and we use GCC 317 # anyway - so we can link OMF DLLs 318 ld_version = None 319 return (gcc_version, ld_version) 320