blob: c2c73b0dec9ad19cbd3c8c2165f531d920dae556 [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.)
Marc-André Lemburg9273ec72002-02-06 18:22:48 +000084
Jeremy Hylton1bba31d2002-06-13 17:28:18 +000085 def compile(self, sources,
86 output_dir=None, macros=None, include_dirs=None, debug=0,
87 extra_preargs=None, extra_postargs=None, depends=None):
88
89 macros, objects, extra_postargs, pp_opts, build = \
90 self._setup_compile(output_dir, macros, include_dirs, sources,
91 depends, extra_postargs)
92 cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
Marc-André Lemburg9273ec72002-02-06 18:22:48 +000093
Jeremy Hylton1bba31d2002-06-13 17:28:18 +000094 for obj, (src, ext) in build.items():
95 if ext == '.rc':
96 # gcc requires '.rc' compiled to binary ('.res') files !!!
97 try:
98 self.spawn (["rc","-r",src])
99 except DistutilsExecError, msg:
100 raise CompileError, msg
101 else: # for other files use the C-compiler
102 try:
103 self.spawn (self.compiler_so + cc_args +
104 [src, '-o', obj] +
105 extra_postargs)
106 except DistutilsExecError, msg:
107 raise CompileError, msg
Marc-André Lemburg9273ec72002-02-06 18:22:48 +0000108
109 # Return *all* object filenames, not just the ones we just built.
110 return objects
111
112 # compile ()
113
114
115 def link (self,
116 target_desc,
117 objects,
118 output_filename,
119 output_dir=None,
120 libraries=None,
121 library_dirs=None,
122 runtime_library_dirs=None,
123 export_symbols=None,
124 debug=0,
125 extra_preargs=None,
126 extra_postargs=None,
127 build_temp=None):
128
129 # use separate copies, so we can modify the lists
130 extra_preargs = copy.copy(extra_preargs or [])
131 libraries = copy.copy(libraries or [])
132 objects = copy.copy(objects or [])
133
134 # Additional libraries
135 libraries.extend(self.dll_libraries)
136
137 # handle export symbols by creating a def-file
138 # with executables this only works with gcc/ld as linker
139 if ((export_symbols is not None) and
140 (target_desc != self.EXECUTABLE)):
141 # (The linker doesn't do anything if output is up-to-date.
142 # So it would probably better to check if we really need this,
143 # but for this we had to insert some unchanged parts of
144 # UnixCCompiler, and this is not what we want.)
145
146 # we want to put some files in the same directory as the
147 # object files are, build_temp doesn't help much
148 # where are the object files
149 temp_dir = os.path.dirname(objects[0])
150 # name of dll to give the helper files the same base name
151 (dll_name, dll_extension) = os.path.splitext(
152 os.path.basename(output_filename))
153
154 # generate the filenames for these files
155 def_file = os.path.join(temp_dir, dll_name + ".def")
Marc-André Lemburg9273ec72002-02-06 18:22:48 +0000156
157 # Generate .def file
158 contents = [
Jeremy Hyltona2f99892002-06-04 20:26:44 +0000159 "LIBRARY %s INITINSTANCE TERMINSTANCE" % \
160 os.path.splitext(os.path.basename(output_filename))[0],
Marc-André Lemburg9273ec72002-02-06 18:22:48 +0000161 "DATA MULTIPLE NONSHARED",
162 "EXPORTS"]
163 for sym in export_symbols:
164 contents.append(' "%s"' % sym)
165 self.execute(write_file, (def_file, contents),
166 "writing %s" % def_file)
167
168 # next add options for def-file and to creating import libraries
169 # for gcc/ld the def-file is specified as any other object files
170 objects.append(def_file)
171
172 #end: if ((export_symbols is not None) and
173 # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
174
175 # who wants symbols and a many times larger output file
176 # should explicitly switch the debug mode on
177 # otherwise we let dllwrap/ld strip the output file
178 # (On my machine: 10KB < stripped_file < ??100KB
179 # unstripped_file = stripped_file + XXX KB
180 # ( XXX=254 for a typical python extension))
181 if not debug:
182 extra_preargs.append("-s")
183
184 UnixCCompiler.link(self,
185 target_desc,
186 objects,
187 output_filename,
188 output_dir,
189 libraries,
190 library_dirs,
191 runtime_library_dirs,
192 None, # export_symbols, we do this in our def-file
193 debug,
194 extra_preargs,
195 extra_postargs,
196 build_temp)
197
198 # link ()
199
200 # -- Miscellaneous methods -----------------------------------------
201
202 # overwrite the one from CCompiler to support rc and res-files
203 def object_filenames (self,
204 source_filenames,
205 strip_dir=0,
206 output_dir=''):
207 if output_dir is None: output_dir = ''
208 obj_names = []
209 for src_name in source_filenames:
210 # use normcase to make sure '.rc' is really '.rc' and not '.RC'
211 (base, ext) = os.path.splitext (os.path.normcase(src_name))
212 if ext not in (self.src_extensions + ['.rc']):
213 raise UnknownFileError, \
214 "unknown file type '%s' (from '%s')" % \
215 (ext, src_name)
216 if strip_dir:
217 base = os.path.basename (base)
218 if ext == '.rc':
219 # these need to be compiled to object files
220 obj_names.append (os.path.join (output_dir,
221 base + self.res_extension))
222 else:
223 obj_names.append (os.path.join (output_dir,
224 base + self.obj_extension))
225 return obj_names
226
227 # object_filenames ()
228
229# class EMXCCompiler
230
231
232# Because these compilers aren't configured in Python's pyconfig.h file by
233# default, we should at least warn the user if he is using a unmodified
234# version.
235
236CONFIG_H_OK = "ok"
237CONFIG_H_NOTOK = "not ok"
238CONFIG_H_UNCERTAIN = "uncertain"
239
240def check_config_h():
241
242 """Check if the current Python installation (specifically, pyconfig.h)
243 appears amenable to building extensions with GCC. Returns a tuple
244 (status, details), where 'status' is one of the following constants:
245 CONFIG_H_OK
246 all is well, go ahead and compile
247 CONFIG_H_NOTOK
248 doesn't look good
249 CONFIG_H_UNCERTAIN
250 not sure -- unable to read pyconfig.h
251 'details' is a human-readable string explaining the situation.
252
253 Note there are two ways to conclude "OK": either 'sys.version' contains
254 the string "GCC" (implying that this Python was built with GCC), or the
255 installed "pyconfig.h" contains the string "__GNUC__".
256 """
257
258 # XXX since this function also checks sys.version, it's not strictly a
259 # "pyconfig.h" check -- should probably be renamed...
260
261 from distutils import sysconfig
262 import string
263 # if sys.version contains GCC then python was compiled with
264 # GCC, and the pyconfig.h file should be OK
265 if string.find(sys.version,"GCC") >= 0:
266 return (CONFIG_H_OK, "sys.version mentions 'GCC'")
267
268 fn = sysconfig.get_config_h_filename()
269 try:
270 # It would probably better to read single lines to search.
271 # But we do this only once, and it is fast enough
272 f = open(fn)
273 s = f.read()
274 f.close()
275
276 except IOError, exc:
277 # if we can't read this file, we cannot say it is wrong
278 # the compiler will complain later about this file as missing
279 return (CONFIG_H_UNCERTAIN,
280 "couldn't read '%s': %s" % (fn, exc.strerror))
281
282 else:
283 # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
284 if string.find(s,"__GNUC__") >= 0:
285 return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
286 else:
287 return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
288
289
290def get_versions():
291 """ Try to find out the versions of gcc and ld.
292 If not possible it returns None for it.
293 """
294 from distutils.version import StrictVersion
295 from distutils.spawn import find_executable
296 import re
297
298 gcc_exe = find_executable('gcc')
299 if gcc_exe:
300 out = os.popen(gcc_exe + ' -dumpversion','r')
301 out_string = out.read()
302 out.close()
303 result = re.search('(\d+\.\d+\.\d+)',out_string)
304 if result:
305 gcc_version = StrictVersion(result.group(1))
306 else:
307 gcc_version = None
308 else:
309 gcc_version = None
310 # EMX ld has no way of reporting version number, and we use GCC
311 # anyway - so we can link OMF DLLs
312 ld_version = None
313 return (gcc_version, ld_version)
314