1# Copyright 2004 Vladimir Prus. 2# Distributed under the Boost Software License, Version 1.0. (See 3# accompanying file LICENSE_1_0.txt or copy at 4# http://www.boost.org/LICENSE_1_0.txt) 5 6# Support for Python and the the Boost.Python library. 7# 8# This module defines 9# 10# - a project 'python' with a target 'python' in it, that corresponds to the 11# python library 12# 13# - a main target rule 'python-extension' which can be used to build a python 14# extension. 15# 16# Extensions that use Boost.Python must explicitly link to it. 17 18import type ; 19import testing ; 20import generators ; 21import project ; 22import errors ; 23import targets ; 24import "class" : new ; 25import os ; 26import common ; 27import toolset ; 28import regex ; 29import numbers ; 30import string ; 31import property ; 32import sequence ; 33import path ; 34import feature ; 35import set ; 36import builtin ; 37import property-set ; 38 39 40# Make this module a project. 41project.initialize $(__name__) ; 42project python ; 43 44# Save the project so that if 'init' is called several times we define new 45# targets in the python project, not in whatever project we were called by. 46.project = [ project.current ] ; 47 48# Dynamic linker lib. Necessary to specify it explicitly on some platforms. 49lib dl ; 50# This contains 'openpty' function need by python. Again, on some system need to 51# pass this to linker explicitly. 52lib util ; 53# Python uses pthread symbols. 54lib pthread : 55 : <target-os>linux:<link>shared 56 ; 57 58# Extra library needed by phtread on some platforms. 59lib rt ; 60 61# The pythonpath feature specifies additional elements for the PYTHONPATH 62# environment variable, set by run-pyd. For example, pythonpath can be used to 63# access Python modules that are part of the product being built, but are not 64# installed in the development system's default paths. 65feature.feature pythonpath : : free optional path ; 66 67# The best configured version of Python 2 and 3. 68py2-version = ; 69py3-version = ; 70 71# Initializes the Python toolset. Note that all parameters are optional. 72# 73# - version -- the version of Python to use. Should be in Major.Minor format, 74# for example 2.3. Do not include the subminor version. 75# 76# - cmd-or-prefix: Preferably, a command that invokes a Python interpreter. 77# Alternatively, the installation prefix for Python libraries and includes. If 78# empty, will be guessed from the version, the platform's installation 79# patterns, and the python executables that can be found in PATH. 80# 81# - includes: the include path to Python headers. If empty, will be guessed. 82# 83# - libraries: the path to Python library binaries. If empty, will be guessed. 84# On MacOS/Darwin, you can also pass the path of the Python framework. 85# 86# - condition: if specified, should be a set of properties that are matched 87# against the build configuration when B2 selects a Python 88# configuration to use. 89# 90# - extension-suffix: A string to append to the name of extension modules before 91# the true filename extension. Ordinarily we would just compute this based on 92# the value of the <python-debugging> feature. However ubuntu's python-dbg 93# package uses the windows convention of appending _d to debug-build extension 94# modules. We have no way of detecting ubuntu, or of probing python for the 95# "_d" requirement, and if you configure and build python using 96# --with-pydebug, you'll be using the standard *nix convention. Defaults to "" 97# (or "_d" when targeting windows and <python-debugging> is set). 98# 99# Example usage: 100# 101# using python : 2.3 ; 102# using python : 2.3 : /usr/local/bin/python ; 103# 104rule init ( version ? : cmd-or-prefix ? : includes * : libraries ? 105 : condition * : extension-suffix ? ) 106{ 107 project.push-current $(.project) ; 108 109 debug-message Configuring python... ; 110 for local v in version cmd-or-prefix includes libraries condition 111 { 112 if $($(v)) 113 { 114 debug-message " user-specified $(v):" \"$($(v))\" ; 115 } 116 } 117 118 configure $(version) : $(cmd-or-prefix) : $(includes) : $(libraries) : $(condition) : $(extension-suffix) ; 119 120 project.pop-current ; 121} 122 123# A simpler version of SHELL that grabs stderr as well as stdout, but returns 124# nothing if there was an error. 125# 126local rule shell-cmd ( cmd ) 127{ 128 debug-message running command '$(cmd)" 2>&1"' ; 129 x = [ SHELL $(cmd)" 2>&1" : exit-status ] ; 130 if $(x[2]) = 0 131 { 132 return $(x[1]) ; 133 } 134 else 135 { 136 return ; 137 } 138} 139 140 141# Try to identify Cygwin symlinks. Invoking such a file directly as an NT 142# executable from a native Windows build of bjam would be fatal to the bjam 143# process. One /can/ invoke them through sh.exe or bash.exe, if you can prove 144# that those are not also symlinks. ;-) 145# 146# If a symlink is found returns non-empty; we try to extract the target of the 147# symlink from the file and return that. 148# 149# Note: 1. only works on NT 2. path is a native path. 150local rule is-cygwin-symlink ( path ) 151{ 152 local is-symlink = ; 153 154 # Look for a file with the given path having the S attribute set, as cygwin 155 # symlinks do. /-C means "do not use thousands separators in file sizes." 156 local dir-listing = [ shell-cmd "DIR /-C /A:S \""$(path)"\"" ] ; 157 158 if $(dir-listing) 159 { 160 # Escape any special regex characters in the base part of the path. 161 local base-pat = [ regex.escape $(path:D=) : "].[()*+?|\\$^" : \\ ] ; 162 163 # Extract the file's size from the directory listing. 164 local size-of-system-file = [ MATCH "([0-9]+) "$(base-pat) : $(dir-listing) : 1 ] ; 165 166 # If the file has a reasonably small size, look for the special symlink 167 # identification text. 168 if $(size-of-system-file) && [ numbers.less $(size-of-system-file) 1000 ] 169 { 170 local link = [ SHELL "FIND /OFF \"!<symlink>\" \""$(path)"\" 2>&1" ] ; 171 if $(link[2]) != 0 172 { 173 local nl = " 174 175" ; 176 is-symlink = [ MATCH ".*!<symlink>([^"$(nl)"]*)" : $(link[1]) : 1 ] ; 177 if $(is-symlink) 178 { 179 is-symlink = [ *nix-path-to-native $(is-symlink) ] ; 180 is-symlink = $(is-symlink:R=$(path:D)) ; 181 } 182 183 } 184 } 185 } 186 return $(is-symlink) ; 187} 188 189 190# Append ext to each member of names that does not contain '.'. 191# 192local rule default-extension ( names * : ext * ) 193{ 194 local result ; 195 for local n in $(names) 196 { 197 switch $(n) 198 { 199 case *.* : result += $(n) ; 200 case * : result += $(n)$(ext) ; 201 } 202 } 203 return $(result) ; 204} 205 206 207# Tries to determine whether invoking "cmd" would actually attempt to launch a 208# cygwin symlink. 209# 210# Note: only works on NT. 211# 212local rule invokes-cygwin-symlink ( cmd ) 213{ 214 local dirs = $(cmd:D) ; 215 if ! $(dirs) 216 { 217 dirs = . [ os.executable-path ] ; 218 } 219 local base = [ default-extension $(cmd:D=) : .exe .cmd .bat ] ; 220 local paths = [ GLOB $(dirs) : $(base) ] ; 221 if $(paths) 222 { 223 # Make sure we have not run into a Cygwin symlink. Invoking such a file 224 # as an NT executable would be fatal for the bjam process. 225 return [ is-cygwin-symlink $(paths[1]) ] ; 226 } 227} 228 229 230local rule debug-message ( message * ) 231{ 232 if --debug-configuration in [ modules.peek : ARGV ] 233 { 234 ECHO "notice:" "[python-cfg]" $(message) ; 235 } 236} 237 238 239# Like W32_GETREG, except prepend HKEY_CURRENT_USER\SOFTWARE and 240# HKEY_LOCAL_MACHINE\SOFTWARE to the first argument, returning the first result 241# found. Also accounts for the fact that on 64-bit machines, 32-bit software has 242# its own area, under SOFTWARE\Wow6432node. 243# 244local rule software-registry-value ( path : data ? ) 245{ 246 local result ; 247 for local root in HKEY_CURRENT_USER HKEY_LOCAL_MACHINE 248 { 249 for local x64elt in "" Wow6432node\\ # Account for 64-bit windows 250 { 251 if ! $(result) 252 { 253 result = [ W32_GETREG $(root)\\SOFTWARE\\$(x64elt)$(path) : $(data) ] ; 254 } 255 } 256 257 } 258 return $(result) ; 259} 260 261 262.windows-drive-letter-re = "^([A-Za-z]):[\\/](.*)" ; 263.cygwin-drive-letter-re = "^/cygdrive/([a-z])/(.*)" ; 264 265.working-directory = [ PWD ] ; 266.working-drive-letter = [ SUBST $(.working-directory) $(.windows-drive-letter-re) $1 ] ; 267.working-drive-letter ?= [ SUBST $(.working-directory) $(.cygwin-drive-letter-re) $1 ] ; 268 269 270local rule windows-to-cygwin-path ( path ) 271{ 272 # If path is rooted with a drive letter, rewrite it using the /cygdrive 273 # mountpoint. 274 local p = [ SUBST $(path:T) $(.windows-drive-letter-re) /cygdrive/$1/$2 ] ; 275 276 # Else if path is rooted without a drive letter, use the working directory. 277 p ?= [ SUBST $(path:T) ^/(.*) /cygdrive/$(.working-drive-letter:L)/$2 ] ; 278 279 # Else return the path unchanged. 280 return $(p:E=$(path:T)) ; 281} 282 283 284# :W only works in Cygwin builds of bjam. This one works on NT builds as well. 285# 286local rule cygwin-to-windows-path ( path ) 287{ 288 path = $(path:R="") ; # strip any trailing slash 289 290 local drive-letter = [ SUBST $(path) $(.cygwin-drive-letter-re) "$1:/$2" ] ; 291 if $(drive-letter) 292 { 293 path = $(drive-letter) ; 294 } 295 else if $(path:R=/x) = $(path) # already rooted? 296 { 297 # Look for a cygwin mount that includes each head sequence in $(path). 298 local head = $(path) ; 299 local tail = "" ; 300 301 while $(head) 302 { 303 local root = [ software-registry-value 304 "Cygnus Solutions\\Cygwin\\mounts v2\\"$(head) : native ] ; 305 306 if $(root) 307 { 308 path = $(tail:R=$(root)) ; 309 head = ; 310 } 311 tail = $(tail:R=$(head:D=)) ; 312 313 if $(head) = / 314 { 315 head = ; 316 } 317 else 318 { 319 head = $(head:D) ; 320 } 321 } 322 } 323 return [ regex.replace $(path:R="") / \\ ] ; 324} 325 326 327# Convert a *nix path to native. 328# 329local rule *nix-path-to-native ( path ) 330{ 331 if [ os.name ] = NT 332 { 333 path = [ cygwin-to-windows-path $(path) ] ; 334 } 335 return $(path) ; 336} 337 338 339# Convert an NT path to native. 340# 341local rule windows-path-to-native ( path ) 342{ 343 if [ os.name ] = NT 344 { 345 return $(path) ; 346 } 347 else 348 { 349 return [ windows-to-cygwin-path $(path) ] ; 350 } 351} 352 353 354# Return nonempty if path looks like a windows path, i.e. it starts with a drive 355# letter or contains backslashes. 356# 357local rule guess-windows-path ( path ) 358{ 359 return [ SUBST $(path) "($(.windows-drive-letter-re)|.*([\\]).*)" $1 ] ; 360} 361 362 363local rule path-to-native ( paths * ) 364{ 365 local result ; 366 367 for local p in $(paths) 368 { 369 if [ guess-windows-path $(p) ] 370 { 371 result += [ windows-path-to-native $(p) ] ; 372 } 373 else 374 { 375 result += [ *nix-path-to-native $(p:T) ] ; 376 } 377 } 378 return $(result) ; 379} 380 381 382# Validate the version string and extract the major/minor part we care about. 383# 384local rule split-version ( version ) 385{ 386 local major-minor = [ MATCH "^([0-9]+)\.([0-9]+)(.*)$" : $(version) : 1 2 3 ] ; 387 if ! $(major-minor[2]) || $(major-minor[3]) 388 { 389 ECHO "Warning: \"using python\" expects a two part (major, minor) version number; got" $(version) instead ; 390 391 # Add a zero to account for the missing digit if necessary. 392 major-minor += 0 ; 393 } 394 395 return $(major-minor[1]) $(major-minor[2]) ; 396} 397 398 399# Build a list of versions from 3.4 down to 1.5. Because bjam can not enumerate 400# registry sub-keys, we have no way of finding a version with a 2-digit minor 401# version, e.g. 2.10 -- let us hope that never happens. 402# 403.version-countdown = ; 404for local v in [ numbers.range 15 34 ] 405{ 406 .version-countdown = [ SUBST $(v) (.)(.*) $1.$2 ] $(.version-countdown) ; 407} 408 409 410local rule windows-installed-pythons ( version ? ) 411{ 412 version ?= $(.version-countdown) ; 413 local interpreters ; 414 415 for local v in $(version) 416 { 417 local install-path = [ 418 software-registry-value "Python\\PythonCore\\"$(v)"\\InstallPath" ] ; 419 420 if $(install-path) 421 { 422 install-path = [ windows-path-to-native $(install-path) ] ; 423 debug-message Registry indicates Python $(v) installed at \"$(install-path)\" ; 424 } 425 426 interpreters += $(:E=python:R=$(install-path)) ; 427 } 428 return $(interpreters) ; 429} 430 431 432local rule darwin-installed-pythons ( version ? ) 433{ 434 version ?= $(.version-countdown) ; 435 436 local prefix 437 = [ GLOB /System/Library/Frameworks /Library/Frameworks 438 : Python.framework ] ; 439 440 return $(prefix)/Versions/$(version)/bin/python ; 441} 442 443 444# Assume "python-cmd" invokes a python interpreter and invoke it to extract all 445# the information we care about from its "sys" module. Returns void if 446# unsuccessful. 447# 448local rule probe ( python-cmd ) 449{ 450 # Avoid invoking a Cygwin symlink on NT. 451 local skip-symlink ; 452 if [ os.name ] = NT 453 { 454 skip-symlink = [ invokes-cygwin-symlink $(python-cmd) ] ; 455 } 456 457 if $(skip-symlink) 458 { 459 debug-message -------------------------------------------------------------------- ; 460 debug-message \"$(python-cmd)\" would attempt to invoke a Cygwin symlink, ; 461 debug-message causing a bjam built for Windows to hang. ; 462 debug-message ; 463 debug-message If you intend to target a Cygwin build of Python, please ; 464 debug-message replace the path to the link with the path to a real executable ; 465 debug-message "(guessing:" \"$(skip-symlink)\") "in" your 'using python' line ; 466 debug-message "in" user-config.jam or site-config.jam. Do not forget to escape ; 467 debug-message backslashes ; 468 debug-message -------------------------------------------------------------------- ; 469 } 470 else 471 { 472 # Prepare a List of Python format strings and expressions that can be 473 # used to print the constants we want from the sys module. 474 475 # We do not really want sys.version since that is a complicated string, 476 # so get the information from sys.version_info instead. 477 local format = "version=%d.%d" ; 478 local exprs = "version_info[0]" "version_info[1]" ; 479 480 for local s in $(sys-elements[2-]) 481 { 482 format += $(s)=%s ; 483 exprs += $(s) ; 484 } 485 486 # Invoke Python and ask it for all those values. 487 local full-cmd = 488 $(python-cmd)" -c \"from sys import *; print('"$(format:J=\\n)"' % ("$(exprs:J=,)"))\"" ; 489 490 local output = [ shell-cmd $(full-cmd) ] ; 491 if $(output) 492 { 493 # Parse the output to get all the results. 494 local nl = " 495 496" ; 497 for s in $(sys-elements) 498 { 499 # These variables are expected to be declared local in the 500 # caller, so Jam's dynamic scoping will set their values there. 501 sys.$(s) = [ SUBST $(output) "\\<$(s)=([^$(nl)]+)" $1 ] ; 502 } 503 } 504 return $(output) ; 505 } 506} 507 508 509# Make sure the "libraries" and "includes" variables (in an enclosing scope) 510# have a value based on the information given. 511# 512local rule compute-default-paths ( target-os : version ? : prefix ? : 513 exec-prefix ? ) 514{ 515 exec-prefix ?= $(prefix) ; 516 517 if $(target-os) = windows 518 { 519 # The exec_prefix is where you're supposed to look for machine-specific 520 # libraries. 521 local default-library-path = $(exec-prefix)\\libs ; 522 local default-include-path = $(:E=Include:R=$(prefix)) ; 523 524 # If the interpreter was found in a directory called "PCBuild" or 525 # "PCBuild8," assume we're looking at a Python built from the source 526 # distro, and go up one additional level to the default root. Otherwise, 527 # the default root is the directory where the interpreter was found. 528 529 # We ask Python itself what the executable path is in case of 530 # intermediate symlinks or shell scripts. 531 local executable-dir = $(sys.executable:D) ; 532 533 if [ MATCH ^(PCBuild) : $(executable-dir:D=) ] 534 { 535 debug-message "This Python appears to reside in a source distribution;" ; 536 debug-message "prepending \""$(executable-dir)"\" to default library search path" ; 537 538 default-library-path = $(executable-dir) $(default-library-path) ; 539 540 default-include-path = $(:E=PC:R=$(executable-dir:D)) $(default-include-path) ; 541 542 debug-message "and \""$(default-include-path[1])"\" to default #include path" ; 543 } 544 545 libraries ?= $(default-library-path) ; 546 includes ?= $(default-include-path) ; 547 } 548 else 549 { 550 includes ?= $(prefix)/include/python$(version) ; 551 552 local lib = $(exec-prefix)/lib ; 553 libraries ?= $(lib)/python$(version)/config $(lib) ; 554 } 555} 556 557# The version of the python interpreter to use. 558feature.feature python : : propagated symmetric ; 559feature.feature python.interpreter : : free ; 560 561toolset.flags python.capture-output PYTHON : <python.interpreter> ; 562 563# 564# Support for Python configured --with-pydebug 565# 566feature.feature python-debugging : off on : propagated ; 567variant debug-python : debug : <python-debugging>on ; 568 569 570# Return a list of candidate commands to try when looking for a Python 571# interpreter. prefix is expected to be a native path. 572# 573local rule candidate-interpreters ( version ? : prefix ? : target-os ) 574{ 575 local bin-path = bin ; 576 if $(target-os) = windows 577 { 578 # On Windows, look in the root directory itself and, to work with the 579 # result of a build-from-source, the PCBuild directory. 580 bin-path = PCBuild8 PCBuild "" ; 581 } 582 583 bin-path = $(bin-path:R=$(prefix)) ; 584 585 if $(target-os) in windows darwin 586 { 587 return # Search: 588 $(:E=python:R=$(bin-path)) # Relative to the prefix, if any 589 python # In the PATH 590 [ $(target-os)-installed-pythons $(version) ] # Standard install locations 591 ; 592 } 593 else 594 { 595 # Search relative to the prefix, or if none supplied, in PATH. 596 local unversioned = $(:E=python:R=$(bin-path:E=)) ; 597 598 # If a version was specified, look for a python with that specific 599 # version appended before looking for one called, simply, "python" 600 return $(unversioned)$(version) $(unversioned) ; 601 } 602} 603 604 605# Compute system library dependencies for targets linking with static Python 606# libraries. 607# 608# On many systems, Python uses libraries such as pthreads or libdl. Since static 609# libraries carry no library dependency information of their own that the linker 610# can extract, these extra dependencies have to be given explicitly on the link 611# line of the client. The information about these dependencies is packaged into 612# the "python" target below. 613# 614# Even where Python itself uses pthreads, it never allows extension modules to 615# be entered concurrently (unless they explicitly give up the interpreter lock). 616# Therefore, extension modules do not need the efficiency overhead of threadsafe 617# code as produced by <threading>multi, and we handle libpthread along with 618# other libraries here. Note: this optimization is based on an assumption that 619# the compiler generates link-compatible code in both the single- and 620# multi-threaded cases, and that system libraries do not change their ABIs 621# either. 622# 623# Returns a list of usage-requirements that link to the necessary system 624# libraries. 625# 626local rule system-library-dependencies ( target-os ) 627{ 628 switch $(target-os) 629 { 630 case s[uo][nl]* : # solaris, sun, sunos 631 # Add a librt dependency for the gcc toolset on SunOS (the sun 632 # toolset adds -lrt unconditionally). While this appears to 633 # duplicate the logic already in gcc.jam, it does not as long as 634 # we are not forcing <threading>multi. 635 636 # On solaris 10, distutils.sysconfig.get_config_var('LIBS') yields 637 # '-lresolv -lsocket -lnsl -lrt -ldl'. However, that does not seem 638 # to be the right list for extension modules. For example, on my 639 # installation, adding -ldl causes at least one test to fail because 640 # the library can not be found and removing it causes no failures. 641 642 # Apparently, though, we need to add -lrt for gcc. 643 return <toolset>gcc:<library>rt ; 644 645 case osf : return <library>pthread <toolset>gcc:<library>rt ; 646 647 case qnx* : return ; 648 case darwin : return ; 649 case windows : return ; 650 case haiku : return ; 651 652 case hpux : return <library>rt ; 653 case *bsd : return <library>pthread <toolset>gcc:<library>util ; 654 655 case aix : return <library>pthread <library>dl ; 656 657 case * : return <library>pthread <library>dl 658 <toolset>gcc:<library>util <toolset-intel:platform>linux:<library>util ; 659 } 660} 661 662 663# Define a version suffix for libraries depending on Python. 664# For example, Boost.Python built for Python 2.7 uses the suffix "27" 665rule version-suffix ( version ) 666{ 667 local major-minor = [ split-version $(version) ] ; 668 local suffix = $(major-minor:J="") ; 669 return $(suffix) ; 670} 671 672# Declare a target to represent Python's library. 673# 674local rule declare-libpython-target ( version ? : requirements * ) 675{ 676 # Compute the representation of Python version in the name of Python's 677 # library file. 678 local lib-version = $(version) ; 679 if <target-os>windows in $(requirements) 680 { 681 local major-minor = [ split-version $(version) ] ; 682 lib-version = $(major-minor:J="") ; 683 if <python-debugging>on in $(requirements) 684 { 685 lib-version = $(lib-version)_d ; 686 } 687 } 688 689 if ! $(lib-version) 690 { 691 ECHO *** "warning:" could not determine Python version, which will ; 692 ECHO *** "warning:" probably prevent us from linking with the python ; 693 ECHO *** "warning:" library. Consider explicitly passing the version ; 694 ECHO *** "warning:" to 'using python'. ; 695 } 696 697 # Declare it. 698 lib python.lib : : <name>python$(lib-version) $(requirements) ; 699} 700 701 702# Implementation of init. 703local rule configure ( version ? : cmd-or-prefix ? : includes * : libraries ? : 704 condition * : extension-suffix ? ) 705{ 706 local prefix ; 707 local exec-prefix ; 708 local cmds-to-try ; 709 local interpreter-cmd ; 710 711 local target-os = [ feature.get-values target-os : $(condition) ] ; 712 target-os ?= [ feature.defaults target-os ] ; 713 target-os = $(target-os:G=) ; 714 715 if $(target-os) = windows && <python-debugging>on in $(condition) 716 { 717 extension-suffix ?= _d ; 718 } 719 extension-suffix ?= "" ; 720 721 local cmds-to-try ; 722 723 if ! $(cmd-or-prefix) || [ GLOB $(cmd-or-prefix) : * ] 724 { 725 # If the user did not pass a command, whatever we got was a prefix. 726 prefix = $(cmd-or-prefix) ; 727 cmds-to-try = [ candidate-interpreters $(version) : $(prefix) : $(target-os) ] ; 728 } 729 else 730 { 731 # Work with the command the user gave us. 732 cmds-to-try = $(cmd-or-prefix) ; 733 734 # On Windows, do not nail down the interpreter command just yet in case 735 # the user specified something that turns out to be a cygwin symlink, 736 # which could bring down bjam if we invoke it. 737 if $(target-os) != windows 738 { 739 interpreter-cmd = $(cmd-or-prefix) ; 740 } 741 } 742 743 # Values to use in case we can not really find anything in the system. 744 local fallback-cmd = $(cmds-to-try[1]) ; 745 local fallback-version ; 746 747 # Anything left to find or check? 748 if ! ( $(interpreter-cmd) && $(version) && $(includes) && $(libraries) ) 749 { 750 # Values to be extracted from python's sys module. These will be set by 751 # the probe rule, above, using Jam's dynamic scoping. 752 local sys-elements = version platform prefix exec_prefix executable ; 753 local sys.$(sys-elements) ; 754 755 # Compute the string Python's sys.platform needs to match. If not 756 # targeting Windows or cygwin we will assume only native builds can 757 # possibly run, so we will not require a match and we leave sys.platform 758 # blank. 759 local platform ; 760 switch $(target-os) 761 { 762 case windows : platform = win32 ; 763 case cygwin : platform = cygwin ; 764 } 765 766 while $(cmds-to-try) 767 { 768 # Pop top command. 769 local cmd = $(cmds-to-try[1]) ; 770 cmds-to-try = $(cmds-to-try[2-]) ; 771 772 debug-message Checking interpreter command \"$(cmd)\"... ; 773 if [ probe $(cmd) ] 774 { 775 fallback-version ?= $(sys.version) ; 776 777 # Check for version/platform validity. 778 for local x in version platform 779 { 780 if $($(x)) && $($(x)) != $(sys.$(x)) 781 { 782 debug-message ...$(x) "mismatch (looking for" 783 $($(x)) but found $(sys.$(x))")" ; 784 cmd = ; 785 } 786 } 787 788 if $(cmd) 789 { 790 debug-message ...requested configuration matched! ; 791 792 exec-prefix = $(sys.exec_prefix) ; 793 794 compute-default-paths $(target-os) : $(sys.version) : 795 $(sys.prefix) : $(sys.exec_prefix) ; 796 797 version = $(sys.version) ; 798 interpreter-cmd ?= $(cmd) ; 799 cmds-to-try = ; # All done. 800 } 801 } 802 else 803 { 804 debug-message ...does not invoke a working interpreter ; 805 } 806 } 807 } 808 809 # Check whether configuration succeeded. 810 if ! ( $(includes) && $(libraries) ) 811 { 812 debug-message Python headers and libraries not found. ; 813 return ; 814 } 815 816 .configured = true ; 817 818 if ! $(interpreter-cmd) 819 { 820 fallback-cmd ?= python ; 821 debug-message No working Python interpreter found. ; 822 if [ os.name ] != NT || ! [ invokes-cygwin-symlink $(fallback-cmd) ] 823 { 824 interpreter-cmd = $(fallback-cmd) ; 825 debug-message falling back to \"$(interpreter-cmd)\" ; 826 } 827 } 828 829 includes = [ path-to-native $(includes) ] ; 830 libraries = [ path-to-native $(libraries) ] ; 831 832 debug-message "Details of this Python configuration:" ; 833 debug-message " interpreter command:" \"$(interpreter-cmd:E=<empty>)\" ; 834 debug-message " include path:" \"$(includes:E=<empty>)\" ; 835 debug-message " library path:" \"$(libraries:E=<empty>)\" ; 836 if $(target-os) = windows 837 { 838 debug-message " DLL search path:" \"$(exec-prefix:E=<empty>)\" ; 839 } 840 841 # 842 # Discover the presence of NumPy 843 # 844 debug-message "Checking for NumPy..." ; 845 local full-cmd = "import sys; sys.stderr = sys.stdout; import numpy; print(numpy.get_include())" ; 846 local full-cmd = $(interpreter-cmd)" -c \"$(full-cmd)\"" ; 847 debug-message "running command '$(full-cmd)'" ; 848 local result = [ SHELL $(full-cmd) : strip-eol : exit-status ] ; 849 if $(result[2]) = 0 850 { 851 .numpy = true ; 852 .numpy-include = $(result[1]) ; 853 debug-message "NumPy enabled" ; 854 } 855 else 856 { 857 debug-message "NumPy disabled. Reason:" ; 858 debug-message " $(full-cmd) aborted with " ; 859 debug-message " $(result[1])" ; 860 } 861 862 # 863 # End autoconfiguration sequence. 864 # 865 866 # Normalize and dissect any version number. 867 local major-minor ; 868 if $(version) 869 { 870 major-minor = [ split-version $(version) ] ; 871 version = $(major-minor:J=.) ; 872 } 873 874 875 local target-requirements = $(condition) ; 876 877 # Add the version, if any, to the target requirements. 878 if $(version) 879 { 880 if ! $(version) in [ feature.values python ] 881 { 882 feature.extend python : $(version) ; 883 py$(major-minor[1])-version ?= $(version) ; 884 if $(py$(major-minor[1])-version) < $(version) 885 { 886 py$(major-minor[1])-version = $(version) ; 887 } 888 } 889 target-requirements += <python>$(version:E=default) ; 890 } 891 892 target-requirements += <target-os>$(target-os) ; 893 894 # See if we can find a framework directory on darwin. 895 local framework-directory ; 896 if $(target-os) = darwin 897 { 898 # Search upward for the framework directory. 899 local framework-directory = $(libraries[-1]) ; 900 while $(framework-directory:D=) && $(framework-directory:D=) != Python.framework 901 { 902 framework-directory = $(framework-directory:D) ; 903 } 904 905 if $(framework-directory:D=) = Python.framework 906 { 907 debug-message framework directory is \"$(framework-directory)\" ; 908 } 909 else 910 { 911 debug-message "no framework directory found; using library path" ; 912 framework-directory = ; 913 } 914 } 915 916 local dll-path = $(libraries) ; 917 918 # Make sure that we can find the Python DLL on Windows. 919 if ( $(target-os) = windows ) && $(exec-prefix) 920 { 921 dll-path += $(exec-prefix) ; 922 } 923 924 # 925 # Prepare usage requirements. 926 # 927 local usage-requirements = [ system-library-dependencies $(target-os) ] ; 928 usage-requirements += <include>$(includes) <python.interpreter>$(interpreter-cmd) ; 929 if <python-debugging>on in $(condition) 930 { 931 if $(target-os) = windows 932 { 933 # In pyconfig.h, Py_DEBUG is set if _DEBUG is set. If we define 934 # Py_DEBUG we will get multiple definition warnings. 935 usage-requirements += <define>_DEBUG ; 936 } 937 else 938 { 939 usage-requirements += <define>Py_DEBUG ; 940 } 941 } 942 943 # In case we added duplicate requirements from what the user specified. 944 target-requirements = [ sequence.unique $(target-requirements) ] ; 945 946 # Global, but conditional, requirements to give access to the interpreter 947 # for general utilities, like other toolsets, that run Python scripts. 948 toolset.add-requirements 949 "$(target-requirements:J=,):<python.interpreter>$(interpreter-cmd)" ; 950 951 # Register the right suffix for extensions. 952 register-extension-suffix $(extension-suffix) : $(target-requirements) ; 953 954 # Make sure that the python feature is always considered 955 # relevant for any targets that depend on python. Without 956 # this, it would only be considered relevant when there are 957 # multiple configurations defined within the same build. 958 target-requirements += <relevant>python ; 959 960 # 961 # Declare the "python" target. This should really be called 962 # python_for_embedding. 963 # 964 965 if $(framework-directory) 966 { 967 alias python 968 : 969 : $(target-requirements) 970 : 971 : $(usage-requirements) <framework>$(framework-directory) 972 ; 973 } 974 else 975 { 976 declare-libpython-target $(version) : $(target-requirements) ; 977 978 # This is an evil hack. On, Windows, when Python is embedded, nothing 979 # seems to set up sys.path to include Python's standard library 980 # (http://article.gmane.org/gmane.comp.python.general/544986). The evil 981 # here, aside from the workaround necessitated by Python's bug, is that: 982 # 983 # a. we're guessing the location of the python standard library from the 984 # location of pythonXX.lib 985 # 986 # b. we're hijacking the <testing.launcher> property to get the 987 # environment variable set up, and the user may want to use it for 988 # something else (e.g. launch the debugger). 989 local set-PYTHONPATH ; 990 if $(target-os) = windows 991 { 992 set-PYTHONPATH = [ common.prepend-path-variable-command PYTHONPATH : 993 $(libraries:D)/Lib ] ; 994 } 995 996 alias python 997 : 998 : $(target-requirements) 999 : 1000 # Why python.lib must be listed here instead of along with the 1001 # system libs is a mystery, but if we do not do it, on cygwin, 1002 # -lpythonX.Y never appears in the command line (although it does on 1003 # linux). 1004 : $(usage-requirements) 1005 <testing.launcher>$(set-PYTHONPATH) 1006 <library-path>$(libraries) <dll-path>$(dll-path) <library>python.lib 1007 ; 1008 } 1009 1010 # On *nix, we do not want to link either Boost.Python or Python extensions 1011 # to libpython, because the Python interpreter itself provides all those 1012 # symbols. If we linked to libpython, we would get duplicate symbols. So 1013 # declare two targets -- one for building extensions and another for 1014 # embedding. 1015 if $(target-os) in windows cygwin 1016 { 1017 alias python_for_extensions : python : $(target-requirements) ; 1018 } 1019 else if $(target-os) = darwin { 1020 alias python_for_extensions 1021 : 1022 : $(target-requirements) 1023 : 1024 : $(usage-requirements) <linkflags>"-undefined dynamic_lookup" 1025 ; 1026 } 1027 # On AIX we need Python extensions and Boost.Python to import symbols from 1028 # the Python interpreter. Dynamic libraries opened with dlopen() do not 1029 # inherit the symbols from the Python interpreter. 1030 else if $(target-os) = aix 1031 { 1032 alias python_for_extensions 1033 : 1034 : $(target-requirements) 1035 : 1036 : $(usage-requirements) <linkflags>"-Wl,-bI:$(libraries[1])/python.exp" 1037 ; 1038 } 1039 else 1040 { 1041 alias python_for_extensions 1042 : 1043 : $(target-requirements) 1044 : 1045 : $(usage-requirements) 1046 ; 1047 } 1048 1049} 1050 1051# Conditional rule specification that will prevent building of a target 1052# if there is no matching python configuration available with the given 1053# required properties. 1054rule require-py ( properties * ) 1055{ 1056 local py-ext-target = [ $(.project).find python_for_extensions : no-error ] ; 1057 if ! $(py-ext-target) 1058 { 1059 return <build>no ; 1060 } 1061 local property-set = [ property-set.create $(properties) ] ; 1062 property-set = [ $(property-set).expand ] ; 1063 local py-ext-alternative = [ $(py-ext-target).select-alternatives $(property-set) ] ; 1064 if ! $(py-ext-alternative) 1065 { 1066 return <build>no ; 1067 } 1068} 1069 1070 1071rule configured ( ) 1072{ 1073 return $(.configured) ; 1074} 1075 1076rule numpy ( ) 1077{ 1078 return $(.numpy) ; 1079} 1080 1081rule numpy-include ( ) 1082{ 1083 return $(.numpy-include) ; 1084} 1085 1086 1087type.register PYTHON_EXTENSION : : SHARED_LIB ; 1088 1089 1090local rule register-extension-suffix ( root : condition * ) 1091{ 1092 local suffix ; 1093 1094 switch [ feature.get-values target-os : $(condition) ] 1095 { 1096 case windows : suffix = pyd ; 1097 case cygwin : suffix = dll ; 1098 case hpux : 1099 { 1100 if [ feature.get-values python : $(condition) ] in 1.5 1.6 2.0 2.1 2.2 2.3 2.4 1101 { 1102 suffix = sl ; 1103 } 1104 else 1105 { 1106 suffix = so ; 1107 } 1108 } 1109 case * : suffix = so ; 1110 } 1111 1112 type.set-generated-target-suffix PYTHON_EXTENSION : $(condition) : <$(root).$(suffix)> ; 1113} 1114 1115 1116# Unset 'lib' prefix for PYTHON_EXTENSION 1117type.set-generated-target-prefix PYTHON_EXTENSION : : "" ; 1118 1119 1120rule python-extension ( name : sources * : requirements * : default-build * : 1121 usage-requirements * ) 1122{ 1123 if [ configured ] 1124 { 1125 requirements += <use>/python//python_for_extensions ; 1126 } 1127 requirements += <suppress-import-lib>true ; 1128 1129 local project = [ project.current ] ; 1130 1131 targets.main-target-alternative 1132 [ new typed-target $(name) : $(project) : PYTHON_EXTENSION 1133 : [ targets.main-target-sources $(sources) : $(name) ] 1134 : [ targets.main-target-requirements $(requirements) : $(project) ] 1135 : [ targets.main-target-default-build $(default-build) : $(project) ] 1136 ] ; 1137} 1138 1139IMPORT python : python-extension : : python-extension ; 1140 1141# Support for testing. 1142type.register PY : py ; 1143type.register RUN_PYD_OUTPUT ; 1144type.register RUN_PYD : : TEST ; 1145 1146 1147class python-test-generator : generator 1148{ 1149 import set ; 1150 1151 rule __init__ ( * : * ) 1152 { 1153 generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; 1154 self.composing = true ; 1155 } 1156 1157 rule run ( project name ? : property-set : sources * : multiple ? ) 1158 { 1159 local pyversion = [ $(property-set).get <python> ] ; 1160 local python ; 1161 local other-pythons ; 1162 1163 for local s in $(sources) 1164 { 1165 if [ $(s).type ] = PY 1166 { 1167 if ! $(python) 1168 { 1169 # First Python source ends up on command line. 1170 python = $(s) ; 1171 1172 } 1173 else 1174 { 1175 # Other Python sources become dependencies. 1176 other-pythons += $(s) ; 1177 } 1178 } 1179 } 1180 1181 local extensions ; 1182 for local s in $(sources) 1183 { 1184 if [ $(s).type ] = PYTHON_EXTENSION 1185 { 1186 extensions += $(s) ; 1187 } 1188 } 1189 1190 local libs ; 1191 for local s in $(sources) 1192 { 1193 if [ type.is-derived [ $(s).type ] LIB ] 1194 && ! $(s) in $(extensions) 1195 { 1196 libs += $(s) ; 1197 } 1198 } 1199 1200 local new-sources ; 1201 for local s in $(sources) 1202 { 1203 if [ type.is-derived [ $(s).type ] CPP ] 1204 { 1205 local name = [ utility.basename [ $(s).name ] ] ; 1206 if $(name) = [ utility.basename [ $(python).name ] ] 1207 { 1208 name = $(name)_ext ; 1209 } 1210 local extension = [ generators.construct $(project) $(name) : 1211 PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ; 1212 1213 # The important part of usage requirements returned from 1214 # PYTHON_EXTENSION generator are xdll-path properties that will 1215 # allow us to find the python extension at runtime. 1216 property-set = [ $(property-set).add $(extension[1]) ] ; 1217 1218 # Ignore usage requirements. We're a top-level generator and 1219 # nobody is going to use what we generate. 1220 new-sources += $(extension[2-]) ; 1221 } 1222 } 1223 1224 property-set = [ $(property-set).add-raw <dependency>$(other-pythons) ] ; 1225 1226 return [ construct-result $(python) $(extensions) $(new-sources) : 1227 $(project) $(name) : $(property-set) ] ; 1228 } 1229} 1230 1231 1232generators.register 1233 [ new python-test-generator python.capture-output : : RUN_PYD_OUTPUT ] ; 1234 1235generators.register-standard testing.expect-success 1236 : RUN_PYD_OUTPUT : RUN_PYD ; 1237 1238 1239# There are two different ways of spelling OS names. One is used for [ os.name ] 1240# and the other is used for the <host-os> and <target-os> properties. Until that 1241# is remedied, this sets up a crude mapping from the latter to the former, that 1242# will work *for the purposes of cygwin/NT cross-builds only*. Could not think 1243# of a better name than "translate". 1244# 1245.translate-os-windows = NT ; 1246.translate-os-cygwin = CYGWIN ; 1247local rule translate-os ( src-os ) 1248{ 1249 local x = $(.translate-os-$(src-os)) [ os.name ] ; 1250 return $(x[1]) ; 1251} 1252 1253 1254# Extract the path to a single ".pyd" source. This is used to build the 1255# PYTHONPATH for running bpl tests. 1256# 1257local rule pyd-pythonpath ( source ) 1258{ 1259 return [ on $(source) return $(LOCATE) $(SEARCH) ] ; 1260} 1261 1262 1263# The flag settings on testing.capture-output do not apply to python.capture 1264# output at the moment. Redo this explicitly. 1265toolset.flags python.capture-output ARGS <testing.arg> ; 1266toolset.flags python.capture-output INPUT_FILES <testing.input-file> ; 1267 1268toolset.uses-features python.capture-output : 1269 <testing.launcher> <testing.execute> <dll-path> <xdll-path> <target-os> 1270 <pythonpath> ; 1271 1272rule capture-output ( target : sources * : properties * ) 1273{ 1274 # Setup up a proper DLL search path. Here, $(sources[1]) is a python module 1275 # and $(sources[2]) is a DLL. Only $(sources[1]) is passed to 1276 # testing.capture-output, so RUN_PATH variable on $(sources[2]) is not 1277 # consulted. Move it over explicitly. 1278 RUN_PATH on $(sources[1]) = [ on $(sources[2-]) return $(RUN_PATH) ] ; 1279 1280 PYTHONPATH = [ sequence.transform pyd-pythonpath : $(sources[2-]) ] ; 1281 PYTHONPATH += [ feature.get-values pythonpath : $(properties) ] ; 1282 1283 # After test is run, we remove the Python module, but not the Python script. 1284 testing.capture-output $(target) : $(sources[1]) : $(properties) ; 1285 1286 # PYTHONPATH is different; it will be interpreted by whichever Python is 1287 # invoked and so must follow path rules for the target os. The only OSes 1288 # where we can run python for other OSes currently are NT and CYGWIN so we 1289 # only need to handle those cases. 1290 local target-os = [ feature.get-values target-os : $(properties) ] ; 1291 # Oddly, host-os is not in properties, so grab the default value. 1292 local host-os = [ feature.defaults host-os ] ; 1293 host-os = $(host-os:G=) ; 1294 if $(target-os) != $(host-os) && $(target-os) in windows cygwin && $(host-os) in windows cygwin 1295 { 1296 PYTHONPATH = [ sequence.transform $(host-os)-to-$(target-os)-path : 1297 $(PYTHONPATH) ] ; 1298 } 1299 local path-separator = [ os.path-separator [ translate-os $(target-os) ] ] ; 1300 local set-PYTHONPATH = [ common.variable-setting-command PYTHONPATH : 1301 $(PYTHONPATH:E=:J=$(path-separator)) ] ; 1302 LAUNCHER on $(target) = $(set-PYTHONPATH) [ on $(target) return \"$(PYTHON)\" ] ; 1303} 1304 1305 1306rule bpl-test ( name : sources * : requirements * ) 1307{ 1308 local s ; 1309 sources ?= $(name).py $(name).cpp ; 1310 return [ testing.make-test run-pyd : $(sources) /boost/python//boost_python 1311 : $(requirements) : $(name) ] ; 1312} 1313 1314# The same as bpl-test but additionally require (and link to) boost_numpy. 1315# Masked whenever NumPy is not enabled. 1316rule numpy-test ( name : sources * : requirements * ) 1317{ 1318 numpy-include = [ python.numpy-include ] ; 1319 # yuk ! 1320 if ! $(.numpy) { requirements += <build>no ; } 1321 sources ?= $(name).py $(name).cpp ; 1322 name = [ regex.replace $(name) "[/]" "~" ] ; 1323 return [ testing.make-test run-pyd 1324 : $(sources) /boost/python//boost_numpy /boost/python//boost_python 1325 : $(requirements) <include>$(numpy-include) 1326 : $(name) ] ; 1327} 1328 1329rule py-version ( n ) 1330{ 1331 return $(py$(n)-version) ; 1332} 1333 1334IMPORT $(__name__) : bpl-test : : bpl-test ; 1335IMPORT $(__name__) : numpy-test : : numpy-test ; 1336IMPORT $(__name__) : py-version : : py-version ; 1337