1
"""distutils.emxccompiler
3
Provides the EMXCCompiler class, a subclass of UnixCCompiler that
4
handles the EMX port of the GNU C compiler to OS/2.
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.
13
# * only use OMF objects and use LINK386 as the linker (-Zomf)
15
# * always build for multithreading (-Zmt) as the accompanying OS/2 port
16
# of Python is only distributed with threads enabled.
18
# tested configurations:
20
# * EMX gcc 2.81/EMX 0.9d fix03
22
__revision__ = "$Id: emxccompiler.py 65860 2008-08-19 17:56:33Z antoine.pitrou $"
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
31
class EMXCCompiler (UnixCCompiler):
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"
47
UnixCCompiler.__init__ (self, verbose, dry_run, force)
49
(status, details) = check_config_h()
50
self.debug_print("Python's GCC status: %s (details: %s)" %
52
if status is not CONFIG_H_OK:
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.")
58
(self.gcc_version, self.ld_version) = \
60
self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" %
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')
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"]
77
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
79
# gcc requires '.rc' compiled to binary ('.res') files !!!
81
self.spawn(["rc", "-r", src])
82
except DistutilsExecError as msg:
83
raise CompileError(msg)
84
else: # for other files use the C-compiler
86
self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
88
except DistutilsExecError as msg:
89
raise CompileError(msg)
98
runtime_library_dirs=None,
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 [])
111
# Additional libraries
112
libraries.extend(self.dll_libraries)
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.)
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))
131
# generate the filenames for these files
132
def_file = os.path.join(temp_dir, dll_name + ".def")
136
"LIBRARY %s INITINSTANCE TERMINSTANCE" % \
137
os.path.splitext(os.path.basename(output_filename))[0],
138
"DATA MULTIPLE NONSHARED",
140
for sym in export_symbols:
141
contents.append(' "%s"' % sym)
142
self.execute(write_file, (def_file, contents),
143
"writing %s" % def_file)
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)
149
#end: if ((export_symbols is not None) and
150
# (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
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))
159
extra_preargs.append("-s")
161
UnixCCompiler.link(self,
168
runtime_library_dirs,
169
None, # export_symbols, we do this in our def-file
178
# -- Miscellaneous methods -----------------------------------------
180
# override the object_filenames method from CCompiler to
181
# support rc and res-files
182
def object_filenames (self,
186
if output_dir is None: output_dir = ''
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("unknown file type '%s' (from '%s')" % \
195
base = os.path.basename (base)
197
# these need to be compiled to object files
198
obj_names.append (os.path.join (output_dir,
199
base + self.res_extension))
201
obj_names.append (os.path.join (output_dir,
202
base + self.obj_extension))
205
# object_filenames ()
207
# override the find_library_file method from UnixCCompiler
208
# to deal with file naming/searching differences
209
def find_library_file(self, dirs, lib, debug=0):
210
shortlib = '%s.lib' % lib
211
longlib = 'lib%s.lib' % lib # this form very rare
213
# get EMX's default library directory search path
215
emx_dirs = os.environ['LIBRARY_PATH'].split(';')
219
for dir in dirs + emx_dirs:
220
shortlibp = os.path.join(dir, shortlib)
221
longlibp = os.path.join(dir, longlib)
222
if os.path.exists(shortlibp):
224
elif os.path.exists(longlibp):
227
# Oops, didn't find it in *any* of 'dirs'
233
# Because these compilers aren't configured in Python's pyconfig.h file by
234
# default, we should at least warn the user if he is using a unmodified
238
CONFIG_H_NOTOK = "not ok"
239
CONFIG_H_UNCERTAIN = "uncertain"
241
def check_config_h():
243
"""Check if the current Python installation (specifically, pyconfig.h)
244
appears amenable to building extensions with GCC. Returns a tuple
245
(status, details), where 'status' is one of the following constants:
247
all is well, go ahead and compile
251
not sure -- unable to read pyconfig.h
252
'details' is a human-readable string explaining the situation.
254
Note there are two ways to conclude "OK": either 'sys.version' contains
255
the string "GCC" (implying that this Python was built with GCC), or the
256
installed "pyconfig.h" contains the string "__GNUC__".
259
# XXX since this function also checks sys.version, it's not strictly a
260
# "pyconfig.h" check -- should probably be renamed...
262
from distutils import sysconfig
263
# if sys.version contains GCC then python was compiled with
264
# GCC, and the pyconfig.h file should be OK
265
if sys.version.find("GCC") >= 0:
266
return (CONFIG_H_OK, "sys.version mentions 'GCC'")
268
fn = sysconfig.get_config_h_filename()
270
# It would probably better to read single lines to search.
271
# But we do this only once, and it is fast enough
276
except IOError as 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))
283
# "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
284
if s.find("__GNUC__") >= 0:
285
return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
287
return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
291
""" Try to find out the versions of gcc and ld.
292
If not possible it returns None for it.
294
from distutils.version import StrictVersion
295
from distutils.spawn import find_executable
298
gcc_exe = find_executable('gcc')
300
out = os.popen(gcc_exe + ' -dumpversion','r')
301
out_string = out.read()
303
result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII)
305
gcc_version = StrictVersion(result.group(1))
310
# EMX ld has no way of reporting version number, and we use GCC
311
# anyway - so we can link OMF DLLs
313
return (gcc_version, ld_version)