blob: 570f71a5c19144fd51e0a5f2da2c60c8e3f14c7d [file] [log] [blame]
Greg Ward28a5f442000-06-06 02:57:07 +00001"""distutils.command.config
2
3Implements the Distutils 'config' command, a (mostly) empty command class
4that exists mainly to be sub-classed by specific module distributions and
5applications. The idea is that while every "config" command is different,
6at least they're all named the same, and users always see "config" in the
7list of standard commands. Also, this is a good place to put common
8configure-like tasks: "try to compile this C code", or "figure out where
9this header file lives".
10"""
11
12# created 2000/05/29, Greg Ward
13
14__revision__ = "$Id$"
15
Greg Ward59ac7092000-06-21 03:00:50 +000016import sys, os, string, re
Greg Ward28a5f442000-06-06 02:57:07 +000017from distutils.core import Command
18from distutils.errors import DistutilsExecError
19
20
21LANG_EXT = {'c': '.c',
22 'c++': '.cxx'}
23
24class config (Command):
25
26 description = "prepare to build"
27
28 user_options = [
29 ('compiler=', None,
30 "specify the compiler type"),
31 ('cc=', None,
32 "specify the compiler executable"),
33 ('include-dirs=', 'I',
34 "list of directories to search for header files"),
35 ('define=', 'D',
36 "C preprocessor macros to define"),
37 ('undef=', 'U',
38 "C preprocessor macros to undefine"),
39 ('libraries=', 'l',
40 "external C libraries to link with"),
41 ('library-dirs=', 'L',
42 "directories to search for external C libraries"),
Greg Ward59ac7092000-06-21 03:00:50 +000043
44 ('noisy', None,
45 "show every action (compile, link, run, ...) taken"),
46 ('dump-source', None,
47 "dump generated source files before attempting to compile them"),
Greg Ward28a5f442000-06-06 02:57:07 +000048 ]
49
50
51 # The three standard command methods: since the "config" command
52 # does nothing by default, these are empty.
53
54 def initialize_options (self):
55 self.compiler = None
56 self.cc = None
57 self.include_dirs = None
58 #self.define = None
59 #self.undef = None
60 self.libraries = None
61 self.library_dirs = None
62
Greg Ward59ac7092000-06-21 03:00:50 +000063 # maximal output for now
64 self.noisy = 1
65 self.dump_source = 1
66
67 # list of temporary files generated along-the-way that we have
68 # to clean at some point
69 self.temp_files = []
70
Greg Ward28a5f442000-06-06 02:57:07 +000071 def finalize_options (self):
72 pass
73
74 def run (self):
75 pass
76
77
78 # Utility methods for actual "config" commands. The interfaces are
79 # loosely based on Autoconf macros of similar names. Sub-classes
80 # may use these freely.
81
82 def _check_compiler (self):
83 """Check that 'self.compiler' really is a CCompiler object;
84 if not, make it one.
85 """
86 # We do this late, and only on-demand, because this is an expensive
87 # import.
88 from distutils.ccompiler import CCompiler, new_compiler
89 if not isinstance(self.compiler, CCompiler):
90 self.compiler = new_compiler (compiler=self.compiler,
Greg Ward59ac7092000-06-21 03:00:50 +000091 verbose=self.noisy,
Greg Ward28a5f442000-06-06 02:57:07 +000092 dry_run=self.dry_run,
93 force=1)
94 if self.include_dirs:
95 self.compiler.set_include_dirs(self.include_dirs)
96 if self.libraries:
97 self.compiler.set_libraries(self.libraries)
98 if self.library_dirs:
99 self.compiler.set_library_dirs(self.library_dirs)
100
101
Greg Ward59ac7092000-06-21 03:00:50 +0000102 def _gen_temp_sourcefile (self, body, headers, lang):
Greg Ward28a5f442000-06-06 02:57:07 +0000103 filename = "_configtest" + LANG_EXT[lang]
104 file = open(filename, "w")
Greg Ward59ac7092000-06-21 03:00:50 +0000105 if headers:
106 for header in headers:
107 file.write("#include <%s>\n" % header)
108 file.write("\n")
Greg Ward28a5f442000-06-06 02:57:07 +0000109 file.write(body)
Greg Ward59ac7092000-06-21 03:00:50 +0000110 if body[-1] != "\n":
111 file.write("\n")
Greg Ward28a5f442000-06-06 02:57:07 +0000112 file.close()
113 return filename
114
Greg Ward59ac7092000-06-21 03:00:50 +0000115 def _preprocess (self, body, headers, lang):
116 src = self._gen_temp_sourcefile(body, headers, lang)
117 out = "_configtest.i"
118 self.temp_files.extend([src, out])
119 self.compiler.preprocess(src, out)
120 return (src, out)
121
122 def _compile (self, body, headers, lang):
123 src = self._gen_temp_sourcefile(body, headers, lang)
124 if self.dump_source:
125 dump_file(src, "compiling '%s':" % src)
126 (obj,) = self.compiler.object_filenames([src])
127 self.temp_files.extend([src, obj])
128 self.compiler.compile([src])
Greg Ward28a5f442000-06-06 02:57:07 +0000129 return (src, obj)
130
Greg Ward59ac7092000-06-21 03:00:50 +0000131 def _link (self, body, headers, libraries, library_dirs, lang):
132 (src, obj) = self._compile(body, headers, lang)
133 prog = os.path.splitext(os.path.basename(src))[0]
134 self.temp_files.append(prog) # XXX should be prog + exe_ext
135 self.compiler.link_executable([obj], prog,
136 libraries=libraries,
137 library_dirs=library_dirs)
138 return (src, obj, prog)
Greg Ward28a5f442000-06-06 02:57:07 +0000139
140 def _clean (self, *filenames):
Greg Ward59ac7092000-06-21 03:00:50 +0000141 if not filenames:
142 filenames = self.temp_files
143 self.temp_files = []
Greg Ward28a5f442000-06-06 02:57:07 +0000144 self.announce("removing: " + string.join(filenames))
145 for filename in filenames:
146 try:
147 os.remove(filename)
148 except OSError:
149 pass
150
151
Greg Ward28a5f442000-06-06 02:57:07 +0000152 # XXX these ignore the dry-run flag: what to do, what to do? even if
153 # you want a dry-run build, you still need some sort of configuration
154 # info. My inclination is to make it up to the real config command to
155 # consult 'dry_run', and assume a default (minimal) configuration if
156 # true. The problem with trying to do it here is that you'd have to
157 # return either true or false from all the 'try' methods, neither of
158 # which is correct.
159
Greg Ward59ac7092000-06-21 03:00:50 +0000160 # XXX need access to the header search path and maybe default macros.
161
162 def try_cpp (self, body=None, headers=None, lang="c"):
163 """Construct a source file from 'body' (a string containing lines
164 of C/C++ code) and 'headers' (a list of header files to include)
165 and run it through the preprocessor. Return true if the
166 preprocessor succeeded, false if there were any errors.
167 ('body' probably isn't of much use, but what the heck.)
168 """
169 from distutils.ccompiler import CompileError
170 self._check_compiler()
171 ok = 1
172 try:
173 self._preprocess(body, headers, lang)
174 except CompileError:
175 ok = 0
176
177 self._clean()
178 return ok
179
180 def search_cpp (self, pattern, body=None, headers=None, lang="c"):
181 """Construct a source file (just like 'try_cpp()'), run it through
182 the preprocessor, and return true if any line of the output matches
183 'pattern'. 'pattern' should either be a compiled regex object or a
184 string containing a regex. If both 'body' and 'headers' are None,
185 preprocesses an empty file -- which can be useful to determine the
186 symbols the preprocessor and compiler set by default.
187 """
188
189 self._check_compiler()
190 (src, out) = self._preprocess(body, headers, lang)
191
192 if type(pattern) is StringType:
193 pattern = re.compile(pattern)
194
195 file = open(out)
196 match = 0
197 while 1:
198 line = file.readline()
199 if line == '':
200 break
201 if pattern.search(pattern):
202 match = 1
203 break
204
205 file.close()
206 self._clean()
207 return match
208
209 def try_compile (self, body, headers=None, lang="c"):
210 """Try to compile a source file built from 'body' and 'headers'.
211 Return true on success, false otherwise.
Greg Ward28a5f442000-06-06 02:57:07 +0000212 """
213 from distutils.ccompiler import CompileError
214 self._check_compiler()
215 try:
Greg Ward59ac7092000-06-21 03:00:50 +0000216 self._compile(body, headers, lang)
Greg Ward28a5f442000-06-06 02:57:07 +0000217 ok = 1
218 except CompileError:
219 ok = 0
220
221 self.announce(ok and "success!" or "failure.")
Greg Ward59ac7092000-06-21 03:00:50 +0000222 self._clean()
Greg Ward28a5f442000-06-06 02:57:07 +0000223 return ok
224
Greg Ward59ac7092000-06-21 03:00:50 +0000225 def try_link (self,
226 body, headers=None,
227 libraries=None, library_dirs=None,
228 lang="c"):
229 """Try to compile and link a source file, built from 'body' and
230 'headers', to executable form. Return true on success, false
Greg Ward28a5f442000-06-06 02:57:07 +0000231 otherwise.
232 """
233 from distutils.ccompiler import CompileError, LinkError
234 self._check_compiler()
235 try:
Greg Ward59ac7092000-06-21 03:00:50 +0000236 self._link(body, headers, libraries, library_dirs, lang)
237 ok = 1
238 except (CompileError, LinkError):
239 ok = 0
240
241 self.announce(ok and "success!" or "failure.")
242 self._clean()
243 return ok
244
245 def try_run (self,
246 body, headers=None,
247 libraries=None, library_dirs=None,
248 lang="c"):
249 """Try to compile, link to an executable, and run a program
250 built from 'body' and 'headers'. Return true on success, false
251 otherwise.
252 """
253 from distutils.ccompiler import CompileError, LinkError
254 self._check_compiler()
255 try:
256 self._link(body, headers, libraries, library_dirs, lang)
Greg Ward28a5f442000-06-06 02:57:07 +0000257 self.spawn([exe])
258 ok = 1
259 except (CompileError, LinkError, DistutilsExecError):
260 ok = 0
261
262 self.announce(ok and "success!" or "failure.")
Greg Ward59ac7092000-06-21 03:00:50 +0000263 self._clean()
Greg Ward28a5f442000-06-06 02:57:07 +0000264 return ok
265
Greg Ward59ac7092000-06-21 03:00:50 +0000266
267 # -- High-level methods --------------------------------------------
268 # (these are the ones that are actually likely to be useful
269 # when implementing a real-world config command!)
270
271 def check_func (self, func, headers=None,
272 libraries=None, library_dirs=None,
273 decl=0, call=0):
274
275 """Determine if function 'func' is available by constructing a
276 source file that refers to 'func', and compiles and links it.
277 If everything succeeds, returns true; otherwise returns false.
278
279 The constructed source file starts out by including the header
280 files listed in 'headers'. If 'decl' is true, it then declares
281 'func' (as "int func()"); you probably shouldn't supply 'headers'
282 and set 'decl' true in the same call, or you might get errors about
283 a conflicting declarations for 'func'. Finally, the constructed
284 'main()' function either references 'func' or (if 'call' is true)
285 calls it. 'libraries' and 'library_dirs' are used when
286 linking.
287 """
288
289 self._check_compiler()
290 body = []
291 if decl:
292 body.append("int %s ();" % func)
293 body.append("int main () {")
294 if call:
295 body.append(" %s();" % func)
296 else:
297 body.append(" %s;" % func)
298 body.append("}")
299 body = string.join(body, "\n") + "\n"
300
301 return self.try_link(body, headers, libraries, library_dirs)
302
303 # check_func ()
304
305 def check_header (self, header, lang="c"):
306 """Determine if the system header file named by 'header_file'
307 exists and can be found by the preprocessor; return true if so,
308 false otherwise.
309 """
310 return self.try_cpp(headers=[header])
311
312
Greg Ward28a5f442000-06-06 02:57:07 +0000313# class config
Greg Ward59ac7092000-06-21 03:00:50 +0000314
315
316def dump_file (filename, head=None):
317 if head is None:
318 print filename + ":"
319 else:
320 print head
321
322 file = open(filename)
323 sys.stdout.write(file.read())
324 file.close()