blob: 34315aa3cc75d72c4bf04ba89c202bf3697df7ab [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""Compiler abstraction model used by packaging.
2
3An abstract base class is defined in the ccompiler submodule, and
4concrete implementations suitable for various platforms are defined in
5the other submodules. The extension module is also placed in this
6package.
7
8In general, code should not instantiate compiler classes directly but
9use the new_compiler and customize_compiler functions provided in this
10module.
11
12The compiler system has a registration API: get_default_compiler,
13set_compiler, show_compilers.
14"""
15
16import os
17import sys
18import re
Tarek Ziade1231a4e2011-05-19 13:07:25 +020019import sysconfig
Tarek Ziade2bc55e42011-05-22 21:21:44 +020020
Tarek Ziade1231a4e2011-05-19 13:07:25 +020021from packaging.util import resolve_name
22from packaging.errors import PackagingPlatformError
Tarek Ziade2bc55e42011-05-22 21:21:44 +020023from packaging import logger
Tarek Ziade1231a4e2011-05-19 13:07:25 +020024
25def customize_compiler(compiler):
26 """Do any platform-specific customization of a CCompiler instance.
27
28 Mainly needed on Unix, so we can plug in the information that
29 varies across Unices and is stored in Python's Makefile.
30 """
31 if compiler.name == "unix":
32 cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags = (
33 sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
34 'CCSHARED', 'LDSHARED', 'SO', 'AR',
35 'ARFLAGS'))
36
37 if 'CC' in os.environ:
38 cc = os.environ['CC']
39 if 'CXX' in os.environ:
40 cxx = os.environ['CXX']
41 if 'LDSHARED' in os.environ:
42 ldshared = os.environ['LDSHARED']
43 if 'CPP' in os.environ:
44 cpp = os.environ['CPP']
45 else:
46 cpp = cc + " -E" # not always
47 if 'LDFLAGS' in os.environ:
48 ldshared = ldshared + ' ' + os.environ['LDFLAGS']
49 if 'CFLAGS' in os.environ:
50 cflags = opt + ' ' + os.environ['CFLAGS']
51 ldshared = ldshared + ' ' + os.environ['CFLAGS']
52 if 'CPPFLAGS' in os.environ:
53 cpp = cpp + ' ' + os.environ['CPPFLAGS']
54 cflags = cflags + ' ' + os.environ['CPPFLAGS']
55 ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
56 if 'AR' in os.environ:
57 ar = os.environ['AR']
58 if 'ARFLAGS' in os.environ:
59 archiver = ar + ' ' + os.environ['ARFLAGS']
60 else:
61 if ar_flags is not None:
62 archiver = ar + ' ' + ar_flags
63 else:
64 # see if its the proper default value
65 # mmm I don't want to backport the makefile
66 archiver = ar + ' rc'
67
68 cc_cmd = cc + ' ' + cflags
69 compiler.set_executables(
70 preprocessor=cpp,
71 compiler=cc_cmd,
72 compiler_so=cc_cmd + ' ' + ccshared,
73 compiler_cxx=cxx,
74 linker_so=ldshared,
75 linker_exe=cc,
76 archiver=archiver)
77
78 compiler.shared_lib_extension = so_ext
79
80
81# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
82# type for that platform. Keys are interpreted as re match
83# patterns. Order is important; platform mappings are preferred over
84# OS names.
85_default_compilers = (
86
87 # Platform string mappings
88
89 # on a cygwin built python we can use gcc like an ordinary UNIXish
90 # compiler
91 ('cygwin.*', 'unix'),
92 ('os2emx', 'emx'),
93
94 # OS name mappings
95 ('posix', 'unix'),
96 ('nt', 'msvc'),
97
98 )
99
100def get_default_compiler(osname=None, platform=None):
101 """ Determine the default compiler to use for the given platform.
102
103 osname should be one of the standard Python OS names (i.e. the
104 ones returned by os.name) and platform the common value
105 returned by sys.platform for the platform in question.
106
107 The default values are os.name and sys.platform in case the
108 parameters are not given.
109
110 """
111 if osname is None:
112 osname = os.name
113 if platform is None:
114 platform = sys.platform
115 for pattern, compiler in _default_compilers:
116 if re.match(pattern, platform) is not None or \
117 re.match(pattern, osname) is not None:
118 return compiler
119 # Defaults to Unix compiler
120 return 'unix'
121
122
123# compiler mapping
124# XXX useful to expose them? (i.e. get_compiler_names)
125_COMPILERS = {
126 'unix': 'packaging.compiler.unixccompiler.UnixCCompiler',
127 'msvc': 'packaging.compiler.msvccompiler.MSVCCompiler',
128 'cygwin': 'packaging.compiler.cygwinccompiler.CygwinCCompiler',
129 'mingw32': 'packaging.compiler.cygwinccompiler.Mingw32CCompiler',
130 'bcpp': 'packaging.compiler.bcppcompiler.BCPPCompiler',
131}
132
133def set_compiler(location):
134 """Add or change a compiler"""
135 cls = resolve_name(location)
136 # XXX we want to check the class here
137 _COMPILERS[cls.name] = cls
138
139
140def show_compilers():
141 """Print list of available compilers (used by the "--help-compiler"
142 options to "build", "build_ext", "build_clib").
143 """
144 from packaging.fancy_getopt import FancyGetopt
145 compilers = []
146
147 for name, cls in _COMPILERS.items():
148 if isinstance(cls, str):
149 cls = resolve_name(cls)
150 _COMPILERS[name] = cls
151
152 compilers.append(("compiler=" + name, None, cls.description))
153
154 compilers.sort()
155 pretty_printer = FancyGetopt(compilers)
156 pretty_printer.print_help("List of available compilers:")
157
158
159def new_compiler(plat=None, compiler=None, verbose=0, dry_run=False,
160 force=False):
161 """Generate an instance of some CCompiler subclass for the supplied
162 platform/compiler combination. 'plat' defaults to 'os.name'
163 (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
164 for that platform. Currently only 'posix' and 'nt' are supported, and
165 the default compilers are "traditional Unix interface" (UnixCCompiler
166 class) and Visual C++ (MSVCCompiler class). Note that it's perfectly
167 possible to ask for a Unix compiler object under Windows, and a
168 Microsoft compiler object under Unix -- if you supply a value for
169 'compiler', 'plat' is ignored.
170 """
171 if plat is None:
172 plat = os.name
173
174 try:
175 if compiler is None:
176 compiler = get_default_compiler(plat)
177
178 cls = _COMPILERS[compiler]
179 except KeyError:
180 msg = "don't know how to compile C/C++ code on platform '%s'" % plat
181 if compiler is not None:
182 msg = msg + " with '%s' compiler" % compiler
183 raise PackagingPlatformError(msg)
184
185 if isinstance(cls, str):
186 cls = resolve_name(cls)
187 _COMPILERS[compiler] = cls
188
189
190 # XXX The None is necessary to preserve backwards compatibility
191 # with classes that expect verbose to be the first positional
192 # argument.
193 return cls(None, dry_run, force)
194
195
196def gen_preprocess_options(macros, include_dirs):
197 """Generate C pre-processor options (-D, -U, -I) as used by at least
198 two types of compilers: the typical Unix compiler and Visual C++.
199 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
200 means undefine (-U) macro 'name', and (name,value) means define (-D)
201 macro 'name' to 'value'. 'include_dirs' is just a list of directory
202 names to be added to the header file search path (-I). Returns a list
203 of command-line options suitable for either Unix compilers or Visual
204 C++.
205 """
206 # XXX it would be nice (mainly aesthetic, and so we don't generate
207 # stupid-looking command lines) to go over 'macros' and eliminate
208 # redundant definitions/undefinitions (ie. ensure that only the
209 # latest mention of a particular macro winds up on the command
210 # line). I don't think it's essential, though, since most (all?)
211 # Unix C compilers only pay attention to the latest -D or -U
212 # mention of a macro on their command line. Similar situation for
213 # 'include_dirs'. I'm punting on both for now. Anyways, weeding out
214 # redundancies like this should probably be the province of
215 # CCompiler, since the data structures used are inherited from it
216 # and therefore common to all CCompiler classes.
217
218 pp_opts = []
219 for macro in macros:
220
221 if not isinstance(macro, tuple) and 1 <= len(macro) <= 2:
222 raise TypeError(
223 "bad macro definition '%s': each element of 'macros'"
224 "list must be a 1- or 2-tuple" % macro)
225
226 if len(macro) == 1: # undefine this macro
227 pp_opts.append("-U%s" % macro[0])
228 elif len(macro) == 2:
229 if macro[1] is None: # define with no explicit value
230 pp_opts.append("-D%s" % macro[0])
231 else:
232 # XXX *don't* need to be clever about quoting the
233 # macro value here, because we're going to avoid the
234 # shell at all costs when we spawn the command!
235 pp_opts.append("-D%s=%s" % macro)
236
237 for dir in include_dirs:
238 pp_opts.append("-I%s" % dir)
239
240 return pp_opts
241
242
243def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
244 """Generate linker options for searching library directories and
245 linking with specific libraries.
246
247 'libraries' and 'library_dirs' are, respectively, lists of library names
248 (not filenames!) and search directories. Returns a list of command-line
249 options suitable for use with some compiler (depending on the two format
250 strings passed in).
251 """
252 lib_opts = []
253
254 for dir in library_dirs:
255 lib_opts.append(compiler.library_dir_option(dir))
256
257 for dir in runtime_library_dirs:
258 opt = compiler.runtime_library_dir_option(dir)
259 if isinstance(opt, list):
260 lib_opts.extend(opt)
261 else:
262 lib_opts.append(opt)
263
264 # XXX it's important that we *not* remove redundant library mentions!
265 # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
266 # resolve all symbols. I just hope we never have to say "-lfoo obj.o
267 # -lbar" to get things to work -- that's certainly a possibility, but a
268 # pretty nasty way to arrange your C code.
269
270 for lib in libraries:
271 lib_dir, lib_name = os.path.split(lib)
272 if lib_dir != '':
273 lib_file = compiler.find_library_file([lib_dir], lib_name)
274 if lib_file is not None:
275 lib_opts.append(lib_file)
276 else:
Tarek Ziade2bc55e42011-05-22 21:21:44 +0200277 logger.warning("no library file corresponding to "
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200278 "'%s' found (skipping)" % lib)
279 else:
280 lib_opts.append(compiler.library_option(lib))
281
282 return lib_opts