|
1 """distutils.emxccompiler |
|
2 |
|
3 Provides the EMXCCompiler class, a subclass of UnixCCompiler that |
|
4 handles 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: emxccompiler.py 34786 2003-12-02 12:17:59Z aimacintyre $" |
|
23 |
|
24 import os,sys,copy |
|
25 from distutils.ccompiler import gen_preprocess_options, gen_lib_options |
|
26 from distutils.unixccompiler import UnixCCompiler |
|
27 from distutils.file_util import write_file |
|
28 from distutils.errors import DistutilsExecError, CompileError, UnknownFileError |
|
29 from distutils import log |
|
30 |
|
31 class 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 |
|
238 CONFIG_H_OK = "ok" |
|
239 CONFIG_H_NOTOK = "not ok" |
|
240 CONFIG_H_UNCERTAIN = "uncertain" |
|
241 |
|
242 def 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 s = f.read() |
|
276 f.close() |
|
277 |
|
278 except IOError, exc: |
|
279 # if we can't read this file, we cannot say it is wrong |
|
280 # the compiler will complain later about this file as missing |
|
281 return (CONFIG_H_UNCERTAIN, |
|
282 "couldn't read '%s': %s" % (fn, exc.strerror)) |
|
283 |
|
284 else: |
|
285 # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar |
|
286 if string.find(s,"__GNUC__") >= 0: |
|
287 return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) |
|
288 else: |
|
289 return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) |
|
290 |
|
291 |
|
292 def get_versions(): |
|
293 """ Try to find out the versions of gcc and ld. |
|
294 If not possible it returns None for it. |
|
295 """ |
|
296 from distutils.version import StrictVersion |
|
297 from distutils.spawn import find_executable |
|
298 import re |
|
299 |
|
300 gcc_exe = find_executable('gcc') |
|
301 if gcc_exe: |
|
302 out = os.popen(gcc_exe + ' -dumpversion','r') |
|
303 out_string = out.read() |
|
304 out.close() |
|
305 result = re.search('(\d+\.\d+\.\d+)',out_string) |
|
306 if result: |
|
307 gcc_version = StrictVersion(result.group(1)) |
|
308 else: |
|
309 gcc_version = None |
|
310 else: |
|
311 gcc_version = None |
|
312 # EMX ld has no way of reporting version number, and we use GCC |
|
313 # anyway - so we can link OMF DLLs |
|
314 ld_version = None |
|
315 return (gcc_version, ld_version) |