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