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