blob: 264c1396b232ad040ac473469d60a51a94350217 [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""Prepare the build.
2
3This module provides config, 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
12import os
13import re
14
15from packaging.command.cmd import Command
16from packaging.errors import PackagingExecError
17from packaging.compiler import customize_compiler
18from packaging import logger
19
20LANG_EXT = {'c': '.c', 'c++': '.cxx'}
21
22class config(Command):
23
24 description = "prepare the build"
25
26 user_options = [
27 ('compiler=', None,
28 "specify the compiler type"),
29 ('cc=', None,
30 "specify the compiler executable"),
31 ('include-dirs=', 'I',
32 "list of directories to search for header files"),
33 ('define=', 'D',
34 "C preprocessor macros to define"),
35 ('undef=', 'U',
36 "C preprocessor macros to undefine"),
37 ('libraries=', 'l',
38 "external C libraries to link with"),
39 ('library-dirs=', 'L',
40 "directories to search for external C libraries"),
41
42 ('noisy', None,
43 "show every action (compile, link, run, ...) taken"),
44 ('dump-source', None,
45 "dump generated source files before attempting to compile them"),
46 ]
47
48
49 # The three standard command methods: since the "config" command
50 # does nothing by default, these are empty.
51
52 def initialize_options(self):
53 self.compiler = None
54 self.cc = None
55 self.include_dirs = None
56 self.libraries = None
57 self.library_dirs = None
58
59 # maximal output for now
60 self.noisy = True
61 self.dump_source = True
62
63 # list of temporary files generated along-the-way that we have
64 # to clean at some point
65 self.temp_files = []
66
67 def finalize_options(self):
68 if self.include_dirs is None:
69 self.include_dirs = self.distribution.include_dirs or []
70 elif isinstance(self.include_dirs, str):
71 self.include_dirs = self.include_dirs.split(os.pathsep)
72
73 if self.libraries is None:
74 self.libraries = []
75 elif isinstance(self.libraries, str):
76 self.libraries = [self.libraries]
77
78 if self.library_dirs is None:
79 self.library_dirs = []
80 elif isinstance(self.library_dirs, str):
81 self.library_dirs = self.library_dirs.split(os.pathsep)
82
83 def run(self):
84 pass
85
86
87 # Utility methods for actual "config" commands. The interfaces are
88 # loosely based on Autoconf macros of similar names. Sub-classes
89 # may use these freely.
90
91 def _check_compiler(self):
92 """Check that 'self.compiler' really is a CCompiler object;
93 if not, make it one.
94 """
95 # We do this late, and only on-demand, because this is an expensive
96 # import.
97 from packaging.compiler.ccompiler import CCompiler
98 from packaging.compiler import new_compiler
99 if not isinstance(self.compiler, CCompiler):
100 self.compiler = new_compiler(compiler=self.compiler,
101 dry_run=self.dry_run, force=True)
102 customize_compiler(self.compiler)
103 if self.include_dirs:
104 self.compiler.set_include_dirs(self.include_dirs)
105 if self.libraries:
106 self.compiler.set_libraries(self.libraries)
107 if self.library_dirs:
108 self.compiler.set_library_dirs(self.library_dirs)
109
110
111 def _gen_temp_sourcefile(self, body, headers, lang):
112 filename = "_configtest" + LANG_EXT[lang]
Victor Stinner21a9c742011-05-19 15:51:27 +0200113 with open(filename, "w") as file:
114 if headers:
115 for header in headers:
116 file.write("#include <%s>\n" % header)
117 file.write("\n")
118 file.write(body)
119 if body[-1] != "\n":
120 file.write("\n")
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200121 return filename
122
123 def _preprocess(self, body, headers, include_dirs, lang):
124 src = self._gen_temp_sourcefile(body, headers, lang)
125 out = "_configtest.i"
126 self.temp_files.extend((src, out))
127 self.compiler.preprocess(src, out, include_dirs=include_dirs)
128 return src, out
129
130 def _compile(self, body, headers, include_dirs, lang):
131 src = self._gen_temp_sourcefile(body, headers, lang)
132 if self.dump_source:
133 dump_file(src, "compiling '%s':" % src)
134 obj = self.compiler.object_filenames([src])[0]
135 self.temp_files.extend((src, obj))
136 self.compiler.compile([src], include_dirs=include_dirs)
137 return src, obj
138
139 def _link(self, body, headers, include_dirs, libraries, library_dirs,
140 lang):
141 src, obj = self._compile(body, headers, include_dirs, lang)
142 prog = os.path.splitext(os.path.basename(src))[0]
143 self.compiler.link_executable([obj], prog,
144 libraries=libraries,
145 library_dirs=library_dirs,
146 target_lang=lang)
147
148 if self.compiler.exe_extension is not None:
149 prog = prog + self.compiler.exe_extension
150 self.temp_files.append(prog)
151
152 return src, obj, prog
153
154 def _clean(self, *filenames):
155 if not filenames:
156 filenames = self.temp_files
157 self.temp_files = []
158 logger.info("removing: %s", ' '.join(filenames))
159 for filename in filenames:
160 try:
161 os.remove(filename)
162 except OSError:
163 pass
164
165
166 # XXX these ignore the dry-run flag: what to do, what to do? even if
167 # you want a dry-run build, you still need some sort of configuration
168 # info. My inclination is to make it up to the real config command to
169 # consult 'dry_run', and assume a default (minimal) configuration if
170 # true. The problem with trying to do it here is that you'd have to
171 # return either true or false from all the 'try' methods, neither of
172 # which is correct.
173
174 # XXX need access to the header search path and maybe default macros.
175
176 def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"):
177 """Construct a source file from 'body' (a string containing lines
178 of C/C++ code) and 'headers' (a list of header files to include)
179 and run it through the preprocessor. Return true if the
180 preprocessor succeeded, false if there were any errors.
181 ('body' probably isn't of much use, but what the heck.)
182 """
183 from packaging.compiler.ccompiler import CompileError
184 self._check_compiler()
185 ok = True
186 try:
187 self._preprocess(body, headers, include_dirs, lang)
188 except CompileError:
189 ok = False
190
191 self._clean()
192 return ok
193
194 def search_cpp(self, pattern, body=None, headers=None, include_dirs=None,
195 lang="c"):
196 """Construct a source file (just like 'try_cpp()'), run it through
197 the preprocessor, and return true if any line of the output matches
198 'pattern'. 'pattern' should either be a compiled regex object or a
199 string containing a regex. If both 'body' and 'headers' are None,
200 preprocesses an empty file -- which can be useful to determine the
201 symbols the preprocessor and compiler set by default.
202 """
203 self._check_compiler()
204 src, out = self._preprocess(body, headers, include_dirs, lang)
205
206 if isinstance(pattern, str):
207 pattern = re.compile(pattern)
208
Victor Stinner21a9c742011-05-19 15:51:27 +0200209 with open(out) as file:
210 match = False
211 while True:
212 line = file.readline()
213 if line == '':
214 break
215 if pattern.search(line):
216 match = True
217 break
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200218
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200219 self._clean()
220 return match
221
222 def try_compile(self, body, headers=None, include_dirs=None, lang="c"):
223 """Try to compile a source file built from 'body' and 'headers'.
224 Return true on success, false otherwise.
225 """
226 from packaging.compiler.ccompiler import CompileError
227 self._check_compiler()
228 try:
229 self._compile(body, headers, include_dirs, lang)
230 ok = True
231 except CompileError:
232 ok = False
233
234 logger.info(ok and "success!" or "failure.")
235 self._clean()
236 return ok
237
238 def try_link(self, body, headers=None, include_dirs=None, libraries=None,
239 library_dirs=None, lang="c"):
240 """Try to compile and link a source file, built from 'body' and
241 'headers', to executable form. Return true on success, false
242 otherwise.
243 """
244 from packaging.compiler.ccompiler import CompileError, LinkError
245 self._check_compiler()
246 try:
247 self._link(body, headers, include_dirs,
248 libraries, library_dirs, lang)
249 ok = True
250 except (CompileError, LinkError):
251 ok = False
252
253 logger.info(ok and "success!" or "failure.")
254 self._clean()
255 return ok
256
257 def try_run(self, body, headers=None, include_dirs=None, libraries=None,
258 library_dirs=None, lang="c"):
259 """Try to compile, link to an executable, and run a program
260 built from 'body' and 'headers'. Return true on success, false
261 otherwise.
262 """
263 from packaging.compiler.ccompiler import CompileError, LinkError
264 self._check_compiler()
265 try:
266 src, obj, exe = self._link(body, headers, include_dirs,
267 libraries, library_dirs, lang)
268 self.spawn([exe])
269 ok = True
270 except (CompileError, LinkError, PackagingExecError):
271 ok = False
272
273 logger.info(ok and "success!" or "failure.")
274 self._clean()
275 return ok
276
277
278 # -- High-level methods --------------------------------------------
279 # (these are the ones that are actually likely to be useful
280 # when implementing a real-world config command!)
281
282 def check_func(self, func, headers=None, include_dirs=None,
283 libraries=None, library_dirs=None, decl=False, call=False):
284
285 """Determine if function 'func' is available by constructing a
286 source file that refers to 'func', and compiles and links it.
287 If everything succeeds, returns true; otherwise returns false.
288
289 The constructed source file starts out by including the header
290 files listed in 'headers'. If 'decl' is true, it then declares
291 'func' (as "int func()"); you probably shouldn't supply 'headers'
292 and set 'decl' true in the same call, or you might get errors about
293 a conflicting declarations for 'func'. Finally, the constructed
294 'main()' function either references 'func' or (if 'call' is true)
295 calls it. 'libraries' and 'library_dirs' are used when
296 linking.
297 """
298
299 self._check_compiler()
300 body = []
301 if decl:
302 body.append("int %s ();" % func)
303 body.append("int main () {")
304 if call:
305 body.append(" %s();" % func)
306 else:
307 body.append(" %s;" % func)
308 body.append("}")
309 body = "\n".join(body) + "\n"
310
311 return self.try_link(body, headers, include_dirs,
312 libraries, library_dirs)
313
314 def check_lib(self, library, library_dirs=None, headers=None,
315 include_dirs=None, other_libraries=[]):
316 """Determine if 'library' is available to be linked against,
317 without actually checking that any particular symbols are provided
318 by it. 'headers' will be used in constructing the source file to
319 be compiled, but the only effect of this is to check if all the
320 header files listed are available. Any libraries listed in
321 'other_libraries' will be included in the link, in case 'library'
322 has symbols that depend on other libraries.
323 """
324 self._check_compiler()
325 return self.try_link("int main (void) { }",
326 headers, include_dirs,
327 [library]+other_libraries, library_dirs)
328
329 def check_header(self, header, include_dirs=None, library_dirs=None,
330 lang="c"):
331 """Determine if the system header file named by 'header_file'
332 exists and can be found by the preprocessor; return true if so,
333 false otherwise.
334 """
335 return self.try_cpp(body="/* No body */", headers=[header],
336 include_dirs=include_dirs)
337
338
339def dump_file(filename, head=None):
340 """Dumps a file content into log.info.
341
342 If head is not None, will be dumped before the file content.
343 """
344 if head is None:
345 logger.info(filename)
346 else:
347 logger.info(head)
348 with open(filename) as file:
349 logger.info(file.read())