]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/build/src/tools/python.jam
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / tools / build / src / tools / python.jam
CommitLineData
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
18import type ;
19import testing ;
20import generators ;
21import project ;
22import errors ;
23import targets ;
24import "class" : new ;
25import os ;
26import common ;
27import toolset ;
28import regex ;
29import numbers ;
30import string ;
31import property ;
32import sequence ;
33import path ;
34import feature ;
35import set ;
36import builtin ;
b32b8144 37import property-set ;
7c673cae
FG
38
39
40# Make this module a project.
41project.initialize $(__name__) ;
42project 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.
49lib dl ;
50# This contains 'openpty' function need by python. Again, on some system need to
51# pass this to linker explicitly.
52lib util ;
53# Python uses pthread symbols.
20effc67
TL
54lib pthread :
55 : <target-os>linux:<link>shared
56 ;
57
7c673cae
FG
58# Extra library needed by phtread on some platforms.
59lib 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.
65feature.feature pythonpath : : free optional path ;
66
b32b8144
FG
67# The best configured version of Python 2 and 3.
68py2-version = ;
69py3-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#
104rule 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#
126local 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.
150local 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#
192local 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#
212local 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
230local 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#
244local 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
270local 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#
286local 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#
329local 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#
341local 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#
357local rule guess-windows-path ( path )
358{
11fdf7f2 359 return [ SUBST $(path) "($(.windows-drive-letter-re)|.*([\\]).*)" $1 ] ;
7c673cae
FG
360}
361
362
363local 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#
384local 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 = ;
404for local v in [ numbers.range 15 34 ]
405{
406 .version-countdown = [ SUBST $(v) (.)(.*) $1.$2 ] $(.version-countdown) ;
407}
408
409
410local 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
432local 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#
448local 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#
512local 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 564feature.feature python : : propagated symmetric ;
7c673cae
FG
565feature.feature python.interpreter : : free ;
566
567toolset.flags python.capture-output PYTHON : <python.interpreter> ;
568
569#
570# Support for Python configured --with-pydebug
571#
572feature.feature python-debugging : off on : propagated ;
b32b8144 573variant 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#
579local 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#
632local 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"
671rule 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#
680local 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.
709local 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.
1063rule 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
1080rule configured ( )
1081{
1082 return $(.configured) ;
1083}
1084
1085rule numpy ( )
1086{
1087 return $(.numpy) ;
1088}
1089
1090rule numpy-include ( )
1091{
1092 return $(.numpy-include) ;
1093}
1094
1095
1096type.register PYTHON_EXTENSION : : SHARED_LIB ;
1097
1098
1099local 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
1126type.set-generated-target-prefix PYTHON_EXTENSION : : "" ;
1127
1128
1129rule 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
1148IMPORT python : python-extension : : python-extension ;
1149
1150# Support for testing.
1151type.register PY : py ;
1152type.register RUN_PYD_OUTPUT ;
1153type.register RUN_PYD : : TEST ;
1154
1155
1156class 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
1241generators.register
1242 [ new python-test-generator python.capture-output : : RUN_PYD_OUTPUT ] ;
1243
1244generators.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 ;
1256local 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#
1266local 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.
1274toolset.flags python.capture-output ARGS <testing.arg> ;
1275toolset.flags python.capture-output INPUT_FILES <testing.input-file> ;
1276
11fdf7f2
TL
1277toolset.uses-features python.capture-output :
1278 <testing.launcher> <testing.execute> <dll-path> <xdll-path> <target-os>
1279 <pythonpath> ;
1280
7c673cae
FG
1281rule 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
1315rule 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.
1325rule 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
1338rule py-version ( n )
1339{
1340 return $(py$(n)-version) ;
1341}
1342
7c673cae
FG
1343IMPORT $(__name__) : bpl-test : : bpl-test ;
1344IMPORT $(__name__) : numpy-test : : numpy-test ;
b32b8144 1345IMPORT $(__name__) : py-version : : py-version ;