1# Copyright 2004 Vladimir Prus.
2# Distributed under the Boost Software License, Version 1.0. (See
3# accompanying file LICENSE_1_0.txt or copy at
4# http://www.boost.org/LICENSE_1_0.txt)
5
6# Support for Python and the the Boost.Python library.
7#
8# This module defines
9#
10# - a project 'python' with a target 'python' in it, that corresponds to the
11#   python library
12#
13# - a main target rule 'python-extension' which can be used to build a python
14#   extension.
15#
16# Extensions that use Boost.Python must explicitly link to it.
17
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 ;
37import property-set ;
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.
54lib pthread :
55    : <target-os>linux:<link>shared
56    ;
57
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
67# The best configured version of Python 2 and 3.
68py2-version = ;
69py3-version = ;
70
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
87#   against the build configuration when B2 selects a Python
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        {
114            debug-message "  user-specified $(v):" \"$($(v))\" ;
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.
161        local base-pat = [ regex.escape $(path:D=) : "].[()*+?|\\$^" : \\ ] ;
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    {
234        ECHO "notice:" "[python-cfg]" $(message) ;
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
262.windows-drive-letter-re = "^([A-Za-z]):[\\/](.*)" ;
263.cygwin-drive-letter-re = "^/cygdrive/([a-z])/(.*)" ;
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
290    local drive-letter = [ SUBST $(path) $(.cygwin-drive-letter-re) "$1:/$2" ] ;
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{
359    return [ SUBST $(path) "($(.windows-drive-letter-re)|.*([\\]).*)" $1 ] ;
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{
386    local major-minor = [ MATCH "^([0-9]+)\.([0-9]+)(.*)$" : $(version) : 1 2 3 ] ;
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 ;
465        debug-message "(guessing:" \"$(skip-symlink)\") "in" your 'using python' line ;
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.
501                sys.$(s) = [ SUBST $(output) "\\<$(s)=([^$(nl)]+)" $1 ] ;
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    {
550        includes ?= $(prefix)/include/python$(version) ;
551
552        local lib = $(exec-prefix)/lib ;
553        libraries ?= $(lib)/python$(version)/config $(lib) ;
554    }
555}
556
557# The version of the python interpreter to use.
558feature.feature python : : propagated symmetric ;
559feature.feature python.interpreter : : free ;
560
561toolset.flags python.capture-output PYTHON : <python.interpreter> ;
562
563#
564# Support for Python configured --with-pydebug
565#
566feature.feature python-debugging : off on : propagated ;
567variant debug-python : debug : <python-debugging>on ;
568
569
570# Return a list of candidate commands to try when looking for a Python
571# interpreter. prefix is expected to be a native path.
572#
573local rule candidate-interpreters ( version ? : prefix ? : target-os )
574{
575    local bin-path = bin ;
576    if $(target-os) = windows
577    {
578        # On Windows, look in the root directory itself and, to work with the
579        # result of a build-from-source, the PCBuild directory.
580        bin-path = PCBuild8 PCBuild "" ;
581    }
582
583    bin-path = $(bin-path:R=$(prefix)) ;
584
585    if $(target-os) in windows darwin
586    {
587        return                                            # Search:
588            $(:E=python:R=$(bin-path))                    #   Relative to the prefix, if any
589            python                                        #   In the PATH
590            [ $(target-os)-installed-pythons $(version) ] #   Standard install locations
591        ;
592    }
593    else
594    {
595        # Search relative to the prefix, or if none supplied, in PATH.
596        local unversioned = $(:E=python:R=$(bin-path:E=)) ;
597
598        # If a version was specified, look for a python with that specific
599        # version appended before looking for one called, simply, "python"
600        return $(unversioned)$(version) $(unversioned) ;
601    }
602}
603
604
605# Compute system library dependencies for targets linking with static Python
606# libraries.
607#
608# On many systems, Python uses libraries such as pthreads or libdl. Since static
609# libraries carry no library dependency information of their own that the linker
610# can extract, these extra dependencies have to be given explicitly on the link
611# line of the client.  The information about these dependencies is packaged into
612# the "python" target below.
613#
614# Even where Python itself uses pthreads, it never allows extension modules to
615# be entered concurrently (unless they explicitly give up the interpreter lock).
616# Therefore, extension modules do not need the efficiency overhead of threadsafe
617# code as produced by <threading>multi, and we handle libpthread along with
618# other libraries here. Note: this optimization is based on an assumption that
619# the compiler generates link-compatible code in both the single- and
620# multi-threaded cases, and that system libraries do not change their ABIs
621# either.
622#
623# Returns a list of usage-requirements that link to the necessary system
624# libraries.
625#
626local rule system-library-dependencies ( target-os )
627{
628    switch $(target-os)
629    {
630        case s[uo][nl]* : # solaris, sun, sunos
631            # Add a librt dependency for the gcc toolset on SunOS (the sun
632            # toolset adds -lrt unconditionally). While this appears to
633            # duplicate the logic already in gcc.jam, it does not as long as
634            # we are not forcing <threading>multi.
635
636            # On solaris 10, distutils.sysconfig.get_config_var('LIBS') yields
637            # '-lresolv -lsocket -lnsl -lrt -ldl'. However, that does not seem
638            # to be the right list for extension modules. For example, on my
639            # installation, adding -ldl causes at least one test to fail because
640            # the library can not be found and removing it causes no failures.
641
642            # Apparently, though, we need to add -lrt for gcc.
643            return <toolset>gcc:<library>rt ;
644
645        case osf : return  <library>pthread <toolset>gcc:<library>rt ;
646
647        case qnx* : return ;
648        case darwin : return ;
649        case windows : return ;
650        case haiku : return ;
651
652        case hpux : return  <library>rt ;
653        case *bsd : return  <library>pthread <toolset>gcc:<library>util ;
654
655        case aix : return  <library>pthread <library>dl ;
656
657        case * : return  <library>pthread <library>dl
658            <toolset>gcc:<library>util <toolset-intel:platform>linux:<library>util ;
659    }
660}
661
662
663# Define a version suffix for libraries depending on Python.
664# For example, Boost.Python built for Python 2.7 uses the suffix "27"
665rule version-suffix ( version )
666{
667    local major-minor = [ split-version $(version) ] ;
668    local suffix = $(major-minor:J="") ;
669    return $(suffix) ;
670}
671
672# Declare a target to represent Python's library.
673#
674local rule declare-libpython-target ( version ? : requirements * )
675{
676    # Compute the representation of Python version in the name of Python's
677    # library file.
678    local lib-version = $(version) ;
679    if <target-os>windows in $(requirements)
680    {
681        local major-minor = [ split-version $(version) ] ;
682        lib-version = $(major-minor:J="") ;
683        if <python-debugging>on in $(requirements)
684        {
685            lib-version = $(lib-version)_d ;
686        }
687    }
688
689    if ! $(lib-version)
690    {
691        ECHO *** "warning:" could not determine Python version, which will ;
692        ECHO *** "warning:" probably prevent us from linking with the python ;
693        ECHO *** "warning:" library.  Consider explicitly passing the version ;
694        ECHO *** "warning:" to 'using python'. ;
695    }
696
697    # Declare it.
698    lib python.lib : : <name>python$(lib-version) $(requirements) ;
699}
700
701
702# Implementation of init.
703local rule configure ( version ? : cmd-or-prefix ? : includes * : libraries ? :
704    condition * : extension-suffix ? )
705{
706    local prefix ;
707    local exec-prefix ;
708    local cmds-to-try ;
709    local interpreter-cmd ;
710
711    local target-os = [ feature.get-values target-os : $(condition) ] ;
712    target-os ?= [ feature.defaults target-os ] ;
713    target-os = $(target-os:G=) ;
714
715    if $(target-os) = windows && <python-debugging>on in $(condition)
716    {
717        extension-suffix ?= _d ;
718    }
719    extension-suffix ?= "" ;
720
721    local cmds-to-try ;
722
723    if ! $(cmd-or-prefix) || [ GLOB $(cmd-or-prefix) : * ]
724    {
725        # If the user did not pass a command, whatever we got was a prefix.
726        prefix = $(cmd-or-prefix) ;
727        cmds-to-try = [ candidate-interpreters $(version) : $(prefix) : $(target-os) ] ;
728    }
729    else
730    {
731        # Work with the command the user gave us.
732        cmds-to-try = $(cmd-or-prefix) ;
733
734        # On Windows, do not nail down the interpreter command just yet in case
735        # the user specified something that turns out to be a cygwin symlink,
736        # which could bring down bjam if we invoke it.
737        if $(target-os) != windows
738        {
739            interpreter-cmd = $(cmd-or-prefix) ;
740        }
741    }
742
743    # Values to use in case we can not really find anything in the system.
744    local fallback-cmd = $(cmds-to-try[1]) ;
745    local fallback-version ;
746
747    # Anything left to find or check?
748    if ! ( $(interpreter-cmd) && $(version) && $(includes) && $(libraries) )
749    {
750        # Values to be extracted from python's sys module. These will be set by
751        # the probe rule, above, using Jam's dynamic scoping.
752        local sys-elements = version platform prefix exec_prefix executable ;
753        local sys.$(sys-elements) ;
754
755        # Compute the string Python's sys.platform needs to match. If not
756        # targeting Windows or cygwin we will assume only native builds can
757        # possibly run, so we will not require a match and we leave sys.platform
758        # blank.
759        local platform ;
760        switch $(target-os)
761        {
762            case windows : platform = win32 ;
763            case cygwin : platform = cygwin ;
764        }
765
766        while $(cmds-to-try)
767        {
768            # Pop top command.
769            local cmd = $(cmds-to-try[1]) ;
770            cmds-to-try = $(cmds-to-try[2-]) ;
771
772            debug-message Checking interpreter command \"$(cmd)\"... ;
773            if [ probe $(cmd) ]
774            {
775                fallback-version ?= $(sys.version) ;
776
777                # Check for version/platform validity.
778                for local x in version platform
779                {
780                    if $($(x)) && $($(x)) != $(sys.$(x))
781                    {
782                        debug-message ...$(x) "mismatch (looking for"
783                            $($(x)) but found $(sys.$(x))")" ;
784                        cmd = ;
785                    }
786                }
787
788                if $(cmd)
789                {
790                    debug-message ...requested configuration matched! ;
791
792                    exec-prefix = $(sys.exec_prefix) ;
793
794                    compute-default-paths $(target-os) : $(sys.version) :
795                        $(sys.prefix) : $(sys.exec_prefix) ;
796
797                    version = $(sys.version) ;
798                    interpreter-cmd ?= $(cmd) ;
799                    cmds-to-try = ;  # All done.
800                }
801            }
802            else
803            {
804                debug-message ...does not invoke a working interpreter ;
805            }
806        }
807    }
808
809    # Check whether configuration succeeded.
810    if ! ( $(includes) && $(libraries) )
811    {
812        debug-message Python headers and libraries not found. ;
813        return ;
814    }
815
816    .configured = true ;
817
818    if ! $(interpreter-cmd)
819    {
820        fallback-cmd ?= python ;
821        debug-message No working Python interpreter found. ;
822        if [ os.name ] != NT || ! [ invokes-cygwin-symlink $(fallback-cmd) ]
823        {
824            interpreter-cmd = $(fallback-cmd) ;
825            debug-message falling back to \"$(interpreter-cmd)\" ;
826        }
827    }
828
829    includes = [ path-to-native $(includes) ] ;
830    libraries = [ path-to-native $(libraries) ] ;
831
832    debug-message "Details of this Python configuration:" ;
833    debug-message "  interpreter command:" \"$(interpreter-cmd:E=<empty>)\" ;
834    debug-message "  include path:" \"$(includes:E=<empty>)\" ;
835    debug-message "  library path:" \"$(libraries:E=<empty>)\" ;
836    if $(target-os) = windows
837    {
838        debug-message "  DLL search path:" \"$(exec-prefix:E=<empty>)\" ;
839    }
840
841    #
842    # Discover the presence of NumPy
843    #
844    debug-message "Checking for NumPy..." ;
845    local full-cmd = "import sys; sys.stderr = sys.stdout; import numpy; print(numpy.get_include())" ;
846    local full-cmd = $(interpreter-cmd)" -c \"$(full-cmd)\"" ;
847    debug-message "running command '$(full-cmd)'" ;
848    local result = [ SHELL $(full-cmd) : strip-eol : exit-status ] ;
849    if $(result[2]) = 0
850    {
851        .numpy = true ;
852        .numpy-include = $(result[1]) ;
853        debug-message "NumPy enabled" ;
854    }
855    else
856    {
857        debug-message "NumPy disabled. Reason:" ;
858        debug-message "  $(full-cmd) aborted with " ;
859        debug-message "  $(result[1])" ;
860    }
861
862    #
863    # End autoconfiguration sequence.
864    #
865
866    # Normalize and dissect any version number.
867    local major-minor ;
868    if $(version)
869    {
870        major-minor = [ split-version $(version) ] ;
871        version = $(major-minor:J=.) ;
872    }
873
874
875    local target-requirements = $(condition) ;
876
877    # Add the version, if any, to the target requirements.
878    if $(version)
879    {
880        if ! $(version) in [ feature.values python ]
881        {
882            feature.extend python : $(version) ;
883            py$(major-minor[1])-version ?= $(version) ;
884            if $(py$(major-minor[1])-version) < $(version)
885            {
886                py$(major-minor[1])-version = $(version) ;
887            }
888        }
889        target-requirements += <python>$(version:E=default) ;
890    }
891
892    target-requirements += <target-os>$(target-os) ;
893
894    # See if we can find a framework directory on darwin.
895    local framework-directory ;
896    if $(target-os) = darwin
897    {
898        # Search upward for the framework directory.
899        local framework-directory = $(libraries[-1]) ;
900        while $(framework-directory:D=) && $(framework-directory:D=) != Python.framework
901        {
902            framework-directory = $(framework-directory:D) ;
903        }
904
905        if $(framework-directory:D=) = Python.framework
906        {
907            debug-message framework directory is \"$(framework-directory)\" ;
908        }
909        else
910        {
911            debug-message "no framework directory found; using library path" ;
912            framework-directory = ;
913        }
914    }
915
916    local dll-path = $(libraries) ;
917
918    # Make sure that we can find the Python DLL on Windows.
919    if ( $(target-os) = windows ) && $(exec-prefix)
920    {
921        dll-path += $(exec-prefix) ;
922    }
923
924    #
925    # Prepare usage requirements.
926    #
927    local usage-requirements = [ system-library-dependencies $(target-os) ] ;
928    usage-requirements += <include>$(includes) <python.interpreter>$(interpreter-cmd) ;
929    if <python-debugging>on in $(condition)
930    {
931        if $(target-os) = windows
932        {
933            # In pyconfig.h, Py_DEBUG is set if _DEBUG is set. If we define
934            # Py_DEBUG we will get multiple definition warnings.
935            usage-requirements += <define>_DEBUG ;
936        }
937        else
938        {
939            usage-requirements += <define>Py_DEBUG ;
940        }
941    }
942
943    # In case we added duplicate requirements from what the user specified.
944    target-requirements = [ sequence.unique $(target-requirements) ] ;
945
946    # Global, but conditional, requirements to give access to the interpreter
947    # for general utilities, like other toolsets, that run Python scripts.
948    toolset.add-requirements
949        "$(target-requirements:J=,):<python.interpreter>$(interpreter-cmd)" ;
950
951    # Register the right suffix for extensions.
952    register-extension-suffix $(extension-suffix) : $(target-requirements) ;
953
954    # Make sure that the python feature is always considered
955    # relevant for any targets that depend on python.  Without
956    # this, it would only be considered relevant when there are
957    # multiple configurations defined within the same build.
958    target-requirements += <relevant>python ;
959
960    #
961    # Declare the "python" target. This should really be called
962    # python_for_embedding.
963    #
964
965    if $(framework-directory)
966    {
967        alias python
968          :
969          : $(target-requirements)
970          :
971          : $(usage-requirements) <framework>$(framework-directory)
972          ;
973    }
974    else
975    {
976        declare-libpython-target $(version) : $(target-requirements) ;
977
978        # This is an evil hack.  On, Windows, when Python is embedded, nothing
979        # seems to set up sys.path to include Python's standard library
980        # (http://article.gmane.org/gmane.comp.python.general/544986). The evil
981        # here, aside from the workaround necessitated by Python's bug, is that:
982        #
983        # a. we're guessing the location of the python standard library from the
984        #    location of pythonXX.lib
985        #
986        # b. we're hijacking the <testing.launcher> property to get the
987        #    environment variable set up, and the user may want to use it for
988        #    something else (e.g. launch the debugger).
989        local set-PYTHONPATH ;
990        if $(target-os) = windows
991        {
992            set-PYTHONPATH = [ common.prepend-path-variable-command PYTHONPATH :
993                $(libraries:D)/Lib ] ;
994        }
995
996        alias python
997          :
998          : $(target-requirements)
999          :
1000            # Why python.lib must be listed here instead of along with the
1001            # system libs is a mystery, but if we do not do it, on cygwin,
1002            # -lpythonX.Y never appears in the command line (although it does on
1003            # linux).
1004          : $(usage-requirements)
1005            <testing.launcher>$(set-PYTHONPATH)
1006              <library-path>$(libraries) <dll-path>$(dll-path) <library>python.lib
1007          ;
1008    }
1009
1010    # On *nix, we do not want to link either Boost.Python or Python extensions
1011    # to libpython, because the Python interpreter itself provides all those
1012    # symbols. If we linked to libpython, we would get duplicate symbols. So
1013    # declare two targets -- one for building extensions and another for
1014    # embedding.
1015    if $(target-os) in windows cygwin
1016    {
1017        alias python_for_extensions : python : $(target-requirements) ;
1018    }
1019    else if $(target-os) = darwin {
1020        alias python_for_extensions
1021            :
1022            : $(target-requirements)
1023            :
1024            : $(usage-requirements) <linkflags>"-undefined dynamic_lookup"
1025            ;
1026    }
1027    # On AIX we need Python extensions and Boost.Python to import symbols from
1028    # the Python interpreter. Dynamic libraries opened with dlopen() do not
1029    # inherit the symbols from the Python interpreter.
1030    else if $(target-os) = aix
1031    {
1032        alias python_for_extensions
1033            :
1034            : $(target-requirements)
1035            :
1036            : $(usage-requirements) <linkflags>"-Wl,-bI:$(libraries[1])/python.exp"
1037            ;
1038    }
1039    else
1040    {
1041        alias python_for_extensions
1042            :
1043            : $(target-requirements)
1044            :
1045            : $(usage-requirements)
1046            ;
1047    }
1048
1049}
1050
1051# Conditional rule specification that will prevent building of a target
1052# if there is no matching python configuration available with the given
1053# required properties.
1054rule require-py ( properties * )
1055{
1056    local py-ext-target = [ $(.project).find python_for_extensions : no-error ] ;
1057    if ! $(py-ext-target)
1058    {
1059        return <build>no ;
1060    }
1061    local property-set = [ property-set.create $(properties) ] ;
1062    property-set = [ $(property-set).expand ] ;
1063    local py-ext-alternative = [ $(py-ext-target).select-alternatives $(property-set) ] ;
1064    if ! $(py-ext-alternative)
1065    {
1066        return <build>no ;
1067    }
1068}
1069
1070
1071rule configured ( )
1072{
1073     return $(.configured) ;
1074}
1075
1076rule numpy ( )
1077{
1078     return $(.numpy) ;
1079}
1080
1081rule numpy-include ( )
1082{
1083     return $(.numpy-include) ;
1084}
1085
1086
1087type.register PYTHON_EXTENSION : : SHARED_LIB ;
1088
1089
1090local rule register-extension-suffix ( root : condition * )
1091{
1092    local suffix ;
1093
1094    switch [ feature.get-values target-os : $(condition) ]
1095    {
1096        case windows : suffix = pyd ;
1097        case cygwin : suffix = dll ;
1098        case hpux :
1099        {
1100            if [ feature.get-values python : $(condition) ] in 1.5 1.6 2.0 2.1 2.2 2.3 2.4
1101            {
1102                suffix = sl ;
1103            }
1104            else
1105            {
1106                suffix = so ;
1107            }
1108        }
1109        case * : suffix = so ;
1110    }
1111
1112    type.set-generated-target-suffix PYTHON_EXTENSION : $(condition) : <$(root).$(suffix)> ;
1113}
1114
1115
1116# Unset 'lib' prefix for PYTHON_EXTENSION
1117type.set-generated-target-prefix PYTHON_EXTENSION : : "" ;
1118
1119
1120rule python-extension ( name : sources * : requirements * : default-build * :
1121                        usage-requirements * )
1122{
1123    if [ configured ]
1124    {
1125        requirements += <use>/python//python_for_extensions ;
1126    }
1127    requirements += <suppress-import-lib>true ;
1128
1129    local project = [ project.current ] ;
1130
1131    targets.main-target-alternative
1132        [ new typed-target $(name) : $(project) : PYTHON_EXTENSION
1133            : [ targets.main-target-sources $(sources) : $(name) ]
1134            : [ targets.main-target-requirements $(requirements) : $(project) ]
1135            : [ targets.main-target-default-build $(default-build) : $(project) ]
1136        ] ;
1137}
1138
1139IMPORT python : python-extension : : python-extension ;
1140
1141# Support for testing.
1142type.register PY : py ;
1143type.register RUN_PYD_OUTPUT ;
1144type.register RUN_PYD : : TEST ;
1145
1146
1147class python-test-generator : generator
1148{
1149    import set ;
1150
1151    rule __init__ ( * : * )
1152    {
1153        generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
1154        self.composing = true ;
1155    }
1156
1157    rule run ( project name ? : property-set : sources * : multiple ? )
1158    {
1159        local pyversion = [ $(property-set).get <python> ] ;
1160        local python ;
1161        local other-pythons ;
1162
1163        for local s in $(sources)
1164        {
1165            if [ $(s).type ] = PY
1166            {
1167                if ! $(python)
1168                {
1169                    # First Python source ends up on command line.
1170                    python = $(s) ;
1171
1172                }
1173                else
1174                {
1175                    # Other Python sources become dependencies.
1176                    other-pythons += $(s) ;
1177                }
1178            }
1179        }
1180
1181        local extensions ;
1182        for local s in $(sources)
1183        {
1184            if [ $(s).type ] = PYTHON_EXTENSION
1185            {
1186                extensions += $(s) ;
1187            }
1188        }
1189
1190        local libs ;
1191        for local s in $(sources)
1192        {
1193            if [ type.is-derived [ $(s).type ] LIB ]
1194              && ! $(s) in $(extensions)
1195            {
1196                libs += $(s) ;
1197            }
1198        }
1199
1200        local new-sources ;
1201        for local s in $(sources)
1202        {
1203            if [ type.is-derived [ $(s).type ] CPP ]
1204            {
1205                local name = [ utility.basename [ $(s).name ] ] ;
1206                if $(name) = [ utility.basename [ $(python).name ] ]
1207                {
1208                    name = $(name)_ext ;
1209                }
1210                local extension = [ generators.construct $(project) $(name) :
1211                  PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ;
1212
1213                # The important part of usage requirements returned from
1214                # PYTHON_EXTENSION generator are xdll-path properties that will
1215                # allow us to find the python extension at runtime.
1216                property-set = [ $(property-set).add $(extension[1]) ] ;
1217
1218                # Ignore usage requirements. We're a top-level generator and
1219                # nobody is going to use what we generate.
1220                new-sources += $(extension[2-]) ;
1221            }
1222        }
1223
1224        property-set = [ $(property-set).add-raw <dependency>$(other-pythons) ] ;
1225
1226        return [ construct-result $(python) $(extensions) $(new-sources) :
1227            $(project) $(name) : $(property-set) ] ;
1228    }
1229}
1230
1231
1232generators.register
1233  [ new python-test-generator python.capture-output : : RUN_PYD_OUTPUT ] ;
1234
1235generators.register-standard testing.expect-success
1236  : RUN_PYD_OUTPUT : RUN_PYD ;
1237
1238
1239# There are two different ways of spelling OS names. One is used for [ os.name ]
1240# and the other is used for the <host-os> and <target-os> properties. Until that
1241# is remedied, this sets up a crude mapping from the latter to the former, that
1242# will work *for the purposes of cygwin/NT cross-builds only*. Could not think
1243# of a better name than "translate".
1244#
1245.translate-os-windows = NT ;
1246.translate-os-cygwin = CYGWIN ;
1247local rule translate-os ( src-os )
1248{
1249    local x = $(.translate-os-$(src-os)) [ os.name ] ;
1250    return $(x[1]) ;
1251}
1252
1253
1254# Extract the path to a single ".pyd" source. This is used to build the
1255# PYTHONPATH for running bpl tests.
1256#
1257local rule pyd-pythonpath ( source )
1258{
1259    return [ on $(source) return $(LOCATE) $(SEARCH) ] ;
1260}
1261
1262
1263# The flag settings on testing.capture-output do not apply to python.capture
1264# output at the moment. Redo this explicitly.
1265toolset.flags python.capture-output ARGS <testing.arg> ;
1266toolset.flags python.capture-output INPUT_FILES <testing.input-file> ;
1267
1268toolset.uses-features python.capture-output :
1269    <testing.launcher> <testing.execute> <dll-path> <xdll-path> <target-os>
1270    <pythonpath> ;
1271
1272rule capture-output ( target : sources * : properties * )
1273{
1274    # Setup up a proper DLL search path. Here, $(sources[1]) is a python module
1275    # and $(sources[2]) is a DLL. Only $(sources[1]) is passed to
1276    # testing.capture-output, so RUN_PATH variable on $(sources[2]) is not
1277    # consulted. Move it over explicitly.
1278    RUN_PATH on $(sources[1]) = [ on $(sources[2-]) return $(RUN_PATH) ] ;
1279
1280    PYTHONPATH  = [ sequence.transform pyd-pythonpath : $(sources[2-]) ] ;
1281    PYTHONPATH += [ feature.get-values pythonpath : $(properties) ] ;
1282
1283    # After test is run, we remove the Python module, but not the Python script.
1284    testing.capture-output $(target) : $(sources[1]) : $(properties) ;
1285
1286    # PYTHONPATH is different; it will be interpreted by whichever Python is
1287    # invoked and so must follow path rules for the target os. The only OSes
1288    # where we can run python for other OSes currently are NT and CYGWIN so we
1289    # only need to handle those cases.
1290    local target-os = [ feature.get-values target-os : $(properties) ] ;
1291    # Oddly, host-os is not in properties, so grab the default value.
1292    local host-os = [ feature.defaults host-os ] ;
1293    host-os = $(host-os:G=) ;
1294    if $(target-os) != $(host-os) && $(target-os) in windows cygwin && $(host-os) in windows cygwin
1295    {
1296        PYTHONPATH = [ sequence.transform $(host-os)-to-$(target-os)-path :
1297            $(PYTHONPATH) ] ;
1298    }
1299    local path-separator = [ os.path-separator [ translate-os $(target-os) ] ] ;
1300    local set-PYTHONPATH = [ common.variable-setting-command PYTHONPATH :
1301        $(PYTHONPATH:E=:J=$(path-separator)) ] ;
1302    LAUNCHER on $(target) = $(set-PYTHONPATH) [ on $(target) return \"$(PYTHON)\" ] ;
1303}
1304
1305
1306rule bpl-test ( name : sources * : requirements * )
1307{
1308    local s ;
1309    sources ?= $(name).py $(name).cpp ;
1310    return [ testing.make-test run-pyd : $(sources) /boost/python//boost_python
1311        : $(requirements) : $(name) ] ;
1312}
1313
1314# The same as bpl-test but additionally require (and link to) boost_numpy.
1315# Masked whenever NumPy is not enabled.
1316rule numpy-test ( name : sources * : requirements * )
1317{
1318    numpy-include = [ python.numpy-include ] ;
1319    # yuk !
1320    if ! $(.numpy) { requirements += <build>no ; }
1321    sources ?= $(name).py $(name).cpp ;
1322    name = [ regex.replace $(name) "[/]" "~" ] ;
1323    return [ testing.make-test run-pyd
1324        : $(sources) /boost/python//boost_numpy /boost/python//boost_python
1325        : $(requirements) <include>$(numpy-include)
1326        : $(name) ] ;
1327}
1328
1329rule py-version ( n )
1330{
1331    return $(py$(n)-version) ;
1332}
1333
1334IMPORT $(__name__) : bpl-test : : bpl-test ;
1335IMPORT $(__name__) : numpy-test : : numpy-test ;
1336IMPORT $(__name__) : py-version : : py-version ;
1337