From 9b89c2f459ade7b544f76353e4a55fdecdb50f3b Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Thu, 15 Mar 2007 04:02:25 +0000 Subject: [PATCH] ** This comment represents the aggregate changes merged from the ** ** bbv2python branch. ** gcc.jam: build and use import libraries on Cygwin and Windows, but accept DLL-only linking with prebuilt DLLs. builtin.jam: fix default-host-os so that actually becomes a functional feature. Improve English in comments property-set.jam: add str method so we can print them in generator debugging output. generators.jam: improved debugging output build-system.jam: add missing semicolon python.jam: * fix cross-NT/CYGWIN build support * add condition to the build requirements of the python targets os.jam: * add the ability to get constants for a particular OS builtin.jam: * remove "optional" attribute from host-os * fix confusing indents python.jam ---------- * Removed comments about known problems because they make no sense. * Unified MacOS initialization with NT and *nix * Updated comment describing init behavior * Support for passing Python command as first argument * Removed unused get-python-interpreter and get-python-version rules, since they can't work with that interface. Working versions of these will be reinstated for Doug Gregor in the near future. * When invoking Python to collect configuration info, collect it all at at once. * When a Cygwin symlink is found by an NT build of bjam, give hints about where to find the file it points at. * Lots of refactoring * Make the logic work plausibly even when no Python executable can be found darwin.jam ---------- * Simplified logic for setting up FRAMEWORK_PATH * Corrected logic for setting up -framework option feature.jam ----------- * Make feature.defaults, feature.attributes, feature.values, and feature.get-values resilient to feature names being passed without grist. gcc.jam, python.jam, builtin.jam -------------------------------- * Include some progress volodya has made toward support for [SVN r37186] --- v2/build-system.jam | 3 +- v2/build/feature.jam | 21 +- v2/build/generators.jam | 9 + v2/build/property-set.jam | 5 + v2/tools/builtin.jam | 30 +- v2/tools/common.jam | 4 + v2/tools/darwin.jam | 13 +- v2/tools/gcc.jam | 39 +- v2/tools/msvc.jam | 15 +- v2/tools/python.jam | 1140 +++++++++++++++++++++++++------------ v2/util/os.jam | 48 +- v2/util/sequence.jam | 12 + 12 files changed, 918 insertions(+), 421 deletions(-) diff --git a/v2/build-system.jam b/v2/build-system.jam index e3d4943c1..4b26bd192 100755 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -83,6 +83,7 @@ local rule load-config ( basename : path + ) { if $(debug-config) { + ECHO notice: searching \"$(path)\" for \"$(basename).jam\" ; local where = [ GLOB $(path) : $(basename).jam ] ; if $(where) { @@ -250,7 +251,7 @@ if ! [ feature.values ] ECHO "warning: Configuring default toolset" \"$(default-toolset)\". ; ECHO "warning: If the default is wrong, you may not be able to build C++ programs." ; ECHO "warning: Use the \"--toolset=xxxxx\" option to override our guess." ; - ECHO "warning: For more configuration options, please consult" + ECHO "warning: For more configuration options, please consult" ; ECHO "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" ; diff --git a/v2/build/feature.jam b/v2/build/feature.jam index 83d928cdc..6c4544ca9 100644 --- a/v2/build/feature.jam +++ b/v2/build/feature.jam @@ -165,13 +165,14 @@ rule defaults ( features * ) local result ; for local f in $(features) { - local a = $($(f).attributes) ; + local gf = $(:E=:G=$(f)) ; + local a = $($(gf).attributes) ; if ( free in $(a) ) || ( optional in $(a) ) { } else { - result += $(f)$($(f).default) ; + result += $(gf)$($(gf).default) ; } } return $(result) ; @@ -189,13 +190,13 @@ rule valid ( names + ) # return the attibutes of the given feature rule attributes ( feature ) { - return $($(feature).attributes) ; + return $($(:E=:G=$(feature)).attributes) ; } # return the values of the given feature rule values ( feature ) { - return $($(feature).values) ; + return $($(:E=:G=$(feature)).values) ; } # returns true iff 'value-string' is a value-string of an implicit feature @@ -640,6 +641,8 @@ local rule expand-composite ( property ) rule get-values ( feature : properties * ) { local result ; + + feature = $(:E=:G=$(feature)) ; # add <> if necessary. for local p in $(properties) { if $(p:G) = $(feature) @@ -1050,6 +1053,8 @@ local rule __test__ ( ) compose debug : _DEBUG off ; compose release : NDEBUG on ; + assert.result dynamic static : values ; + assert.result dynamic static : values runtime-link ; try ; { @@ -1128,12 +1133,20 @@ local rule __test__ ( ) : defaults ; + # make sure defaults is resilient to missing grist. + assert.result dynamic on + : defaults runtime-link define optimization + ; + feature dummy : dummy1 dummy2 ; subfeature dummy : subdummy : x y z : optional ; feature fu : fu1 fu2 : optional ; subfeature fu : subfu : x y z : optional ; subfeature fu : subfu2 : q r s ; + + assert.result optional : attributes ; + assert.result optional : attributes fu ; assert.result static foobar on gcc:FOO gcc debug native dummy1 2.95.2 diff --git a/v2/build/generators.jam b/v2/build/generators.jam index b4fd63758..75e1dd4e6 100644 --- a/v2/build/generators.jam +++ b/v2/build/generators.jam @@ -842,6 +842,9 @@ local rule try-one-generator-really ( project name ? : generator : local usage-requirements ; local success ; + + generators.dout [ indent ] returned $(targets) ; + if $(targets) { success = true ; @@ -859,6 +862,12 @@ local rule try-one-generator-really ( project name ? : generator : generators.dout [ indent ] " generator" [ $(generator).id ] " spawned " ; generators.dout [ indent ] " " $(targets) ; + if $(usage-requirements) + { + generators.dout [ indent ] " with usage requirements:" $(x) ; + } + + if $(success) { return $(usage-requirements) $(targets) ; diff --git a/v2/build/property-set.jam b/v2/build/property-set.jam index 0dd7d958c..9ccf8677f 100644 --- a/v2/build/property-set.jam +++ b/v2/build/property-set.jam @@ -101,6 +101,11 @@ class property-set return $(self.raw) ; } + rule str ( ) + { + return "[" $(self.raw) "]" ; + } + # Returns properties that are neither incidental nor free rule base ( ) { diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index d919482ec..d5fa1d2c9 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -33,14 +33,17 @@ import generate ; local os = [ modules.peek : OS ] ; feature os : $(os) : propagated link-incompatible ; +.os-names = amiga aix bsd cygwin darwin dos emx freebsd hpux + linux netbsd openbsd osf qnx qnxnto sgi solaris sun sunos + svr4 sysv ultrix unix unixware vms windows ; + # Translates from bjam current OS to the os tags used # in host-os and target-os. I.e. it returns the # running host-os. local rule default-host-os ( ) { local host-os ; - local os-list = [ feature.values host-os ] ; - if [ os.name ] in $(os-list:U) + if [ os.name ] in $(.os-names:U) { host-os = [ os.name ] ; } @@ -70,18 +73,13 @@ local rule default-host-os ( ) # to list all the values to prevent unkown value errors. # Both set the default value to the current OS to account for # the default use case of building on the target OS. -feature host-os - : amiga aix bsd cygwin darwin dos emx freebsd hpux - linux netbsd openbsd osf qnx qnxnto sgi solaris sun sunos - svr4 sysv ultrix unix unixware vms windows - : optional ; - feature.set-default host-os : [ default-host-os ] ; +feature host-os : $(.os-names) ; +feature.set-default host-os : [ default-host-os ] ; + feature target-os - : amiga aix bsd cygwin darwin dos emx freebsd hpux - linux netbsd openbsd osf qnx qnxnto sgi solaris sun sunos - svr4 sysv ultrix unix unixware vms windows + : $(.os-names) : propagated link-incompatible ; - feature.set-default target-os : [ default-host-os ] ; +feature.set-default target-os : [ default-host-os ] ; feature toolset : : implicit propagated symmetric ; @@ -169,15 +167,17 @@ feature dll-path : : free path ; feature hardcode-dll-paths : true false : incidental ; -# This is internal feature which holds the paths of all dependency +# An internal feature that holds the paths of all dependency # dynamic libraries. On Windows, it's needed so that we can all -# those paths to PATH, when running applications. +# those paths to PATH when running applications. # On Linux, it's needed to add proper -rpath-link command line options. feature xdll-path : : free path ; #provides means to specify def-file for windows dlls. feature def-file : : free dependency ; +feature.feature suppress-import-lib : false true : incidental ; + # This is internal feature which is used to store the name of # bjam action to call when building a target. feature.feature action : : free ; @@ -726,7 +726,7 @@ class linking-generator : generator } # Hardcode dll paths only when linking executables. - # Pros: don't need to relinking libraries when installing. + # Pros: don't need to relink libraries when installing. # Cons: "standalone" libraries (plugins, python extensions) # can't hardcode paths to dependent libraries. if [ $(property-set).get ] = true diff --git a/v2/tools/common.jam b/v2/tools/common.jam index e737a907a..c350063ac 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -338,6 +338,10 @@ rule check-tool-aux ( command ) if $(command:D) { if [ path.exists $(command) ] + # Both NT and Cygwin will run .exe files by their unqualified names + || [ os.on-windows ] && [ path.exists $(command).exe ] + # Only NT will run .bat files by their unqualified names + || [ os.name ] = NT && [ path.exists $(command).bat ] { return $(command) ; } diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index b14ff5407..e3def2490 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -65,14 +65,7 @@ local rule prepare-framework-path ( target + ) { local framework-path = [ on $(target) return $(FRAMEWORK:D) ] ; - if $(framework-path) - { - FRAMEWORK_PATH on $(target) += -F$(framework-path) ; - } - else - { - FRAMEWORK_PATH on $(target) = ; - } + FRAMEWORK_PATH on $(target) += -F$(framework-path) ; } rule link @@ -82,7 +75,7 @@ rule link actions link bind LIBRARIES { - $(CONFIG_COMMAND) -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=) $(OPTIONS) $(USER_OPTIONS) + $(CONFIG_COMMAND) -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=:S=) $(OPTIONS) $(USER_OPTIONS) $(NEED_STRIP)strip $(NEED_STRIP)"$(<)" } @@ -93,7 +86,7 @@ rule link.dll actions link.dll bind LIBRARIES { - $(CONFIG_COMMAND) -dynamiclib -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=) $(OPTIONS) $(USER_OPTIONS) + $(CONFIG_COMMAND) -dynamiclib -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=:S=) $(OPTIONS) $(USER_OPTIONS) } actions piecemeal archive diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 8b8d86352..efe907827 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -44,6 +44,9 @@ generators.override gcc.searched-lib-generator : searched-lib-generator ; type.set-generated-target-suffix OBJ : gcc : o ; type.set-generated-target-suffix STATIC_LIB : gcc : a ; +type.set-generated-target-suffix IMPORT_LIB : gcc cygwin : dll.a ; +type.set-generated-target-suffix IMPORT_LIB : gcc windows : a ; + import rc ; # Initializes the gcc toolset for the given version. @@ -75,7 +78,7 @@ rule init ( version ? : command * : options * ) # Autodetect the version and flavor if not given. if $(command) { - # The 'command' variable can have multiple-element. When calling + # The 'command' variable can have multiple elements. When calling # the SHELL builtin we need a single string. local command-string = $(command:J=" ") ; local command-info = [ MATCH "^[^ ]+[ ]+[^ ]+[ ]+([^ ]+)[^(]*[(]?([^)]*)" @@ -427,8 +430,26 @@ class gcc-linking-generator : unix-linking-generator } else { - return [ unix-linking-generator.run $(project) $(name) + local generated-targets = [ unix-linking-generator.run $(project) $(name) : $(property-set) : $(sources) ] ; + + # If more than one target was generated, throw out the + # last one, which on windows just leaves the import + # library. Most generators on windows simply don't accept + # shared libraries as input, but being able to link + # directly to a shared library without an import library + # is an important capability of GCC. Therefore, we remove + # the target after the action sees it so that dependent + # targets don't try to link to both the import library and + # the DLL. + if [ $(property-set).get ] = true + { + return $(generated-targets[0]) $(generated-targets[-1]) ; + } + else + { + return $(generated-targets[1-2]) ; + } } } } @@ -436,9 +457,17 @@ class gcc-linking-generator : unix-linking-generator generators.register [ new gcc-linking-generator gcc.link : LIB OBJ : EXE : gcc ] ; -generators.register [ new gcc-linking-generator gcc.link.dll : LIB OBJ : SHARED_LIB - : gcc ] ; +.IMPLIB-COMMAND = ; +.IMPLIB-TYPE = ; +if [ os.on-windows ] +{ + .IMPLIB-COMMAND = "-Wl,--out-implib," ; + .IMPLIB-TYPE = IMPORT_LIB ; +} +generators.register + [ new gcc-linking-generator gcc.link.dll : LIB OBJ : $(.IMPLIB-TYPE) SHARED_LIB + : gcc ] ; # Declare flags for linking # First, the common flags @@ -601,7 +630,7 @@ rule link.dll ( targets * : sources * : properties * ) # Differ from 'link' above only by -shared. actions link.dll bind LIBRARIES { - "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,"$(RPATH)" -o "$(<)" $(HAVE_SONAME)-Wl,$(SONAME_OPTION)$(SPACE)-Wl,$(<[1]:D=) -shared $(START-GROUP) "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-ST) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,"$(RPATH)" "$(.IMPLIB-COMMAND)$(<[1])" -o "$(<[-1])" $(HAVE_SONAME)-Wl,$(SONAME_OPTION)$(SPACE)-Wl,$(<[-1]:D=) -shared $(START-GROUP) "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-ST) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) } # Set up threading support. It's somewhat contrived, so perform it at the end, diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index f256a2721..6ae565b9f 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -301,7 +301,7 @@ local rule configure-really ( setup-option = x86 x86_amd64 x86_iPF ; # Use a native x64 compiler if possible - if [ os.environ PROCESSOR_ARCHITECTURE ] = AMD64 + if [ MATCH ^(AMD64) : [ os.environ PROCESSOR_IDENTIFIER ] ] { setup-option = x86 amd64 x86_IPF ; } @@ -968,14 +968,19 @@ if [ os.name ] in NT CYGWIN { if $(.version-$(i)-reg) { - local vc-path = [ W32_GETREG - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"$(.version-$(i)-reg) + local vc-path ; + for local x in "" "Wow6432Node\\" + { + vc-path += [ W32_GETREG + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"$(x)$(.version-$(i)-reg) : "ProductDir" ] ; + } + if $(vc-path) { - vc-path = [ path.native [ path.join [ path.make-NT $(vc-path) ] "bin" ] ] ; - register-configuration $(i) : $(vc-path) ; + vc-path = [ path.native [ path.join [ path.make-NT $(vc-path[1]) ] "bin" ] ] ; + register-configuration $(i) : $(vc-path[1]) ; } } } diff --git a/v2/tools/python.jam b/v2/tools/python.jam index 3a19b604f..e6d50b24c 100644 --- a/v2/tools/python.jam +++ b/v2/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,12 @@ import os ; import common ; import toolset : flags ; import regex ; +import numbers ; +import string ; +import property ; +import sequence ; +import path ; +import feature ; # Make this module a project project.initialize $(__name__) ; @@ -56,263 +56,800 @@ 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. # # 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 * ) { .configured = true ; project.push-current $(.project) ; - - if [ os.name ] = NT + + debug-message Configuring python... ; + for local v in version root 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 [ os.on-windows ] && ! $(.alias-defined) - { - .alias-defined = true ; - alias python_for_extensions : python ; + if $($(v)) + { + debug-message " user-specified "$(v): \"$($(v))\" ; + } } + configure $(version) : $(cmd-or-prefix) : $(includes) : $(libraries) : $(condition) ; 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) ; + # debug-message running command '$(cmd)' ; + x = [ SHELL $(cmd)" 2>&1" : exit-status ] ; + if $(x[2]) = 0 + { + return $(x[1]) ; + } + else + { + return ; + } } -# Retrieves the Python version number -rule get-python-version ( ) +# 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 ) { - 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 ] ; + local is-symlink = ; - if $(version[2]) = 0 + # 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 [ MATCH ^([0-9]+.[0-9]+) : $(version[1]) : 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-interpreter ( cmd ) +# Append ext to each member of names that does not contain '.' +local rule default-extension ( names * : ext * ) { - local which = [ SHELL "which "$(cmd) : exit-status ] ; - if $(which[2]) = 0 + local result ; + for local n in $(names) { - return $(which[1]) ; - } - else - { - return ; + switch $(n) + { + case *.* : result += $(n) ; + case * : result += $(n)$(ext) ; + } } + return $(result) ; } -local rule python-root ( cmd ) +# Find the path to the python executable invoked by the given command +# +# 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 ) { - return [ MATCH (.*)/bin/[^/]* : [ SHELL "which "$(cmd) ] : 1 ] ; + 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 and +# HKEY_LOCAL_MACHINE to the argument, returning the first result +# found. +local rule 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]) + if ! $(result) { - ECHO "Warning: \"using python\" expects a two part (major, minor) version number; got" $(version) instead ; - if $(v) - { - version = $(v[1]) ; - } + result = [ W32_GETREG $(root)"\\"$(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="")) ; + return $(result) ; +} - local interpreter ; - while $(cmds) +.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 ; - } - else - { - debug-message $(interpreter) invokes actual Python (major,minor) version $(true-version) ; + local root = [ + registry-value "SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\"$(head) + : native + ] ; - # if no version was specified, assume that's OK - version ?= $(true-version) ; - - # if the version is a match, stop searching - if $(version) = $(true-version) + if $(root) { - debug-message qualifying Python interpreter found ; - root ?= [ python-root $(interpreter) ] ; - cmds = ; # break + path = $(tail:R=$(root)) ; + head = ; + } + tail = $(tail:R=$(head:D=)) ; + + if $(head) = / + { + head = ; + } + else + { + head = $(head:D) ; } } } - 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 [ regex.replace $(path:R="") / \\ ] ; +} + +# Convert a *nix path to native +local rule *nix-path-to-native ( path ) +{ + if [ os.name ] = NT { - PYTHON = $(interpreter) ; + 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 - { - 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 : + 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 ( path ) +{ + if [ guess-windows-path $(path) ] + { + return [ windows-path-to-native $(path) ] ; + } + else + { + return [ *nix-path-to-native $(path:T) ] ; + } +} + +# 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]) + { + 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) + { + for local x64elt in "" Wow6432node\\ # Account for 64-bit windows { - extra-libs = pthread dl ; + local install-path = [ + registry-value "SOFTWARE\\"$(x64elt)"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. +rule dump-sys ( 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 + { + # 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-]) + { + 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 ? : executable ? ) +{ + exec-prefix ?= $(prefix) ; + + if $(target-os) = windows + { + executable ?= $(:E=python:R=$(exec-prefix)) ; + + # The exec-prefix is where you're supposed to look for + # machine-specific libraries. + local default-library-path = $(:E=libs:R=$(exec-prefix)) ; + local default-include-path = $(:E=Include:R=$(prefix)) ; + + if $(default-library-path) + { + default-library-path = [ path-to-native $(default-library-path) ] ; + } + if $(default-include-path) + { + default-include-path = [ path-to-native $(default-include-path) ] ; + } + + # 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 = $(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 = [ path-to-native $(executable-dir) ] + $(default-library-path) ; + + default-include-path = [ path-to-native $(: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 + { + # $(prefix)/include/python$(version) + includes ?= [ path-to-native $(:E=python$(version):R=$(:E=include:R=$(prefix))) ] ; + + # $(exec-prefix)/lib/python$(version)/config $(exec-prefix)/lib + local lib = [ path-to-native $(:E=lib:R=$(exec-prefix)) ] ; + libraries ?= [ path-to-native $(:E=config:R=$(:E=python$(version):R=$(lib))) ] + [ path-to-native $(lib) ] ; + } +} + +# The version of the python interpreter to use +feature.feature python : : propagated ; + +# Return a list of candidate commands to try when looking for a Python +# interpreter. +local rule candidate-interpreters ( version ? : prefix ? : target-os ) +{ + if $(prefix) + { + prefix = [ path-to-native $(prefix) ] ; + } + + 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 consisting of ungristed library names to be used as +# direct dependencies and conditional library names to be added to +# target conditions. +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 ; - } + return pthread dl gcc:rt ; + - case OSF : - { - extra-libs = pthread ; - extra-libs-conditional = gcc:rt ; - } + case osf : return pthread gcc:rt ; - case QNX* : - { - extra-libs = ; - } + case qnx* : return ; + case darwin : return ; + case windows : return ; - case HPUX : - { - extra-libs = pthread rt ; - } + case hpux : return pthread rt ; - case * : extra-libs = pthread dl util ; + case * : return pthread dl util ; + } +} + +# Declare a target to represent Python's library, returning the target +# name. +local rule declare-libpython-target ( version ? : target-os ) +{ + # Compute the representation of Python version in the name of + # Python's library file. + local lib-version = $(version) ; + if $(target-os) = windows + { + local major-minor = [ split-version $(version) ] ; + lib-version = $(major-minor:J="") ; } - if ! [ os.on-windows ] + # Compute the name of the target to declare + local libpython-target-name = python $(version) $(sys.platform) ; + libpython-target-name = $(libpython-target-name:J=-) ; + + # Declare and return it + lib $(libpython-target-name) : : python$(lib-version) ; + return $(libpython-target-name) ; +} + +# implementation of init +local rule configure ( + version ? : cmd-or-prefix ? : includes ? : libraries ? : condition * ) +{ + local prefix ; + local cmds-to-try ; + local interpreter-cmd ; + + local target-os = [ feature.get-values target-os : $(condition) ] ; + target-os ?= [ feature.defaults target-os ] ; + target-os = $(target-os:G=) ; + + # Normalize and dissect any version number + local major-minor ; + if $(version) + { + major-minor = [ split-version $(version) ] ; + version = $(major-minor:J=.) ; + } + + local cmds-to-try ; + + 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 + { + # 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 + { + interpreter-cmd = $(cmd-or-prefix) ; + } + } + + # 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 dump-sys 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 ; + } + + local fallback-cmd = $(cmds-to-try[1]) ; + local fallback-version ; + 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 [ dump-sys $(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! ; + + compute-default-paths + $(target-os) : $(sys.version) + : $(sys.prefix) + : $(sys.exec_prefix) + : $(sys.executable) + ; + 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) ) + { + version ?= $(fallback-version) ; + version ?= 2.5 ; + + if ! $(interpreter-cmd) + { + fallback-cmd ?= python ; + ECHO warning: No working Python interpreter found. ; + if [ os.name ] != NT || ! [ invokes-cygwin-symlink $(fallback-cmd) ] + { + interpreter-cmd = $(fallback-cmd) ; + ECHO warning: falling back to \"$(interpreter-cmd)\" ; + } + } + compute-default-paths $(target-os) : $(version) : $(prefix) ; + } + } + + debug-message "Python interpreter command is" \"$(interpreter-cmd:E=)\" ; + debug-message "Python include path is" \"$(includes:E=)\" ; + debug-message "Python library path is" \"$(libraries:E=)\" ; + # + # End autoconfiguration sequence + # + + # Add the version, if any, to the condition + if $(version) + { + if ! $(version) in [ feature.values python ] + { + feature.extend python : $(version) ; + } + condition += $(version:E=default) ; + } + + # Collect dependencies on system libraries. + local system-libs ; + for local x in [ system-library-dependencies $(target-os) ] + { + if $(x:G) + { + condition += $(x) ; + } + else + { + system-libs += $(x) ; + } + } + + condition += $(target-os) ; + + # Set up the PYTHON variable to point at the interpreter. + flags python.capture-output PYTHON $(condition:J=/) : $(interpreter-cmd) ; + + # Declare the "python" target. This should really be called python_for_embedding + if $(target-os) = darwin + { + # Search upward for the framework directory + local fwk = $(libraries[-1]) ; + while $(fwk:D=) && $(fwk:D=) != Python.framework + { + fwk = $(fwk:D) ; + } + + debug-message framework directory is \"$(fwk)\" ; + alias python + : + : $(condition) + : + : $(includes) $(fwk) + ; + } + else + { + local libpython-target-name = [ declare-libpython-target $(version) : $(target-os) ] ; + alias python + : $(system-libs) + : $(condition) + : + : $(includes) $(libraries) $(libpython-target-name) + ; + } + + # 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 : $(condition) ; + } + else { - # 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) @@ -320,179 +857,8 @@ rule init-unix ( version ? : root ? : includes ? : libraries ? : 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 ? ) -{ - if ! $(root) - { - if [ GLOB /System/Library/Frameworks : Python.framework ] - { - root = /System/Library/Frameworks/Python.framework/Versions/$(version) ; - } - 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 - { - PYTHON_FRAMEWORK = $(PYTHON_FRAMEWORK:D) ; - } - 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 ; -} - -rule init-nt ( version : root ? : includes ? : libraries ? : cygwin-condition ? ) -{ - 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 ] ; - - PYTHON_VERSION_NODOT = $(PYTHON_VERSION_NODOT:J="") ; - - root ?= c:/python$(PYTHON_VERSION_NODOT) ; - - local PATH = [ modules.peek : PATH ] ; - local PATH = [ modules.peek : Path ] ; - - PYTHON_INCLUDES = $(includes) ; - PYTHON_LIB_PATH = $(libraries) ; - - 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) ; - } - else - { - root ?= /usr ; - if $(root) = /usr - { - CYGWIN_PYTHON_DLL_PATH ?= /bin ; - } - else - { - CYGWIN_PYTHON_DLL_PATH ?= $(root)/bin ; - } - 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) ; - - alias python - : - : $(cygwin-condition) - : - : $(properties) - ; - } -} - - rule configured ( ) { return $(.configured) ; @@ -505,8 +871,8 @@ type.register PYTHON_EXTENSION : : SHARED_LIB ; # 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 ; +type.set-generated-target-suffix PYTHON_EXTENSION : windows : pyd ; +type.set-generated-target-suffix PYTHON_EXTENSION : cygwin : dll ; # Unset 'lib' prefix for PYTHON_EXTENSION type.set-generated-target-prefix PYTHON_EXTENSION : : "" ; @@ -514,7 +880,7 @@ type.set-generated-target-prefix PYTHON_EXTENSION : : "" ; 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 ] ; @@ -609,7 +975,7 @@ class python-test-generator : generator } property-set = [ $(property-set).add-raw $(other-pythons) ] ; - + result = [ construct-result $(python) $(extensions) $(new-sources) : $(project) $(name) : $(property-set) ] ; } @@ -621,6 +987,19 @@ generators.register 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. @@ -634,13 +1013,32 @@ rule capture-output ( target : sources * : properties * ) # 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) ] ; # 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) ] ; + + # 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 * ) diff --git a/v2/util/os.jam b/v2/util/os.jam index f95c69a24..fbc44a5a3 100644 --- a/v2/util/os.jam +++ b/v2/util/os.jam @@ -5,6 +5,7 @@ # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) import modules ; +import string ; # Return the value(s) of the given environment variable(s) at the time # bjam was invoked. @@ -17,50 +18,77 @@ rule environ ( variable-names + ) .platform = [ modules.peek : OSPLAT ] ; .version = [ modules.peek : OSVER ] ; -local rule constant ( c ) +local rule constant ( c : os ? ) { + os ?= $(.name) ; # First look for platform-specific name, then general value - local variables = .$(c)-$(.name) .$(c) ; + local variables = .$(c)-$(os) .$(c) ; local result = $($(variables)) ; return $(result[1]) ; } -rule get-constant ( ) +rule get-constant ( os ? ) { # Find the name of the constant being accessed, which is # equal to the name used to invoke us. local bt = [ BACKTRACE 1 ] ; local rulename = [ MATCH ([^.]*)$ : $(bt[4]) ] ; - return [ constant $(rulename) ] ; + return [ constant $(rulename) : $(os) ] ; } # export all the common constants -.constants = name platform version shared-library-path-variable path-separator ; +.constants = name platform version shared-library-path-variable path-separator executable-path-variable executable-suffix ; for local constant in $(.constants) { IMPORT $(__name__) : get-constant : $(__name__) : $(constant) ; } EXPORT $(__name__) : $(.constants) ; -.shared-library-path-variable-NT = PATH ; +.executable-path-variable-NT = PATH ; +# On Windows the case and capitalization of PATH is not always +# predictable, so let's find out what variable name was really set. +if $(.name) = NT +{ + for local n in [ VARNAMES .ENVIRON ] + { + if $(n:L) = path + { + .executable-path-variable-NT = $(n) ; + } + } +} + +# Specific constants for various platforms. There's no need to define +# any constant whose value would be the same as the default, below. +.shared-library-path-variable-NT = $(.executable-path-variable-NT) ; .path-separator-NT = ";" ; .expand-variable-prefix-NT = % ; .expand-variable-suffix-NT = % ; +.executable-suffix-NT = .exe ; .shared-library-path-variable-CYGWIN = PATH ; -.path-separator-CYGWIN = ":" ; -.expand-variable-prefix-CYGWIN = $ ; -.expand-variable-suffix-CYGWIN = "" ; .shared-library-path-variable-MACOSX = DYLD_LIBRARY_PATH ; - +# Default constants .shared-library-path-variable = LD_LIBRARY_PATH ; .path-separator = ":" ; .expand-variable-prefix = $ ; .expand-variable-suffix = "" ; +.executable-path-variable = PATH ; +.executable-suffix = "" ; +# Return a list of the directories in the PATH. Yes, that information +# is (sort of) available in the global module, but jam code can change +# those values, and it isn't always clear what case/capitalization to +# use when looking. This rule is a more reliable way to get there. +rule executable-path ( ) +{ + return [ string.words [ environ [ constant executable-path-variable ] ] + : [ constant path-separator ] ] ; +} + if $(.name) = NT { local home = [ environ HOMEDRIVE HOMEPATH ] ; diff --git a/v2/util/sequence.jam b/v2/util/sequence.jam index 00a60c5aa..8cbd1e8fb 100644 --- a/v2/util/sequence.jam +++ b/v2/util/sequence.jam @@ -46,6 +46,16 @@ rule transform ( function + : sequence * ) return $(result) ; } +rule reverse ( s * ) +{ + local r ; + for local x in $(s) + { + r = $(x) $(r) ; + } + return $(r) ; +} + rule less ( a b ) { @@ -304,5 +314,7 @@ local rule __test__ ( ) assert.result e-3 h-3 : sequence.select-highest-ranked e-1 e-3 h-3 m-2 : 1 3 3 2 ; + + assert.result 7 6 5 4 3 2 1 : sequence.reverse 1 2 3 4 5 6 7 ; } }