mirror of
https://github.com/boostorg/build.git
synced 2026-02-15 13:02:11 +00:00
616 lines
20 KiB
Plaintext
616 lines
20 KiB
Plaintext
# Copyright 2004 Vladimir Prus.
|
|
# Distributed under the Boost Software License, Version 1.0. (See
|
|
# accompanying file LICENSE_1_0.txt or copy at
|
|
# http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
# Support for Python and the the Boost.Python library.
|
|
#
|
|
# This module defines
|
|
#
|
|
# - a project 'python' with a target 'python' in it, that corresponds
|
|
# to the python library
|
|
#
|
|
# - a main target rule 'python-extension' which can be used
|
|
# to build a python extension.
|
|
#
|
|
# 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 ;
|
|
import project ;
|
|
import errors ;
|
|
import targets ;
|
|
import "class" : new ;
|
|
import os ;
|
|
import common ;
|
|
import toolset : flags ;
|
|
import regex ;
|
|
|
|
# Make this module a project
|
|
project.initialize $(__name__) ;
|
|
project python ;
|
|
|
|
# Save the project so that if 'init' is called several
|
|
# times we define new targets in the python project,
|
|
# not in whatever project we were called by.
|
|
.project = [ project.current ] ;
|
|
|
|
.alias-defined = ;
|
|
|
|
# Dynamic linker lib. Necessary to specify it explicitly
|
|
# on some platforms.
|
|
lib dl ;
|
|
# This contains 'openpty' function need by python. Again, on
|
|
# some system need to pass this to linker explicitly.
|
|
lib util ;
|
|
# Python uses pthread symbols.
|
|
lib pthread ;
|
|
# Extra library needed by phtread on some platforms.
|
|
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.
|
|
#
|
|
# Example usage:
|
|
#
|
|
# using python 2.3 ; # Use default root
|
|
# using python 2.3 : /usr/local ; # Root specified, include and lib paths
|
|
# # will be guessed
|
|
#
|
|
rule init ( version ? : root ? : includes ? : libraries ?
|
|
: cygwin-condition * )
|
|
{
|
|
.configured = true ;
|
|
|
|
project.push-current $(.project) ;
|
|
|
|
if [ os.name ] = NT
|
|
{
|
|
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 ;
|
|
}
|
|
|
|
|
|
project.pop-current ;
|
|
}
|
|
|
|
local rule python-version ( cmd )
|
|
{
|
|
cmd ?= python ;
|
|
local version = [ SHELL $(cmd)" -c 'import sys; print sys.version'" : exit-status ] ;
|
|
|
|
if $(version[2]) = 0
|
|
{
|
|
return [ MATCH ^([0-9]+.[0-9]+) : $(version[1]) : 1 ] ;
|
|
}
|
|
else
|
|
{
|
|
return ;
|
|
}
|
|
}
|
|
|
|
local rule python-interpreter ( cmd )
|
|
{
|
|
local which = [ SHELL "which "$(cmd) : exit-status ] ;
|
|
if $(which[2]) = 0
|
|
{
|
|
return $(which[1]) ;
|
|
}
|
|
else
|
|
{
|
|
return ;
|
|
}
|
|
}
|
|
|
|
local rule python-root ( cmd )
|
|
{
|
|
return [ MATCH (.*)/bin/[^/]* : [ SHELL "which "$(cmd) ] : 1 ] ;
|
|
}
|
|
|
|
|
|
local rule debug-message ( message * )
|
|
{
|
|
if --debug-configuration in [ modules.peek : ARGV ]
|
|
{
|
|
ECHO notice: $(message) ;
|
|
}
|
|
}
|
|
|
|
# condition is a list of properties for which this python initialization applies.
|
|
rule init-unix ( version ? : root ? : includes ? : libraries ? : condition * )
|
|
{
|
|
#
|
|
# Autoconfiguration sequence
|
|
#
|
|
if $(version)
|
|
{
|
|
local v = [ MATCH ^([0-9]+\.[0-9]+)(.*)$ : $(version) : 1 2 ] ;
|
|
if ! $(v) || $(v[2])
|
|
{
|
|
ECHO "Warning: \"using python\" expects a two part (major, minor) version number; got" $(version) instead ;
|
|
if $(v)
|
|
{
|
|
version = $(v[1]) ;
|
|
}
|
|
}
|
|
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 cmds = $(bin:E="")python$(version:E="") $(bin:E="")python ;
|
|
|
|
local interpreter ;
|
|
while $(cmds)
|
|
{
|
|
# 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)
|
|
{
|
|
debug-message $(interpreter) does not invoke a working Python interpreter ;
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
PYTHON = $(interpreter) ;
|
|
}
|
|
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 <threading>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 :
|
|
{
|
|
extra-libs = pthread dl ;
|
|
|
|
# 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 <threading>multi.
|
|
extra-libs-conditional = <toolset>gcc:<source>rt ;
|
|
}
|
|
|
|
case OSF :
|
|
{
|
|
extra-libs = pthread z ;
|
|
extra-libs-conditional = <toolset>gcc:<source>rt ;
|
|
}
|
|
|
|
case QNX* :
|
|
{
|
|
extra-libs = ;
|
|
}
|
|
|
|
case * : extra-libs = pthread dl 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)
|
|
:
|
|
: <include>$(includes)
|
|
;
|
|
}
|
|
|
|
|
|
# This should really be called python_for_embedding
|
|
alias python
|
|
: $(extra-libs)
|
|
: $(condition) $(extra-libs-conditional)
|
|
:
|
|
: <include>$(includes)
|
|
<library-path>$(libraries)
|
|
<find-shared-library>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) ;
|
|
}
|
|
|
|
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
|
|
:
|
|
: <os>MACOSX <toolset>darwin
|
|
:
|
|
: <include>$(includes) <framework>$(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)
|
|
{
|
|
root ?= c:/tools/python ;
|
|
|
|
local PATH = [ modules.peek : PATH ] ;
|
|
local PATH = [ modules.peek : Path ] ;
|
|
|
|
PYTHON_LIB_PATH ?= $(root)/libs [ GLOB $(root) : PCbuild ] ;
|
|
|
|
PYTHON_INCLUDES ?= $(root)/include [ GLOB $(root) : PC ] ;
|
|
|
|
# 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="") ;
|
|
|
|
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 : python : : $(root)/bin
|
|
$(root)
|
|
$(root)/PCBuild
|
|
: path-last ] ;
|
|
|
|
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) ;
|
|
ECHO "notice: Python library name is" python$(PYTHON_VERSION_NODOT) ;
|
|
}
|
|
|
|
flags python.capture-output PYTHON : $(interpreter) ;
|
|
|
|
|
|
properties += <library-path>$(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
|
|
:
|
|
: <toolset>msvc
|
|
:
|
|
: <library-path>$(PYTHON_LIB_PATH)
|
|
<include>$(PYTHON_INCLUDES)
|
|
;
|
|
|
|
local lib = python$(PYTHON_VERSION_NODOT) ;
|
|
|
|
alias python
|
|
:
|
|
:
|
|
:
|
|
: <library-path>$(PYTHON_LIB_PATH)
|
|
<include>$(PYTHON_INCLUDES)
|
|
<find-shared-library>$(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 += <library-path>$(CYGWIN_PYTHON_LIB_PATH)
|
|
<find-shared-library>python$(CYGWIN_PYTHON_VERSION).dll ;
|
|
|
|
properties += <include>$(root)/include/python$(version) ;
|
|
|
|
alias python
|
|
:
|
|
: $(cygwin-condition)
|
|
:
|
|
: $(properties)
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
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 : <os>NT : pyd ;
|
|
type.set-generated-target-suffix PYTHON_EXTENSION : <os>CYGWIN : dll ;
|
|
|
|
# Unset 'lib' prefix for PYTHON_EXTENSION
|
|
type.set-generated-target-prefix PYTHON_EXTENSION : : "" ;
|
|
|
|
rule python-extension ( name : sources * : requirements * : default-build * :
|
|
usage-requirements * )
|
|
{
|
|
requirements += <use>/python//python_for_extensions ;
|
|
|
|
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) ]
|
|
] ;
|
|
}
|
|
|
|
IMPORT python : python-extension : : python-extension ;
|
|
|
|
# Support for testing
|
|
type.register PY : py ;
|
|
type.register RUN_PYD_OUTPUT ;
|
|
#type.set-generated-target-suffix RUN_PYD : : run ;
|
|
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 ;
|
|
for local s in $(sources)
|
|
{
|
|
if [ $(s).type ] = PY
|
|
{
|
|
python = $(s) ;
|
|
}
|
|
}
|
|
|
|
local extensions ;
|
|
for local s in $(sources)
|
|
{
|
|
if [ $(s).type ] = PYTHON_EXTENSION
|
|
{
|
|
extensions += $(s) ;
|
|
}
|
|
}
|
|
|
|
local libs ;
|
|
for local s in $(sources)
|
|
{
|
|
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 ]
|
|
{
|
|
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.
|
|
property-set = [ $(property-set).add $(extension[1]) ] ;
|
|
# Ignore usage requirements. We're top-level generator and
|
|
# nobody is going to use us.
|
|
new-sources += $(extension[2-]) ;
|
|
}
|
|
}
|
|
|
|
|
|
result = [ construct-result $(python) $(extensions) $(new-sources)
|
|
: $(project) $(name) : $(property-set) ] ;
|
|
}
|
|
}
|
|
|
|
generators.register
|
|
[ new python-test-generator python.capture-output : : RUN_PYD_OUTPUT ] ;
|
|
|
|
generators.register-standard testing.expect-success
|
|
: RUN_PYD_OUTPUT : RUN_PYD ;
|
|
|
|
|
|
rule capture-output ( target : sources * : properties * )
|
|
{
|
|
# Setup up proper DLL search path.
|
|
# Here, $(sources[1]) is python module and $(sources[2]) is
|
|
# 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) ] ;
|
|
testing.capture-output $(target) : $(sources[1]) : $(properties) ;
|
|
local c = [ common.prepend-path-variable-command PYTHONPATH : $(PYTHONPATH) ] ;
|
|
LAUNCHER on $(target) = $(c) [ on $(target) return $(PYTHON) ] ;
|
|
}
|
|
|
|
rule bpl-test ( name : sources * : requirements * )
|
|
{
|
|
sources ?= $(name).py $(name).cpp ;
|
|
return [ testing.make-test
|
|
run-pyd : $(sources) /boost/python//boost_python
|
|
: $(requirements) : $(name) ] ;
|
|
}
|
|
|
|
IMPORT $(__name__) : bpl-test : : bpl-test ;
|
|
|
|
|