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

Last change on this file since 977 was 977, checked in by Yuri Dario, 9 years ago

python: add cx to list of std libs, improve search filter for import libs.

  • Property svn:eol-style set to native
File size: 12.0 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 # get pyd name and extension
110 pyd_name8, pyd_ext = os.path.splitext( os.path.basename(output_filename))
111 # if name is longer than 8.3, generate a hashed 8.3 name
112 if len(os.path.basename(output_filename)) > 8+1+3:
113 pyd_name8 = os.path.basename(output_filename)[:3] + str(reduce(lambda x,y:x+y, map(ord, output_filename)) % 65536)
114
115 # full relative path of pyd
116 pyd_name = os.path.join(os.path.dirname(output_filename),
117 pyd_name8 + ".pyd")
118
119 # handle export symbols by creating a def-file
120 # with executables this only works with gcc/ld as linker
121 if ((export_symbols is not None) and
122 (target_desc != self.EXECUTABLE)):
123 # (The linker doesn't do anything if output is up-to-date.
124 # So it would probably better to check if we really need this,
125 # but for this we had to insert some unchanged parts of
126 # UnixCCompiler, and this is not what we want.)
127
128 # we want to put some files in the same directory as the
129 # object files are, build_temp doesn't help much
130 # where are the object files
131 temp_dir = os.path.dirname(objects[0])
132 # name of dll to give the helper files the same base name
133 (dll_name, dll_extension) = os.path.splitext(
134 os.path.basename(output_filename))
135
136 # generate the filenames for these files
137 def_file = os.path.join(temp_dir, dll_name + ".def")
138
139 # Generate .def file
140 contents = [
141 "LIBRARY %s INITINSTANCE TERMINSTANCE" % \
142 pyd_name8,
143 "DATA MULTIPLE NONSHARED",
144 "EXPORTS"]
145 for sym in export_symbols:
146 contents.append(' "_%s"' % sym)
147 self.execute(write_file, (def_file, contents),
148 "writing %s" % def_file)
149
150 # next add options for def-file and to creating import libraries
151 # for gcc/ld the def-file is specified as any other object files
152 objects.append(def_file)
153
154 #end: if ((export_symbols is not None) and
155 # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
156
157 # who wants symbols and a many times larger output file
158 # should explicitly switch the debug mode on
159 # otherwise we let dllwrap/ld strip the output file
160 # (On my machine: 10KB < stripped_file < ??100KB
161 # unstripped_file = stripped_file + XXX KB
162 # ( XXX=254 for a typical python extension))
163 if not debug:
164 extra_preargs.append("-s")
165
166 UnixCCompiler.link(self,
167 target_desc,
168 objects,
169 pyd_name,
170 output_dir,
171 libraries,
172 library_dirs,
173 runtime_library_dirs,
174 None, # export_symbols, we do this in our def-file
175 debug,
176 extra_preargs,
177 extra_postargs,
178 build_temp,
179 target_lang)
180 # if filename exceed 8.3, create a symlink to 8.3 pyd
181 if len(os.path.basename(output_filename)) > 8+1+3:
182 try:
183 os.remove( output_filename)
184 except OSError:
185 pass
186 os.symlink( pyd_name8 + ".pyd", output_filename)
187
188 # link ()
189
190 # -- Miscellaneous methods -----------------------------------------
191
192 def object_filenames (self,
193 source_filenames,
194 strip_dir=0,
195 output_dir=''):
196 # Copied from ccompiler.py, extended to return .res as 'object'-file
197 # for .rc input file
198 if output_dir is None: output_dir = ''
199 obj_names = []
200 for src_name in source_filenames:
201 (base, ext) = os.path.splitext (src_name)
202 base = os.path.splitdrive(base)[1] # Chop off the drive
203 base = base[os.path.isabs(base):] # If abs, chop off leading /
204 if ext not in (self.src_extensions + self._rc_extensions):
205 raise UnknownFileError, \
206 "unknown file type '%s' (from '%s')" % \
207 (ext, src_name)
208 if strip_dir:
209 base = os.path.basename (base)
210 if ext in self._rc_extensions:
211 obj_names.append (os.path.join (output_dir,
212 base + self.res_extension))
213 else:
214 obj_names.append (os.path.join (output_dir,
215 base + self.obj_extension))
216 return obj_names
217
218 # object_filenames ()
219
220 # override the find_library_file method from UnixCCompiler
221 # to deal with file naming/searching differences
222 def find_library_file(self, dirs, lib, debug=0):
223 try_names = [lib + ".lib", lib + ".a", "lib" + lib + ".lib", "lib" + lib + ".a", "lib" + lib + "_dll.a" ]
224
225 # get EMX's default library directory search path
226 try:
227 emx_dirs = os.environ['LIBRARY_PATH'].split(';')
228 except KeyError:
229 emx_dirs = []
230
231 for dir in dirs + emx_dirs:
232 for name in try_names:
233 libfile = os.path.join(dir, name)
234 #print "libfile:",libfile
235 if os.path.exists(libfile):
236 return libfile
237
238 # Oops, didn't find it in *any* of 'dirs'
239 return None
240
241# class EMXCCompiler
242
243
244# Because these compilers aren't configured in Python's pyconfig.h file by
245# default, we should at least warn the user if he is using a unmodified
246# version.
247
248CONFIG_H_OK = "ok"
249CONFIG_H_NOTOK = "not ok"
250CONFIG_H_UNCERTAIN = "uncertain"
251
252def check_config_h():
253
254 """Check if the current Python installation (specifically, pyconfig.h)
255 appears amenable to building extensions with GCC. Returns a tuple
256 (status, details), where 'status' is one of the following constants:
257 CONFIG_H_OK
258 all is well, go ahead and compile
259 CONFIG_H_NOTOK
260 doesn't look good
261 CONFIG_H_UNCERTAIN
262 not sure -- unable to read pyconfig.h
263 'details' is a human-readable string explaining the situation.
264
265 Note there are two ways to conclude "OK": either 'sys.version' contains
266 the string "GCC" (implying that this Python was built with GCC), or the
267 installed "pyconfig.h" contains the string "__GNUC__".
268 """
269
270 # XXX since this function also checks sys.version, it's not strictly a
271 # "pyconfig.h" check -- should probably be renamed...
272
273 from distutils import sysconfig
274 import string
275 # if sys.version contains GCC then python was compiled with
276 # GCC, and the pyconfig.h file should be OK
277 if string.find(sys.version,"GCC") >= 0:
278 return (CONFIG_H_OK, "sys.version mentions 'GCC'")
279
280 fn = sysconfig.get_config_h_filename()
281 try:
282 # It would probably better to read single lines to search.
283 # But we do this only once, and it is fast enough
284 f = open(fn)
285 try:
286 s = f.read()
287 finally:
288 f.close()
289
290 except IOError, exc:
291 # if we can't read this file, we cannot say it is wrong
292 # the compiler will complain later about this file as missing
293 return (CONFIG_H_UNCERTAIN,
294 "couldn't read '%s': %s" % (fn, exc.strerror))
295
296 else:
297 # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
298 if string.find(s,"__GNUC__") >= 0:
299 return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
300 else:
301 return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
302
303
304def get_versions():
305 """ Try to find out the versions of gcc and ld.
306 If not possible it returns None for it.
307 """
308 from distutils.version import StrictVersion
309 from distutils.spawn import find_executable
310 import re
311
312 gcc_exe = find_executable('gcc')
313 if gcc_exe:
314 out = os.popen(gcc_exe + ' -dumpversion','r')
315 try:
316 out_string = out.read()
317 finally:
318 out.close()
319 result = re.search('(\d+\.\d+\.\d+)',out_string)
320 if result:
321 gcc_version = StrictVersion(result.group(1))
322 else:
323 gcc_version = None
324 else:
325 gcc_version = None
326 # EMX ld has no way of reporting version number, and we use GCC
327 # anyway - so we can link OMF DLLs
328 ld_version = None
329 return (gcc_version, ld_version)
Note: See TracBrowser for help on using the repository browser.