blob: 644c6fc3912b3175f2e21e665dc1ad78862001b5 [file] [log] [blame]
Marc-André Lemburg9273ec72002-02-06 18:22:48 +00001"""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# created 2001/5/7, Andrew MacIntyre, from Rene Liebscher's cywinccompiler.py
23
24__revision__ = "$Id$"
25
26import os,sys,copy
27from distutils.ccompiler import gen_preprocess_options, gen_lib_options
28from distutils.unixccompiler import UnixCCompiler
29from distutils.file_util import write_file
30from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000031from distutils import log
Marc-André Lemburg9273ec72002-02-06 18:22:48 +000032
33class EMXCCompiler (UnixCCompiler):
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 # Hard-code GCC because that's what this is all about.
67 # XXX optimization, warnings etc. should be customizable.
68 self.set_executables(compiler='gcc -Zomf -Zmt -O2 -Wall',
69 compiler_so='gcc -Zomf -Zmt -O2 -Wall',
70 linker_exe='gcc -Zomf -Zmt -Zcrtdll',
71 linker_so='gcc -Zomf -Zmt -Zcrtdll -Zdll')
72
73 # want the gcc library statically linked (so that we don't have
74 # to distribute a version dependent on the compiler we have)
75 self.dll_libraries=["gcc"]
76
77 # __init__ ()
78
79 # not much different of the compile method in UnixCCompiler,
80 # but we have to insert some lines in the middle of it, so
81 # we put here a adapted version of it.
82 # (If we would call compile() in the base class, it would do some
83 # initializations a second time, this is why all is done here.)
84 def compile (self,
85 sources,
86 output_dir=None,
87 macros=None,
88 include_dirs=None,
89 debug=0,
90 extra_preargs=None,
91 extra_postargs=None):
92
93 (output_dir, macros, include_dirs) = \
94 self._fix_compile_args (output_dir, macros, include_dirs)
95 (objects, skip_sources) = self._prep_compile (sources, output_dir)
96
97 # Figure out the options for the compiler command line.
98 pp_opts = gen_preprocess_options (macros, include_dirs)
99 cc_args = pp_opts + ['-c']
100 if debug:
101 cc_args[:0] = ['-g']
102 if extra_preargs:
103 cc_args[:0] = extra_preargs
104 if extra_postargs is None:
105 extra_postargs = []
106
107 # Compile all source files that weren't eliminated by
108 # '_prep_compile()'.
109 for i in range (len (sources)):
110 src = sources[i] ; obj = objects[i]
111 ext = (os.path.splitext (src))[1]
112 if skip_sources[src]:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000113 log.debug("skipping %s (%s up-to-date)", src, obj)
Marc-André Lemburg9273ec72002-02-06 18:22:48 +0000114 else:
115 self.mkpath (os.path.dirname (obj))
116 if ext == '.rc':
117 # gcc requires '.rc' compiled to binary ('.res') files !!!
118 try:
119 self.spawn (["rc","-r",src])
120 except DistutilsExecError, msg:
121 raise CompileError, msg
122 else: # for other files use the C-compiler
123 try:
124 self.spawn (self.compiler_so + cc_args +
125 [src, '-o', obj] +
126 extra_postargs)
127 except DistutilsExecError, msg:
128 raise CompileError, msg
129
130 # Return *all* object filenames, not just the ones we just built.
131 return objects
132
133 # compile ()
134
135
136 def link (self,
137 target_desc,
138 objects,
139 output_filename,
140 output_dir=None,
141 libraries=None,
142 library_dirs=None,
143 runtime_library_dirs=None,
144 export_symbols=None,
145 debug=0,
146 extra_preargs=None,
147 extra_postargs=None,
148 build_temp=None):
149
150 # use separate copies, so we can modify the lists
151 extra_preargs = copy.copy(extra_preargs or [])
152 libraries = copy.copy(libraries or [])
153 objects = copy.copy(objects or [])
154
155 # Additional libraries
156 libraries.extend(self.dll_libraries)
157
158 # handle export symbols by creating a def-file
159 # with executables this only works with gcc/ld as linker
160 if ((export_symbols is not None) and
161 (target_desc != self.EXECUTABLE)):
162 # (The linker doesn't do anything if output is up-to-date.
163 # So it would probably better to check if we really need this,
164 # but for this we had to insert some unchanged parts of
165 # UnixCCompiler, and this is not what we want.)
166
167 # we want to put some files in the same directory as the
168 # object files are, build_temp doesn't help much
169 # where are the object files
170 temp_dir = os.path.dirname(objects[0])
171 # name of dll to give the helper files the same base name
172 (dll_name, dll_extension) = os.path.splitext(
173 os.path.basename(output_filename))
174
175 # generate the filenames for these files
176 def_file = os.path.join(temp_dir, dll_name + ".def")
Marc-André Lemburg9273ec72002-02-06 18:22:48 +0000177
178 # Generate .def file
179 contents = [
Jeremy Hyltona2f99892002-06-04 20:26:44 +0000180 "LIBRARY %s INITINSTANCE TERMINSTANCE" % \
181 os.path.splitext(os.path.basename(output_filename))[0],
Marc-André Lemburg9273ec72002-02-06 18:22:48 +0000182 "DATA MULTIPLE NONSHARED",
183 "EXPORTS"]
184 for sym in export_symbols:
185 contents.append(' "%s"' % sym)
186 self.execute(write_file, (def_file, contents),
187 "writing %s" % def_file)
188
189 # next add options for def-file and to creating import libraries
190 # for gcc/ld the def-file is specified as any other object files
191 objects.append(def_file)
192
193 #end: if ((export_symbols is not None) and
194 # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
195
196 # who wants symbols and a many times larger output file
197 # should explicitly switch the debug mode on
198 # otherwise we let dllwrap/ld strip the output file
199 # (On my machine: 10KB < stripped_file < ??100KB
200 # unstripped_file = stripped_file + XXX KB
201 # ( XXX=254 for a typical python extension))
202 if not debug:
203 extra_preargs.append("-s")
204
205 UnixCCompiler.link(self,
206 target_desc,
207 objects,
208 output_filename,
209 output_dir,
210 libraries,
211 library_dirs,
212 runtime_library_dirs,
213 None, # export_symbols, we do this in our def-file
214 debug,
215 extra_preargs,
216 extra_postargs,
217 build_temp)
218
219 # link ()
220
221 # -- Miscellaneous methods -----------------------------------------
222
223 # overwrite the one from CCompiler to support rc and res-files
224 def object_filenames (self,
225 source_filenames,
226 strip_dir=0,
227 output_dir=''):
228 if output_dir is None: output_dir = ''
229 obj_names = []
230 for src_name in source_filenames:
231 # use normcase to make sure '.rc' is really '.rc' and not '.RC'
232 (base, ext) = os.path.splitext (os.path.normcase(src_name))
233 if ext not in (self.src_extensions + ['.rc']):
234 raise UnknownFileError, \
235 "unknown file type '%s' (from '%s')" % \
236 (ext, src_name)
237 if strip_dir:
238 base = os.path.basename (base)
239 if ext == '.rc':
240 # these need to be compiled to object files
241 obj_names.append (os.path.join (output_dir,
242 base + self.res_extension))
243 else:
244 obj_names.append (os.path.join (output_dir,
245 base + self.obj_extension))
246 return obj_names
247
248 # object_filenames ()
249
250# class EMXCCompiler
251
252
253# Because these compilers aren't configured in Python's pyconfig.h file by
254# default, we should at least warn the user if he is using a unmodified
255# version.
256
257CONFIG_H_OK = "ok"
258CONFIG_H_NOTOK = "not ok"
259CONFIG_H_UNCERTAIN = "uncertain"
260
261def check_config_h():
262
263 """Check if the current Python installation (specifically, pyconfig.h)
264 appears amenable to building extensions with GCC. Returns a tuple
265 (status, details), where 'status' is one of the following constants:
266 CONFIG_H_OK
267 all is well, go ahead and compile
268 CONFIG_H_NOTOK
269 doesn't look good
270 CONFIG_H_UNCERTAIN
271 not sure -- unable to read pyconfig.h
272 'details' is a human-readable string explaining the situation.
273
274 Note there are two ways to conclude "OK": either 'sys.version' contains
275 the string "GCC" (implying that this Python was built with GCC), or the
276 installed "pyconfig.h" contains the string "__GNUC__".
277 """
278
279 # XXX since this function also checks sys.version, it's not strictly a
280 # "pyconfig.h" check -- should probably be renamed...
281
282 from distutils import sysconfig
283 import string
284 # if sys.version contains GCC then python was compiled with
285 # GCC, and the pyconfig.h file should be OK
286 if string.find(sys.version,"GCC") >= 0:
287 return (CONFIG_H_OK, "sys.version mentions 'GCC'")
288
289 fn = sysconfig.get_config_h_filename()
290 try:
291 # It would probably better to read single lines to search.
292 # But we do this only once, and it is fast enough
293 f = open(fn)
294 s = f.read()
295 f.close()
296
297 except IOError, exc:
298 # if we can't read this file, we cannot say it is wrong
299 # the compiler will complain later about this file as missing
300 return (CONFIG_H_UNCERTAIN,
301 "couldn't read '%s': %s" % (fn, exc.strerror))
302
303 else:
304 # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
305 if string.find(s,"__GNUC__") >= 0:
306 return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
307 else:
308 return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
309
310
311def get_versions():
312 """ Try to find out the versions of gcc and ld.
313 If not possible it returns None for it.
314 """
315 from distutils.version import StrictVersion
316 from distutils.spawn import find_executable
317 import re
318
319 gcc_exe = find_executable('gcc')
320 if gcc_exe:
321 out = os.popen(gcc_exe + ' -dumpversion','r')
322 out_string = out.read()
323 out.close()
324 result = re.search('(\d+\.\d+\.\d+)',out_string)
325 if result:
326 gcc_version = StrictVersion(result.group(1))
327 else:
328 gcc_version = None
329 else:
330 gcc_version = None
331 # EMX ld has no way of reporting version number, and we use GCC
332 # anyway - so we can link OMF DLLs
333 ld_version = None
334 return (gcc_version, ld_version)
335