From cd7aabdb8dbf3327bcfcdb8cc0de4c19bd393310 Mon Sep 17 00:00:00 2001 From: Roland Schwarz Date: Mon, 20 Aug 2007 19:14:14 +0000 Subject: [PATCH] Merge from RC_1_34_0 (CVS 1.12.2.53) to trunk. CVS RC_1_34_0 - 1.12.2.23 and CVS HEAD - 1.51 are the last versions that were equal. Between these and trunk was only a small diff for HPUX that removed pthread lib. This fix seems to be already present in the merged in version. [SVN r38803] --- src/tools/python.jam | 1319 +++++++++++++++++++++++++++++------------- 1 file changed, 915 insertions(+), 404 deletions(-) diff --git a/src/tools/python.jam b/src/tools/python.jam index a7beae1ea..d4a96a1f5 100644 --- a/src/tools/python.jam +++ b/src/tools/python.jam @@ -15,12 +15,6 @@ # # Extensions that use Boost.Python must explicitly link to it. -# Known problems: -# - the directory where extension is generated is different from V2 -# - the ext + py -> pyd_run_output generator is declared to take -# SHARED_LIB, not PYTHON_EXTENSION. That's because we reuse -# 'lib-target-class', which creates SHARED_LIB explicitly. - import type ; import testing ; import generators ; @@ -32,6 +26,14 @@ import os ; import common ; import toolset : flags ; import regex ; +import numbers ; +import string ; +import property ; +import sequence ; +import path ; +import feature ; +import set ; +import builtin ; # Make this module a project project.initialize $(__name__) ; @@ -42,11 +44,9 @@ project python ; # not in whatever project we were called by. .project = [ project.current ] ; -.alias-defined = ; - -# Dynamic linker lib. Necessary to specify it explicitly +# Dynamic linker lib. Necessary to specify it explicitly # on some platforms. -lib dl ; +lib dl ; # This contains 'openpty' function need by python. Again, on # some system need to pass this to linker explicitly. lib util ; @@ -56,74 +56,73 @@ lib pthread ; lib rt ; -# Initializes the Python toolset. -# - version -- the version of Python to use. Should be in Major.Minor format, -# for example 2.3 -# - 'root' -- the install root for Python -# - 'includes' -- the include path to Python headers. If empty, will be -# guessed from 'root' -# - 'libraries' -- the path to Python libraries. If empty, will be guessed -# from 'root' -# - 'cygwin-condition' -- if specified, should be a set of properties which -# are present when we're building with cygwin gcc. -# This argument is not used yet. +# Initializes the Python toolset. Note that all parameters are +# optional. +# +# - version -- the version of Python to use. Should be in Major.Minor +# format, for example 2.3. Do not include the subminor version. +# +# - cmd-or-prefix: Preferably, a command that invokes a Python +# interpreter. Alternatively, the installation prefix for Python +# libraries and includes. If empty, will be guessed from the +# version, the platform's installation patterns, and the python +# executables that can be found in PATH. +# +# - includes: the include path to Python headers. If empty, will be +# guessed. +# +# - libraries: the path to Python library binaries. If empty, will be +# guessed. On MacOS/Darwin, you can also pass the path of the +# Python framework. +# +# - condition: if specified, should be a set of properties that are +# matched against the build configuration when Boost.Build selects a +# Python configuration to use. +# +# - extension-suffix: A string to append to the name of extension +# modules before the true filename extension. Ordinarily we would +# just compute this based on the value of the +# feature. However ubuntu's python-dbg package uses the windows +# convention of appending _d to debug-build extension modules. We +# have no way of detecting ubuntu, or of probing python for the "_d" +# requirement, and if you configure and build python using +# --with-pydebug, you'll be using the standard *nix convention. +# Defaults to "" (or "_d" when targeting windows and +# is set). # # Example usage: # -# using python 2.3 ; # Use default root -# using python 2.3 : /usr/local ; # Root specified, include and lib paths -# # will be guessed +# using python 2.3 ; +# using python 2.3 : /usr/local/bin/python ; # -rule init ( version ? : root ? : includes ? : libraries ? - : cygwin-condition * ) +rule init ( version ? : cmd-or-prefix ? : includes * : libraries ? + : condition * : extension-suffix ? ) { - .configured = true ; - project.push-current $(.project) ; - - if [ os.name ] = NT + + debug-message Configuring python... ; + for local v in version cmd-or-prefix includes libraries condition { - init-nt $(version) : $(root) : $(includes) : $(libraries) : $(cygwin-condition) ; - } - else if [ os.name ] = MACOSX - { - init-mac $(version) : $(root) : $(includes) : $(libraries) ; - } - else if [ modules.peek : UNIX ] - { - init-unix $(version) : $(root) : $(includes) : $(libraries) : $(cygwin-condition) ; + if $($(v)) + { + debug-message " user-specified "$(v): \"$($(v))\" ; + } } - if [ os.on-windows ] && ! $(.alias-defined) - { - .alias-defined = true ; - alias python_for_extensions : python ; - } - - + configure $(version) : $(cmd-or-prefix) : $(includes) : $(libraries) : $(condition) : $(extension-suffix) ; + project.pop-current ; } -# Retrieves the Python interpreter -rule get-python-interpreter ( ) +# A simpler version of SHELL that grabs stderr as well as stdout, but +# returns nothing if there's an error. +local rule shell-cmd ( cmd ) { - return $(PYTHON) ; -} - -# Retrieves the Python version number -rule get-python-version ( ) -{ - return [ python-version [ get-python-interpreter ] ] ; -} - -local rule python-version ( cmd ) -{ - cmd ?= python ; - local version = [ SHELL $(cmd)" -c 'import sys; print sys.version'" : exit-status ] ; - - if $(version[2]) = 0 + debug-message running command '$(cmd)" 2>&1"' ; + x = [ SHELL $(cmd)" 2>&1" : exit-status ] ; + if $(x[2]) = 0 { - return [ MATCH ^([0-9]+.[0-9]+) : $(version[1]) : 1 ] ; + return $(x[1]) ; } else { @@ -131,401 +130,877 @@ local rule python-version ( cmd ) } } -local rule python-interpreter ( cmd ) +# Try to identify Cygwin symlinks. Invoking such a file directly as +# an NT executable from a native Windows build of bjam would be fatal +# to the bjam process. One /can/ invoke them through sh.exe or +# bash.exe, if you can prove that those aren't also symlinks ;-) +# +# If a symlink is found returns non-empty; we try to extract the +# target of the symlink from the file and return that. +# +# Note: 1. only works on NT 2. path is a native path. +local rule is-cygwin-symlink ( path ) { - local which = [ SHELL "which "$(cmd) : exit-status ] ; - if $(which[2]) = 0 + local is-symlink = ; + + # Look for a file with the given path having the S attribute set, + # as cygwin symlinks do. /-C means "do not use thousands + # separators in file sizes." + local dir-listing = [ shell-cmd "DIR /-C /A:S "$(path) ] ; + + if $(dir-listing) { - return $(which[1]) ; - } - else - { - return ; + # escape any special regex characters in the base part of the path + local base-pat = [ regex.escape $(path:D=) : ].[()*+?|\\$^ : \\ ] ; + + # extract the file's size from the directory listing + local size-of-system-file = [ MATCH "([0-9]+) "$(base-pat) : $(dir-listing) : 1 ] ; + + # if the file has a reasonably small size, look for the + # special symlink identification text + if $(size-of-system-file) && [ numbers.less $(size-of-system-file) 1000 ] + { + local link = [ SHELL "FIND /OFF \"!\" \""$(path)"\" 2>&1" ] ; + if $(link[2]) != 0 + { + local nl = " + +" ; + is-symlink = [ MATCH ".*!([^"$(nl)"]*)" : $(link[1]) : 1 ] ; + if $(is-symlink) + { + is-symlink = [ *nix-path-to-native $(is-symlink) ] ; + is-symlink = $(is-symlink:R=$(path:D)) ; + } + + } + } } + return $(is-symlink) ; } -local rule python-root ( cmd ) +# Append ext to each member of names that does not contain '.' +local rule default-extension ( names * : ext * ) { - return [ MATCH (.*)/bin/[^/]* : [ SHELL "which "$(cmd) ] : 1 ] ; + local result ; + for local n in $(names) + { + switch $(n) + { + case *.* : result += $(n) ; + case * : result += $(n)$(ext) ; + } + } + return $(result) ; } +# Tries to determine whether invoking "cmd" would actually attempt to +# launch a cygwin symlink. +# +# Note: only works on NT +local rule invokes-cygwin-symlink ( cmd ) +{ + local dirs = $(cmd:D) ; + if ! $(dirs) + { + dirs = . [ os.executable-path ] ; + } + local base = [ default-extension $(cmd:D=) : .exe .bat ] ; + local paths = [ GLOB $(dirs) : $(base) ] ; + if $(paths) + { + # Make sure we didn't find a Cygwin symlink. Invoking such a + # file as an NT executable will be fatal to the bjam process. + return [ is-cygwin-symlink $(paths[1]) ] ; + } +} local rule debug-message ( message * ) { if --debug-configuration in [ modules.peek : ARGV ] { - ECHO notice: $(message) ; + ECHO notice: [python-cfg] $(message) ; } } -# condition is a list of properties for which this python initialization applies. -rule init-unix ( version ? : root ? : includes ? : libraries ? : condition * ) +# Like W32_GETREG, except prepend HKEY_CURRENT_USER\SOFTWARE and +# HKEY_LOCAL_MACHINE\SOFTWARE to the first argument, returning the +# first result found. Also accounts for the fact that on 64-bit +# machines, 32-bit software has its own area, under +# SOFTWARE\Wow6432node. +local rule software-registry-value ( path : data ? ) { - # - # Autoconfiguration sequence - # - if $(version) + local result ; + for local root in HKEY_CURRENT_USER HKEY_LOCAL_MACHINE { - local v = [ MATCH ^([0-9]+\.[0-9]+)(.*)$ : $(version) : 1 2 ] ; - if ! $(v) || $(v[2]) + for local x64elt in "" Wow6432node\\ # Account for 64-bit windows { - ECHO "Warning: \"using python\" expects a two part (major, minor) version number; got" $(version) instead ; - if $(v) + if ! $(result) { - version = $(v[1]) ; + result = [ W32_GETREG $(root)\\SOFTWARE\\$(x64elt)$(path) : $(data) ] ; } } - debug-message looking for python $(version) ; - } - - # if root is explicitly specified, look in its bin subdirectory - local bin = bin/ ; - bin = $(bin:R=$(root)) ; # will null out $(bin) if $(root) is empty. - - if $(bin) - { - debug-message searching for python binaries in $(bin) ; - } - - # Form the python commands to try in order. First look for python - # with the explicit version number, then without it - local python = python ; - local cmds = $(python:D=$(bin:E=""))$(version:E="") $(python:D=$(bin:E="")) ; - local interpreter ; - while $(cmds) + } + return $(result) ; +} + +.windows-drive-letter-re = ^([A-Za-z]):[\\/](.*) ; +.cygwin-drive-letter-re = ^/cygdrive/([a-z])/(.*) ; + +.working-directory = [ PWD ] ; +.working-drive-letter = [ SUBST $(.working-directory) $(.windows-drive-letter-re) $1 ] ; +.working-drive-letter ?= [ SUBST $(.working-directory) $(.cygwin-drive-letter-re) $1 ] ; + +local rule windows-to-cygwin-path ( path ) +{ + # if path is rooted with a drive letter, rewrite it using the + # /cygdrive mountpoint + local p = [ SUBST $(path:T) $(.windows-drive-letter-re) /cygdrive/$1/$2 ] ; + + # else if path is rooted without a drive letter, use the working directory + p ?= [ SUBST $(path:T) ^/(.*) /cygdrive/$(.working-drive-letter:L)/$2 ] ; + + # else return the path unchanged + return $(p:E=$(path:T)) ; +} + +# :W only works in Cygwin builds of bjam. This one works on NT builds +# as well. +local rule cygwin-to-windows-path ( path ) +{ + path = $(path:R="") ; # strip any trailing slash + + local drive-letter = [ SUBST $(path) $(.cygwin-drive-letter-re) $1:/$2 ] ; + if $(drive-letter) { - # pop a command - interpreter = $(cmds[0]) ; cmds = $(cmds[2-]) ; - debug-message trying Python interpreter command $(interpreter) ; - - # Check to see what version that command actually runs, if any - local true-version = [ python-version $(interpreter) ] ; - - if ! $(true-version) + path = $(drive-letter) ; + } + else if $(path:R=/x) = $(path) # already rooted? + { + # Look for a cygwin mount that includes each head sequence in $(path). + local head = $(path) ; + local tail = "" ; + + while $(head) { - debug-message $(interpreter) does not invoke a working Python interpreter ; + local root = [ + software-registry-value "Cygnus Solutions\\Cygwin\\mounts v2\\"$(head) + : native + ] ; + + if $(root) + { + path = $(tail:R=$(root)) ; + head = ; + } + tail = $(tail:R=$(head:D=)) ; + + if $(head) = / + { + head = ; + } + else + { + head = $(head:D) ; + } + } + } + return [ regex.replace $(path:R="") / \\ ] ; +} + +# Convert a *nix path to native +local rule *nix-path-to-native ( path ) +{ + if [ os.name ] = NT + { + path = [ cygwin-to-windows-path $(path) ] ; + } + return $(path) ; +} + +# Convert an NT path to native +local rule windows-path-to-native ( path ) +{ + if [ os.name ] = NT + { + return $(path) ; + } + else + { + return [ windows-to-cygwin-path $(path) ] ; + } +} + +# Return nonempty if path looks like a windows path, i.e. it starts +# with a drive letter or contains backslashes. +local rule guess-windows-path ( path ) +{ + return [ SUBST $(path) ($(.windows-drive-letter-re)|.*([\\]).*) $1 ] ; +} + +local rule path-to-native ( paths * ) +{ + local result ; + + for local p in $(paths) + { + if [ guess-windows-path $(p) ] + { + result += [ windows-path-to-native $(p) ] ; } else { - debug-message $(interpreter) invokes actual Python (major,minor) version $(true-version) ; - - # if no version was specified, assume that's OK - version ?= $(true-version) ; - - # if the version is a match, stop searching - if $(version) = $(true-version) - { - debug-message qualifying Python interpreter found ; - root ?= [ python-root $(interpreter) ] ; - cmds = ; # break - } + result += [ *nix-path-to-native $(p:T) ] ; } } - debug-message "Python interpreter command is" $(interpreter) ; - - includes ?= $(root)/include/python$(version) ; - debug-message "Python include path is" $(includes) ; - - libraries ?= $(root)/lib/python$(version)/config ; - debug-message "Python library path is" $(libraries) ; - - # - # End autoconfiguration sequence - # - - - # Set up the PYTHON variable to point at the interpreter. - # If no specific condition is specified, set global value - # If condition is specified, set PYTHON on target. It will - # override the global value. - if ! $(condition) + return $(result) ; +} + +# Validate the version string and extract the major/minor part we care +# about +local rule split-version ( version ) +{ + local major-minor = [ MATCH ^([0-9]+)\.([0-9]+)(.*)$ : $(version) : 1 2 3 ] ; + if ! $(major-minor[2]) || $(major-minor[3]) { - PYTHON = $(interpreter) ; + ECHO "Warning: \"using python\" expects a two part (major, minor) version number; got" $(version) instead ; + + # Add a zero to account for the missing digit if necessary. + major-minor += 0 ; + } + + return $(major-minor[1]) $(major-minor[2]) ; +} + +# Build a list of versions from 3.0 down to 1.5. Because bjam +# can't enumerate registry sub-keys, we have no way of finding +# a version with a 2-digit minor version, e.g. 2.10 -- let's +# hope that never happens. +.version-countdown = ; +for local v in [ numbers.range 15 30 ] +{ + .version-countdown = [ SUBST $(v) (.)(.*) $1.$2 ] $(.version-countdown) ; +} + +local rule windows-installed-pythons ( version ? ) +{ + version ?= $(.version-countdown) ; + local interpreters ; + + for local v in $(version) + { + local install-path = [ + software-registry-value "Python\\PythonCore\\"$(v)"\\InstallPath" ] ; + + if $(install-path) + { + install-path = [ windows-path-to-native $(install-path) ] ; + debug-message Registry indicates Python $(v) installed at \"$(install-path)\" ; + } + + interpreters += $(:E=python:R=$(install-path)) ; + } + return $(interpreters) ; +} + +local rule darwin-installed-pythons ( version ? ) +{ + version ?= $(.version-countdown) ; + + local prefix + = [ GLOB /System/Library/Frameworks /Library/Frameworks + : Python.framework ] ; + + return $(prefix)/Versions/$(version)/bin/python ; +} + +# Assume "python-cmd" invokes a python interpreter and invoke it to +# extract all the information we care about from its "sys" module. +# Returns void if unsuccessful. +local rule probe ( python-cmd ) +{ + # Avoid invoking a Cygwin symlink on NT + local skip-symlink ; + if [ os.name ] = NT + { + skip-symlink = [ invokes-cygwin-symlink $(python-cmd) ] ; + } + + if $(skip-symlink) + { + debug-message -------------------------------------------------------------------- ; + debug-message \"$(python-cmd)\" would attempt to invoke a Cygwin symlink, ; + debug-message causing a bjam built for Windows to hang. ; + debug-message ; + debug-message If you intend to target a Cygwin build of Python, please ; + debug-message replace the path to the link with the path to a real executable ; + debug-message (guessing: \"$(skip-symlink)\") "in" your 'using python' line ; + debug-message "in" user-config.jam or site-config.jam. Don't forget to escape ; + debug-message backslashes ; + debug-message -------------------------------------------------------------------- ; } else - { - flags python.capture-output PYTHON $(condition:J=/) : $(interpreter) ; - } - - # Provide system library dependencies for targets linking with - # static Python libraries. - # - # On many systems, Python uses libraries such as pthreads or - # libdl. Since static libraries carry no library dependency - # information of their own that the linker can extract, these - # extra dependencies have to be given explicitly on the link line - # of the client. The information about these dependencies is - # packaged into the "python" target below. - - # Even where Python itself uses pthreads, it never allows - # extension modules to be entered concurrently (unless they - # explicitly give up the interpreter lock). Therefore, extension - # modules don't need the efficiency overhead of threadsafe code as - # produced by multi, and we handle libpthread along - # with other libraries here. Note: this optimization is based on - # an assumption that the compiler generates link-compatible code - # in both the single- and multi-threaded cases, and that system - # libraries don't change their ABIs either. - - # Most libraries are added to 'extra-libs'. Those that depend on - # the toolset are added to 'extra-libs-conditional', which will be - # used to form more specific target alternatives. - - local extra-libs extra-libs-conditional ; - - switch [ os.name ] { - case SOLARIS : + # Prepare a List of Python format strings and expressions that + # can be used to print the constants we want from the sys + # module. + + # We don't really want sys.version since that's a complicated + # string, so get the information from sys.version_info + # instead. + local format = "version=%d.%d" ; + local exprs = "version_info[0]" "version_info[1]" ; + + for local s in $(sys-elements[2-]) { - extra-libs = pthread dl ; - + format += $(s)=%s ; + exprs += $(s) ; + } + + # Invoke Python and ask it for all those values + local full-cmd = + $(python-cmd)" -c \"from sys import *; print '"$(format:J=\\n)"' % ("$(exprs:J=,)")\"" ; + + local output = [ shell-cmd $(full-cmd) ] ; + if $(output) + { + # Parse the output to get all the results + local nl = " + +" ; + for s in $(sys-elements) + { + # These variables are expected to be declared local in + # the caller, so Jam's dynamic scoping will set their + # values there. + sys.$(s) = [ SUBST $(output) \\<$(s)=([^$(nl)]+) $1 ] ; + } + } + return $(output) ; + } +} + +# Make sure the "libraries" and "includes" variables (in an enclosing +# scope) have a value based on the information given. +local rule compute-default-paths ( + target-os : version ? : prefix ? : exec-prefix ? ) +{ + exec-prefix ?= $(prefix) ; + + if $(target-os) = windows + { + # The exec_prefix is where you're supposed to look for + # machine-specific libraries. + local default-library-path = $(exec-prefix)\\libs ; + local default-include-path = $(:E=Include:R=$(prefix)) ; + + # If the interpreter was found in a directory + # called "PCBuild" or "PCBuild8," assume we're + # looking at a Python built from the source + # distro, and go up one additional level to the + # default root. Otherwise, the default root is + # the directory where the interpreter was found. + + # We ask Python itself what the executable path is + # in case of intermediate symlinks or shell + # scripts. + local executable-dir = $(sys.executable:D) ; + + if [ MATCH ^(PCBuild) : $(executable-dir:D=) ] + { + debug-message "This Python appears to reside in a source distribution;" ; + debug-message "prepending \""$(executable-dir)"\" to default library search path" ; + + default-library-path = $(executable-dir) + $(default-library-path) ; + + default-include-path = $(:E=PC:R=$(executable-dir:D)) $(default-include-path) ; + + debug-message "and \""$(default-include-path[1])"\" to default #include path" ; + } + + libraries ?= $(default-library-path) ; + includes ?= $(default-include-path) ; + } + else + { + includes ?= $(prefix)/include/python$(version) ; + + local lib = $(exec-prefix)/lib ; + libraries ?= $(lib)/python$(version)/config $(lib) ; + } +} + +# The version of the python interpreter to use +feature.feature python : : propagated ; +feature.feature python.interpreter : : free ; + +flags python.capture-output PYTHON : ; + +# +# Support for Python configured --with-pydebug +# +feature.feature python-debugging : off on : propagated ; +builtin.variant debug-python : debug : on ; + +# Return a list of candidate commands to try when looking for a Python +# interpreter. prefix is expected to be a native path. +local rule candidate-interpreters ( version ? : prefix ? : target-os ) +{ + local bin-path = bin ; + if $(target-os) = windows + { + # on Windows, look in the root directory itself and, to work + # with the result of a build-from-source, the PCBuild directory + bin-path = PCBuild8 PCBuild "" ; + } + + bin-path = $(bin-path:R=$(prefix)) ; + + if $(target-os) in windows darwin + { + return # Search: + $(:E=python:R=$(bin-path)) # Relative to the prefix, if any + python # In the PATH + [ $(target-os)-installed-pythons $(version) ] # Standard install locations + ; + } + else + { + # Search relative to the prefix, or if none supplied, in PATH + local unversioned = $(:E=python:R=$(bin-path:E=)) ; + + # if a version was specified, look for a python with that + # specific version appended before looking for one called, + # simply, "python" + return $(unversioned)$(version) $(unversioned) ; + } +} + +# Compute system library dependencies for targets linking with +# static Python libraries. +# +# On many systems, Python uses libraries such as pthreads or +# libdl. Since static libraries carry no library dependency +# information of their own that the linker can extract, these +# extra dependencies have to be given explicitly on the link line +# of the client. The information about these dependencies is +# packaged into the "python" target below. + +# Even where Python itself uses pthreads, it never allows +# extension modules to be entered concurrently (unless they +# explicitly give up the interpreter lock). Therefore, extension +# modules don't need the efficiency overhead of threadsafe code as +# produced by multi, and we handle libpthread along +# with other libraries here. Note: this optimization is based on +# an assumption that the compiler generates link-compatible code +# in both the single- and multi-threaded cases, and that system +# libraries don't change their ABIs either. +# +# Returns a list of usage-requirements that link to the necessary +# system libraries. +local rule system-library-dependencies ( target-os ) +{ + switch $(target-os) + { + case s[uo][nl]* : # solaris, sun, sunos # Add a librt dependency for the gcc toolset on SunOS (the # sun toolset adds -lrt unconditionally). While this # appears to duplicate the logic already in gcc.jam, it # doesn't as long as we're not forcing multi. - extra-libs-conditional = gcc:rt ; - } - - case OSF : - { - extra-libs = pthread ; - extra-libs-conditional = gcc:rt ; - } - - case QNX* : - { - extra-libs = ; - } - - case HPUX : - { - extra-libs = rt ; - } - - case * : extra-libs = pthread dl util ; + + # Caleb Epstein reports that his python's + # distutils.sysconfig.get_config_var('LIBS') yields + # -lresolv -lsocket -lnsl -lrt -ldl. However, we're not + # yet sure that is the right list for extension modules. + # Being conservative, we add rt and remove pthread, which + # was causing errors. + return dl gcc:rt ; + + case osf : return pthread gcc:rt ; + + case qnx* : return ; + case darwin : return ; + case windows : return ; + + case hpux : return rt ; + + case aix : return pthread dl ; + + case * : return pthread dl gcc:util ; } - - if ! [ os.on-windows ] - { - # On *nix, we don't want to link either Boost.Python or Python - # extensions to libpython, because the Python interpreter itself - # provides all those symbols. If we linked to libpython, we'd get - # duplicate symbols. So declare two targets -- one for building - # extensions and another for embedding - alias python_for_extensions - : - : $(condition) - : - : $(includes) - ; - } - - - # This should really be called python_for_embedding - alias python - : $(extra-libs) - : $(condition) $(extra-libs-conditional) - : - : $(includes) - $(libraries) - python$(version) - ; } -rule init-mac ( version : root ? : includes ? : libraries ? ) +# Declare a target to represent Python's library. +local rule declare-libpython-target ( version ? : requirements * ) { - if ! $(root) + # Compute the representation of Python version in the name of + # Python's library file. + local lib-version = $(version) ; + if windows in $(requirements) { - if [ GLOB /System/Library/Frameworks : Python.framework ] + local major-minor = [ split-version $(version) ] ; + lib-version = $(major-minor:J="") ; + if on in $(requirements) { - root = /System/Library/Frameworks/Python.framework/Versions/$(version) ; + lib-version = $(lib-version)_d ; } - else - { - root = /Library/Frameworks/Python.framework/Versions/$(version) ; - } - } - - # includes ?= $(PYTHON_ROOT)/include/python$(PYTHON_VERSION) ; - includes ?= $(root)/include/python$(version) ; - libraries ?= $(root)/lib/python$(version)/config ; - - # Find the 'python' binary, which is used for testing. - # Look first in $(root)/bin, then in PATH. - local interpreter = [ common.get-invocation-command - python : python : : $(root)/bin : path-last ] ; - - # debug support - if --debug-configuration in [ modules.peek : ARGV ] - { - ECHO "notice: Python include path is" $(includes) ; - ECHO "notice: Python library path is" $(libraries) ; - ECHO "notice: Python interpreter is" $(interpreter) ; } - - PYTHON = $(interpreter) ; - flags python.capture-output PYTHON : $(interpreter) ; - - PYTHON_FRAMEWORK ?= $(root) ; - while $(PYTHON_FRAMEWORK:D=) && $(PYTHON_FRAMEWORK:D=) != Python.framework + + if ! $(lib-version) { - PYTHON_FRAMEWORK = $(PYTHON_FRAMEWORK:D) ; + ECHO *** warning: could not determine Python version, which will ; + ECHO *** warning: probably prevent us from linking with the python ; + ECHO *** warning: library. Consider explicitly passing the version ; + ECHO *** warning: to 'using python'. ; } - PYTHON_FRAMEWORK = $(PYTHON_FRAMEWORK:D)/Python ; - alias python - : - : MACOSX darwin - : - : $(includes) $(PYTHON_FRAMEWORK) - ; - - # Unlike most *nix systems, Mac OS X's linker does not permit undefined - # symbols when linking a shared library. So, we still need to link - # against the Python framework, even when building extensions. - # Note that framework builds of Python always use shared libraries, - # so we do not need to worry about duplicate Python symbols. - .alias-defined = true ; - alias python_for_extensions : python ; + # Declare it + lib python.lib : : python$(lib-version) $(requirements) ; } -rule init-nt ( version : root ? : includes ? : libraries ? : cygwin-condition ? ) +# implementation of init +local rule configure ( + version ? : cmd-or-prefix ? : includes * : libraries ? : condition * : extension-suffix ? ) { - if ! $(cygwin-condition) - { - # The name of Python library file does not have a dot between - # major and minor version. - local PYTHON_VERSION_NODOT = [ regex.match ([0-9]+)[.]([0-9]+).* : $(version) : 1 2 ] ; + local prefix ; + local exec-prefix ; + local cmds-to-try ; + local interpreter-cmd ; - PYTHON_VERSION_NODOT = $(PYTHON_VERSION_NODOT:J="") ; + local target-os = [ feature.get-values target-os : $(condition) ] ; + target-os ?= [ feature.defaults target-os ] ; + target-os = $(target-os:G=) ; + + if $(target-os) = windows && on in $(condition) + { + extension-suffix ?= _d ; + } + extension-suffix ?= "" ; + + # Normalize and dissect any version number + local major-minor ; + if $(version) + { + major-minor = [ split-version $(version) ] ; + version = $(major-minor:J=.) ; + } - root ?= c:/python$(PYTHON_VERSION_NODOT) ; - - local PATH = [ modules.peek : PATH ] ; - local PATH = [ modules.peek : Path ] ; - - PYTHON_INCLUDES = $(includes) ; - PYTHON_LIB_PATH = $(libraries) ; + local cmds-to-try ; - PYTHON_LIB_PATH ?= $(root)/libs [ GLOB $(root) : PCbuild ] ; - - PYTHON_INCLUDES ?= $(root)/include [ GLOB $(root) : PC ] ; - - - PYTHON_DLL ?= [ GLOB $(PATH) $(Path) : python$(PYTHON_VERSION_NODOT).dll ] ; - PYTHON_DEBUG_DLL ?= [ GLOB $(PATH) $(Path) : python$(PYTHON_VERSION_NODOT)_d.dll ] ; - PYTHON_IMPORT_LIB ?= [ GLOB $(PYTHON_LIB_PATH) : libpython$(PYTHON_VERSION_NODOT).* ] ; - PYTHON_DEBUG_IMPORT_LIB ?= [ GLOB $(PYTHON_LIB_PATH) : libpython$(PYTHON_VERSION_NODOT).* ] ; - - local interpreter = [ common.get-invocation-command - python.exe : python.exe : : $(root)/bin - $(root) - $(root)/PCBuild - : path-last ] ; - - if --debug-configuration in [ modules.peek : ARGV ] - { - ECHO "notice: Python include path is" $(PYTHON_INCLUDES) ; - ECHO "notice: Python library path is" $(PYTHON_LIB_PATH) ; - ECHO "notice: Python interpreter is" $(interpreter) ; - ECHO "notice: Python library name is" python$(PYTHON_VERSION_NODOT) ; - } - - PYTHON = $(interpreter) ; - flags python.capture-output PYTHON : $(interpreter) ; - - properties += $(PYTHON_LIB_PATH) ; - - # msvc compilers auto-find the python library - # declare two alternatives -- one for msvc and another - # for the rest of the world - alias python - : - : msvc - : - : $(PYTHON_LIB_PATH) - $(PYTHON_INCLUDES) - ; - - local lib = python$(PYTHON_VERSION_NODOT) ; - - alias python - : - : - : - : $(PYTHON_LIB_PATH) - $(PYTHON_INCLUDES) - $(lib) ; + if ! $(cmd-or-prefix) || [ GLOB $(cmd-or-prefix) : * ] + { + # if the user didn't pass a command, whatever we got was a prefix + prefix = $(cmd-or-prefix) ; + cmds-to-try = [ candidate-interpreters $(version) : $(prefix) : $(target-os) ] ; } else - { - root ?= /usr ; - if $(root) = /usr + { + # Work with the command the user gave us. + cmds-to-try = $(cmd-or-prefix) ; + + # On windows, don't nail down the interpreter command just yet + # in case the user specified something that turns out to be a + # cygwin symlink, which could bring down bjam if we invoke it. + if $(target-os) != windows { - CYGWIN_PYTHON_DLL_PATH ?= /bin ; + interpreter-cmd = $(cmd-or-prefix) ; + } + } + + # Values to use in case we can't really find anything in the system. + local fallback-cmd = $(cmds-to-try[1]) ; + local fallback-version ; + + # Anything left to find or check? + if ! ( $(interpreter-cmd) && $(includes) && $(libraries) ) + { + # Values to be extracted from python's sys module. These will + # be set by the probe rule, above, using Jam's dynamic scoping. + local sys-elements = version platform prefix exec_prefix executable ; + local sys.$(sys-elements) ; + + # compute the string Python's sys.platform needs to match. If + # not targeting windows or cygwin we'll assume only native + # builds can possibly run, so we won't require a match and we + # leave sys.platform blank. + local platform ; + switch $(target-os) + { + case windows : platform = win32 ; + case cygwin : platform = cygwin ; + } + + while $(cmds-to-try) + { + # pop top command + local cmd = $(cmds-to-try[1]) ; + cmds-to-try = $(cmds-to-try[2-]) ; + + debug-message Checking interpreter command \"$(cmd)\"... ; + if [ probe $(cmd) ] + { + fallback-version ?= $(sys.version) ; + + # Check for version/platform validity + for local x in version platform + { + if $($(x)) && $($(x)) != $(sys.$(x)) + { + debug-message ...$(x) "mismatch (looking for" + $($(x)) but found $(sys.$(x))")" ; + cmd = ; + } + } + + if $(cmd) + { + debug-message ...requested configuration matched! ; + + exec-prefix = $(sys.exec_prefix) ; + + compute-default-paths + $(target-os) + : $(sys.version) + : $(sys.prefix) + : $(sys.exec_prefix) ; + + version = $(sys.version) ; + interpreter-cmd ?= $(cmd) ; + cmds-to-try = ; # All done. + } + } + else + { + debug-message ...does not invoke a working interpreter ; + } + } + } + + # Anything left to compute? + if $(includes) && $(libraries) + { + .configured = true ; + } + else + { + version ?= $(fallback-version) ; + version ?= 2.5 ; + exec-prefix ?= $(prefix) ; + compute-default-paths $(target-os) : $(version) : $(prefix:E=) ; + } + + if ! $(interpreter-cmd) + { + fallback-cmd ?= python ; + debug-message No working Python interpreter found. ; + if [ os.name ] != NT || ! [ invokes-cygwin-symlink $(fallback-cmd) ] + { + interpreter-cmd = $(fallback-cmd) ; + debug-message falling back to \"$(interpreter-cmd)\" ; + } + } + + includes = [ path-to-native $(includes) ] ; + libraries = [ path-to-native $(libraries) ] ; + + debug-message "Details of this Python configuration:" ; + debug-message " interpreter command:" \"$(interpreter-cmd:E=)\" ; + debug-message " include path:" \"$(includes:E=)\" ; + debug-message " library path:" \"$(libraries:E=)\" ; + if $(target-os) = windows + { + debug-message " DLL search path:" \"$(exec-prefix:E=)\" ; + } + + + # + # End autoconfiguration sequence + # + local target-requirements = $(condition) ; + + # Add the version, if any, to the target requirements + if $(version) + { + if ! $(version) in [ feature.values python ] + { + feature.extend python : $(version) ; + } + target-requirements += $(version:E=default) ; + } + + target-requirements += $(target-os) ; + + # See if we can find a framework directory on darwin + local framework-directory ; + if $(target-os) = darwin + { + # Search upward for the framework directory + local framework-directory = $(libraries[-1]) ; + while $(framework-directory:D=) && $(framework-directory:D=) != Python.framework + { + framework-directory = $(framework-directory:D) ; + } + + if $(framework-directory) = Python.framework + { + debug-message framework directory is \"$(fwk)\" ; } else { - CYGWIN_PYTHON_DLL_PATH ?= $(root)/bin ; + debug-message no framework directory found; using library path ; + framework-directory = ; } - CYGWIN_PYTHON_LIB_PATH ?= $(CYGWIN_PYTHON_ROOT)/lib/python$(version)/config ; - - CYGWIN_PYTHON_DEBUG_VERSION ?= $(version) ; - CYGWIN_PYTHON_DEBUG_ROOT ?= /usr/local/pydebug ; - CYGWIN_PYTHON_DEBUG_DLL_PATH ?= $(CYGWIN_PYTHON_DEBUG_ROOT)/bin ; - CYGWIN_PYTHON_DEBUG_LIB_PATH ?= $(CYGWIN_PYTHON_DEBUG_ROOT)/lib/python$(CYGWIN_PYTHON_DEBUG_VERSION)/config ; - - local properties ; + } - properties += $(CYGWIN_PYTHON_LIB_PATH) - python$(CYGWIN_PYTHON_VERSION).dll ; - - properties += $(root)/include/python$(version) ; - + local dll-path = $(libraries) ; + + # Make sure that we can find the Python DLL on windows + if $(target-os) = windows && $(exec-prefix) + { + dll-path += $(exec-prefix) ; + } + + # + # prepare usage requirements + # + local usage-requirements = [ system-library-dependencies $(target-os) ] ; + usage-requirements += $(includes) $(interpreter-cmd) ; + if on in $(condition) + { + if $(target-os) = windows + { + # in pyconfig.h, Py_DEBUG is set if _DEBUG is set. If we + # define Py_DEBUG we'll get multiple definition warnings. + usage-requirements += _DEBUG ; + } + else + { + usage-requirements += Py_DEBUG ; + } + } + + # Register the right suffix for extensions + register-extension-suffix $(extension-suffix) : $(target-requirements) ; + + # + # Declare the "python" target. This should really be called + # python_for_embedding + # + + if $(framework-directory) + { alias python : - : $(cygwin-condition) + : $(target-requirements) : - : $(properties) - ; - } + : $(usage-requirements) $(fwk) + ; + } + else + { + declare-libpython-target $(version) : $(target-requirements) ; + alias python + : + : $(target-requirements) + : + # why python.lib must be listed here instead of along with + # the system libs is a mystery, but if we don't do it, on + # cygwin, -lpythonX.Y never appears in the command line + # (although it does on linux). + : $(usage-requirements) $(libraries) $(dll-path) python.lib + ; + } + + # On *nix, we don't want to link either Boost.Python or Python + # extensions to libpython, because the Python interpreter itself + # provides all those symbols. If we linked to libpython, we'd get + # duplicate symbols. So declare two targets -- one for building + # extensions and another for embedding + # + # Unlike most *nix systems, Mac OS X's linker does not permit undefined + # symbols when linking a shared library. So, we still need to link + # against the Python framework, even when building extensions. + # Note that framework builds of Python always use shared libraries, + # so we do not need to worry about duplicate Python symbols. + if $(target-os) in windows cygwin darwin + { + alias python_for_extensions : python : $(target-requirements) ; + } + # On AIX we need Python extensions and Boost.Python to import symbols + # from the Python interpreter. Dynamic libraries opened with dlopen() + # do not inherit the symbols from the Python interpreter. + else if $(target-os) = aix + { + alias python_for_extensions + : + : $(target-requirements) + : + : $(usage-requirements) -Wl,-bI:$(libraries[1])/python.exp + ; + } + else + { + alias python_for_extensions + : + : $(target-requirements) + : + : $(usage-requirements) + ; + } } - -rule configured ( ) +rule configured ( ) { return $(.configured) ; } - + type.register PYTHON_EXTENSION : : SHARED_LIB ; -# We can't give "dll" suffix to PYTHON_EXTENSION, because -# we would not know what "a.dll" is: python extenstion or -# ordinary library. Therefore, we specify only suffixes -# used for generation of targets. -type.set-generated-target-suffix PYTHON_EXTENSION : : so ; -type.set-generated-target-suffix PYTHON_EXTENSION : NT : pyd ; -type.set-generated-target-suffix PYTHON_EXTENSION : CYGWIN : dll ; +local rule register-extension-suffix ( root : condition * ) +{ + local suffix ; + + switch [ feature.get-values target-os : $(condition) ] + { + case windows : suffix = pyd ; + case cygwin : suffix = dll ; + case hpux : + { + if [ feature.get-values python : $(condition) ] in 1.5 1.6 2.0 2.1 2.2 2.3 2.4 + { + suffix = sl ; + } + else + { + suffix = so ; + } + } + case * : suffix = so ; + } + + type.set-generated-target-suffix PYTHON_EXTENSION : $(condition) : <$(root).$(suffix)> ; +} # Unset 'lib' prefix for PYTHON_EXTENSION type.set-generated-target-prefix PYTHON_EXTENSION : : "" ; -rule python-extension ( name : sources * : requirements * : default-build * : +rule python-extension ( name : sources * : requirements * : default-build * : usage-requirements * ) { - requirements += /python//python_for_extensions ; - + requirements += /python//python_for_extensions true ; + local project = [ project.current ] ; - + targets.main-target-alternative [ new typed-target $(name) : $(project) : PYTHON_EXTENSION : [ targets.main-target-sources $(sources) : $(name) ] - : [ targets.main-target-requirements $(requirements) : $(project) ] - : [ targets.main-target-default-build $(default-build) : $(project) ] + : [ targets.main-target-requirements $(requirements) : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; -} +} IMPORT python : python-extension : : python-extension ; @@ -538,15 +1013,15 @@ type.register RUN_PYD : : TEST ; class python-test-generator : generator { import set ; - + rule __init__ ( * : * ) { generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; self.composing = true ; } - + rule run ( project name ? : property-set : sources * : multiple ? ) - { + { local python ; local other-pythons ; for local s in $(sources) @@ -562,10 +1037,10 @@ class python-test-generator : generator { # Other Python sources become dependencies. other-pythons += $(s) ; - } + } } } - + local extensions ; for local s in $(sources) { @@ -574,53 +1049,69 @@ class python-test-generator : generator extensions += $(s) ; } } - + local libs ; for local s in $(sources) { - if [ type.is-derived [ $(s).type ] LIB ] + if [ type.is-derived [ $(s).type ] LIB ] && ! $(s) in $(extensions) { libs += $(s) ; } } - + local new-sources ; for local s in $(sources) { - if [ type.is-derived [ $(s).type ] CPP ] + if [ type.is-derived [ $(s).type ] CPP ] { local name = [ utility.basename [ $(s).name ] ] ; if $(name) = [ utility.basename [ $(python).name ] ] { name = $(name)_ext ; - } + } local extension = [ generators.construct $(project) $(name) : PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ; - # The important part of usage requirements returned from - # PYTHON_EXTENSION genrator are xdll-path properties that - # will allow to find python extension at runtime. + # The important part of usage requirements returned + # from PYTHON_EXTENSION generator are xdll-path + # properties that will allow us to find the python + # extension at runtime. property-set = [ $(property-set).add $(extension[1]) ] ; - # Ignore usage requirements. We're top-level generator and - # nobody is going to use us. + + # Ignore usage requirements. We're a top-level + # generator and nobody is going to use what we + # generate. new-sources += $(extension[2-]) ; } } property-set = [ $(property-set).add-raw $(other-pythons) ] ; - - result = [ construct-result $(python) $(extensions) $(new-sources) - : $(project) $(name) : $(property-set) ] ; - } + + result = [ construct-result $(python) $(extensions) $(new-sources) + : $(project) $(name) : $(property-set) ] ; + } } -generators.register +generators.register [ new python-test-generator python.capture-output : : RUN_PYD_OUTPUT ] ; -generators.register-standard testing.expect-success +generators.register-standard testing.expect-success : RUN_PYD_OUTPUT : RUN_PYD ; +# There are two different ways of spelling OS names. One is used for +# [ os.name ] and the other is used for the and +# properties. Until that is remedied, this sets up a crude mapping +# from the latter to the former, that will work *for the purposes of +# cygwin/NT cross-builds only*. Couldn't think of a better name than +# "translate" +.translate-os-windows = NT ; +.translate-os-cygwin = CYGWIN ; +local rule translate-os ( src-os ) +{ + local x = $(.translate-os-$(src-os)) [ os.name ] ; + return $(x[1]) ; +} # The flag settings on testing.capture-output do not # apply to python.capture output at the moment. @@ -633,24 +1124,44 @@ rule capture-output ( target : sources * : properties * ) # DLL. Only $(sources[1]) is passed to testing.capture-output, # so RUN_PATH variable on $(sources[2]) is not consulted. Move it # over explicitly. - RUN_PATH on $(sources[1]) = [ on $(sources[2]) return $(RUN_PATH) ] ; - PYTHONPATH = [ on $(sources[2]) return $(LOCATE) ] ; + RUN_PATH on $(sources[1]) = [ on $(sources[2-]) return $(RUN_PATH) ] ; + + PYTHONPATH = [ on $(sources[2-]) return $(LOCATE) $(SEARCH) ] ; + # After test is run, we remove the Python module, but not the Python # script. - testing.capture-output $(target) : $(sources[1]) : $(properties) - : $(sources[2]) ; - local c = [ common.prepend-path-variable-command PYTHONPATH : $(PYTHONPATH) ] ; - LAUNCHER on $(target) = $(c) [ on $(target) return $(PYTHON) ] ; + testing.capture-output $(target) : $(sources[1]) : $(properties) + : $(sources[2-]) ; + + # PYTHONPATH is different; it will be interpreted by whichever + # Python is invoked and so must follow path rules for the target + # os. The only OSes where we can run pythons for other OSes + # currently are NT and CYGWIN, so we only need to handle those + # cases. + local target-os = [ feature.get-values target-os : $(properties) ] ; + # oddly, host-os isn't in properties, so grab the default value. + local host-os = [ feature.defaults host-os ] ; + host-os = $(host-os:G=) ; + if $(target-os) != $(host-os) + { + PYTHONPATH = + [ sequence.transform $(host-os)-to-$(target-os)-path : $(PYTHONPATH) ] ; + } + local path-separator = + [ os.path-separator [ translate-os $(target-os) ] ] ; + local set-PYTHONPATH = + [ common.variable-setting-command PYTHONPATH : $(PYTHONPATH:J=$(path-separator)) ] ; + LAUNCHER on $(target) = $(set-PYTHONPATH) [ on $(target) return $(PYTHON) ] ; } rule bpl-test ( name : sources * : requirements * ) -{ +{ sources ?= $(name).py $(name).cpp ; - return [ testing.make-test + return [ testing.make-test run-pyd : $(sources) /boost/python//boost_python : $(requirements) : $(name) ] ; } IMPORT $(__name__) : bpl-test : : bpl-test ; - - + +