# 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 ; # Make this module a project project.initialize $(__name__) ; project python ; rule init ( version ? : root : includes ? : libraries ? : cygwin-condition ? ) { .configured = true ; 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) ; } } rule init-unix ( version ? : root : includes ? : libraries ? ) { root ?= /usr ; includes ?= $(root)/include/python$(version) ; libraries ?= $(root)/lib/python$(version)/config ; if --debug-configuration in [ modules.peek : ARGV ] { ECHO "notice: Python include path is" $(includes) ; ECHO "notice: Python library path is" $(libraries) ; } # On Linux, we don't want to link either Boost.Python or # Python extensions to libpython, so that when extensions # loaded in the interpreter, the symbols in the interpreter # are used. If we linked to libpython, we'd get duplicate # symbols. So declare two targets -- one for headers and another # for library. alias python_for_extensions : : : : $(includes) ; alias python : : : : $(includes) $(libraries) python$(version) ; # TODO: handle the following V1 code: # PYTHON_PROPERTIES ?= # $(PYTHON_INCLUDES) # $(PYTHON_LIB_PATH) # python-intel-use-gcc-stdlib # python-static-multithread # ; # TODO: need to figure out when the following code is needed: # for builtin extensions only or in some other cases too. # if [ modules.peek $(OS) ] = OSF # { # PYTHON_PROPERTIES += <*><*>"-expect_unresolved 'Py*' -expect_unresolved '_Py*'" ; # } # else if [ modules.peek $(OS) ] = AIX # { # PYTHON_PROPERTIES # += <*><*>"-Wl,-bI:$(PYTHON_LIB_PATH)/python.exp" # <*><*>pthreads ; # } } 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) ; # FIXME: not sure what PYTHON_FRAMEWORK variable is 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 : : : : $(includes) ; alias python : : MACOSXX darwin : : $(PYTHON_FRAMEWORK) ; } rule init-nt ( version : root : includes ? : libraries ? : cygwin-condition ? ) { # PYTHON_PROPERTIES = # boost-python-disable-borland # select-nt-python-includes # dynamic # @boost # <$(gcc-compilers)><*>USE_DL_IMPORT # ; if ! $(cygwin-condition) { root ?= c:/tools/python ; PYTHON_LIB_PATH ?= $(root)/libs [ GLOB $(root) : PCbuild ] ; PYTHON_INCLUDES ?= $(root)/include ; 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).* ] ; # This is mingw-specific V1 code. I don't yet understand # why mingw must be specially-cased. #local lib = $(PYTHON_IMPORT_LIB) ; #if BOOST_DEBUG_PYTHON in $(properties) #{ # lib = $(PYTHON_DEBUG_IMPORT_LIB) ; #} #lib ?= $(PYTHON_DLL) ; #if BOOST_DEBUG_PYTHON in $(properties) #{ # lib ?= $(PYTHON_DEBUG_DLL) ; #} #properties += $(lib) ; #} 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 : : : : $(PYTHON_LIB_PATH) msvc ; if $(toolset) != msvc { properties += $(PYTHON_LIB_PATH) ; local lib = python$(PYTHON_VERSION_NODOT) ; if BOOST_DEBUG_PYTHON in $(properties) { lib = python$(PYTHON_VERSION_NODOT)_d ; } properties += $(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 ; if BOOST_DEBUG_PYTHON in $(properties) { properties += $(CYGWIN_PYTHON_DEBUG_LIB_PATH) python$(CYGWIN_PYTHON_DEBUG_VERSION).dll ; } else { properties += $(CYGWIN_PYTHON_LIB_PATH) python$(CYGWIN_PYTHON_VERSION).dll ; } } } 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 : so ; type.set-generated-target-suffix PYTHON_EXTENSION : CYGWIN : dll ; rule python-extension ( name : sources * : requirements * : default-build * : usage-requirements * ) { requirements += /python//python_for_extensions ; # TODO: handle the following V1 code #if $(OS) = MACOSX && $(toolset) = darwin #{ # if PYD in $(properties) # { # properties += bundle ; # } # properties += $(PYTHON_FRAMEWORK) ; #} # TODO: handle # python-intel-use-gcc-stdlib # <*>"-inline deferred" # <*>"-inline deferred" # added for internal testing purposes # <*>@boost/boost/compatibility/cpp_c_headers # BOOST_PYTHON_DYNAMIC_LIB # if $(OS) = OSF # { # PYTHON_PROPERTIES += <*><*>"-expect_unresolved 'Py*' -expect_unresolved '_Py*'" ; # } # else if $(OS) = AIX # { # PYTHON_PROPERTIES # += <*><*>"-Wl,-bI:$(PYTHON_LIB_PATH)/python.exp" # <*><*>pthreads ; # } # PYTHON_PROPERTIES += # @boost # on # select-python-library # boost-python-disable-borland # select-nt-python-includes # dynamic # @boost # <$(gcc-compilers)><*>USE_DL_IMPORT # $(PYTHON_INCLUDES) # $(PYTHON_INCLUDES) # $(PYTHON_LIB_PATH) # python-intel-use-gcc-stdlib # python-static-multithread 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) ] ; # 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 * ) { PYTHONPATH = [ on $(sources[2]) return $(LOCATE) ] ; testing.capture-output $(target) : $(sources[1]) : $(properties) ; LAUNCHER on $(target) = PYTHONPATH=$(PYTHONPATH) "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 ;