source: python/trunk/Lib/distutils/emxccompiler.py@ 395

Last change on this file since 395 was 395, checked in by dmik, 11 years ago

python: Fix EMXCompiler to stop polluting source tree with .obj files.

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