]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | # Copyright 2004 Vladimir Prus. |
2 | # Distributed under the Boost Software License, Version 1.0. (See | |
3 | # accompanying file LICENSE_1_0.txt or copy at | |
4 | # http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | # Support for Python and the the Boost.Python library. | |
7 | # | |
8 | # This module defines | |
9 | # | |
10 | # - a project 'python' with a target 'python' in it, that corresponds to the | |
11 | # python library | |
12 | # | |
13 | # - a main target rule 'python-extension' which can be used to build a python | |
14 | # extension. | |
15 | # | |
16 | # Extensions that use Boost.Python must explicitly link to it. | |
17 | ||
18 | import type ; | |
19 | import testing ; | |
20 | import generators ; | |
21 | import project ; | |
22 | import errors ; | |
23 | import targets ; | |
24 | import "class" : new ; | |
25 | import os ; | |
26 | import common ; | |
27 | import toolset ; | |
28 | import regex ; | |
29 | import numbers ; | |
30 | import string ; | |
31 | import property ; | |
32 | import sequence ; | |
33 | import path ; | |
34 | import feature ; | |
35 | import set ; | |
36 | import builtin ; | |
b32b8144 | 37 | import property-set ; |
7c673cae FG |
38 | |
39 | ||
40 | # Make this module a project. | |
41 | project.initialize $(__name__) ; | |
42 | project python ; | |
43 | ||
44 | # Save the project so that if 'init' is called several times we define new | |
45 | # targets in the python project, not in whatever project we were called by. | |
46 | .project = [ project.current ] ; | |
47 | ||
48 | # Dynamic linker lib. Necessary to specify it explicitly on some platforms. | |
49 | lib dl ; | |
50 | # This contains 'openpty' function need by python. Again, on some system need to | |
51 | # pass this to linker explicitly. | |
52 | lib util ; | |
53 | # Python uses pthread symbols. | |
54 | lib pthread ; | |
55 | # Extra library needed by phtread on some platforms. | |
56 | lib rt ; | |
57 | ||
58 | # The pythonpath feature specifies additional elements for the PYTHONPATH | |
59 | # environment variable, set by run-pyd. For example, pythonpath can be used to | |
60 | # access Python modules that are part of the product being built, but are not | |
61 | # installed in the development system's default paths. | |
62 | feature.feature pythonpath : : free optional path ; | |
63 | ||
b32b8144 FG |
64 | # The best configured version of Python 2 and 3. |
65 | py2-version = ; | |
66 | py3-version = ; | |
67 | ||
7c673cae FG |
68 | # Initializes the Python toolset. Note that all parameters are optional. |
69 | # | |
70 | # - version -- the version of Python to use. Should be in Major.Minor format, | |
71 | # for example 2.3. Do not include the subminor version. | |
72 | # | |
73 | # - cmd-or-prefix: Preferably, a command that invokes a Python interpreter. | |
74 | # Alternatively, the installation prefix for Python libraries and includes. If | |
75 | # empty, will be guessed from the version, the platform's installation | |
76 | # patterns, and the python executables that can be found in PATH. | |
77 | # | |
78 | # - includes: the include path to Python headers. If empty, will be guessed. | |
79 | # | |
80 | # - libraries: the path to Python library binaries. If empty, will be guessed. | |
81 | # On MacOS/Darwin, you can also pass the path of the Python framework. | |
82 | # | |
83 | # - condition: if specified, should be a set of properties that are matched | |
84 | # against the build configuration when Boost.Build selects a Python | |
85 | # configuration to use. | |
86 | # | |
87 | # - extension-suffix: A string to append to the name of extension modules before | |
88 | # the true filename extension. Ordinarily we would just compute this based on | |
89 | # the value of the <python-debugging> feature. However ubuntu's python-dbg | |
90 | # package uses the windows convention of appending _d to debug-build extension | |
91 | # modules. We have no way of detecting ubuntu, or of probing python for the | |
92 | # "_d" requirement, and if you configure and build python using | |
93 | # --with-pydebug, you'll be using the standard *nix convention. Defaults to "" | |
94 | # (or "_d" when targeting windows and <python-debugging> is set). | |
95 | # | |
96 | # Example usage: | |
97 | # | |
98 | # using python : 2.3 ; | |
99 | # using python : 2.3 : /usr/local/bin/python ; | |
100 | # | |
101 | rule init ( version ? : cmd-or-prefix ? : includes * : libraries ? | |
102 | : condition * : extension-suffix ? ) | |
103 | { | |
104 | project.push-current $(.project) ; | |
105 | ||
106 | debug-message Configuring python... ; | |
107 | for local v in version cmd-or-prefix includes libraries condition | |
108 | { | |
109 | if $($(v)) | |
110 | { | |
11fdf7f2 | 111 | debug-message " user-specified $(v):" \"$($(v))\" ; |
7c673cae FG |
112 | } |
113 | } | |
114 | ||
115 | configure $(version) : $(cmd-or-prefix) : $(includes) : $(libraries) : $(condition) : $(extension-suffix) ; | |
116 | ||
117 | project.pop-current ; | |
118 | } | |
119 | ||
120 | # A simpler version of SHELL that grabs stderr as well as stdout, but returns | |
121 | # nothing if there was an error. | |
122 | # | |
123 | local rule shell-cmd ( cmd ) | |
124 | { | |
125 | debug-message running command '$(cmd)" 2>&1"' ; | |
126 | x = [ SHELL $(cmd)" 2>&1" : exit-status ] ; | |
127 | if $(x[2]) = 0 | |
128 | { | |
129 | return $(x[1]) ; | |
130 | } | |
131 | else | |
132 | { | |
133 | return ; | |
134 | } | |
135 | } | |
136 | ||
137 | ||
138 | # Try to identify Cygwin symlinks. Invoking such a file directly as an NT | |
139 | # executable from a native Windows build of bjam would be fatal to the bjam | |
140 | # process. One /can/ invoke them through sh.exe or bash.exe, if you can prove | |
141 | # that those are not also symlinks. ;-) | |
142 | # | |
143 | # If a symlink is found returns non-empty; we try to extract the target of the | |
144 | # symlink from the file and return that. | |
145 | # | |
146 | # Note: 1. only works on NT 2. path is a native path. | |
147 | local rule is-cygwin-symlink ( path ) | |
148 | { | |
149 | local is-symlink = ; | |
150 | ||
151 | # Look for a file with the given path having the S attribute set, as cygwin | |
152 | # symlinks do. /-C means "do not use thousands separators in file sizes." | |
153 | local dir-listing = [ shell-cmd "DIR /-C /A:S \""$(path)"\"" ] ; | |
154 | ||
155 | if $(dir-listing) | |
156 | { | |
157 | # Escape any special regex characters in the base part of the path. | |
11fdf7f2 | 158 | local base-pat = [ regex.escape $(path:D=) : "].[()*+?|\\$^" : \\ ] ; |
7c673cae FG |
159 | |
160 | # Extract the file's size from the directory listing. | |
161 | local size-of-system-file = [ MATCH "([0-9]+) "$(base-pat) : $(dir-listing) : 1 ] ; | |
162 | ||
163 | # If the file has a reasonably small size, look for the special symlink | |
164 | # identification text. | |
165 | if $(size-of-system-file) && [ numbers.less $(size-of-system-file) 1000 ] | |
166 | { | |
167 | local link = [ SHELL "FIND /OFF \"!<symlink>\" \""$(path)"\" 2>&1" ] ; | |
168 | if $(link[2]) != 0 | |
169 | { | |
170 | local nl = " | |
171 | ||
172 | " ; | |
173 | is-symlink = [ MATCH ".*!<symlink>([^"$(nl)"]*)" : $(link[1]) : 1 ] ; | |
174 | if $(is-symlink) | |
175 | { | |
176 | is-symlink = [ *nix-path-to-native $(is-symlink) ] ; | |
177 | is-symlink = $(is-symlink:R=$(path:D)) ; | |
178 | } | |
179 | ||
180 | } | |
181 | } | |
182 | } | |
183 | return $(is-symlink) ; | |
184 | } | |
185 | ||
186 | ||
187 | # Append ext to each member of names that does not contain '.'. | |
188 | # | |
189 | local rule default-extension ( names * : ext * ) | |
190 | { | |
191 | local result ; | |
192 | for local n in $(names) | |
193 | { | |
194 | switch $(n) | |
195 | { | |
196 | case *.* : result += $(n) ; | |
197 | case * : result += $(n)$(ext) ; | |
198 | } | |
199 | } | |
200 | return $(result) ; | |
201 | } | |
202 | ||
203 | ||
204 | # Tries to determine whether invoking "cmd" would actually attempt to launch a | |
205 | # cygwin symlink. | |
206 | # | |
207 | # Note: only works on NT. | |
208 | # | |
209 | local rule invokes-cygwin-symlink ( cmd ) | |
210 | { | |
211 | local dirs = $(cmd:D) ; | |
212 | if ! $(dirs) | |
213 | { | |
214 | dirs = . [ os.executable-path ] ; | |
215 | } | |
216 | local base = [ default-extension $(cmd:D=) : .exe .cmd .bat ] ; | |
217 | local paths = [ GLOB $(dirs) : $(base) ] ; | |
218 | if $(paths) | |
219 | { | |
220 | # Make sure we have not run into a Cygwin symlink. Invoking such a file | |
221 | # as an NT executable would be fatal for the bjam process. | |
222 | return [ is-cygwin-symlink $(paths[1]) ] ; | |
223 | } | |
224 | } | |
225 | ||
226 | ||
227 | local rule debug-message ( message * ) | |
228 | { | |
229 | if --debug-configuration in [ modules.peek : ARGV ] | |
230 | { | |
11fdf7f2 | 231 | ECHO "notice:" "[python-cfg]" $(message) ; |
7c673cae FG |
232 | } |
233 | } | |
234 | ||
235 | ||
236 | # Like W32_GETREG, except prepend HKEY_CURRENT_USER\SOFTWARE and | |
237 | # HKEY_LOCAL_MACHINE\SOFTWARE to the first argument, returning the first result | |
238 | # found. Also accounts for the fact that on 64-bit machines, 32-bit software has | |
239 | # its own area, under SOFTWARE\Wow6432node. | |
240 | # | |
241 | local rule software-registry-value ( path : data ? ) | |
242 | { | |
243 | local result ; | |
244 | for local root in HKEY_CURRENT_USER HKEY_LOCAL_MACHINE | |
245 | { | |
246 | for local x64elt in "" Wow6432node\\ # Account for 64-bit windows | |
247 | { | |
248 | if ! $(result) | |
249 | { | |
250 | result = [ W32_GETREG $(root)\\SOFTWARE\\$(x64elt)$(path) : $(data) ] ; | |
251 | } | |
252 | } | |
253 | ||
254 | } | |
255 | return $(result) ; | |
256 | } | |
257 | ||
258 | ||
11fdf7f2 TL |
259 | .windows-drive-letter-re = "^([A-Za-z]):[\\/](.*)" ; |
260 | .cygwin-drive-letter-re = "^/cygdrive/([a-z])/(.*)" ; | |
7c673cae FG |
261 | |
262 | .working-directory = [ PWD ] ; | |
263 | .working-drive-letter = [ SUBST $(.working-directory) $(.windows-drive-letter-re) $1 ] ; | |
264 | .working-drive-letter ?= [ SUBST $(.working-directory) $(.cygwin-drive-letter-re) $1 ] ; | |
265 | ||
266 | ||
267 | local rule windows-to-cygwin-path ( path ) | |
268 | { | |
269 | # If path is rooted with a drive letter, rewrite it using the /cygdrive | |
270 | # mountpoint. | |
271 | local p = [ SUBST $(path:T) $(.windows-drive-letter-re) /cygdrive/$1/$2 ] ; | |
272 | ||
273 | # Else if path is rooted without a drive letter, use the working directory. | |
274 | p ?= [ SUBST $(path:T) ^/(.*) /cygdrive/$(.working-drive-letter:L)/$2 ] ; | |
275 | ||
276 | # Else return the path unchanged. | |
277 | return $(p:E=$(path:T)) ; | |
278 | } | |
279 | ||
280 | ||
281 | # :W only works in Cygwin builds of bjam. This one works on NT builds as well. | |
282 | # | |
283 | local rule cygwin-to-windows-path ( path ) | |
284 | { | |
285 | path = $(path:R="") ; # strip any trailing slash | |
286 | ||
11fdf7f2 | 287 | local drive-letter = [ SUBST $(path) $(.cygwin-drive-letter-re) "$1:/$2" ] ; |
7c673cae FG |
288 | if $(drive-letter) |
289 | { | |
290 | path = $(drive-letter) ; | |
291 | } | |
292 | else if $(path:R=/x) = $(path) # already rooted? | |
293 | { | |
294 | # Look for a cygwin mount that includes each head sequence in $(path). | |
295 | local head = $(path) ; | |
296 | local tail = "" ; | |
297 | ||
298 | while $(head) | |
299 | { | |
300 | local root = [ software-registry-value | |
301 | "Cygnus Solutions\\Cygwin\\mounts v2\\"$(head) : native ] ; | |
302 | ||
303 | if $(root) | |
304 | { | |
305 | path = $(tail:R=$(root)) ; | |
306 | head = ; | |
307 | } | |
308 | tail = $(tail:R=$(head:D=)) ; | |
309 | ||
310 | if $(head) = / | |
311 | { | |
312 | head = ; | |
313 | } | |
314 | else | |
315 | { | |
316 | head = $(head:D) ; | |
317 | } | |
318 | } | |
319 | } | |
320 | return [ regex.replace $(path:R="") / \\ ] ; | |
321 | } | |
322 | ||
323 | ||
324 | # Convert a *nix path to native. | |
325 | # | |
326 | local rule *nix-path-to-native ( path ) | |
327 | { | |
328 | if [ os.name ] = NT | |
329 | { | |
330 | path = [ cygwin-to-windows-path $(path) ] ; | |
331 | } | |
332 | return $(path) ; | |
333 | } | |
334 | ||
335 | ||
336 | # Convert an NT path to native. | |
337 | # | |
338 | local rule windows-path-to-native ( path ) | |
339 | { | |
340 | if [ os.name ] = NT | |
341 | { | |
342 | return $(path) ; | |
343 | } | |
344 | else | |
345 | { | |
346 | return [ windows-to-cygwin-path $(path) ] ; | |
347 | } | |
348 | } | |
349 | ||
350 | ||
351 | # Return nonempty if path looks like a windows path, i.e. it starts with a drive | |
352 | # letter or contains backslashes. | |
353 | # | |
354 | local rule guess-windows-path ( path ) | |
355 | { | |
11fdf7f2 | 356 | return [ SUBST $(path) "($(.windows-drive-letter-re)|.*([\\]).*)" $1 ] ; |
7c673cae FG |
357 | } |
358 | ||
359 | ||
360 | local rule path-to-native ( paths * ) | |
361 | { | |
362 | local result ; | |
363 | ||
364 | for local p in $(paths) | |
365 | { | |
366 | if [ guess-windows-path $(p) ] | |
367 | { | |
368 | result += [ windows-path-to-native $(p) ] ; | |
369 | } | |
370 | else | |
371 | { | |
372 | result += [ *nix-path-to-native $(p:T) ] ; | |
373 | } | |
374 | } | |
375 | return $(result) ; | |
376 | } | |
377 | ||
378 | ||
379 | # Validate the version string and extract the major/minor part we care about. | |
380 | # | |
381 | local rule split-version ( version ) | |
382 | { | |
11fdf7f2 | 383 | local major-minor = [ MATCH "^([0-9]+)\.([0-9]+)(.*)$" : $(version) : 1 2 3 ] ; |
7c673cae FG |
384 | if ! $(major-minor[2]) || $(major-minor[3]) |
385 | { | |
386 | ECHO "Warning: \"using python\" expects a two part (major, minor) version number; got" $(version) instead ; | |
387 | ||
388 | # Add a zero to account for the missing digit if necessary. | |
389 | major-minor += 0 ; | |
390 | } | |
391 | ||
392 | return $(major-minor[1]) $(major-minor[2]) ; | |
393 | } | |
394 | ||
395 | ||
396 | # Build a list of versions from 3.4 down to 1.5. Because bjam can not enumerate | |
397 | # registry sub-keys, we have no way of finding a version with a 2-digit minor | |
398 | # version, e.g. 2.10 -- let us hope that never happens. | |
399 | # | |
400 | .version-countdown = ; | |
401 | for local v in [ numbers.range 15 34 ] | |
402 | { | |
403 | .version-countdown = [ SUBST $(v) (.)(.*) $1.$2 ] $(.version-countdown) ; | |
404 | } | |
405 | ||
406 | ||
407 | local rule windows-installed-pythons ( version ? ) | |
408 | { | |
409 | version ?= $(.version-countdown) ; | |
410 | local interpreters ; | |
411 | ||
412 | for local v in $(version) | |
413 | { | |
414 | local install-path = [ | |
415 | software-registry-value "Python\\PythonCore\\"$(v)"\\InstallPath" ] ; | |
416 | ||
417 | if $(install-path) | |
418 | { | |
419 | install-path = [ windows-path-to-native $(install-path) ] ; | |
420 | debug-message Registry indicates Python $(v) installed at \"$(install-path)\" ; | |
421 | } | |
422 | ||
423 | interpreters += $(:E=python:R=$(install-path)) ; | |
424 | } | |
425 | return $(interpreters) ; | |
426 | } | |
427 | ||
428 | ||
429 | local rule darwin-installed-pythons ( version ? ) | |
430 | { | |
431 | version ?= $(.version-countdown) ; | |
432 | ||
433 | local prefix | |
434 | = [ GLOB /System/Library/Frameworks /Library/Frameworks | |
435 | : Python.framework ] ; | |
436 | ||
437 | return $(prefix)/Versions/$(version)/bin/python ; | |
438 | } | |
439 | ||
440 | ||
441 | # Assume "python-cmd" invokes a python interpreter and invoke it to extract all | |
442 | # the information we care about from its "sys" module. Returns void if | |
443 | # unsuccessful. | |
444 | # | |
445 | local rule probe ( python-cmd ) | |
446 | { | |
447 | # Avoid invoking a Cygwin symlink on NT. | |
448 | local skip-symlink ; | |
449 | if [ os.name ] = NT | |
450 | { | |
451 | skip-symlink = [ invokes-cygwin-symlink $(python-cmd) ] ; | |
452 | } | |
453 | ||
454 | if $(skip-symlink) | |
455 | { | |
456 | debug-message -------------------------------------------------------------------- ; | |
457 | debug-message \"$(python-cmd)\" would attempt to invoke a Cygwin symlink, ; | |
458 | debug-message causing a bjam built for Windows to hang. ; | |
459 | debug-message ; | |
460 | debug-message If you intend to target a Cygwin build of Python, please ; | |
461 | debug-message replace the path to the link with the path to a real executable ; | |
11fdf7f2 | 462 | debug-message "(guessing:" \"$(skip-symlink)\") "in" your 'using python' line ; |
7c673cae FG |
463 | debug-message "in" user-config.jam or site-config.jam. Do not forget to escape ; |
464 | debug-message backslashes ; | |
465 | debug-message -------------------------------------------------------------------- ; | |
466 | } | |
467 | else | |
468 | { | |
469 | # Prepare a List of Python format strings and expressions that can be | |
470 | # used to print the constants we want from the sys module. | |
471 | ||
472 | # We do not really want sys.version since that is a complicated string, | |
473 | # so get the information from sys.version_info instead. | |
474 | local format = "version=%d.%d" ; | |
475 | local exprs = "version_info[0]" "version_info[1]" ; | |
476 | ||
477 | for local s in $(sys-elements[2-]) | |
478 | { | |
479 | format += $(s)=%s ; | |
480 | exprs += $(s) ; | |
481 | } | |
482 | ||
483 | # Invoke Python and ask it for all those values. | |
484 | local full-cmd = | |
485 | $(python-cmd)" -c \"from sys import *; print('"$(format:J=\\n)"' % ("$(exprs:J=,)"))\"" ; | |
486 | ||
487 | local output = [ shell-cmd $(full-cmd) ] ; | |
488 | if $(output) | |
489 | { | |
490 | # Parse the output to get all the results. | |
491 | local nl = " | |
492 | ||
493 | " ; | |
494 | for s in $(sys-elements) | |
495 | { | |
496 | # These variables are expected to be declared local in the | |
497 | # caller, so Jam's dynamic scoping will set their values there. | |
11fdf7f2 | 498 | sys.$(s) = [ SUBST $(output) "\\<$(s)=([^$(nl)]+)" $1 ] ; |
7c673cae FG |
499 | } |
500 | } | |
501 | return $(output) ; | |
502 | } | |
503 | } | |
504 | ||
505 | ||
506 | # Make sure the "libraries" and "includes" variables (in an enclosing scope) | |
507 | # have a value based on the information given. | |
508 | # | |
509 | local rule compute-default-paths ( target-os : version ? : prefix ? : | |
510 | exec-prefix ? ) | |
511 | { | |
512 | exec-prefix ?= $(prefix) ; | |
513 | ||
514 | if $(target-os) = windows | |
515 | { | |
516 | # The exec_prefix is where you're supposed to look for machine-specific | |
517 | # libraries. | |
518 | local default-library-path = $(exec-prefix)\\libs ; | |
519 | local default-include-path = $(:E=Include:R=$(prefix)) ; | |
520 | ||
521 | # If the interpreter was found in a directory called "PCBuild" or | |
522 | # "PCBuild8," assume we're looking at a Python built from the source | |
523 | # distro, and go up one additional level to the default root. Otherwise, | |
524 | # the default root is the directory where the interpreter was found. | |
525 | ||
526 | # We ask Python itself what the executable path is in case of | |
527 | # intermediate symlinks or shell scripts. | |
528 | local executable-dir = $(sys.executable:D) ; | |
529 | ||
530 | if [ MATCH ^(PCBuild) : $(executable-dir:D=) ] | |
531 | { | |
532 | debug-message "This Python appears to reside in a source distribution;" ; | |
533 | debug-message "prepending \""$(executable-dir)"\" to default library search path" ; | |
534 | ||
535 | default-library-path = $(executable-dir) $(default-library-path) ; | |
536 | ||
537 | default-include-path = $(:E=PC:R=$(executable-dir:D)) $(default-include-path) ; | |
538 | ||
539 | debug-message "and \""$(default-include-path[1])"\" to default #include path" ; | |
540 | } | |
541 | ||
542 | libraries ?= $(default-library-path) ; | |
543 | includes ?= $(default-include-path) ; | |
544 | } | |
545 | else | |
546 | { | |
547 | includes ?= $(prefix)/include/python$(version) ; | |
548 | ||
549 | local lib = $(exec-prefix)/lib ; | |
550 | libraries ?= $(lib)/python$(version)/config $(lib) ; | |
551 | } | |
552 | } | |
553 | ||
554 | # The version of the python interpreter to use. | |
92f5a8d4 | 555 | feature.feature python : : propagated symmetric ; |
7c673cae FG |
556 | feature.feature python.interpreter : : free ; |
557 | ||
558 | toolset.flags python.capture-output PYTHON : <python.interpreter> ; | |
559 | ||
560 | # | |
561 | # Support for Python configured --with-pydebug | |
562 | # | |
563 | feature.feature python-debugging : off on : propagated ; | |
b32b8144 | 564 | variant debug-python : debug : <python-debugging>on ; |
7c673cae FG |
565 | |
566 | ||
567 | # Return a list of candidate commands to try when looking for a Python | |
568 | # interpreter. prefix is expected to be a native path. | |
569 | # | |
570 | local rule candidate-interpreters ( version ? : prefix ? : target-os ) | |
571 | { | |
572 | local bin-path = bin ; | |
573 | if $(target-os) = windows | |
574 | { | |
575 | # On Windows, look in the root directory itself and, to work with the | |
576 | # result of a build-from-source, the PCBuild directory. | |
577 | bin-path = PCBuild8 PCBuild "" ; | |
578 | } | |
579 | ||
580 | bin-path = $(bin-path:R=$(prefix)) ; | |
581 | ||
582 | if $(target-os) in windows darwin | |
583 | { | |
584 | return # Search: | |
585 | $(:E=python:R=$(bin-path)) # Relative to the prefix, if any | |
586 | python # In the PATH | |
587 | [ $(target-os)-installed-pythons $(version) ] # Standard install locations | |
588 | ; | |
589 | } | |
590 | else | |
591 | { | |
592 | # Search relative to the prefix, or if none supplied, in PATH. | |
593 | local unversioned = $(:E=python:R=$(bin-path:E=)) ; | |
594 | ||
595 | # If a version was specified, look for a python with that specific | |
596 | # version appended before looking for one called, simply, "python" | |
597 | return $(unversioned)$(version) $(unversioned) ; | |
598 | } | |
599 | } | |
600 | ||
601 | ||
602 | # Compute system library dependencies for targets linking with static Python | |
603 | # libraries. | |
604 | # | |
605 | # On many systems, Python uses libraries such as pthreads or libdl. Since static | |
606 | # libraries carry no library dependency information of their own that the linker | |
607 | # can extract, these extra dependencies have to be given explicitly on the link | |
608 | # line of the client. The information about these dependencies is packaged into | |
609 | # the "python" target below. | |
610 | # | |
611 | # Even where Python itself uses pthreads, it never allows extension modules to | |
612 | # be entered concurrently (unless they explicitly give up the interpreter lock). | |
613 | # Therefore, extension modules do not need the efficiency overhead of threadsafe | |
614 | # code as produced by <threading>multi, and we handle libpthread along with | |
615 | # other libraries here. Note: this optimization is based on an assumption that | |
616 | # the compiler generates link-compatible code in both the single- and | |
617 | # multi-threaded cases, and that system libraries do not change their ABIs | |
618 | # either. | |
619 | # | |
620 | # Returns a list of usage-requirements that link to the necessary system | |
621 | # libraries. | |
622 | # | |
623 | local rule system-library-dependencies ( target-os ) | |
624 | { | |
625 | switch $(target-os) | |
626 | { | |
627 | case s[uo][nl]* : # solaris, sun, sunos | |
628 | # Add a librt dependency for the gcc toolset on SunOS (the sun | |
629 | # toolset adds -lrt unconditionally). While this appears to | |
630 | # duplicate the logic already in gcc.jam, it does not as long as | |
631 | # we are not forcing <threading>multi. | |
632 | ||
633 | # On solaris 10, distutils.sysconfig.get_config_var('LIBS') yields | |
634 | # '-lresolv -lsocket -lnsl -lrt -ldl'. However, that does not seem | |
635 | # to be the right list for extension modules. For example, on my | |
636 | # installation, adding -ldl causes at least one test to fail because | |
637 | # the library can not be found and removing it causes no failures. | |
638 | ||
639 | # Apparently, though, we need to add -lrt for gcc. | |
640 | return <toolset>gcc:<library>rt ; | |
641 | ||
642 | case osf : return <library>pthread <toolset>gcc:<library>rt ; | |
643 | ||
644 | case qnx* : return ; | |
645 | case darwin : return ; | |
646 | case windows : return ; | |
647 | case haiku : return ; | |
648 | ||
649 | case hpux : return <library>rt ; | |
650 | case *bsd : return <library>pthread <toolset>gcc:<library>util ; | |
651 | ||
652 | case aix : return <library>pthread <library>dl ; | |
653 | ||
654 | case * : return <library>pthread <library>dl | |
655 | <toolset>gcc:<library>util <toolset-intel:platform>linux:<library>util ; | |
656 | } | |
657 | } | |
658 | ||
659 | ||
11fdf7f2 TL |
660 | # Define a version suffix for libraries depending on Python. |
661 | # For example, Boost.Python built for Python 2.7 uses the suffix "27" | |
662 | rule version-suffix ( version ) | |
663 | { | |
664 | local major-minor = [ split-version $(version) ] ; | |
665 | local suffix = $(major-minor:J="") ; | |
666 | return $(suffix) ; | |
667 | } | |
668 | ||
7c673cae FG |
669 | # Declare a target to represent Python's library. |
670 | # | |
671 | local rule declare-libpython-target ( version ? : requirements * ) | |
672 | { | |
673 | # Compute the representation of Python version in the name of Python's | |
674 | # library file. | |
675 | local lib-version = $(version) ; | |
676 | if <target-os>windows in $(requirements) | |
677 | { | |
678 | local major-minor = [ split-version $(version) ] ; | |
679 | lib-version = $(major-minor:J="") ; | |
680 | if <python-debugging>on in $(requirements) | |
681 | { | |
682 | lib-version = $(lib-version)_d ; | |
683 | } | |
684 | } | |
685 | ||
686 | if ! $(lib-version) | |
687 | { | |
11fdf7f2 TL |
688 | ECHO *** "warning:" could not determine Python version, which will ; |
689 | ECHO *** "warning:" probably prevent us from linking with the python ; | |
690 | ECHO *** "warning:" library. Consider explicitly passing the version ; | |
691 | ECHO *** "warning:" to 'using python'. ; | |
7c673cae FG |
692 | } |
693 | ||
694 | # Declare it. | |
695 | lib python.lib : : <name>python$(lib-version) $(requirements) ; | |
696 | } | |
697 | ||
698 | ||
699 | # Implementation of init. | |
700 | local rule configure ( version ? : cmd-or-prefix ? : includes * : libraries ? : | |
701 | condition * : extension-suffix ? ) | |
702 | { | |
703 | local prefix ; | |
704 | local exec-prefix ; | |
705 | local cmds-to-try ; | |
706 | local interpreter-cmd ; | |
707 | ||
708 | local target-os = [ feature.get-values target-os : $(condition) ] ; | |
709 | target-os ?= [ feature.defaults target-os ] ; | |
710 | target-os = $(target-os:G=) ; | |
711 | ||
712 | if $(target-os) = windows && <python-debugging>on in $(condition) | |
713 | { | |
714 | extension-suffix ?= _d ; | |
715 | } | |
716 | extension-suffix ?= "" ; | |
717 | ||
7c673cae FG |
718 | local cmds-to-try ; |
719 | ||
720 | if ! $(cmd-or-prefix) || [ GLOB $(cmd-or-prefix) : * ] | |
721 | { | |
722 | # If the user did not pass a command, whatever we got was a prefix. | |
723 | prefix = $(cmd-or-prefix) ; | |
724 | cmds-to-try = [ candidate-interpreters $(version) : $(prefix) : $(target-os) ] ; | |
725 | } | |
726 | else | |
727 | { | |
728 | # Work with the command the user gave us. | |
729 | cmds-to-try = $(cmd-or-prefix) ; | |
730 | ||
731 | # On Windows, do not nail down the interpreter command just yet in case | |
732 | # the user specified something that turns out to be a cygwin symlink, | |
733 | # which could bring down bjam if we invoke it. | |
734 | if $(target-os) != windows | |
735 | { | |
736 | interpreter-cmd = $(cmd-or-prefix) ; | |
737 | } | |
738 | } | |
739 | ||
740 | # Values to use in case we can not really find anything in the system. | |
741 | local fallback-cmd = $(cmds-to-try[1]) ; | |
742 | local fallback-version ; | |
743 | ||
744 | # Anything left to find or check? | |
745 | if ! ( $(interpreter-cmd) && $(version) && $(includes) && $(libraries) ) | |
746 | { | |
747 | # Values to be extracted from python's sys module. These will be set by | |
748 | # the probe rule, above, using Jam's dynamic scoping. | |
749 | local sys-elements = version platform prefix exec_prefix executable ; | |
750 | local sys.$(sys-elements) ; | |
751 | ||
752 | # Compute the string Python's sys.platform needs to match. If not | |
753 | # targeting Windows or cygwin we will assume only native builds can | |
754 | # possibly run, so we will not require a match and we leave sys.platform | |
755 | # blank. | |
756 | local platform ; | |
757 | switch $(target-os) | |
758 | { | |
759 | case windows : platform = win32 ; | |
760 | case cygwin : platform = cygwin ; | |
761 | } | |
762 | ||
763 | while $(cmds-to-try) | |
764 | { | |
765 | # Pop top command. | |
766 | local cmd = $(cmds-to-try[1]) ; | |
767 | cmds-to-try = $(cmds-to-try[2-]) ; | |
768 | ||
769 | debug-message Checking interpreter command \"$(cmd)\"... ; | |
770 | if [ probe $(cmd) ] | |
771 | { | |
772 | fallback-version ?= $(sys.version) ; | |
773 | ||
774 | # Check for version/platform validity. | |
775 | for local x in version platform | |
776 | { | |
777 | if $($(x)) && $($(x)) != $(sys.$(x)) | |
778 | { | |
779 | debug-message ...$(x) "mismatch (looking for" | |
780 | $($(x)) but found $(sys.$(x))")" ; | |
781 | cmd = ; | |
782 | } | |
783 | } | |
784 | ||
785 | if $(cmd) | |
786 | { | |
787 | debug-message ...requested configuration matched! ; | |
788 | ||
789 | exec-prefix = $(sys.exec_prefix) ; | |
790 | ||
791 | compute-default-paths $(target-os) : $(sys.version) : | |
792 | $(sys.prefix) : $(sys.exec_prefix) ; | |
793 | ||
794 | version = $(sys.version) ; | |
795 | interpreter-cmd ?= $(cmd) ; | |
796 | cmds-to-try = ; # All done. | |
797 | } | |
798 | } | |
799 | else | |
800 | { | |
801 | debug-message ...does not invoke a working interpreter ; | |
802 | } | |
803 | } | |
804 | } | |
805 | ||
806 | # Check whether configuration succeeded. | |
807 | if ! ( $(includes) && $(libraries) ) | |
808 | { | |
809 | debug-message Python headers and libraries not found. ; | |
810 | return ; | |
811 | } | |
812 | ||
813 | .configured = true ; | |
814 | ||
815 | if ! $(interpreter-cmd) | |
816 | { | |
817 | fallback-cmd ?= python ; | |
818 | debug-message No working Python interpreter found. ; | |
819 | if [ os.name ] != NT || ! [ invokes-cygwin-symlink $(fallback-cmd) ] | |
820 | { | |
821 | interpreter-cmd = $(fallback-cmd) ; | |
822 | debug-message falling back to \"$(interpreter-cmd)\" ; | |
823 | } | |
824 | } | |
825 | ||
826 | includes = [ path-to-native $(includes) ] ; | |
827 | libraries = [ path-to-native $(libraries) ] ; | |
828 | ||
829 | debug-message "Details of this Python configuration:" ; | |
830 | debug-message " interpreter command:" \"$(interpreter-cmd:E=<empty>)\" ; | |
831 | debug-message " include path:" \"$(includes:E=<empty>)\" ; | |
832 | debug-message " library path:" \"$(libraries:E=<empty>)\" ; | |
833 | if $(target-os) = windows | |
834 | { | |
835 | debug-message " DLL search path:" \"$(exec-prefix:E=<empty>)\" ; | |
836 | } | |
837 | ||
838 | # | |
839 | # Discover the presence of NumPy | |
840 | # | |
841 | debug-message "Checking for NumPy..." ; | |
842 | local full-cmd = "import sys; sys.stderr = sys.stdout; import numpy; print(numpy.get_include())" ; | |
843 | local full-cmd = $(interpreter-cmd)" -c \"$(full-cmd)\"" ; | |
844 | debug-message "running command '$(full-cmd)'" ; | |
845 | local result = [ SHELL $(full-cmd) : strip-eol : exit-status ] ; | |
846 | if $(result[2]) = 0 | |
847 | { | |
848 | .numpy = true ; | |
849 | .numpy-include = $(result[1]) ; | |
b32b8144 | 850 | debug-message "NumPy enabled" ; |
7c673cae FG |
851 | } |
852 | else | |
853 | { | |
b32b8144 FG |
854 | debug-message "NumPy disabled. Reason:" ; |
855 | debug-message " $(full-cmd) aborted with " ; | |
856 | debug-message " $(result[1])" ; | |
7c673cae FG |
857 | } |
858 | ||
859 | # | |
860 | # End autoconfiguration sequence. | |
861 | # | |
b32b8144 FG |
862 | |
863 | # Normalize and dissect any version number. | |
864 | local major-minor ; | |
865 | if $(version) | |
866 | { | |
867 | major-minor = [ split-version $(version) ] ; | |
868 | version = $(major-minor:J=.) ; | |
869 | } | |
870 | ||
871 | ||
7c673cae FG |
872 | local target-requirements = $(condition) ; |
873 | ||
874 | # Add the version, if any, to the target requirements. | |
875 | if $(version) | |
876 | { | |
877 | if ! $(version) in [ feature.values python ] | |
878 | { | |
879 | feature.extend python : $(version) ; | |
b32b8144 FG |
880 | py$(major-minor[1])-version ?= $(version) ; |
881 | if $(py$(major-minor[1])-version) < $(version) | |
882 | { | |
883 | py$(major-minor[1])-version = $(version) ; | |
884 | } | |
7c673cae FG |
885 | } |
886 | target-requirements += <python>$(version:E=default) ; | |
887 | } | |
888 | ||
889 | target-requirements += <target-os>$(target-os) ; | |
890 | ||
891 | # See if we can find a framework directory on darwin. | |
892 | local framework-directory ; | |
893 | if $(target-os) = darwin | |
894 | { | |
895 | # Search upward for the framework directory. | |
896 | local framework-directory = $(libraries[-1]) ; | |
897 | while $(framework-directory:D=) && $(framework-directory:D=) != Python.framework | |
898 | { | |
899 | framework-directory = $(framework-directory:D) ; | |
900 | } | |
901 | ||
902 | if $(framework-directory:D=) = Python.framework | |
903 | { | |
904 | debug-message framework directory is \"$(framework-directory)\" ; | |
905 | } | |
906 | else | |
907 | { | |
908 | debug-message "no framework directory found; using library path" ; | |
909 | framework-directory = ; | |
910 | } | |
911 | } | |
912 | ||
913 | local dll-path = $(libraries) ; | |
914 | ||
915 | # Make sure that we can find the Python DLL on Windows. | |
916 | if ( $(target-os) = windows ) && $(exec-prefix) | |
917 | { | |
918 | dll-path += $(exec-prefix) ; | |
919 | } | |
920 | ||
921 | # | |
922 | # Prepare usage requirements. | |
923 | # | |
924 | local usage-requirements = [ system-library-dependencies $(target-os) ] ; | |
925 | usage-requirements += <include>$(includes) <python.interpreter>$(interpreter-cmd) ; | |
926 | if <python-debugging>on in $(condition) | |
927 | { | |
928 | if $(target-os) = windows | |
929 | { | |
930 | # In pyconfig.h, Py_DEBUG is set if _DEBUG is set. If we define | |
931 | # Py_DEBUG we will get multiple definition warnings. | |
932 | usage-requirements += <define>_DEBUG ; | |
933 | } | |
934 | else | |
935 | { | |
936 | usage-requirements += <define>Py_DEBUG ; | |
937 | } | |
938 | } | |
939 | ||
b32b8144 FG |
940 | # In case we added duplicate requirements from what the user specified. |
941 | target-requirements = [ sequence.unique $(target-requirements) ] ; | |
942 | ||
7c673cae FG |
943 | # Global, but conditional, requirements to give access to the interpreter |
944 | # for general utilities, like other toolsets, that run Python scripts. | |
945 | toolset.add-requirements | |
11fdf7f2 | 946 | "$(target-requirements:J=,):<python.interpreter>$(interpreter-cmd)" ; |
7c673cae | 947 | |
7c673cae FG |
948 | # Register the right suffix for extensions. |
949 | register-extension-suffix $(extension-suffix) : $(target-requirements) ; | |
950 | ||
92f5a8d4 TL |
951 | # Make sure that the python feature is always considered |
952 | # relevant for any targets that depend on python. Without | |
953 | # this, it would only be considered relevant when there are | |
954 | # multiple configurations defined within the same build. | |
955 | target-requirements += <relevant>python ; | |
956 | ||
7c673cae FG |
957 | # |
958 | # Declare the "python" target. This should really be called | |
959 | # python_for_embedding. | |
960 | # | |
961 | ||
962 | if $(framework-directory) | |
963 | { | |
964 | alias python | |
965 | : | |
966 | : $(target-requirements) | |
967 | : | |
968 | : $(usage-requirements) <framework>$(framework-directory) | |
969 | ; | |
970 | } | |
971 | else | |
972 | { | |
973 | declare-libpython-target $(version) : $(target-requirements) ; | |
974 | ||
975 | # This is an evil hack. On, Windows, when Python is embedded, nothing | |
976 | # seems to set up sys.path to include Python's standard library | |
977 | # (http://article.gmane.org/gmane.comp.python.general/544986). The evil | |
978 | # here, aside from the workaround necessitated by Python's bug, is that: | |
979 | # | |
980 | # a. we're guessing the location of the python standard library from the | |
981 | # location of pythonXX.lib | |
982 | # | |
983 | # b. we're hijacking the <testing.launcher> property to get the | |
984 | # environment variable set up, and the user may want to use it for | |
985 | # something else (e.g. launch the debugger). | |
986 | local set-PYTHONPATH ; | |
987 | if $(target-os) = windows | |
988 | { | |
989 | set-PYTHONPATH = [ common.prepend-path-variable-command PYTHONPATH : | |
990 | $(libraries:D)/Lib ] ; | |
991 | } | |
992 | ||
993 | alias python | |
994 | : | |
995 | : $(target-requirements) | |
996 | : | |
997 | # Why python.lib must be listed here instead of along with the | |
998 | # system libs is a mystery, but if we do not do it, on cygwin, | |
999 | # -lpythonX.Y never appears in the command line (although it does on | |
1000 | # linux). | |
1001 | : $(usage-requirements) | |
1002 | <testing.launcher>$(set-PYTHONPATH) | |
1003 | <library-path>$(libraries) <dll-path>$(dll-path) <library>python.lib | |
1004 | ; | |
1005 | } | |
1006 | ||
1007 | # On *nix, we do not want to link either Boost.Python or Python extensions | |
1008 | # to libpython, because the Python interpreter itself provides all those | |
1009 | # symbols. If we linked to libpython, we would get duplicate symbols. So | |
1010 | # declare two targets -- one for building extensions and another for | |
1011 | # embedding. | |
1012 | if $(target-os) in windows cygwin | |
1013 | { | |
1014 | alias python_for_extensions : python : $(target-requirements) ; | |
1015 | } | |
1016 | else if $(target-os) = darwin { | |
1017 | alias python_for_extensions | |
1018 | : | |
1019 | : $(target-requirements) | |
1020 | : | |
1021 | : $(usage-requirements) <linkflags>"-undefined dynamic_lookup" | |
1022 | ; | |
1023 | } | |
1024 | # On AIX we need Python extensions and Boost.Python to import symbols from | |
1025 | # the Python interpreter. Dynamic libraries opened with dlopen() do not | |
1026 | # inherit the symbols from the Python interpreter. | |
1027 | else if $(target-os) = aix | |
1028 | { | |
1029 | alias python_for_extensions | |
1030 | : | |
1031 | : $(target-requirements) | |
1032 | : | |
11fdf7f2 | 1033 | : $(usage-requirements) <linkflags>"-Wl,-bI:$(libraries[1])/python.exp" |
7c673cae FG |
1034 | ; |
1035 | } | |
1036 | else | |
1037 | { | |
1038 | alias python_for_extensions | |
1039 | : | |
1040 | : $(target-requirements) | |
1041 | : | |
1042 | : $(usage-requirements) | |
1043 | ; | |
1044 | } | |
b32b8144 FG |
1045 | |
1046 | } | |
1047 | ||
1048 | # Conditional rule specification that will prevent building of a target | |
1049 | # if there is no matching python configuration available with the given | |
1050 | # required properties. | |
1051 | rule require-py ( properties * ) | |
1052 | { | |
11fdf7f2 TL |
1053 | local py-ext-target = [ $(.project).find python_for_extensions : no-error ] ; |
1054 | if ! $(py-ext-target) | |
1055 | { | |
1056 | return <build>no ; | |
1057 | } | |
b32b8144 FG |
1058 | local property-set = [ property-set.create $(properties) ] ; |
1059 | property-set = [ $(property-set).expand ] ; | |
1060 | local py-ext-alternative = [ $(py-ext-target).select-alternatives $(property-set) ] ; | |
1061 | if ! $(py-ext-alternative) | |
1062 | { | |
1063 | return <build>no ; | |
1064 | } | |
7c673cae FG |
1065 | } |
1066 | ||
1067 | ||
1068 | rule configured ( ) | |
1069 | { | |
1070 | return $(.configured) ; | |
1071 | } | |
1072 | ||
1073 | rule numpy ( ) | |
1074 | { | |
1075 | return $(.numpy) ; | |
1076 | } | |
1077 | ||
1078 | rule numpy-include ( ) | |
1079 | { | |
1080 | return $(.numpy-include) ; | |
1081 | } | |
1082 | ||
1083 | ||
1084 | type.register PYTHON_EXTENSION : : SHARED_LIB ; | |
1085 | ||
1086 | ||
1087 | local rule register-extension-suffix ( root : condition * ) | |
1088 | { | |
1089 | local suffix ; | |
1090 | ||
1091 | switch [ feature.get-values target-os : $(condition) ] | |
1092 | { | |
1093 | case windows : suffix = pyd ; | |
1094 | case cygwin : suffix = dll ; | |
1095 | case hpux : | |
1096 | { | |
1097 | if [ feature.get-values python : $(condition) ] in 1.5 1.6 2.0 2.1 2.2 2.3 2.4 | |
1098 | { | |
1099 | suffix = sl ; | |
1100 | } | |
1101 | else | |
1102 | { | |
1103 | suffix = so ; | |
1104 | } | |
1105 | } | |
1106 | case * : suffix = so ; | |
1107 | } | |
1108 | ||
1109 | type.set-generated-target-suffix PYTHON_EXTENSION : $(condition) : <$(root).$(suffix)> ; | |
1110 | } | |
1111 | ||
1112 | ||
1113 | # Unset 'lib' prefix for PYTHON_EXTENSION | |
1114 | type.set-generated-target-prefix PYTHON_EXTENSION : : "" ; | |
1115 | ||
1116 | ||
1117 | rule python-extension ( name : sources * : requirements * : default-build * : | |
1118 | usage-requirements * ) | |
1119 | { | |
1120 | if [ configured ] | |
1121 | { | |
1122 | requirements += <use>/python//python_for_extensions ; | |
1123 | } | |
1124 | requirements += <suppress-import-lib>true ; | |
1125 | ||
1126 | local project = [ project.current ] ; | |
1127 | ||
1128 | targets.main-target-alternative | |
1129 | [ new typed-target $(name) : $(project) : PYTHON_EXTENSION | |
1130 | : [ targets.main-target-sources $(sources) : $(name) ] | |
1131 | : [ targets.main-target-requirements $(requirements) : $(project) ] | |
1132 | : [ targets.main-target-default-build $(default-build) : $(project) ] | |
1133 | ] ; | |
1134 | } | |
1135 | ||
1136 | IMPORT python : python-extension : : python-extension ; | |
1137 | ||
1138 | # Support for testing. | |
1139 | type.register PY : py ; | |
1140 | type.register RUN_PYD_OUTPUT ; | |
1141 | type.register RUN_PYD : : TEST ; | |
1142 | ||
1143 | ||
1144 | class python-test-generator : generator | |
1145 | { | |
1146 | import set ; | |
1147 | ||
1148 | rule __init__ ( * : * ) | |
1149 | { | |
1150 | generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; | |
1151 | self.composing = true ; | |
1152 | } | |
1153 | ||
1154 | rule run ( project name ? : property-set : sources * : multiple ? ) | |
1155 | { | |
1156 | local pyversion = [ $(property-set).get <python> ] ; | |
1157 | local python ; | |
1158 | local other-pythons ; | |
1159 | ||
1160 | for local s in $(sources) | |
1161 | { | |
1162 | if [ $(s).type ] = PY | |
1163 | { | |
1164 | if ! $(python) | |
1165 | { | |
1166 | # First Python source ends up on command line. | |
1167 | python = $(s) ; | |
1168 | ||
1169 | } | |
1170 | else | |
1171 | { | |
1172 | # Other Python sources become dependencies. | |
1173 | other-pythons += $(s) ; | |
1174 | } | |
1175 | } | |
1176 | } | |
1177 | ||
1178 | local extensions ; | |
1179 | for local s in $(sources) | |
1180 | { | |
1181 | if [ $(s).type ] = PYTHON_EXTENSION | |
1182 | { | |
1183 | extensions += $(s) ; | |
1184 | } | |
1185 | } | |
1186 | ||
1187 | local libs ; | |
1188 | for local s in $(sources) | |
1189 | { | |
1190 | if [ type.is-derived [ $(s).type ] LIB ] | |
1191 | && ! $(s) in $(extensions) | |
1192 | { | |
1193 | libs += $(s) ; | |
1194 | } | |
1195 | } | |
1196 | ||
1197 | local new-sources ; | |
1198 | for local s in $(sources) | |
1199 | { | |
1200 | if [ type.is-derived [ $(s).type ] CPP ] | |
1201 | { | |
1202 | local name = [ utility.basename [ $(s).name ] ] ; | |
1203 | if $(name) = [ utility.basename [ $(python).name ] ] | |
1204 | { | |
1205 | name = $(name)_ext ; | |
1206 | } | |
1207 | local extension = [ generators.construct $(project) $(name) : | |
1208 | PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ; | |
1209 | ||
1210 | # The important part of usage requirements returned from | |
1211 | # PYTHON_EXTENSION generator are xdll-path properties that will | |
1212 | # allow us to find the python extension at runtime. | |
1213 | property-set = [ $(property-set).add $(extension[1]) ] ; | |
1214 | ||
1215 | # Ignore usage requirements. We're a top-level generator and | |
1216 | # nobody is going to use what we generate. | |
1217 | new-sources += $(extension[2-]) ; | |
1218 | } | |
1219 | } | |
1220 | ||
1221 | property-set = [ $(property-set).add-raw <dependency>$(other-pythons) ] ; | |
1222 | ||
1223 | return [ construct-result $(python) $(extensions) $(new-sources) : | |
1224 | $(project) $(name) : $(property-set) ] ; | |
1225 | } | |
1226 | } | |
1227 | ||
1228 | ||
1229 | generators.register | |
1230 | [ new python-test-generator python.capture-output : : RUN_PYD_OUTPUT ] ; | |
1231 | ||
1232 | generators.register-standard testing.expect-success | |
1233 | : RUN_PYD_OUTPUT : RUN_PYD ; | |
1234 | ||
1235 | ||
1236 | # There are two different ways of spelling OS names. One is used for [ os.name ] | |
1237 | # and the other is used for the <host-os> and <target-os> properties. Until that | |
1238 | # is remedied, this sets up a crude mapping from the latter to the former, that | |
1239 | # will work *for the purposes of cygwin/NT cross-builds only*. Could not think | |
1240 | # of a better name than "translate". | |
1241 | # | |
1242 | .translate-os-windows = NT ; | |
1243 | .translate-os-cygwin = CYGWIN ; | |
1244 | local rule translate-os ( src-os ) | |
1245 | { | |
1246 | local x = $(.translate-os-$(src-os)) [ os.name ] ; | |
1247 | return $(x[1]) ; | |
1248 | } | |
1249 | ||
1250 | ||
1251 | # Extract the path to a single ".pyd" source. This is used to build the | |
1252 | # PYTHONPATH for running bpl tests. | |
1253 | # | |
1254 | local rule pyd-pythonpath ( source ) | |
1255 | { | |
1256 | return [ on $(source) return $(LOCATE) $(SEARCH) ] ; | |
1257 | } | |
1258 | ||
1259 | ||
1260 | # The flag settings on testing.capture-output do not apply to python.capture | |
1261 | # output at the moment. Redo this explicitly. | |
1262 | toolset.flags python.capture-output ARGS <testing.arg> ; | |
1263 | toolset.flags python.capture-output INPUT_FILES <testing.input-file> ; | |
1264 | ||
11fdf7f2 TL |
1265 | toolset.uses-features python.capture-output : |
1266 | <testing.launcher> <testing.execute> <dll-path> <xdll-path> <target-os> | |
1267 | <pythonpath> ; | |
1268 | ||
7c673cae FG |
1269 | rule capture-output ( target : sources * : properties * ) |
1270 | { | |
1271 | # Setup up a proper DLL search path. Here, $(sources[1]) is a python module | |
1272 | # and $(sources[2]) is a DLL. Only $(sources[1]) is passed to | |
1273 | # testing.capture-output, so RUN_PATH variable on $(sources[2]) is not | |
1274 | # consulted. Move it over explicitly. | |
1275 | RUN_PATH on $(sources[1]) = [ on $(sources[2-]) return $(RUN_PATH) ] ; | |
1276 | ||
1277 | PYTHONPATH = [ sequence.transform pyd-pythonpath : $(sources[2-]) ] ; | |
1278 | PYTHONPATH += [ feature.get-values pythonpath : $(properties) ] ; | |
1279 | ||
1280 | # After test is run, we remove the Python module, but not the Python script. | |
11fdf7f2 | 1281 | testing.capture-output $(target) : $(sources[1]) : $(properties) ; |
7c673cae FG |
1282 | |
1283 | # PYTHONPATH is different; it will be interpreted by whichever Python is | |
1284 | # invoked and so must follow path rules for the target os. The only OSes | |
1285 | # where we can run python for other OSes currently are NT and CYGWIN so we | |
1286 | # only need to handle those cases. | |
1287 | local target-os = [ feature.get-values target-os : $(properties) ] ; | |
1288 | # Oddly, host-os is not in properties, so grab the default value. | |
1289 | local host-os = [ feature.defaults host-os ] ; | |
1290 | host-os = $(host-os:G=) ; | |
1291 | if $(target-os) != $(host-os) && $(target-os) in windows cygwin && $(host-os) in windows cygwin | |
1292 | { | |
1293 | PYTHONPATH = [ sequence.transform $(host-os)-to-$(target-os)-path : | |
1294 | $(PYTHONPATH) ] ; | |
1295 | } | |
1296 | local path-separator = [ os.path-separator [ translate-os $(target-os) ] ] ; | |
1297 | local set-PYTHONPATH = [ common.variable-setting-command PYTHONPATH : | |
11fdf7f2 | 1298 | $(PYTHONPATH:E=:J=$(path-separator)) ] ; |
7c673cae FG |
1299 | LAUNCHER on $(target) = $(set-PYTHONPATH) [ on $(target) return \"$(PYTHON)\" ] ; |
1300 | } | |
1301 | ||
1302 | ||
1303 | rule bpl-test ( name : sources * : requirements * ) | |
1304 | { | |
1305 | local s ; | |
1306 | sources ?= $(name).py $(name).cpp ; | |
1307 | return [ testing.make-test run-pyd : $(sources) /boost/python//boost_python | |
1308 | : $(requirements) : $(name) ] ; | |
1309 | } | |
1310 | ||
1311 | # The same as bpl-test but additionally require (and link to) boost_numpy. | |
1312 | # Masked whenever NumPy is not enabled. | |
1313 | rule numpy-test ( name : sources * : requirements * ) | |
1314 | { | |
1315 | numpy-include = [ python.numpy-include ] ; | |
1316 | # yuk ! | |
1317 | if ! $(.numpy) { requirements += <build>no ; } | |
1318 | sources ?= $(name).py $(name).cpp ; | |
1319 | name = [ regex.replace $(name) "[/]" "~" ] ; | |
1320 | return [ testing.make-test run-pyd | |
1321 | : $(sources) /boost/python//boost_numpy /boost/python//boost_python | |
1322 | : $(requirements) <include>$(numpy-include) | |
1323 | : $(name) ] ; | |
1324 | } | |
1325 | ||
b32b8144 FG |
1326 | rule py-version ( n ) |
1327 | { | |
1328 | return $(py$(n)-version) ; | |
1329 | } | |
1330 | ||
7c673cae FG |
1331 | IMPORT $(__name__) : bpl-test : : bpl-test ; |
1332 | IMPORT $(__name__) : numpy-test : : numpy-test ; | |
b32b8144 | 1333 | IMPORT $(__name__) : py-version : : py-version ; |