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

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

python: generate both 8.3 and long names for pyd dynamic libraries. fixes ticket#185.

  • Property svn:eol-style set to native
File size: 11.9 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 os.remove( output_filename)
183 os.symlink( pyd_name8 + ".pyd", output_filename)
184
185 # link ()
186
187 # -- Miscellaneous methods -----------------------------------------
188
189 def object_filenames (self,
190 source_filenames,
191 strip_dir=0,
192 output_dir=''):
193 # Copied from ccompiler.py, extended to return .res as 'object'-file
194 # for .rc input file
195 if output_dir is None: output_dir = ''
196 obj_names = []
197 for src_name in source_filenames:
198 (base, ext) = os.path.splitext (src_name)
199 base = os.path.splitdrive(base)[1] # Chop off the drive
200 base = base[os.path.isabs(base):] # If abs, chop off leading /
201 if ext not in (self.src_extensions + self._rc_extensions):
202 raise UnknownFileError, \
203 "unknown file type '%s' (from '%s')" % \
204 (ext, src_name)
205 if strip_dir:
206 base = os.path.basename (base)
207 if ext in self._rc_extensions:
208 obj_names.append (os.path.join (output_dir,
209 base + self.res_extension))
210 else:
211 obj_names.append (os.path.join (output_dir,
212 base + self.obj_extension))
213 return obj_names
214
215 # object_filenames ()
216
217 # override the find_library_file method from UnixCCompiler
218 # to deal with file naming/searching differences
219 def find_library_file(self, dirs, lib, debug=0):
220 try_names = [lib + ".lib", lib + ".a", "lib" + lib + ".lib", "lib" + lib + ".a"]
221
222 # get EMX's default library directory search path
223 try:
224 emx_dirs = os.environ['LIBRARY_PATH'].split(';')
225 except KeyError:
226 emx_dirs = []
227
228 for dir in dirs + emx_dirs:
229 for name in try_names:
230 libfile = os.path.join(dir, name)
231 #print "libfile:",libfile
232 if os.path.exists(libfile):
233 return libfile
234
235 # Oops, didn't find it in *any* of 'dirs'
236 return None
237
238# class EMXCCompiler
239
240
241# Because these compilers aren't configured in Python's pyconfig.h file by
242# default, we should at least warn the user if he is using a unmodified
243# version.
244
245CONFIG_H_OK = "ok"
246CONFIG_H_NOTOK = "not ok"
247CONFIG_H_UNCERTAIN = "uncertain"
248
249def check_config_h():
250
251 """Check if the current Python installation (specifically, pyconfig.h)
252 appears amenable to building extensions with GCC. Returns a tuple
253 (status, details), where 'status' is one of the following constants:
254 CONFIG_H_OK
255 all is well, go ahead and compile
256 CONFIG_H_NOTOK
257 doesn't look good
258 CONFIG_H_UNCERTAIN
259 not sure -- unable to read pyconfig.h
260 'details' is a human-readable string explaining the situation.
261
262 Note there are two ways to conclude "OK": either 'sys.version' contains
263 the string "GCC" (implying that this Python was built with GCC), or the
264 installed "pyconfig.h" contains the string "__GNUC__".
265 """
266
267 # XXX since this function also checks sys.version, it's not strictly a
268 # "pyconfig.h" check -- should probably be renamed...
269
270 from distutils import sysconfig
271 import string
272 # if sys.version contains GCC then python was compiled with
273 # GCC, and the pyconfig.h file should be OK
274 if string.find(sys.version,"GCC") >= 0:
275 return (CONFIG_H_OK, "sys.version mentions 'GCC'")
276
277 fn = sysconfig.get_config_h_filename()
278 try:
279 # It would probably better to read single lines to search.
280 # But we do this only once, and it is fast enough
281 f = open(fn)
282 try:
283 s = f.read()
284 finally:
285 f.close()
286
287 except IOError, exc:
288 # if we can't read this file, we cannot say it is wrong
289 # the compiler will complain later about this file as missing
290 return (CONFIG_H_UNCERTAIN,
291 "couldn't read '%s': %s" % (fn, exc.strerror))
292
293 else:
294 # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
295 if string.find(s,"__GNUC__") >= 0:
296 return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
297 else:
298 return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
299
300
301def get_versions():
302 """ Try to find out the versions of gcc and ld.
303 If not possible it returns None for it.
304 """
305 from distutils.version import StrictVersion
306 from distutils.spawn import find_executable
307 import re
308
309 gcc_exe = find_executable('gcc')
310 if gcc_exe:
311 out = os.popen(gcc_exe + ' -dumpversion','r')
312 try:
313 out_string = out.read()
314 finally:
315 out.close()
316 result = re.search('(\d+\.\d+\.\d+)',out_string)
317 if result:
318 gcc_version = StrictVersion(result.group(1))
319 else:
320 gcc_version = None
321 else:
322 gcc_version = None
323 # EMX ld has no way of reporting version number, and we use GCC
324 # anyway - so we can link OMF DLLs
325 ld_version = None
326 return (gcc_version, ld_version)
Note: See TracBrowser for help on using the repository browser.