From c3949bf5a1b04c07524730b23d8e247823095ffc Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 17 Apr 2009 07:42:34 +0000 Subject: [PATCH 001/198] Make toolchain check work if version is provided. [SVN r52437] --- v2/test/prebuilt/ext/jamfile2.jam | 4 ++-- v2/test/prebuilt/ext/jamfile3.jam | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/v2/test/prebuilt/ext/jamfile2.jam b/v2/test/prebuilt/ext/jamfile2.jam index 2030b6465..dd79f4ea2 100644 --- a/v2/test/prebuilt/ext/jamfile2.jam +++ b/v2/test/prebuilt/ext/jamfile2.jam @@ -5,7 +5,7 @@ local dll-suffix = so ; local prefix = "" ; if [ modules.peek : OS ] in CYGWIN NT { - if $toolset = gcc + if [ MATCH ^(gcc) : $toolset ] { dll-suffix = dll ; } @@ -18,7 +18,7 @@ else { prefix = "lib" ; } -if $toolset = darwin +if [ MATCH ^(darwin) : $toolset ] { dll-suffix = dylib ; } diff --git a/v2/test/prebuilt/ext/jamfile3.jam b/v2/test/prebuilt/ext/jamfile3.jam index 868761cb5..f172a89d0 100644 --- a/v2/test/prebuilt/ext/jamfile3.jam +++ b/v2/test/prebuilt/ext/jamfile3.jam @@ -9,7 +9,7 @@ local dll-suffix = so ; local prefix = "" ; if [ modules.peek : OS ] in CYGWIN NT { - if $toolset = gcc + if [ MATCH ^(gcc) : $toolset ] { dll-suffix = dll ; } @@ -22,7 +22,7 @@ else { prefix = "lib" ; } -if $toolset = darwin +if [ MATCH ^(darwin) : $toolset ] { dll-suffix = dylib ; } From 1ee245715a741e26bcfba1f3e430ff7786acadd2 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 22 Apr 2009 15:22:55 +0000 Subject: [PATCH 002/198] Don't crash when the absolute path to compiler cannot be found. [SVN r52541] --- v2/tools/msvc.jam | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index 372757e8e..52af14d62 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -729,7 +729,10 @@ local rule configure-really ( version ? : options * ) # MSVC 7.1 compiler even though it thinks it is using the msvc-9.0 # toolset version. command = [ common.get-absolute-tool-path $(command[-1]) ] ; - + } + + if $(command) + { local parent = [ path.make $(command) ] ; parent = [ path.parent $(parent) ] ; parent = [ path.native $(parent) ] ; From 1c702bfab997d38839219086f374877fee2ef757 Mon Sep 17 00:00:00 2001 From: "Troy D. Straszheim" Date: Thu, 23 Apr 2009 20:18:23 +0000 Subject: [PATCH 003/198] Yet another test commit, sorry about the noise, testing git mirror. [SVN r52575] --- CMake/doc/cmake.qbk | 1 + 1 file changed, 1 insertion(+) diff --git a/CMake/doc/cmake.qbk b/CMake/doc/cmake.qbk index 49230981d..c2205441a 100644 --- a/CMake/doc/cmake.qbk +++ b/CMake/doc/cmake.qbk @@ -686,4 +686,5 @@ boost_add_library(boost_libname ] [endsect] + [endsect] From d61f063e74c7390b7d7e5db3cbf9d9077b13a020 Mon Sep 17 00:00:00 2001 From: "Troy D. Straszheim" Date: Thu, 23 Apr 2009 20:49:50 +0000 Subject: [PATCH 004/198] Last test commit. [SVN r52576] --- CMake/doc/cmake.qbk | 1 + 1 file changed, 1 insertion(+) diff --git a/CMake/doc/cmake.qbk b/CMake/doc/cmake.qbk index c2205441a..01cc35bc9 100644 --- a/CMake/doc/cmake.qbk +++ b/CMake/doc/cmake.qbk @@ -687,4 +687,5 @@ boost_add_library(boost_libname [endsect] + [endsect] From 25e9baf862cd71032940c3a3216c42d0311480ad Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 27 Apr 2009 10:45:26 +0000 Subject: [PATCH 005/198] Drop Google.Analytics setup from offline copy. [SVN r52621] --- v2/index.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/v2/index.html b/v2/index.html index 160949ce2..e505175a2 100644 --- a/v2/index.html +++ b/v2/index.html @@ -160,12 +160,6 @@ div.sidebar p.rubric { -->

- - From b2710e88f450838da5eb390acd9832c4d0254e11 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 27 Apr 2009 10:46:42 +0000 Subject: [PATCH 006/198] When building releases, add Google.Analytics code to index.html. [SVN r52622] --- v2/roll.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/roll.sh b/v2/roll.sh index 035cc77a5..829c56eed 100755 --- a/v2/roll.sh +++ b/v2/roll.sh @@ -65,6 +65,6 @@ urchinTracker(); EOF` echo $x -perl -pi -e "s||$x|" `find doc -name '*.html'` +perl -pi -e "s||$x|" index.html `find doc -name '*.html'` scp -r doc example boost_build.png *.html hacking.txt vladimir_prus,boost@web.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2 scp ../userman.pdf vladimir_prus,boost@web.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2/doc From 61add925726541d1a825a7160c36f51cd63a5055 Mon Sep 17 00:00:00 2001 From: "Troy D. Straszheim" Date: Sat, 9 May 2009 22:57:30 +0000 Subject: [PATCH 007/198] Merge cmake files release -> trunk. [SVN r52866] --- CMake/BoostBuildSlave.cmake | 157 ---------- CMake/BoostCore.cmake | 281 ++++++++++-------- CMake/BoostTesting.cmake | 159 ++++------ CMake/LinkTest/CMakeLists.txt | 6 +- CMake/boost_build_slave.py.in | 57 ---- CMake/classify.py.in | 51 ---- CMake/finish.py.in | 23 -- CMake/info.py.in | 26 -- CMake/marshal.py.in | 110 ------- CMake/passthru.py.in | 73 ----- CMake/post.py.in | 56 ---- CMake/run_continuous_slave.py.in | 82 ----- CMake/sanity/CMakeLists.txt | 39 --- .../sanity/one_clean_one_warn/CMakeLists.txt | 7 - .../one_clean_one_warn/src/CMakeLists.txt | 5 - CMake/sanity/one_clean_one_warn/src/green.cpp | 2 - CMake/sanity/one_clean_one_warn/src/warn.cpp | 4 - .../one_clean_one_warn/test/CMakeLists.txt | 4 - .../one_clean_one_warn/test/times_out.cpp | 4 - CMake/sanity/wont_link/CMakeLists.txt | 7 - CMake/sanity/wont_link/src/CMakeLists.txt | 5 - .../sanity/wont_link/src/compiles_nowarn.cpp | 8 - CMake/sanity/wont_link/test/CMakeLists.txt | 2 - CMake/sanity/wont_link/test/link_error.cpp | 9 - CMake/start.py.in | 38 --- CMake/unix_kill.py.in | 6 - CMake/windows_kill.py.in | 9 - 27 files changed, 222 insertions(+), 1008 deletions(-) delete mode 100644 CMake/BoostBuildSlave.cmake delete mode 100755 CMake/boost_build_slave.py.in delete mode 100755 CMake/classify.py.in delete mode 100755 CMake/finish.py.in delete mode 100755 CMake/info.py.in delete mode 100755 CMake/marshal.py.in delete mode 100755 CMake/passthru.py.in delete mode 100755 CMake/post.py.in delete mode 100755 CMake/run_continuous_slave.py.in delete mode 100644 CMake/sanity/CMakeLists.txt delete mode 100644 CMake/sanity/one_clean_one_warn/CMakeLists.txt delete mode 100644 CMake/sanity/one_clean_one_warn/src/CMakeLists.txt delete mode 100644 CMake/sanity/one_clean_one_warn/src/green.cpp delete mode 100644 CMake/sanity/one_clean_one_warn/src/warn.cpp delete mode 100644 CMake/sanity/one_clean_one_warn/test/CMakeLists.txt delete mode 100644 CMake/sanity/one_clean_one_warn/test/times_out.cpp delete mode 100644 CMake/sanity/wont_link/CMakeLists.txt delete mode 100644 CMake/sanity/wont_link/src/CMakeLists.txt delete mode 100644 CMake/sanity/wont_link/src/compiles_nowarn.cpp delete mode 100644 CMake/sanity/wont_link/test/CMakeLists.txt delete mode 100644 CMake/sanity/wont_link/test/link_error.cpp delete mode 100755 CMake/start.py.in delete mode 100755 CMake/unix_kill.py.in delete mode 100755 CMake/windows_kill.py.in diff --git a/CMake/BoostBuildSlave.cmake b/CMake/BoostBuildSlave.cmake deleted file mode 100644 index 26f63496b..000000000 --- a/CMake/BoostBuildSlave.cmake +++ /dev/null @@ -1,157 +0,0 @@ -########################################################################## -# Boost Build Slave Support # -########################################################################## -# Copyright (C) 2008 Troy D. Straszheim # -# # -# 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 # -########################################################################## -option(BOOST_BUILD_SLAVE "Be a build slave, report build/testing" OFF) - -file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}" BOOST_BUILD_SLAVE_PYTHONPATH) - -if(BOOST_BUILD_SLAVE) - set(BOOST_BUILD_SLAVE_SUBMIT_URL "http://boost:boost@boost.resophonic.com/trac/login/xmlrpc" - CACHE STRING "URL to post regression testing results to.") - - file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}" BOOST_BUILD_SLAVE_PYTHONPATH) - - set(BOOST_BUILD_SLAVE_TIMEOUT 300 - CACHE STRING "Seconds until build slave times out any individual build step") - - set(BOOST_BUILD_SLAVE_DETAILS_FILE "slave-description.txt" - CACHE FILEPATH "Path to file, absolute or relative to build directory, containing descriptive text about the build (configuration peculiarities, etc) to be reported to the server") - - set(BOOST_BUILD_SLAVE_CONTACT_INFO "buildmeister@example.com" - CACHE STRING "Contact information regarding this build") - - set(BOOST_BUILD_SLAVE_HOSTNAME "" - CACHE STRING "If set, don't report what python determines to be the FQDN of this host, report this string instead.") - - set(BOOST_BUILD_SLAVE_SLEEP_DURATION "60" - CACHE STRING "Number of seconds to sleep between checks for updates from the repository.") - -endif(BOOST_BUILD_SLAVE) - -message(STATUS "Configuring test/compile drivers") - -if(CMAKE_VERBOSE_MAKEFILE) - set(BOOST_DRIVER_VERBOSE True) -else(CMAKE_VERBOSE_MAKEFILE) - set(BOOST_DRIVER_VERBOSE False) -endif(CMAKE_VERBOSE_MAKEFILE) - -# -# the programs that do the dirty work. -# -foreach(PYFILE boost_build_slave passthru marshal start finish info post classify) - configure_file(tools/build/CMake/${PYFILE}.py.in - ${BOOST_BUILD_SLAVE_PYTHONPATH}/${PYFILE}.py - @ONLY - ) -endforeach() - -if(WIN32) - configure_file(tools/build/CMake/windows_kill.py.in - ${BOOST_BUILD_SLAVE_PYTHONPATH}/kill_subprocess.py - COPYONLY - ) -else(WIN32) - configure_file(tools/build/CMake/unix_kill.py.in - ${BOOST_BUILD_SLAVE_PYTHONPATH}/kill_subprocess.py - COPYONLY - ) -endif(WIN32) - - -# -# the test driver is either marshal or passthru depending on whether -# you're in build slave mode or not. The compilation/link rules -# aren't modified if you're not in slave mode, BUUUT the tests still -# need a driver script that knows whether to expect failure or not -# and 'flips' the return status accordingly: thus passthru.py. -# -if(BOOST_BUILD_SLAVE) - file(TO_NATIVE_PATH ${BOOST_BUILD_SLAVE_PYTHONPATH}/marshal.py BOOST_TEST_DRIVER) - - configure_file(tools/build/CMake/run_continuous_slave.py.in - ${CMAKE_BINARY_DIR}/run_continuous_slave.py - @ONLY - ) - -else(BOOST_BUILD_SLAVE) - file(TO_NATIVE_PATH ${BOOST_BUILD_SLAVE_PYTHONPATH}/passthru.py BOOST_TEST_DRIVER) -endif(BOOST_BUILD_SLAVE) - -if(BOOST_BUILD_SLAVE) - # - # Redirect various build steps - # - - set(CMAKE_CXX_COMPILE_OBJECT - "\"${PYTHON_EXECUTABLE}\" \"${BOOST_TEST_DRIVER}\" cxx_compile_object ${CMAKE_CXX_COMPILE_OBJECT}" ) - - set(CMAKE_CXX_CREATE_SHARED_LIBRARY - "\"${PYTHON_EXECUTABLE}\" \"${BOOST_TEST_DRIVER}\" create_shared_library ${CMAKE_CXX_CREATE_SHARED_LIBRARY}") - - set(CMAKE_CXX_CREATE_STATIC_LIBRARY - "\"${PYTHON_EXECUTABLE}\" \"${BOOST_TEST_DRIVER}\" create_static_library ${CMAKE_CXX_CREATE_STATIC_LIBRARY}") - - set(CMAKE_CXX_LINK_EXECUTABLE - "\"${PYTHON_EXECUTABLE}\" \"${BOOST_TEST_DRIVER}\" link_executable ${CMAKE_CXX_LINK_EXECUTABLE}") - - # - # Custom targets for talking to the server via xmlrpc - # - - - # - # Get us a new build id from the server - # - file(TO_NATIVE_PATH ${BOOST_BUILD_SLAVE_PYTHONPATH}/start.py - BOOST_BUILD_SLAVE_START_PY) - add_custom_target(slave-start - COMMAND ${PYTHON_EXECUTABLE} ${BOOST_BUILD_SLAVE_START_PY} - COMMENT "Slave starting build" - ) - - # - # Tell server we're done... it'll update finish time in the db. - # - file(TO_NATIVE_PATH ${BOOST_BUILD_SLAVE_PYTHONPATH}/finish.py - BOOST_BUILD_SLAVE_FINISH_PY) - add_custom_target(slave-finish - COMMAND ${PYTHON_EXECUTABLE} ${BOOST_BUILD_SLAVE_FINISH_PY} - COMMENT "Slave finishing build" - ) - # - - # Local only: show what we report to server (our platform description, toolset, etc) - # - file(TO_NATIVE_PATH ${BOOST_BUILD_SLAVE_PYTHONPATH}/info.py - BOOST_BUILD_SLAVE_INFO_PY) - add_custom_target(slave-info - COMMAND ${PYTHON_EXECUTABLE} ${BOOST_BUILD_SLAVE_INFO_PY} - COMMENT "Print slave info" - ) -endif(BOOST_BUILD_SLAVE) - -# -# Used over in BoostTesting and BoostCore to attach xmlrpc submission rules -# to various intermediate build targets (libraries, test suites) -# - -file(TO_NATIVE_PATH ${BOOST_BUILD_SLAVE_PYTHONPATH}/post.py - BOOST_BUILD_SLAVE_POST_PY) -macro(boost_post_results PROJECT_NAME_ PARENT_TARGET BUILD_OR_TEST LOGDIR) - if(BOOST_BUILD_SLAVE) - add_custom_command(TARGET ${PARENT_TARGET} - POST_BUILD - COMMAND ${PYTHON_EXECUTABLE} ${BOOST_BUILD_SLAVE_POST_PY} ${PROJECT_NAME_} ${PARENT_TARGET} ${BUILD_OR_TEST} ${LOGDIR} - COMMENT "Submitting results for '${BUILD_OR_TEST}' of ${PARENT_TARGET} in ${PROJECT_NAME_}" - ) - set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${LOGDIR}/Log.marshal) - add_dependencies(test ${PARENT_TARGET}) - endif(BOOST_BUILD_SLAVE) -endmacro(boost_post_results PARENT_TARGET) diff --git a/CMake/BoostCore.cmake b/CMake/BoostCore.cmake index cfcb1f437..e89dacf0a 100644 --- a/CMake/BoostCore.cmake +++ b/CMake/BoostCore.cmake @@ -313,23 +313,24 @@ macro(boost_library_project LIBNAME) if(BUILD_TESTING AND THIS_PROJECT_TESTDIRS) # Testing is enabled globally and this project has some - # tests. So, include the tests - add_custom_target(${PROJECT_NAME}-test) - - add_dependencies(test ${PROJECT_NAME}-test) - - # the last argument here, the binary directory that the - # logs are in, has to match the binary directory - # passed to 'add_subdirectory', in the foreach() just below - boost_post_results(${PROJECT_NAME} ${PROJECT_NAME}-test - test - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-test - ) + # tests. Check whether we should include these tests. + if (BOOST_TEST_LIBRARIES) + set(SAVED_TESTDIRS ${THIS_PROJECT_TESTDIRS}) + set(THIS_PROJECT_TESTDIRS) + foreach (TESTLIB ${BOOST_TEST_LIBRARIES}) + if (${TESTLIB} STREQUAL ${libname}) + # We are allowed to test this library; restore the set of + # test directories for this library. + set(THIS_PROJECT_TESTDIRS ${SAVED_TESTDIRS}) + endif() + endforeach () + endif() + # Include the test directories. foreach(SUBDIR ${THIS_PROJECT_TESTDIRS}) - add_subdirectory(${SUBDIR} ${PROJECT_NAME}-test) + add_subdirectory(${SUBDIR}) endforeach() - endif(BUILD_TESTING AND THIS_PROJECT_TESTDIRS) + endif() if (BUILD_DOCUMENTATION AND THIS_PROJECT_DOCDIRS) foreach(SUBDIR ${THIS_PROJECT_DOCDIRS}) @@ -647,7 +648,6 @@ macro(boost_library_variant LIBNAME) boost_feature_interactions("THIS_VARIANT" ${ARGN}) if (THIS_VARIANT_OKAY) - # Determine the suffix for this library target boost_library_variant_target_name(${ARGN}) set(VARIANT_LIBNAME "${LIBNAME}${VARIANT_TARGET_NAME}") @@ -683,6 +683,7 @@ macro(boost_library_variant LIBNAME) CLEAN_DIRECT_OUTPUT 1 COMPILE_FLAGS "${THIS_VARIANT_COMPILE_FLAGS}" LINK_FLAGS "${THIS_VARIANT_LINK_FLAGS}" + LABELS "${PROJECT_NAME}" ) elseif (THIS_LIB_MODULE) # Add a module @@ -691,10 +692,12 @@ macro(boost_library_variant LIBNAME) # Set properties on this library set_target_properties(${VARIANT_LIBNAME} PROPERTIES - OUTPUT_NAME "${LIBNAME}${VARIANT_VERSIONED_NAME}" + OUTPUT_NAME ${LIBNAME} CLEAN_DIRECT_OUTPUT 1 COMPILE_FLAGS "${THIS_VARIANT_COMPILE_FLAGS}" LINK_FLAGS "${THIS_VARIANT_LINK_FLAGS}" + LABELS "${PROJECT_NAME}" + PREFIX "" # SOVERSION "${BOOST_VERSION}" ) else (THIS_LIB_IS_STATIC) @@ -708,6 +711,7 @@ macro(boost_library_variant LIBNAME) CLEAN_DIRECT_OUTPUT 1 COMPILE_FLAGS "${THIS_VARIANT_COMPILE_FLAGS}" LINK_FLAGS "${THIS_VARIANT_LINK_FLAGS}" + LABELS "${PROJECT_NAME}" # SOVERSION "${BOOST_VERSION}" ) endif (THIS_LIB_IS_STATIC) @@ -715,8 +719,6 @@ macro(boost_library_variant LIBNAME) # The basic LIBNAME target depends on each of the variants add_dependencies(${LIBNAME} ${VARIANT_LIBNAME}) - boost_post_results(${PROJECT_NAME} ${VARIANT_LIBNAME} build ${CMAKE_CURRENT_BINARY_DIR}) - # Link against whatever libraries this library depends on target_link_libraries(${VARIANT_LIBNAME} ${THIS_VARIANT_LINK_LIBS}) foreach(dependency ${THIS_LIB_DEPENDS}) @@ -764,7 +766,7 @@ macro(boost_library_variant LIBNAME) DEPENDS ${THIS_LIB_COMPONENT_DEPENDS}) endif () endif(NOT THIS_LIB_NO_INSTALL) - endif (THIS_VARIANT_OKAY) + endif () endmacro(boost_library_variant) # Updates the set of default build variants to account for variations @@ -816,13 +818,13 @@ endmacro(boost_library_variant) # # BOOST_ADDLIB_OPTION_NAMES: # Like BOOST_ADD_ARG_NAMES, this variable describes -# feature-specific options to boost_library that can be used to +# feature-specific options to boost_add_library that can be used to # turn off building of the library when the variant would require # certain features. For example, the NO_SINGLE_THREADED option # turns off building of single-threaded variants for a library. # # BOOST_ADDEXE_OPTION_NAMES: -# Like BOOST_ADDLIB_OPTION_NAMES, execept that that variable +# Like BOOST_ADDLIB_OPTION_NAMES, except that that variable # describes options to boost_add_executable that can be used to # describe which features are needed to build the executable. # For example, the MULTI_THREADED option requires that the @@ -911,6 +913,116 @@ macro(boost_add_extra_variant) list(APPEND BOOST_FEATURES ${BOOST_EXTVAR_FEATURES}) endmacro(boost_add_extra_variant) +# Compute the variant that will be used to build this executable or +# module, taking into account both the requested features passed to +# boost_add_executable or boost_add_library and what options the user +# has set. +macro(boost_select_variant NAME PREFIX) + set(${PREFIX}_DEBUG_AND_RELEASE FALSE) + set(SELECT_VARIANT_OKAY TRUE) + set(${PREFIX}_VARIANT) + + foreach(FEATURESET_STR ${BOOST_FEATURES}) + string(REPLACE ":" ";" FEATURESET ${FEATURESET_STR}) + separate_arguments(FEATURESET) + set(${PREFIX}_REQUESTED_FROM_SET FALSE) + foreach (FEATURE ${FEATURESET}) + if (${PREFIX}_${FEATURE}) + # Make this feature part of the variant + list(APPEND ${PREFIX}_VARIANT ${FEATURE}) + set(${PREFIX}_REQUESTED_FROM_SET TRUE) + + # The caller has requested this particular feature be used + # when building the executable or module. If we can't satisfy + # that request (because the user has turned off the build + # variants with that feature), then we won't build this + # executable or module. + if (NOT BUILD_${FEATURE}) + set(SELECT_VARIANT_OKAY FALSE) + message(STATUS "* ${NAME} is NOT being built because BUILD_${FEATURE} is FALSE") + endif (NOT BUILD_${FEATURE}) + endif (${PREFIX}_${FEATURE}) + endforeach (FEATURE ${FEATURESET}) + + if (NOT ${PREFIX}_REQUESTED_FROM_SET) + # The caller did not specify which feature value to use from + # this set, so find the first feature value that actually works. + set(${PREFIX}_FOUND_FEATURE FALSE) + + # If this feature set decides between Release and Debug, we + # either query CMAKE_BUILD_TYPE to determine which to use (for + # makefile targets) or handle both variants separately (for IDE + # targets). We only build both variants separately for executable targets. + if (FEATURESET_STR STREQUAL "RELEASE:DEBUG") + if (CMAKE_CONFIGURATION_TYPES) + # IDE target: can we build both debug and release? + if (BUILD_DEBUG AND BUILD_RELEASE) + if (${PREFIX} STREQUAL "THIS_EXE") + # Remember that we're capable of building both configurations + set(${PREFIX}_DEBUG_AND_RELEASE TRUE) + + # Don't add RELEASE or DEBUG to the variant (yet) + set(${PREFIX}_FOUND_FEATURE TRUE) + endif () + endif () + else (CMAKE_CONFIGURATION_TYPES) + # Makefile target: CMAKE_BUILD_TYPE tells us which variant to build + if (CMAKE_BUILD_TYPE STREQUAL "Release") + # Okay, build the release variant + list(APPEND ${PREFIX}_VARIANT RELEASE) + set(${PREFIX}_FOUND_FEATURE TRUE) + elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") + # Okay, build the debug variant + list(APPEND ${PREFIX}_VARIANT DEBUG) + set(${PREFIX}_FOUND_FEATURE TRUE) + endif (CMAKE_BUILD_TYPE STREQUAL "Release") + endif (CMAKE_CONFIGURATION_TYPES) + endif (FEATURESET_STR STREQUAL "RELEASE:DEBUG") + + # Search through all of the features in the set to find one that works + foreach (FEATURE ${FEATURESET}) + # We only care about the first feature value we find... + if (NOT ${PREFIX}_FOUND_FEATURE) + # Are we allowed to build this feature? + if (BUILD_${FEATURE}) + # Found it: we're done + list(APPEND ${PREFIX}_VARIANT ${FEATURE}) + set(${PREFIX}_FOUND_FEATURE TRUE) + endif (BUILD_${FEATURE}) + endif (NOT ${PREFIX}_FOUND_FEATURE) + endforeach (FEATURE ${FEATURESET}) + + if (NOT ${PREFIX}_FOUND_FEATURE) + # All of the features in this set were turned off. + # Just don't build anything. + set(SELECT_VARIANT_OKAY FALSE) + endif (NOT ${PREFIX}_FOUND_FEATURE) + endif (NOT ${PREFIX}_REQUESTED_FROM_SET) + endforeach(FEATURESET_STR ${BOOST_FEATURES}) + + # Propagate flags from each of the features + if (SELECT_VARIANT_OKAY) + foreach (FEATURE ${${PREFIX}_VARIANT}) + # Add all of the flags for this feature + set(${PREFIX}_COMPILE_FLAGS + "${${PREFIX}_COMPILE_FLAGS} ${${PREFIX}_${FEATURE}_COMPILE_FLAGS} ${${FEATURE}_COMPILE_FLAGS}") + set(${PREFIX}_LINK_FLAGS + "${${PREFIX}_LINK_FLAGS} ${${PREFIX}_${FEATURE}_LINK_FLAGS} ${${FEATURE}_LINK_FLAGS}") + if (${PREFIX} STREQUAL "THIS_EXE") + set(${PREFIX}_LINK_FLAGS + "${${PREFIX}_LINK_FLAGS} ${${FEATURE}_EXE_LINK_FLAGS}") + endif() + set(${PREFIX}_LINK_LIBS + ${${PREFIX}_LINK_LIBS} ${${PREFIX}_${FEATURE}_LINK_LIBS} ${${FEATURE}_LINK_LIBS}) + endforeach (FEATURE ${${PREFIX}_VARIANT}) + + # Handle feature interactions + boost_feature_interactions("${PREFIX}" ${${PREFIX}_VARIANT}) + else () + set(${PREFIX}_VARIANT) + endif () +endmacro(boost_select_variant) + # Creates a new Boost library target that generates a compiled library # (.a, .lib, .dll, .so, etc) from source files. This routine will # actually build several different variants of the same library, with @@ -1087,10 +1199,32 @@ macro(boost_add_library LIBNAME) if (THIS_LIB_FORCE_VARIANTS) set(BUILD_${THIS_LIB_FORCE_VARIANTS} ${BUILD_${THIS_LIB_FORCE_VARIANTS}_PREV} ) # message(STATUS "* ^^ BUILD_${THIS_LIB_FORCE_VARIANTS} ${BUILD_${THIS_LIB_FORCE_VARIANTS}}") - endif (THIS_LIB_FORCE_VARIANTS) - + endif (THIS_LIB_FORCE_VARIANTS) endmacro(boost_add_library) +# Like boost_add_library, but builds a single library variant +# FIXME: I'm not sure if I like this or not. Document it if it survives. +macro(boost_add_single_library LIBNAME) + parse_arguments(THIS_LIB + "DEPENDS;COMPILE_FLAGS;LINK_FLAGS;LINK_LIBS;${BOOST_ADD_ARG_NAMES}" + "NO_INSTALL;MODULE;${BOOST_ADDEXE_OPTION_NAMES}" + ${ARGN} + ) + set(THIS_LIB_SOURCES ${THIS_LIB_DEFAULT_ARGS}) + + string(TOUPPER "${LIBNAME}_COMPILED_LIB" compiled_lib) + set (${compiled_lib} TRUE CACHE INTERNAL "") + + if (NOT TEST_INSTALLED_TREE) + boost_select_variant(${LIBNAME} THIS_LIB) + if (THIS_LIB_VARIANT) + add_custom_target(${LIBNAME}) + separate_arguments(THIS_LIB_VARIANT) + boost_library_variant(${LIBNAME} ${THIS_LIB_VARIANT}) + endif () + endif (NOT TEST_INSTALLED_TREE) +endmacro(boost_add_single_library) + # Creates a new executable from source files. # # boost_add_executable(exename @@ -1201,101 +1335,13 @@ macro(boost_add_executable EXENAME) # Compute the variant that will be used to build this executable, # taking into account both the requested features passed to # boost_add_executable and what options the user has set. - set(THIS_EXE_OKAY TRUE) - set(THIS_EXE_VARIANT) + boost_select_variant(${EXENAME} THIS_EXE) - foreach(FEATURESET_STR ${BOOST_FEATURES}) - string(REPLACE ":" ";" FEATURESET ${FEATURESET_STR}) - separate_arguments(FEATURESET) - set(THIS_EXE_REQUESTED_FROM_SET FALSE) - foreach (FEATURE ${FEATURESET}) - if (THIS_EXE_${FEATURE}) - # Make this feature part of the variant - list(APPEND THIS_EXE_VARIANT ${FEATURE}) - set(THIS_EXE_REQUESTED_FROM_SET TRUE) + set(THIS_EXE_OKAY FALSE) + if (THIS_EXE_VARIANT) + # It's okay to build this executable + set(THIS_EXE_OKAY TRUE) - # The caller has requested this particular feature be used - # when building the executable. If we can't satisfy that - # request (because the user has turned off the build variants - # with that feature), then we won't build this executable. - if (NOT BUILD_${FEATURE}) - set(THIS_EXE_OKAY FALSE) - message(STATUS "* ${EXENAME} is NOT being built because BUILD_${FEATURE} is FALSE") - endif (NOT BUILD_${FEATURE}) - endif (THIS_EXE_${FEATURE}) - endforeach (FEATURE ${FEATURESET}) - - if (NOT THIS_EXE_REQUESTED_FROM_SET) - # The caller did not specify which feature value to use from - # this set, so find the first feature value that actually works. - set(THIS_EXE_FOUND_FEATURE FALSE) - - # If this feature set decides between Release and Debug, we - # either query CMAKE_BUILD_TYPE to determine which to use (for - # makefile targets) or handle both variants separately (for IDE - # targets). - if (FEATURESET_STR STREQUAL "RELEASE:DEBUG") - if (CMAKE_CONFIGURATION_TYPES) - # IDE target: can we build both debug and release? - if (BUILD_DEBUG AND BUILD_RELEASE) - # Remember that we're capable of building both configurations - set(THIS_EXE_DEBUG_AND_RELEASE TRUE) - - # Don't add RELEASE or DEBUG to the variant (yet) - set(THIS_EXE_FOUND_FEATURE TRUE) - endif (BUILD_DEBUG AND BUILD_RELEASE) - else (CMAKE_CONFIGURATION_TYPES) - # Makefile target: CMAKE_BUILD_TYPE tells us which variant to build - if (CMAKE_BUILD_TYPE STREQUAL "Release") - # Okay, build the release variant - list(APPEND THIS_EXE_VARIANT RELEASE) - set(THIS_EXE_FOUND_FEATURE TRUE) - elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") - # Okay, build the debug variant - list(APPEND THIS_EXE_VARIANT DEBUG) - set(THIS_EXE_FOUND_FEATURE TRUE) - endif (CMAKE_BUILD_TYPE STREQUAL "Release") - endif (CMAKE_CONFIGURATION_TYPES) - endif (FEATURESET_STR STREQUAL "RELEASE:DEBUG") - - # Search through all of the features in the set to find one that works - foreach (FEATURE ${FEATURESET}) - # We only care about the first feature value we find... - if (NOT THIS_EXE_FOUND_FEATURE) - # Are we allowed to build this feature? - if (BUILD_${FEATURE}) - # Found it: we're done - list(APPEND THIS_EXE_VARIANT ${FEATURE}) - set(THIS_EXE_FOUND_FEATURE TRUE) - endif (BUILD_${FEATURE}) - endif (NOT THIS_EXE_FOUND_FEATURE) - endforeach (FEATURE ${FEATURESET}) - - if (NOT THIS_EXE_FOUND_FEATURE) - # All of the features in this set were turned off. - # Just don't build anything. - set(THIS_EXE_OKAY FALSE) - endif (NOT THIS_EXE_FOUND_FEATURE) - endif (NOT THIS_EXE_REQUESTED_FROM_SET) - endforeach(FEATURESET_STR ${BOOST_FEATURES}) - - # Propagate flags from each of the features - if (THIS_EXE_OKAY) - foreach (FEATURE ${THIS_EXE_VARIANT}) - # Add all of the flags for this feature - set(THIS_EXE_COMPILE_FLAGS - "${THIS_EXE_COMPILE_FLAGS} ${THIS_EXE_${FEATURE}_COMPILE_FLAGS} ${${FEATURE}_COMPILE_FLAGS}") - set(THIS_EXE_LINK_FLAGS - "${THIS_EXE_LINK_FLAGS} ${THIS_EXE_${FEATURE}_LINK_FLAGS} ${${FEATURE}_LINK_FLAGS} ${${FEATURE}_EXE_LINK_FLAGS}") - set(THIS_EXE_LINK_LIBS - ${THIS_EXE_LINK_LIBS} ${THIS_EXE_${FEATURE}_LINK_LIBS} ${${FEATURE}_LINK_LIBS}) - endforeach (FEATURE ${THIS_EXE_VARIANT}) - - # Handle feature interactions - boost_feature_interactions("THIS_EXE" ${THIS_EXE_VARIANT}) - endif (THIS_EXE_OKAY) - - if (THIS_EXE_OKAY) # Compute the name of the variant targets that we'll be linking # against. We'll use this to link against the appropriate # dependencies. For IDE targets where we can build both debug and @@ -1353,6 +1399,7 @@ macro(boost_add_executable EXENAME) PROPERTIES COMPILE_FLAGS "${THIS_EXE_COMPILE_FLAGS}" LINK_FLAGS "${THIS_EXE_LINK_FLAGS}" + LABELS "${PROJECT_NAME}" ) # For IDE generators where we can build both debug and release @@ -1399,5 +1446,5 @@ macro(boost_add_executable EXENAME) if (NOT THIS_EXE_NO_INSTALL) install(TARGETS ${THIS_EXE_NAME} DESTINATION bin) endif (NOT THIS_EXE_NO_INSTALL) - endif (THIS_EXE_OKAY) + endif () endmacro(boost_add_executable) diff --git a/CMake/BoostTesting.cmake b/CMake/BoostTesting.cmake index 73a42e4be..886ed4704 100644 --- a/CMake/BoostTesting.cmake +++ b/CMake/BoostTesting.cmake @@ -40,26 +40,18 @@ option(BUILD_TESTING "Enable testing" OFF) if (BUILD_TESTING) - add_custom_target(test COMMENT "Running all tests") + enable_testing() option(TEST_INSTALLED_TREE "Enable testing of an already-installed tree" OFF) + set(BOOST_TEST_LIBRARIES "" + CACHE STRING "Semicolon-separated list of Boost libraries to test") + if (TEST_INSTALLED_TREE) include("${CMAKE_INSTALL_PREFIX}/lib/Boost${BOOST_VERSION}/boost-targets.cmake") endif (TEST_INSTALLED_TREE) endif (BUILD_TESTING) -option(BOOST_BUILD_SANITY_TEST - "Don't build regular boost libraries, build libraries that test the boost cmake build system itself" OFF) - -if(BOOST_BUILD_SANITY_TEST) - set(BOOST_LIBS_DIR ${CMAKE_SOURCE_DIR}/tools/build/CMake/sanity) - configure_file(${CMAKE_SOURCE_DIR}/libs/CMakeLists.txt ${BOOST_LIBS_DIR}/CMakeLists.txt COPYONLY) -else(BOOST_BUILD_SANITY_TEST) - set(BOOST_LIBS_DIR ${CMAKE_SOURCE_DIR}/libs) -endif(BOOST_BUILD_SANITY_TEST) - - #------------------------------------------------------------------------------- # This macro adds additional include directories based on the dependencies of # the library being tested 'libname' and all of its dependencies. @@ -164,9 +156,6 @@ endmacro(boost_additional_test_dependencies libname) # source files, BOOST_TEST_SOURCES will contain those; otherwise, # BOOST_TEST_SOURCES will only contain "testname.cpp". # -# BOOST_TEST_TAG: compile, compile_fail, run, or run_fail. -# Used in test-reporting systems. -# # BOOST_TEST_TESTNAME: A (hopefully) globally unique target name # for the test, constructed from PROJECT-testname-TAG # @@ -211,20 +200,7 @@ macro(boost_test_parse_args testname) set(BOOST_TEST_SOURCES "${testname}.cpp") endif (BOOST_TEST_DEFAULT_ARGS) - #message("Sources: ${BOOST_TEST_SOURCES}") - if (BOOST_TEST_RUN) - set(BOOST_TEST_TAG "run") - elseif(BOOST_TEST_COMPILE) - set(BOOST_TEST_TAG "compile") - elseif(BOOST_TEST_LINK) - set(BOOST_TEST_TAG "link") - endif(BOOST_TEST_RUN) - - if (BOOST_TEST_FAIL) - set(BOOST_TEST_TAG ${BOOST_TEST_TAG}-fail) - endif(BOOST_TEST_FAIL) - - set(BOOST_TEST_TESTNAME "${PROJECT_NAME}-${testname}-${BOOST_TEST_TAG}") + set(BOOST_TEST_TESTNAME "${PROJECT_NAME}-${testname}") #message("testname: ${BOOST_TEST_TESTNAME}") # If testing is turned off, this test is not okay if (NOT BUILD_TESTING) @@ -295,33 +271,20 @@ macro(boost_test_run testname) ${BOOST_TEST_EXTRA_OPTIONS}) if (THIS_EXE_OKAY) - # This target builds and runs the test - add_custom_target(${BOOST_TEST_TESTNAME}) + get_target_property(THIS_TEST_OUTPUT_DIRECTORY ${testname} + RUNTIME_OUTPUT_DIRECTORY) + add_test (${BOOST_TEST_TESTNAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/tests/${PROJECT_NAME}/${testname} + ${BOOST_TEST_ARGS}) - file( TO_NATIVE_PATH "${BOOST_TEST_DRIVER}" NATIVE_BOOST_TEST_DRIVER ) - - set(THIS_TEST_PREFIX_ARGS - ${PYTHON_EXECUTABLE} ${NATIVE_BOOST_TEST_DRIVER} - ${CMAKE_CURRENT_BINARY_DIR} ${BOOST_TEST_TAG} ${testname} - ) - - add_custom_command(TARGET ${BOOST_TEST_TESTNAME} - POST_BUILD - COMMAND - ${THIS_TEST_PREFIX_ARGS} - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/tests/${PROJECT_NAME}/${testname} - ${BOOST_TEST_ARGS} - COMMENT "${PROJECT_NAME} => Running '${testname}'" - ) - - add_dependencies(${BOOST_TEST_TESTNAME} - ${PROJECT_NAME}-${testname} - ) - - add_dependencies(${PROJECT_NAME}-test - ${BOOST_TEST_TESTNAME} - ) + set_tests_properties(${BOOST_TEST_TESTNAME} + PROPERTIES + LABELS "${PROJECT_NAME}" + ) + if (BOOST_TEST_FAIL) + set_tests_properties(${BOOST_TEST_TESTNAME} PROPERTIES WILL_FAIL ON) + endif () endif(THIS_EXE_OKAY) endif (BOOST_TEST_OKAY) endmacro(boost_test_run) @@ -335,7 +298,6 @@ macro(boost_test_run_fail testname) boost_test_run(${testname} ${ARGN} FAIL) endmacro(boost_test_run_fail) - # This macro creates a Boost regression test that will be compiled, # but not linked or executed. If the test can be compiled with no # failures, the test passes. @@ -368,34 +330,31 @@ macro(boost_test_compile testname) get_directory_property(BOOST_TEST_INCLUDE_DIRS INCLUDE_DIRECTORIES) set(BOOST_TEST_INCLUDES "") foreach(DIR ${BOOST_TEST_INCLUDE_DIRS}) - set(BOOST_TEST_INCLUDES "${BOOST_TEST_INCLUDES};-I${DIR}") + set(BOOST_TEST_INCLUDES "${BOOST_TEST_INCLUDES};${DIR}") endforeach(DIR ${BOOST_TEST_INCLUDE_DIRS}) - set(THIS_TEST_PREFIX_ARGS - ${PYTHON_EXECUTABLE} ${BOOST_TEST_DRIVER} ${CMAKE_CURRENT_BINARY_DIR} ${BOOST_TEST_TAG} ${testname} - ) - - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${BOOST_TEST_TESTNAME}.${CMAKE_CXX_OUTPUT_EXTENSION} - COMMAND - ${THIS_TEST_PREFIX_ARGS} - ${CMAKE_CXX_COMPILER} - ${BOOST_TEST_COMPILE_FLAGS} - ${BOOST_TEST_INCLUDES} - -c ${BOOST_TEST_SOURCES} - -o ${CMAKE_CURRENT_BINARY_DIR}/${BOOST_TEST_TESTNAME}${CMAKE_CXX_OUTPUT_EXTENSION} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${BOOST_TEST_SOURCES} - COMMENT "${PROJECT_NAME} => Running Compile ${test_pass} Test '${BOOST_TEST_SOURCES}'" + add_test(${BOOST_TEST_TESTNAME} + ${CMAKE_CTEST_COMMAND} + --build-and-test + ${Boost_SOURCE_DIR}/tools/build/CMake/CompileTest + ${Boost_BINARY_DIR}/tools/build/CMake/CompileTest + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-project CompileTest + --build-options + "-DSOURCE:STRING=${CMAKE_CURRENT_SOURCE_DIR}/${BOOST_TEST_SOURCES}" + "-DINCLUDES:STRING=${BOOST_TEST_INCLUDES}" + "-DCOMPILE_FLAGS:STRING=${BOOST_TEST_COMPILE_FLAGS}" ) - add_custom_target(${BOOST_TEST_TESTNAME} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${BOOST_TEST_TESTNAME}.${CMAKE_CXX_OUTPUT_EXTENSION} - ) - - add_dependencies(${PROJECT_NAME}-test - ${BOOST_TEST_TESTNAME} + set_tests_properties(${BOOST_TEST_TESTNAME} + PROPERTIES + LABELS "${PROJECT_NAME}" ) + if (BOOST_TEST_FAIL) + set_tests_properties(${BOOST_TEST_TESTNAME} PROPERTIES WILL_FAIL ON) + endif () endif(BOOST_TEST_OKAY) endmacro(boost_test_compile) @@ -418,37 +377,27 @@ endmacro(boost_test_compile_fail) macro(boost_test_link testname) boost_test_parse_args(${testname} ${ARGN} LINK) if(BOOST_TEST_OKAY) - - set(THIS_TEST_PREFIX_ARGS - ${PYTHON_EXECUTABLE} ${BOOST_TEST_DRIVER} ${CMAKE_CURRENT_BINARY_DIR} test_link ${testname} - ) - - # - # FIXME: no ctest. - # - add_custom_target(TARGET ${BOOST_TEST_TESTNAME} - COMMAND /link/tests/are/failing/at/the/moment - COMMENT "${PROJECT_NAME} => Link test '${testname}' is failing." + add_test(${BOOST_TEST_TESTNAME} + ${CMAKE_CTEST_COMMAND} + --build-and-test + ${Boost_SOURCE_DIR}/tools/build/CMake/LinkTest + ${Boost_BINARY_DIR}/tools/build/CMake/LinkTest + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-project LinkTest + "-DSOURCE:STRING=${CMAKE_CURRENT_SOURCE_DIR}/${BOOST_TEST_SOURCES}" + "-DINCLUDES:STRING=${BOOST_TEST_INCLUDES}" + "-DCOMPILE_FLAGS:STRING=${BOOST_TEST_COMPILE_FLAGS}" ) - # POST_BUILD - # COMMAND - # ${THIS_TEST_PREFIX_ARGS} - # ${CMAKE_CTEST_COMMAND} - # --build-and-test - # ${Boost_SOURCE_DIR}/tools/build/CMake/LinkTest - # ${Boost_BINARY_DIR}/tools/build/CMake/LinkTest - # --build-generator \\"${CMAKE_GENERATOR}\\" - # --build-makeprogram \\"${MAKEPROGRAM}\\" - # --build-project LinkTest - # --build-options -DSOURCE=${CMAKE_CURRENT_SOURCE_DIR}/${BOOST_TEST_SOURCES} -DINCLUDES=${Boost_SOURCE_DIR} -DCOMPILE_FLAGS=\\"${BOOST_TEST_COMPILE_FLAGS}\\" - # COMMENT "Running ${testname} (link) in project ${PROJECT_NAME}" - # ) - - add_dependencies(${PROJECT_NAME}-test - ${BOOST_TEST_TESTNAME} + set_tests_properties(${BOOST_TEST_TESTNAME} + PROPERTIES + LABELS "${PROJECT_NAME}" ) - + + if (BOOST_TEST_FAIL) + set_tests_properties(${BOOST_TEST_TESTNAME} PROPERTIES WILL_FAIL ON) + endif () endif(BOOST_TEST_OKAY) endmacro(boost_test_link) diff --git a/CMake/LinkTest/CMakeLists.txt b/CMake/LinkTest/CMakeLists.txt index 35de4479b..1f25bb353 100644 --- a/CMake/LinkTest/CMakeLists.txt +++ b/CMake/LinkTest/CMakeLists.txt @@ -1,5 +1,7 @@ -cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR) +cmake_minimum_required(VERSION 2.6) project(LinkTest) include_directories (${INCLUDES}) -add_executable(link ${SOURCE} COMPILE_FLAGS "${COMPILE_FLAGS}") +add_executable(link-test ${SOURCE} COMPILE_FLAGS "${COMPILE_FLAGS}") +set_source_files_properties(${SOURCE} + PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") \ No newline at end of file diff --git a/CMake/boost_build_slave.py.in b/CMake/boost_build_slave.py.in deleted file mode 100755 index 9d80ed872..000000000 --- a/CMake/boost_build_slave.py.in +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python -# -# copyright (C) 2008 troy d. straszheim -# -# 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 -# - -# -# Utilities, variables, imports for build slave python -# - -import os, os.path, marshal, xmlrpclib, pysvn, socket, platform -from pprint import pprint - -repo_path = "@CMAKE_SOURCE_DIR@" -client = pysvn.Client() -svn_entry = client.info(repo_path) -_configured_hostname = "@BOOST_BUILD_SLAVE_HOSTNAME@" -fqdn = _configured_hostname if len(_configured_hostname) > 0 else socket.getfqdn() -uname = platform.uname() -toolset = "@BOOST_TOOLSET@" - -timeout_seconds = @BOOST_BUILD_SLAVE_TIMEOUT@ -slave_details_file = "@BOOST_BUILD_SLAVE_DETAILS_FILE@" -contact_info = "@BOOST_BUILD_SLAVE_CONTACT_INFO@" - -xmlrpc_url = "@BOOST_BUILD_SLAVE_SUBMIT_URL@" - -build_id_file = os.path.join(r'@BOOST_BUILD_SLAVE_PYTHONPATH@', "build_id.txt") - -try: - f = open(build_id_file) - build_id = int(f.read()) -except: - build_id = None - -def set_build_id(build_id): - print "Setting new build id %d locally" % build_id - f = open(build_id_file, "w") - f.write(str(build_id)) - f.close() - -def details(): - if os.path.isabs(slave_details_file): - thefile = slave_details_file - else: - thefile = os.path.join("@CMAKE_BINARY_DIR@", slave_details_file) - - if os.path.exists(thefile): - f = open(thefile) - txt = f.read() - else: - txt = "Build slave details file @BOOST_BUILD_SLAVE_DETAILS_FILE@ not found." - - return txt diff --git a/CMake/classify.py.in b/CMake/classify.py.in deleted file mode 100755 index 212cc8ba6..000000000 --- a/CMake/classify.py.in +++ /dev/null @@ -1,51 +0,0 @@ -# -# Classifies pass/fail/warn for the sake of traash -# - -toolset = '@BOOST_TOOLSET@' -import os - -# 'cxx_compile_object' -# 'run' -# 'link_executable' -# 'create_shared_library' -# 'create_static_library' -# 'compile-fail' - -def classify(step): - print "step=", step - if 'errno' in step: - if step['errno'] == 666: - step['status'] = 'timeout' - else: - step['status'] = 'not_executed' - return - - if step['returncode'] != 0 and not step['expect_fail']: - step['status'] = 'fail' - return - - if step['returncode'] == 0 and step['expect_fail']: - step['status'] = 'unexpected_pass' - return - - # - # if it is an expected failure, don't warn just cause of warnings. - # - if step['returncode'] != 0 and step['expect_fail']: - step['status'] = 'pass' - return - - - if step['op'] != 'run' and len(step['stderr']) != 0 and not step['stderr'].isspace(): - step['status'] = 'warn' - return - - # on windoze, warnings are to be found in stdout... but the compiler always - # prints the name of the file first. So warn if there is more than one line - # in stdout. For now. - if os.name == 'nt' and step['op'] == 'cxx_compile_object' and step['stdout'].count('\n') > 1: - step['status'] = 'warn' - return - - step['status'] = 'pass' diff --git a/CMake/finish.py.in b/CMake/finish.py.in deleted file mode 100755 index 9952949b9..000000000 --- a/CMake/finish.py.in +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python -# -# copyright (C) 2008 troy d. straszheim -# -# 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 -# - -# -# Start a new build, notify server via xmlrpc -# - -import sys -sys.path.append("@BOOST_BUILD_SLAVE_PYTHONPATH@") -from boost_build_slave import * - -print '\nFinishing build %d with %s via XML-RPC' % (build_id, xmlrpc_url) -s = xmlrpclib.Server(xmlrpc_url) - -s.traash.finish_build(build_id) - - diff --git a/CMake/info.py.in b/CMake/info.py.in deleted file mode 100755 index 0694cf6c0..000000000 --- a/CMake/info.py.in +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python -# -# copyright (C) 2008 troy d. straszheim -# -# 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 -# - -# -# Start a new build, notify server via xmlrpc -# - -import sys -sys.path.append("@BOOST_BUILD_SLAVE_PYTHONPATH@") -from boost_build_slave import * - -print ' Url:', svn_entry.url, "at rev", svn_entry.revision.number -print ' FQDN:', fqdn -print ' Uname:', uname -print ' Toolset:', toolset -print ' Build ID:', build_id -print ' Contact:', contact_info -pref = '\n ' -print ' Details: ' + pref.join(details().splitlines()), '\n\n' - diff --git a/CMake/marshal.py.in b/CMake/marshal.py.in deleted file mode 100755 index b59a77c3b..000000000 --- a/CMake/marshal.py.in +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/python -# -# copyright (C) 2008 troy d. straszheim -# -# 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 -# -# -# Compiler driver. Takes a few arguments describing what is to be done -# (used to mark up the output) and executes compiler in a subshell, checking -# for errors and marshalling output to disk. -# -import sys, signal, threading, subprocess -sys.path.append("@BOOST_BUILD_SLAVE_PYTHONPATH@") -from boost_build_slave import * - -import datetime, time, signal -from subprocess import Popen, PIPE -from kill_subprocess import kill_subprocess -from classify import classify - -log = os.path.join(sys.argv[1], "Log.marshal") -op = sys.argv[2] -target = sys.argv[3] -argv = sys.argv[4:] -expect_fail = op.endswith("fail") - -if os.name == 'nt': - os.environ['PATH'] = r'@CMAKE_LIBRARY_OUTPUT_DIRECTORY@;' + os.environ['PATH'] - -print "***\n*** Executing op:" + op + "\n*** " + str(argv) + "\n*** log=" + log + "\n***" -# -# execute subprocess, watch for timeout -# -class SubprocThread(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.ex = None - - def run(self): - try: - self.proc = Popen(argv, stdout=PIPE, stderr=PIPE) - (self.stdout, self.stderr) = self.proc.communicate() - except EnvironmentError, e: - self.ex = e - -t = SubprocThread() -starttime = datetime.datetime.now() -t.start() -t.join(timeout_seconds) - -if t.isAlive(): - print "*** Killing subprocess after timeout" - kill_subprocess(t.proc.pid) - e = OSError() - e.errno = 666 - e.message = e.strerror = "TIMEOUT AFTER %d SECONDS" % timeout_seconds - e.filename = argv[0] - t.ex = e - -duration = datetime.datetime.now() - starttime - -# -# Collect and store subprocess info -# -result = { 'expect_fail' : expect_fail, - 'wallclock_duration' : duration.seconds + duration.microseconds * 10**-6 } - -if not t.ex: - result['returncode'] = t.proc.returncode - result['stdout'] = t.stdout - result['stderr'] = t.stderr - - if t.proc.returncode != 0 and not expect_fail: - print "*** returncode: %d" % t.proc.returncode - print "*** stdout:" + result['stdout'] - print "*** stderr:" + result['stderr'] -else: - result['errno'] = t.ex.errno - result['filename'] = t.ex.filename - result['message'] = t.ex.message - result['strerror'] = t.ex.strerror - print "Errno:" + str(t.ex.errno) + ": " + t.ex.strerror - -result.update({'op' : op, - 'target' : target, - 'cmdline' : argv }) - -classify(result) -print "post classification: result=", result - -f = open(log, "ab", 0) -marshal.dump(result, f) -f.close() - -if t.ex: - sys.exit(t.ex.errno) -else: - if expect_fail: - if t.proc.returncode != 0: - sys.exit(0) - else: - print "UNEXPECTED SUCCESS" - sys.exit(1) # we need an exit status for 'unexpected success' - else: - sys.exit(t.proc.returncode) - - - diff --git a/CMake/passthru.py.in b/CMake/passthru.py.in deleted file mode 100755 index dd60ec353..000000000 --- a/CMake/passthru.py.in +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/python -# -# copyright (C) 2008 troy d. straszheim -# -# 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 -# - -# -# "Passthru" driver, only responsible for 'flipping' exit status of -# tests that are expected to fail. See driver.py for the version -# that is run when BOOST_BUILD_SLAVE is on, which does xmlizaton -# and the like -# -import sys, os, os.path -from subprocess import Popen, PIPE - -def verbose(what): - if @BOOST_DRIVER_VERBOSE@: - print what - -# ignored -# log = os.path.join(sys.argv[1], "Log.xml") -op = sys.argv[2] -# target = sys.argv[3] -argv = sys.argv[4:] -expect_fail = op.endswith("fail") - -# -# execute subprocess -# -subproc = None -returncode = None -ex = None -stdout = None -stderr = None -try: - subproc = Popen(argv, stdout=PIPE, stderr=PIPE) - (stdout, stderr) = subproc.communicate() -except EnvironmentError, e: - ex = e - -returncode = subproc.returncode - -if stdout: - print stdout -if stderr: - print stderr - -if not ex: - # possibly flip the return code - if not expect_fail: - if not returncode: - verbose("ok.") - else: - verbose("error.") - sys.exit(returncode) - else: - if returncode != 0: - verbose("ok.") - sys.exit(0) - else: - verbose("*** UNEXPECTED SUCCESS ***") - sys.exit(1) # we need an exit status for 'unexpected success' -else: - # if there is an os error 'above' the actual exit status of the subprocess, - # use the errno - print "Error in build system: " + str(ex.strerror) - sys.exit(ex.errno) - - - diff --git a/CMake/post.py.in b/CMake/post.py.in deleted file mode 100755 index 4f44294b0..000000000 --- a/CMake/post.py.in +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python -# -# copyright (C) 2008 troy d. straszheim -# -# 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 -# - -# -# Send the build log via XML-RPC -# - -import sys -sys.path.append("@BOOST_BUILD_SLAVE_PYTHONPATH@") -from boost_build_slave import * - -s = xmlrpclib.Server(xmlrpc_url, allow_none=True) - -project_name = sys.argv[1] -parent_target = sys.argv[2] -build_or_test = sys.argv[3] -logdir = sys.argv[4] - -# print "\n>>>\n>>> Project " + project_name \ -# + "\n>>> POST build log for " + parent_target \ -# + "\n>>> from log dir" + logdir \ -# + "\n>>> to " + xmlrpc_url \ -# + "\n>>> Server build ID: %d" % build_id \ -# + "\n>>>" - -p = os.path.join(logdir, "Log.marshal") - -if not os.path.exists(p): - print "No results to submit" - sys.exit(0) - -f = open(p, "rb") - -i = 0 -while True: - try: - r = marshal.load(f) - r.update({ 'build_id' : build_id, - 'project' : project_name, - 'parent_target' : parent_target, - 'build_or_test' : build_or_test }) - s.traash.step(r) - i += 1 - except EOFError, e: - break - -print "Submitted ", i, " steps." -f.close() -os.remove(p) - diff --git a/CMake/run_continuous_slave.py.in b/CMake/run_continuous_slave.py.in deleted file mode 100755 index 8c8dd34d0..000000000 --- a/CMake/run_continuous_slave.py.in +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python -# -# Build slave script. -# - -import pysvn, os, time -from optparse import OptionParser - -parser = OptionParser() - -parser.add_option("-b", "--build-first", - action="store_true", dest="build_first", default=False, - help="Build on startup") - -parser.add_option("-c", "--clean-first", - action="store_true", dest="clean_first", default=False, - help="Clean on startup") - -parser.add_option("-C", "--clean-every-time", - action="store_true", dest="clean_every_time", default=False, - help="Clean before every build") - -parser.add_option("-k", "--keep-rebuilding", - action="store_true", dest="keep_rebuilding", default=False, - help="Rebuild even if there are no updates to svn") - -(options, args) = parser.parse_args() - - -client = pysvn.Client() - -wc_path = r'@CMAKE_SOURCE_DIR@' - -def do_build(clean_): - if clean_: - clean = 'clean' - else: - clean = '' - if os.name == 'nt': - cmd = 'nmake /I ' + clean + ' slave-start test slave-finish' - else: - cmd = 'make -i ' + clean + ' slave-start test slave-finish' - print "Starting build:\n>>> ", cmd - os.system(cmd) - - -if options.build_first: - do_build(options.clean_first) - -while True: - try: - svn_entry = client.info(wc_path) - - print "Wc has url %s rev %d.\nChecking for updates." \ - % (svn_entry.url, svn_entry.revision.number) - - ds = client.diff_summarize(url_or_path1=svn_entry.url, - revision1=pysvn.Revision(pysvn.opt_revision_kind.number, - svn_entry.revision.number), - url_or_path2=svn_entry.url, - revision2=pysvn.Revision(pysvn.opt_revision_kind.head) - ) - - if len(ds): - print "There are %d changesets:" % len(ds) - for j in ds: - print ">>>", j.path - print "Updating." - client.update(wc_path) - if len(ds) or options.keep_rebuilding: - do_build(options.clean_every_time) - else: - print "No updates." - except Exception, e: - print e - print "Error. Will retry." - - print "Sleeping %d seconds" % @BOOST_BUILD_SLAVE_SLEEP_DURATION@ - time.sleep(@BOOST_BUILD_SLAVE_SLEEP_DURATION@) - - - diff --git a/CMake/sanity/CMakeLists.txt b/CMake/sanity/CMakeLists.txt deleted file mode 100644 index 8f68a7c41..000000000 --- a/CMake/sanity/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# Find each subdirectory containing a CMakeLists.txt file, and include -# it. This avoids the need to manually list which libraries in Boost -# have CMakeLists.txt files. - -# return a list of directories that we should add_subdirectory() -macro(BOOST_COLLECT_SUBPROJECT_DIRECTORY_NAMES varname filename) - file(GLOB BOOST_LIBRARY_CMAKE_FILES - RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*/${filename}") - foreach(BOOST_LIB_CMAKE_FILE ${BOOST_LIBRARY_CMAKE_FILES}) - get_filename_component(BOOST_LIB_DIR ${BOOST_LIB_CMAKE_FILE} PATH) - set(${varname} ${${varname}} ${BOOST_LIB_DIR}) - endforeach(BOOST_LIB_CMAKE_FILE ${BOOST_LIBRARY_CMAKE_FILES}) -endmacro(BOOST_COLLECT_SUBPROJECT_DIRECTORY_NAMES varname) - -macro(ADD_SUBDIRECTORIES prefix) - foreach(subdir ${ARGN}) - MESSAGE(STATUS "${prefix}${subdir}") - add_subdirectory(${subdir}) - endforeach(subdir ${ARGN}) -endmacro(ADD_SUBDIRECTORIES prefix) - -# Find all of the subdirectories with .cmake files in them. These are -# the libraries with dependencies. -boost_collect_subproject_directory_names(BOOST_MODULE_DIRS "module.cmake") - -foreach(subdir ${BOOST_MODULE_DIRS}) - include("${CMAKE_CURRENT_SOURCE_DIR}/${subdir}/module.cmake") -endforeach(subdir) - -# Find all of the subdirectories with CMakeLists.txt files in -# them. This contains all of the Boost libraries. -boost_collect_subproject_directory_names(BOOST_SUBPROJECT_DIRS "CMakeLists.txt") - -# Add all of the Boost projects in reverse topological order, so that -# a library's dependencies show up before the library itself. -set(CPACK_INSTALL_CMAKE_COMPONENTS_ALL) -list(SORT BOOST_SUBPROJECT_DIRS) -topological_sort(BOOST_SUBPROJECT_DIRS BOOST_ _DEPENDS) -add_subdirectories(" + " ${BOOST_SUBPROJECT_DIRS}) diff --git a/CMake/sanity/one_clean_one_warn/CMakeLists.txt b/CMake/sanity/one_clean_one_warn/CMakeLists.txt deleted file mode 100644 index 72886c1fb..000000000 --- a/CMake/sanity/one_clean_one_warn/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -boost_library_project( - one_clean_one_warn - SRCDIRS src - TESTDIRS test -) - - diff --git a/CMake/sanity/one_clean_one_warn/src/CMakeLists.txt b/CMake/sanity/one_clean_one_warn/src/CMakeLists.txt deleted file mode 100644 index 4253be666..000000000 --- a/CMake/sanity/one_clean_one_warn/src/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -boost_add_library( - one_clean_one_warn - green.cpp warn.cpp - ) - diff --git a/CMake/sanity/one_clean_one_warn/src/green.cpp b/CMake/sanity/one_clean_one_warn/src/green.cpp deleted file mode 100644 index 0c269c1ed..000000000 --- a/CMake/sanity/one_clean_one_warn/src/green.cpp +++ /dev/null @@ -1,2 +0,0 @@ -static void foo() { } - diff --git a/CMake/sanity/one_clean_one_warn/src/warn.cpp b/CMake/sanity/one_clean_one_warn/src/warn.cpp deleted file mode 100644 index 2c73cc0a3..000000000 --- a/CMake/sanity/one_clean_one_warn/src/warn.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#warning this one has a warning - -static void bar() { } - diff --git a/CMake/sanity/one_clean_one_warn/test/CMakeLists.txt b/CMake/sanity/one_clean_one_warn/test/CMakeLists.txt deleted file mode 100644 index fccf9e427..000000000 --- a/CMake/sanity/one_clean_one_warn/test/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -boost_test_run(times_out) -boost_test_compile(this_passes times_out.cpp) -boost_test_compile_fail(this_unexpectedly_passes times_out.cpp) - diff --git a/CMake/sanity/one_clean_one_warn/test/times_out.cpp b/CMake/sanity/one_clean_one_warn/test/times_out.cpp deleted file mode 100644 index 0507ac4e6..000000000 --- a/CMake/sanity/one_clean_one_warn/test/times_out.cpp +++ /dev/null @@ -1,4 +0,0 @@ -int main(int, char**) -{ - while(true) ; -} diff --git a/CMake/sanity/wont_link/CMakeLists.txt b/CMake/sanity/wont_link/CMakeLists.txt deleted file mode 100644 index bf0fb1392..000000000 --- a/CMake/sanity/wont_link/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -boost_library_project( - WontLinkProject - SRCDIRS src - TESTDIRS test -) - - diff --git a/CMake/sanity/wont_link/src/CMakeLists.txt b/CMake/sanity/wont_link/src/CMakeLists.txt deleted file mode 100644 index f65af80df..000000000 --- a/CMake/sanity/wont_link/src/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -boost_add_library( - wont_link - compiles_nowarn.cpp - ) - diff --git a/CMake/sanity/wont_link/src/compiles_nowarn.cpp b/CMake/sanity/wont_link/src/compiles_nowarn.cpp deleted file mode 100644 index 2ff6e10b1..000000000 --- a/CMake/sanity/wont_link/src/compiles_nowarn.cpp +++ /dev/null @@ -1,8 +0,0 @@ -int this_function_is_declared_but_not_defined(); - -void provoke_link_error() -{ - this_function_is_declared_but_not_defined(); -} - - diff --git a/CMake/sanity/wont_link/test/CMakeLists.txt b/CMake/sanity/wont_link/test/CMakeLists.txt deleted file mode 100644 index a0e95a024..000000000 --- a/CMake/sanity/wont_link/test/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -boost_test_run(link_error DEPENDS wont_link) - diff --git a/CMake/sanity/wont_link/test/link_error.cpp b/CMake/sanity/wont_link/test/link_error.cpp deleted file mode 100644 index fe75aff1d..000000000 --- a/CMake/sanity/wont_link/test/link_error.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -void provoke_link_error(); - -int main(int, char**) -{ - provoke_link_error(); - return 0; -} diff --git a/CMake/start.py.in b/CMake/start.py.in deleted file mode 100755 index b068608fd..000000000 --- a/CMake/start.py.in +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/python -# -# copyright (C) 2008 troy d. straszheim -# -# 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 -# - -# -# Start a new build, notify server via xmlrpc -# - -import sys -sys.path.append("@BOOST_BUILD_SLAVE_PYTHONPATH@") -from boost_build_slave import * - -print 'Url:', svn_entry.url, "at rev", svn_entry.revision.number -print 'FQDN:', fqdn -print 'Uname:', uname -print 'Toolset:', toolset - -print '\nNotifying %s of new build via XML-RPC' % xmlrpc_url -s = xmlrpclib.Server(xmlrpc_url) - -build_id = s.traash.start_build({ 'svn_url' : svn_entry.url, - 'svn_rev' : svn_entry.revision.number, - 'sysname' : uname[0], - 'nodename' : uname[1], - 'sys_release' : uname[2], - 'sys_version' : uname[3], - 'sys_machine' : uname[4], - 'fqdn' : fqdn, - 'toolset' : toolset, - 'contact' : contact_info, - 'details' : details() }) - -set_build_id(build_id) diff --git a/CMake/unix_kill.py.in b/CMake/unix_kill.py.in deleted file mode 100755 index 166b32a7a..000000000 --- a/CMake/unix_kill.py.in +++ /dev/null @@ -1,6 +0,0 @@ -import os, signal -def kill_subprocess(pid): - os.kill(pid, signal.SIGKILL) - os.waitpid(-1, os.WNOHANG) - - diff --git a/CMake/windows_kill.py.in b/CMake/windows_kill.py.in deleted file mode 100755 index b96aad32e..000000000 --- a/CMake/windows_kill.py.in +++ /dev/null @@ -1,9 +0,0 @@ -import os -def kill_subprocess(pid): - cmd = 'TASKKILL /PID ' + str(pid) + ' /T /F' - print "Timeout, killing subprocess:\n" + cmd - os.popen(cmd) - -import win32api, win32con -win32api.SetErrorMode( win32con.SEM_NOGPFAULTERRORBOX | win32con.SEM_FAILCRITICALERRORS ) - From 52d4bf2c8735a6ebbc2f9e96cf954481c882c392 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 16 May 2009 10:24:16 +0000 Subject: [PATCH 008/198] Fix the prototype of stlport.init to match docs. Fixes #2976. [SVN r53042] --- v2/tools/stlport.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/stlport.jam b/v2/tools/stlport.jam index d6d2dc6bc..62eebda5f 100644 --- a/v2/tools/stlport.jam +++ b/v2/tools/stlport.jam @@ -279,7 +279,7 @@ local .version-subfeature-defined ; # Initialize stlport support. rule init ( version ? : - headers # Location of header files + headers : # Location of header files libraries * # Location of libraries, lib and bin subdirs of STLport. ) { From b545b0257e0a7a860d8cfcd06f3fb26139e8bc7b Mon Sep 17 00:00:00 2001 From: "Troy D. Straszheim" Date: Thu, 21 May 2009 14:05:15 +0000 Subject: [PATCH 009/198] Merge of cmakefiles *from* (that's right, *FROM* :/ ) release branch. [SVN r53150] --- CMake/BoostConfig.cmake | 68 ++++++++++++++++++-- CMake/BoostCore.cmake | 77 ++++++++++------------ CMake/BoostDocs.cmake | 117 +++++++++++++++------------------- CMake/BoostTesting.cmake | 72 +++++++++++++++++---- CMake/BoostUtils.cmake | 14 ++++ CMake/LinkTest/CMakeLists.txt | 17 ++++- 6 files changed, 238 insertions(+), 127 deletions(-) diff --git a/CMake/BoostConfig.cmake b/CMake/BoostConfig.cmake index fd0dd1ff9..1b2aef80e 100644 --- a/CMake/BoostConfig.cmake +++ b/CMake/BoostConfig.cmake @@ -31,29 +31,42 @@ include(CheckCXXSourceCompiles) # Python interpreter # include(FindPythonInterp) -message(STATUS "found python executable ${PYTHON_EXECUTABLE}") include(FindPythonLibs) -message(STATUS "found python includes ${PYTHON_INCLUDE_PATH}") -message(STATUS "found python libs ${PYTHON_LIBRARIES}") # Toolset detection. if (NOT BOOST_TOOLSET) set(BOOST_TOOLSET "unknown") if (MSVC60) set(BOOST_TOOLSET "vc6") + set(BOOST_COMPILER "msvc") + set(BOOST_COMPILER_VERSION "6.0") elseif(MSVC70) set(BOOST_TOOLSET "vc7") + set(BOOST_COMPILER "msvc") + set(BOOST_COMPILER_VERSION "7.0") elseif(MSVC71) set(BOOST_TOOLSET "vc71") + set(BOOST_COMPILER "msvc") + set(BOOST_COMPILER_VERSION "7.1") elseif(MSVC80) set(BOOST_TOOLSET "vc80") + set(BOOST_COMPILER "msvc") + set(BOOST_COMPILER_VERSION "8.0") elseif(MSVC90) set(BOOST_TOOLSET "vc90") + set(BOOST_COMPILER "msvc") + set(BOOST_COMPILER_VERSION "9.0") elseif(MSVC) set(BOOST_TOOLSET "vc") + set(BOOST_COMPILER "msvc") + set(BOOST_COMPILER_VERSION "unknown") elseif(BORLAND) set(BOOST_TOOLSET "bcb") + set(BOOST_COMPILER "msvc") + set(BOOST_COMPILER_VERSION "unknown") elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + set(BOOST_COMPILER "gcc") + # Execute GCC with the -dumpversion option, to give us a version string execute_process( COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" @@ -62,6 +75,10 @@ if (NOT BOOST_TOOLSET) # Match only the major and minor versions of the version string string(REGEX MATCH "[0-9]+.[0-9]+" GCC_MAJOR_MINOR_VERSION_STRING "${GCC_VERSION_STRING}") + + # Match the full compiler version for the build name + string(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" BOOST_COMPILER_VERSION + "${GCC_VERSION_STRING}") # Strip out the period between the major and minor versions string(REGEX REPLACE "\\." "" BOOST_VERSIONING_GCC_VERSION @@ -69,33 +86,71 @@ if (NOT BOOST_TOOLSET) # Set the GCC versioning toolset set(BOOST_TOOLSET "gcc${BOOST_VERSIONING_GCC_VERSION}") + elseif(CMAKE_CXX_COMPILER MATCHES "/icpc$" OR CMAKE_CXX_COMPILER MATCHES "/icpc.exe$") + set(BOOST_TOOLSET "intel") + set(BOOST_COMPILER "intel") + set(CMAKE_COMPILER_IS_INTEL ON) + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" + OUTPUT_VARIABLE INTEL_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(BOOST_COMPILER_VERSION ${INTEL_VERSION_STRING}) endif(MSVC60) endif (NOT BOOST_TOOLSET) +message(STATUS "Boost compiler: ${BOOST_COMPILER}") +message(STATUS "Boost toolset: ${BOOST_TOOLSET}") + +# create cache entry +set(BOOST_PLATFORM "unknown" CACHE STRING "Boost platform name") + # Multi-threading support if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(MULTI_THREADED_COMPILE_FLAGS "-pthreads") set(MULTI_THREADED_LINK_LIBS rt) + set(BOOST_PLATFORM "sunos") elseif(CMAKE_SYSTEM_NAME STREQUAL "BeOS") # No threading options necessary for BeOS + set(BOOST_PLATFORM "beos") elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSD") set(MULTI_THREADED_COMPILE_FLAGS "-pthread") set(MULTI_THREADED_LINK_LIBS pthread) + set(BOOST_PLATFORM "bsd") elseif(CMAKE_SYSTEM_NAME STREQUAL "DragonFly") - # DragonFly is FreeBSD bariant + # DragonFly is a FreeBSD bariant set(MULTI_THREADED_COMPILE_FLAGS "-pthread") + set(BOOST_PLATFORM "dragonfly") elseif(CMAKE_SYSTEM_NAME STREQUAL "IRIX") # TODO: GCC on Irix doesn't support multi-threading? + set(BOOST_PLATFORM "irix") elseif(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") # TODO: gcc on HP-UX does not support multi-threading? + set(BOOST_PLATFORM "hpux") elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") # No threading options necessary for Mac OS X + set(BOOST_PLATFORM "macos") elseif(UNIX) # Assume -pthread and -lrt on all other variants set(MULTI_THREADED_COMPILE_FLAGS "-pthread -D_REENTRANT") set(MULTI_THREADED_LINK_FLAGS "") set(MULTI_THREADED_LINK_LIBS pthread rt) -endif(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + + if (MINGW) + set(BOOST_PLATFORM "mingw") + elseif(CYGWIN) + set(BOOST_PLATFORM "cygwin") + elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(BOOST_PLATFORM "linux") + else() + set(BOOST_PLATFORM "unix") + endif() +elseif(WIN32) + set(BOOST_PLATFORM "windows") +else() + set(BOOST_PLATFORM "unknown") +endif() + +message(STATUS "Boost platform: ${BOOST_PLATFORM}") # Setup DEBUG_COMPILE_FLAGS, RELEASE_COMPILE_FLAGS, DEBUG_LINK_FLAGS and # and RELEASE_LINK_FLAGS based on the CMake equivalents @@ -159,3 +214,6 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "" CACHE INTERNAL "Unused by Boost") set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "" CACHE INTERNAL "Unused by Boost") set(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO "" CACHE INTERNAL "Unused by Boost") set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "" CACHE INTERNAL "Unused by Boost") + +# Set the build name +set(BUILDNAME "${BOOST_COMPILER}-${BOOST_COMPILER_VERSION}-${BOOST_PLATFORM}") diff --git a/CMake/BoostCore.cmake b/CMake/BoostCore.cmake index e89dacf0a..d1116f713 100644 --- a/CMake/BoostCore.cmake +++ b/CMake/BoostCore.cmake @@ -1,8 +1,8 @@ ########################################################################## # Core Functionality for Boost # ########################################################################## -# Copyright (C) 2007-2008 Douglas Gregor # -# Copyright (C) 2007 Troy Straszheim # +# Copyright (C) 2007-2009 Douglas Gregor # +# Copyright (C) 2007-2009 Troy Straszheim # # # # Distributed under the Boost Software License, Version 1.0. # # See accompanying file LICENSE_1_0.txt or copy at # @@ -56,7 +56,7 @@ add_custom_target(modularize) # therefore, will build and install the library binary. # # For libraries that have regression tests, and when testing is -# enabled globally by the BUILD_TESTING option, this macro also +# enabled globally by the BUILD_REGRESSION_TESTS option, this macro also # defines the TEST_BOOST_LIBNAME option (defaults to ON). When ON, the # generated makefiles/project files will contain regression tests for # this library. @@ -181,7 +181,8 @@ macro(boost_library_project LIBNAME) endif (THIS_PROJECT_DESCRIPTION) # Create a component group for this library - cpack_add_component_group(${libname} + fix_cpack_component_name(CPACK_COMPONENT_GROUP_NAME ${libname}) + cpack_add_component_group(${CPACK_COMPONENT_GROUP_NAME} DISPLAY_NAME "${LIBNAME}" DESCRIPTION ${THIS_PROJECT_DESCRIPTION}) endif () @@ -191,9 +192,18 @@ macro(boost_library_project LIBNAME) # Add this module's include directory include_directories("${Boost_SOURCE_DIR}/libs/${libname}/include") + # + # Horrible hackery. Make install of headers from modularized directories + # OPTIONAL, which only works on cmake >= 2.7 + # + if (${CMAKE_MAJOR_VERSION} GREATER 1 AND ${CMAKE_MINOR_VERSION} GREATER 6) + set(_INSTALL_OPTIONAL "OPTIONAL") + endif() + # Install this module's headers install(DIRECTORY include/boost DESTINATION ${BOOST_HEADER_DIR} + ${_INSTALL_OPTIONAL} COMPONENT ${libname}_headers PATTERN "CVS" EXCLUDE PATTERN ".svn" EXCLUDE) @@ -212,9 +222,10 @@ macro(boost_library_project LIBNAME) endforeach(DEP) # Tell CPack about the headers component + fix_cpack_component_name(CPACK_COMPONENT_GROUP_NAME ${libname}) cpack_add_component(${libname}_headers DISPLAY_NAME "Header files" - GROUP ${libname} + GROUP ${CPACK_COMPONENT_GROUP_NAME} DEPENDS ${THIS_PROJECT_HEADER_DEPENDS}) endif () endif () @@ -311,7 +322,7 @@ macro(boost_library_project LIBNAME) endforeach(SUBDIR ${THIS_PROJECT_SRCDIRS}) endif() - if(BUILD_TESTING AND THIS_PROJECT_TESTDIRS) + if(BUILD_REGRESSION_TESTS AND THIS_PROJECT_TESTDIRS) # Testing is enabled globally and this project has some # tests. Check whether we should include these tests. if (BOOST_TEST_LIBRARIES) @@ -326,6 +337,14 @@ macro(boost_library_project LIBNAME) endforeach () endif() + # Create a target -test, which will run all of + # this library's tests. + if (THIS_PROJECT_TESTDIRS) + add_custom_target(${PROJECT_NAME}-test + COMMAND ${CMAKE_CTEST_COMMAND} -R "^${PROJECT_NAME}-*" + MESSAGE "Running tests for Boost.${PROJECT_NAME}...") + endif () + # Include the test directories. foreach(SUBDIR ${THIS_PROJECT_TESTDIRS}) add_subdirectory(${SUBDIR}) @@ -613,7 +632,7 @@ endmacro(boost_feature_interactions) # # If any of the features listed conflict with this library, no new # targets will be built. For example, if the library provides the -# option NOT_MULTI_THREADED, and one of the features provided is +# option NO_MULTI_THREADED, and one of the features provided is # MULTI_THREADED, this macro will essentially be a no-op. macro(boost_library_variant LIBNAME) set(THIS_VARIANT_COMPILE_FLAGS "${THIS_LIB_COMPILE_FLAGS}") @@ -655,15 +674,6 @@ macro(boost_library_variant LIBNAME) # We handle static vs. dynamic libraries differently list_contains(THIS_LIB_IS_STATIC "STATIC" ${ARGN}) if (THIS_LIB_IS_STATIC) - # If the STATIC_TAG flag was set, we append "-s" to the name of - # the library. This is an unfortunate hack, needed only for the - # test library. - if (THIS_LIB_STATIC_TAG) - set(THIS_LIB_STATIC_TAG "-s") - else(THIS_LIB_STATIC_TAG) - set(THIS_LIB_STATIC_TAG "") - endif(THIS_LIB_STATIC_TAG) - # On Windows, we need static and shared libraries to have # different names, so we follow the Boost.Build version 2 style # and prepend "lib" to the name. @@ -679,7 +689,7 @@ macro(boost_library_variant LIBNAME) # Set properties on this library set_target_properties(${VARIANT_LIBNAME} PROPERTIES - OUTPUT_NAME "${LIBPREFIX}${LIBNAME}${VARIANT_VERSIONED_NAME}${THIS_LIB_STATIC_TAG}" + OUTPUT_NAME "${LIBPREFIX}${LIBNAME}${VARIANT_VERSIONED_NAME}" CLEAN_DIRECT_OUTPUT 1 COMPILE_FLAGS "${THIS_VARIANT_COMPILE_FLAGS}" LINK_FLAGS "${THIS_VARIANT_LINK_FLAGS}" @@ -718,7 +728,9 @@ macro(boost_library_variant LIBNAME) # The basic LIBNAME target depends on each of the variants add_dependencies(${LIBNAME} ${VARIANT_LIBNAME}) - + + export(TARGETS ${VARIANT_LIBNAME} FILE ${CMAKE_BINARY_DIR}/exports/${VARIANT_LIBNAME}.cmake) + # Link against whatever libraries this library depends on target_link_libraries(${VARIANT_LIBNAME} ${THIS_VARIANT_LINK_LIBS}) foreach(dependency ${THIS_LIB_DEPENDS}) @@ -760,9 +772,10 @@ macro(boost_library_variant LIBNAME) endforeach(DEP) if (COMMAND cpack_add_component) + fix_cpack_component_name(CPACK_COMPONENT_GROUP_NAME ${libname}) cpack_add_component(${LIB_COMPONENT} DISPLAY_NAME "${VARIANT_DISPLAY_NAME}" - GROUP ${libname} + GROUP ${CPACK_COMPONENT_GROUP_NAME} DEPENDS ${THIS_LIB_COMPONENT_DEPENDS}) endif () endif(NOT THIS_LIB_NO_INSTALL) @@ -1038,9 +1051,8 @@ endmacro(boost_select_variant) # [LINK_LIBS linklibs] # [feature_LINK_LIBS linklibs] # [DEPENDS libdepend1 libdepend2 ...] -# [STATIC_TAG] # [MODULE] -# [NOT_feature] +# [NO_feature] # [EXTRA_VARIANTS variant1 variant2 ...] # [FORCE_VARIANTS variant1]) # @@ -1107,16 +1119,6 @@ endmacro(boost_select_variant) # boost_python, multi-threaded variants of boost_mpi_python will # link against multi-threaded variants of boost_python. # -# STATIC_TAG: States that the name of static library variants on -# Unix need to be named differently from shared library -# variants. This particular option should only be used in rare cases -# where the static and shared library variants are incompatible, -# such that linking against the shared library rather than the -# static library will cause features. When this option is provided, -# static libraries on Unix variants will have "-s" appended to their -# names. Note: we hope that this is a temporary solution. At -# present, it is only used by the Test library. -# # MODULE: This option states that, when building a shared library, # the shared library should be built as a module rather than a # normal shared library. Modules have special meaning an behavior on @@ -1151,14 +1153,11 @@ endmacro(boost_select_variant) macro(boost_add_library LIBNAME) parse_arguments(THIS_LIB "DEPENDS;COMPILE_FLAGS;LINK_FLAGS;LINK_LIBS;EXTRA_VARIANTS;FORCE_VARIANTS;${BOOST_ADD_ARG_NAMES}" - "STATIC_TAG;MODULE;NO_INSTALL;${BOOST_ADDLIB_OPTION_NAMES}" + "MODULE;NO_INSTALL;${BOOST_ADDLIB_OPTION_NAMES}" ${ARGN} ) set(THIS_LIB_SOURCES ${THIS_LIB_DEFAULT_ARGS}) - string(TOUPPER "${LIBNAME}_COMPILED_LIB" compiled_lib) - set (${compiled_lib} TRUE CACHE INTERNAL "") - if (NOT TEST_INSTALLED_TREE) # A top-level target that refers to all of the variants of the # library, collectively. @@ -1212,9 +1211,6 @@ macro(boost_add_single_library LIBNAME) ) set(THIS_LIB_SOURCES ${THIS_LIB_DEFAULT_ARGS}) - string(TOUPPER "${LIBNAME}_COMPILED_LIB" compiled_lib) - set (${compiled_lib} TRUE CACHE INTERNAL "") - if (NOT TEST_INSTALLED_TREE) boost_select_variant(${LIBNAME} THIS_LIB) if (THIS_LIB_VARIANT) @@ -1382,11 +1378,6 @@ macro(boost_add_executable EXENAME) endforeach(LIB ${THIS_EXE_DEPENDS}) # Build the executable - # TODO: the use of ${PROJECT_NAME}/${EXENAME} is a bit strange. - # It's designed to keep the names of regression tests from one library - # separate from the regression tests of another library, but this can - # be handled better with OUTPUT_NAME. This would also allow us to eliminate - # the directory-creation logic in boost_library_project. if (THIS_PROJECT_IS_TOOL) set(THIS_EXE_NAME ${EXENAME}) else() diff --git a/CMake/BoostDocs.cmake b/CMake/BoostDocs.cmake index 17652a3be..034108f3c 100644 --- a/CMake/BoostDocs.cmake +++ b/CMake/BoostDocs.cmake @@ -118,6 +118,9 @@ macro(xsl_transform OUTPUT INPUT) elseif(THIS_XSL_MAKE_TARGET) add_custom_target(${THIS_XSL_MAKE_TARGET} DEPENDS ${THIS_XSL_OUTPUT_FILE}) + set_target_properties(${THIS_XSL_MAKE_TARGET} + PROPERTIES + EXCLUDE_FROM_ALL ON) endif() endif() endmacro(xsl_transform) @@ -166,20 +169,12 @@ macro(doxygen_to_boostbook OUTPUT) file(APPEND ${DOXYFILE} "${PARAM}\n") endforeach(PARAM) - set(THIS_DOXY_MODULAR_HEADER_PATH ${CMAKE_SOURCE_DIR}/libs/${libname}/include) + set(THIS_DOXY_HEADER_PATH ${CMAKE_SOURCE_DIR}/libs/${libname}/include) set(THIS_DOXY_HEADER_LIST "") set(THIS_DOXY_HEADERS) foreach(HDR ${THIS_DOXY_DEFAULT_ARGS}) - if(EXISTS ${CMAKE_SOURCE_DIR}/${HDR}) - list(APPEND THIS_DOXY_HEADERS ${CMAKE_SOURCE_DIR}/${HDR}) - elseif(EXISTS ${THIS_DOXY_MODULAR_HEADER_PATH}/${HDR}) - list(APPEND THIS_DOXY_HEADERS ${THIS_DOXY_MODULAR_HEADER_PATH}/${HDR}) - else(EXISTS ${CMAKE_SOURCE_DIR}/${HDR}) - message("Warning: Attempting to generate doxygen to boostbook target for header ${HDR},") - message(" which was not found in the main source directory or in a modularized location") - endif(EXISTS ${CMAKE_SOURCE_DIR}/${HDR}) - + list(APPEND THIS_DOXY_HEADERS ${THIS_DOXY_HEADER_PATH}/${HDR}) set(THIS_DOXY_HEADER_LIST "${THIS_DOXY_HEADER_LIST} ${THIS_DOXY_HEADER_PATH}/${HDR}") endforeach(HDR) @@ -286,17 +281,22 @@ macro(boost_add_documentation SOURCE) get_filename_component(THIS_DOC_EXT ${SOURCE} EXT) string(TOUPPER ${THIS_DOC_EXT} THIS_DOC_EXT) if (THIS_DOC_EXT STREQUAL ".QBK") - # Transform Quickbook into BoostBook XML - get_filename_component(SOURCE_FILENAME ${SOURCE} NAME_WE) - set(BOOSTBOOK_FILE ${SOURCE_FILENAME}.xml) - add_custom_command(OUTPUT ${BOOSTBOOK_FILE} - COMMAND quickbook "--output-file=${BOOSTBOOK_FILE}" - ${THIS_DOC_SOURCE_PATH} - DEPENDS ${THIS_DOC_SOURCE_PATH} ${THIS_DOC_DEFAULT_ARGS} - COMMENT "Generating BoostBook documentation for Boost.${PROJECT_NAME}...") + if (BUILD_QUICKBOOK) + # Transform Quickbook into BoostBook XML + get_filename_component(SOURCE_FILENAME ${SOURCE} NAME_WE) + set(BOOSTBOOK_FILE ${SOURCE_FILENAME}.xml) + add_custom_command(OUTPUT ${BOOSTBOOK_FILE} + COMMAND quickbook "--output-file=${BOOSTBOOK_FILE}" + ${THIS_DOC_SOURCE_PATH} + DEPENDS ${THIS_DOC_SOURCE_PATH} ${THIS_DOC_DEFAULT_ARGS} + COMMENT "Generating BoostBook documentation for Boost.${PROJECT_NAME}...") - # Transform BoostBook into other formats - boost_add_documentation(${CMAKE_CURRENT_BINARY_DIR}/${BOOSTBOOK_FILE}) + # Transform BoostBook into other formats + boost_add_documentation(${CMAKE_CURRENT_BINARY_DIR}/${BOOSTBOOK_FILE}) + else() + message(SEND_ERROR + "Quickbook is required to build Boost documentation.\nQuickbook can be built by enabling the BUILD_QUICKBOOK.") + endif() elseif (THIS_DOC_EXT STREQUAL ".XML") # Transform BoostBook XML into DocBook XML get_filename_component(SOURCE_FILENAME ${SOURCE} NAME_WE) @@ -319,25 +319,17 @@ macro(boost_add_documentation SOURCE) STYLESHEET ${BOOSTBOOK_XSL_DIR}/html.xsl CATALOG ${CMAKE_BINARY_DIR}/catalog.xml DIRECTORY HTML.manifest - PARAMETERS admon.graphics.path=images/ - navig.graphics.path=images/ + PARAMETERS admon.graphics.path=images + navig.graphics.path=images boost.image.src=boost.png - COMMENT "Generating HTML documentation for Boost.${PROJECT_NAME}..." + COMMENT "Generating HTML documentaiton for Boost.${PROJECT_NAME}..." MAKE_TARGET ${PROJECT_NAME}-html) - add_dependencies(html ${PROJECT_NAME}-html) - # - # Install associated stuff - # add_custom_command(TARGET ${PROJECT_NAME}-html POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/doc/src/boostbook.css ${CMAKE_CURRENT_BINARY_DIR}/html/boostbook.css - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/doc/src/docutils.css ${CMAKE_CURRENT_BINARY_DIR}/html/docutils.css - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/doc/src/reference.css ${CMAKE_CURRENT_BINARY_DIR}/html/reference.css - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/boost.png ${CMAKE_CURRENT_BINARY_DIR}/html/boost.png - COMMENT "Copying in associated stuff, boostbook.css and boost.png" + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/doc/src/boostbook.css ${CMAKE_CURRENT_BINARY_DIR}/html + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/boost.png ${CMAKE_CURRENT_BINARY_DIR}/html ) - # Install generated documentation install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/boost-${BOOST_VERSION} @@ -354,8 +346,7 @@ macro(boost_add_documentation SOURCE) CATALOG ${CMAKE_BINARY_DIR}/catalog.xml DIRECTORY man.manifest COMMENT "Generating man pages for Boost.${PROJECT_NAME}..." - MAKE_ALL_TARGET ${PROJECT_NAME}-manpages) - add_dependencies(manpages ${PROJECT_NAME}-manpages) + MAKE_TARGET ${PROJECT_NAME}-man) # Install man pages install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/man @@ -456,9 +447,6 @@ set(WANT_DOCBOOK_XSL_VERSION 1.73.2) find_program(XSLTPROC xsltproc DOC "xsltproc transforms XML via XSLT") set(XSLTPROC_FLAGS "--xinclude" CACHE STRING "Flags to pass to xsltproc to transform XML documents") -if(NOT XSLTPROC) - message(STATUS "xsltproc not found... this will disable build of documentation.") -endif() # Find the DocBook DTD (version 4.2) find_path(DOCBOOK_DTD_DIR docbookx.dtd @@ -489,12 +477,8 @@ if (XSLTPROC AND DOXYGEN) if (DOCBOOK_DTD_DIR AND DOCBOOK_XSL_DIR) # Documentation build options option(BUILD_DOCUMENTATION "Whether to build library documentation" ON) - option(BUILD_DOCUMENTATION_HTML "Whether to build HTML documentation" ON) - add_custom_target(html) - option(BUILD_DOCUMENTATION_MAN_PAGES "Whether to build Unix man pages" ON) - add_custom_target(manpages) # Generate an XML catalog file. configure_file(${CMAKE_SOURCE_DIR}/tools/build/CMake/catalog.xml.in @@ -520,30 +504,31 @@ if (XSLTPROC AND DOXYGEN) endif() endif() -set(BUILD_OCUMENTATION_OKAY TRUE) -if (NOT XSLTPROC) - message(STATUS "XSLTPROC not found, disabling build of documentation") - set(BUILD_DOCUMENTATION_OKAY FALSE) -elseif (NOT DOXYGEN) - message(STATUS "DOXYGEN not found, disabling build of documentation") - set(BUILD_DOCUMENTATION_OKAY FALSE) -elseif (NOT DOCBOOK_DTD_DIR) - message(STATUS "DOCBOOK_DTD_DIR not found, disabling build of documentation") - message(STATUS "Set DOCBOOK_AUTOCONFIG to ON to get it automatically") - set(BUILD_DOCUMENTATION_OKAY FALSE) -elseif (NOT DOCBOOK_XSL_DIR) - message(STATUS "DOCBOOK_XSL_DIR not found, disabling build of documentation") - message(STATUS "Set DOCBOOK_AUTOCONFIG to ON to get it automatically") - set(BUILD_DOCUMENTATION_OKAY FALSE) -else() - message(STATUS "Documentation prerequisites found, enabling docs build.") +# Turn off BUILD_DOCUMENTATION if it isn't going to succeed. +if (BUILD_DOCUMENTATION) set(BUILD_DOCUMENTATION_OKAY TRUE) -endif() - -if (NOT BUILD_DOCUMENTATION_OKAY) - if (BUILD_DOCUMENTATION) - set(BUILD_DOCUMENTATION OFF CACHE BOOL - "Whether to build library documentation" FORCE) + if (NOT XSLTPROC) + set(BUILD_DOCUMENTATION_OKAY FALSE) + message(STATUS "Docs build disabled due to missing xsltproc") + elseif (NOT DOXYGEN) + set(BUILD_DOCUMENTATION_OKAY FALSE) + message(STATUS "Docs build disabled due to missing doxygen") + elseif (NOT DOCBOOK_DTD_DIR) + set(BUILD_DOCUMENTATION_OKAY FALSE) + message(STATUS "Docs build disabled due to missing docbook dtd dir") + message(STATUS "You can set DOCBOOK_AUTOCONFIG to attempt this automatically.") + elseif (NOT DOCBOOK_XSL_DIR) + set(BUILD_DOCUMENTATION_OKAY FALSE) + message(STATUS "Docs build disabled due to missing docbook xsl dir") + message(STATUS "You can set DOCBOOK_AUTOCONFIG to attempt this automatically.") + else() + set(BUILD_DOCUMENTATION_OKAY TRUE) endif() -endif() + if (NOT BUILD_DOCUMENTATION_OKAY) + if (BUILD_DOCUMENTATION) + set(BUILD_DOCUMENTATION OFF CACHE BOOL + "Whether to build library documentation" FORCE) + endif() + endif() +endif() \ No newline at end of file diff --git a/CMake/BoostTesting.cmake b/CMake/BoostTesting.cmake index 886ed4704..50ea4a76c 100644 --- a/CMake/BoostTesting.cmake +++ b/CMake/BoostTesting.cmake @@ -37,10 +37,11 @@ # want or need to perform regression testing on Boost. The Boost build # is significantly faster when we aren't also building regression # tests. -option(BUILD_TESTING "Enable testing" OFF) +option(BUILD_REGRESSION_TESTS "Enable regression testing" OFF) -if (BUILD_TESTING) +if (BUILD_REGRESSION_TESTS) enable_testing() + mark_as_advanced(BUILD_TESTING) option(TEST_INSTALLED_TREE "Enable testing of an already-installed tree" OFF) @@ -50,7 +51,10 @@ if (BUILD_TESTING) if (TEST_INSTALLED_TREE) include("${CMAKE_INSTALL_PREFIX}/lib/Boost${BOOST_VERSION}/boost-targets.cmake") endif (TEST_INSTALLED_TREE) -endif (BUILD_TESTING) + + set(DART_TESTING_TIMEOUT=15 CACHE INTEGER "Timeout after this much madness") + +endif (BUILD_REGRESSION_TESTS) #------------------------------------------------------------------------------- # This macro adds additional include directories based on the dependencies of @@ -121,9 +125,9 @@ macro(boost_additional_test_dependencies libname) endwhile() endforeach() - foreach (include ${THIS_TEST_DEPENDS_ALL}) - include_directories("${Boost_SOURCE_DIR}/libs/${include}/include") - endforeach (include ${includes}) + foreach (include ${THIS_TEST_DEPENDS_ALL}) + include_directories("${Boost_SOURCE_DIR}/libs/${include}/include") + endforeach (include ${includes}) endmacro(boost_additional_test_dependencies libname) #------------------------------------------------------------------------------- @@ -140,6 +144,7 @@ endmacro(boost_additional_test_dependencies libname) # [LINK_FLAGS linkflags] # [LINK_LIBS linklibs] # [DEPENDS libdepend1 libdepend2 ...] +# [KNOWN_FAILURES string1 string2 ...] # [COMPILE] [RUN] [FAIL]) # # testname is the name of the test. The remaining arguments passed to @@ -169,7 +174,7 @@ macro(boost_test_parse_args testname) set(BOOST_TEST_OKAY TRUE) set(BOOST_TEST_COMPILE_FLAGS "") parse_arguments(BOOST_TEST - "BOOST_LIB;LINK_LIBS;LINK_FLAGS;DEPENDS;COMPILE_FLAGS;ARGS;EXTRA_OPTIONS" + "BOOST_LIB;LINK_LIBS;LINK_FLAGS;DEPENDS;COMPILE_FLAGS;ARGS;EXTRA_OPTIONS;KNOWN_FAILURES" "COMPILE;RUN;LINK;FAIL;RELEASE;DEBUG" ${ARGN} ) @@ -203,12 +208,24 @@ macro(boost_test_parse_args testname) set(BOOST_TEST_TESTNAME "${PROJECT_NAME}-${testname}") #message("testname: ${BOOST_TEST_TESTNAME}") # If testing is turned off, this test is not okay - if (NOT BUILD_TESTING) + if (NOT BUILD_REGRESSION_TESTS) set(BOOST_TEST_OKAY FALSE) - endif(NOT BUILD_TESTING) - + endif(NOT BUILD_REGRESSION_TESTS) endmacro(boost_test_parse_args) +# This macro attaches a the "known-failure" label to the given test +# target if the build name matches any of the declared, known +# failures. +macro(boost_test_known_failures TEST) + foreach(PATTERN ${ARGN}) + if (${BUILDNAME} MATCHES ${PATTERN}) + set_tests_properties("${PROJECT_NAME}-${TEST}" + PROPERTIES LABELS "${PROJECT_NAME};known-failure") + endif() + endforeach() +endmacro(boost_test_known_failures) + + # This macro creates a Boost regression test that will be executed. If # the test can be built, executed, and exits with a return code of # zero, it will be considered to have passed. @@ -281,6 +298,11 @@ macro(boost_test_run testname) PROPERTIES LABELS "${PROJECT_NAME}" ) + boost_test_known_failures(${testname} ${BOOST_TEST_KNOWN_FAILURES}) + + # Make sure that the -test target that corresponds to this + # library or tool depends on this test executable. + add_dependencies(${PROJECT_NAME}-test ${THIS_EXE_NAME}) if (BOOST_TEST_FAIL) set_tests_properties(${BOOST_TEST_TESTNAME} PROPERTIES WILL_FAIL ON) @@ -352,6 +374,8 @@ macro(boost_test_compile testname) LABELS "${PROJECT_NAME}" ) + boost_test_known_failures(${testname} ${BOOST_TEST_KNOWN_FAILURES}) + if (BOOST_TEST_FAIL) set_tests_properties(${BOOST_TEST_TESTNAME} PROPERTIES WILL_FAIL ON) endif () @@ -372,22 +396,46 @@ endmacro(boost_test_compile_fail) # # boost_test_link: # -# Under construction. +# +# Each library "exports" itself to +# ${CMAKE_BINARY_DIR}/exports/.cmake +# +# The list of 'depends' for these libraries has to match one of those +# files, this way the export mechanism works. The generated +# cmakelists will include() those exported .cmake files, for each +# DEPENDS. +# # macro(boost_test_link testname) boost_test_parse_args(${testname} ${ARGN} LINK) if(BOOST_TEST_OKAY) + # Determine the include directories to pass along to the underlying + # project. + # works but not great + get_directory_property(BOOST_TEST_INCLUDE_DIRS INCLUDE_DIRECTORIES) + set(BOOST_TEST_INCLUDES "") + foreach(DIR ${BOOST_TEST_INCLUDE_DIRS}) + set(BOOST_TEST_INCLUDES "${BOOST_TEST_INCLUDES};${DIR}") + endforeach(DIR ${BOOST_TEST_INCLUDE_DIRS}) + add_test(${BOOST_TEST_TESTNAME} ${CMAKE_CTEST_COMMAND} + -VV --build-and-test ${Boost_SOURCE_DIR}/tools/build/CMake/LinkTest ${Boost_BINARY_DIR}/tools/build/CMake/LinkTest --build-generator ${CMAKE_GENERATOR} --build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-project LinkTest + --build-options + "-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}" + "-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}" + "-DBOOST_EXPORTS_DIR:FILEPATH=${CMAKE_BINARY_DIR}/exports" "-DSOURCE:STRING=${CMAKE_CURRENT_SOURCE_DIR}/${BOOST_TEST_SOURCES}" "-DINCLUDES:STRING=${BOOST_TEST_INCLUDES}" "-DCOMPILE_FLAGS:STRING=${BOOST_TEST_COMPILE_FLAGS}" + "-DLINK_LIBS:STRING=${BOOST_TEST_LINK_LIBS}" + "-DDEPENDS:STRING=${BOOST_TEST_DEPENDS}" ) set_tests_properties(${BOOST_TEST_TESTNAME} @@ -395,6 +443,8 @@ macro(boost_test_link testname) LABELS "${PROJECT_NAME}" ) + boost_test_known_failures(${testname} ${BOOST_TEST_KNOWN_FAILURES}) + if (BOOST_TEST_FAIL) set_tests_properties(${BOOST_TEST_TESTNAME} PROPERTIES WILL_FAIL ON) endif () diff --git a/CMake/BoostUtils.cmake b/CMake/BoostUtils.cmake index 77b17f783..c87064d8c 100644 --- a/CMake/BoostUtils.cmake +++ b/CMake/BoostUtils.cmake @@ -211,3 +211,17 @@ function(topological_sort LIST PREFIX SUFFIX) set(${LIST} ${${LIST}} PARENT_SCOPE) endfunction(topological_sort) + +# Small little hack that tweaks a component name (as used for CPack) +# to make sure to avoid certain names that cause problems. Sets the +# variable named varname to the "sanitized" name. +# +# FIXME: This is a complete hack. We probably need to fix the CPack +# generators (NSIS in particular) to get rid of the need for this. +macro(fix_cpack_component_name varname name) + if (${name} STREQUAL "foreach") + set(${varname} "boost_foreach") + else() + set(${varname} ${name}) + endif() +endmacro() \ No newline at end of file diff --git a/CMake/LinkTest/CMakeLists.txt b/CMake/LinkTest/CMakeLists.txt index 1f25bb353..f2c434387 100644 --- a/CMake/LinkTest/CMakeLists.txt +++ b/CMake/LinkTest/CMakeLists.txt @@ -1,7 +1,20 @@ cmake_minimum_required(VERSION 2.6) +file(GLOB BOOST_EXPORTS "${BOOST_EXPORTS_DIR}/*.cmake") + +foreach(import + ${DEPENDS}) + include(${BOOST_EXPORTS_DIR}/${import}.cmake) +endforeach() + project(LinkTest) include_directories (${INCLUDES}) -add_executable(link-test ${SOURCE} COMPILE_FLAGS "${COMPILE_FLAGS}") +add_executable(link-test ${SOURCE}) set_source_files_properties(${SOURCE} - PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") \ No newline at end of file + PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") + +message("DEPENDS=====${DEPENDS}") + +target_link_libraries(link-test + ${LINK_LIBS} + ${DEPENDS}) From 1deaaf0b585117f3b9d2c1c381ecae5db5cdda39 Mon Sep 17 00:00:00 2001 From: "Troy D. Straszheim" Date: Thu, 21 May 2009 15:36:35 +0000 Subject: [PATCH 010/198] get mpi cmake tests working on trunk [SVN r53156] --- CMake/BoostCore.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMake/BoostCore.cmake b/CMake/BoostCore.cmake index d1116f713..fe5f19afd 100644 --- a/CMake/BoostCore.cmake +++ b/CMake/BoostCore.cmake @@ -340,9 +340,12 @@ macro(boost_library_project LIBNAME) # Create a target -test, which will run all of # this library's tests. if (THIS_PROJECT_TESTDIRS) + add_custom_target(${PROJECT_NAME}-test-drivers) add_custom_target(${PROJECT_NAME}-test COMMAND ${CMAKE_CTEST_COMMAND} -R "^${PROJECT_NAME}-*" MESSAGE "Running tests for Boost.${PROJECT_NAME}...") + add_dependencies(${PROJECT_NAME}-test + ${PROJECT_NAME}-test-drivers) endif () # Include the test directories. From ac5c31c1b25df225aa572b965deb187241198b5a Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 23 May 2009 15:26:16 +0000 Subject: [PATCH 011/198] MSVC 10 autodetection. Closes #3069. [SVN r53206] --- v2/tools/msvc.jam | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index 52af14d62..4f5afd3dd 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -677,11 +677,15 @@ local rule configure-really ( version ? : options * ) # version from the path. # FIXME: We currently detect both Microsoft Visual Studio 9.0 and # 9.0express as 9.0 here. - if [ MATCH "(Microsoft Visual Studio 9)" : $(command) ] + if [ MATCH "(Microsoft Visual Studio 10)" : $(command) ] + { + version = 10.0 ; + } + else if [ MATCH "(Microsoft Visual Studio 9)" : $(command) ] { version = 9.0 ; } - if [ MATCH "(Microsoft Visual Studio 8)" : $(command) ] + else if [ MATCH "(Microsoft Visual Studio 8)" : $(command) ] { version = 8.0 ; } @@ -1266,7 +1270,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] # Known toolset versions, in order of preference. -.known-versions = 9.0 9.0express 8.0 8.0express 7.1 7.1toolkit 7.0 6.0 ; +.known-versions = 10.0 9.0 9.0express 8.0 8.0express 7.1 7.1toolkit 7.0 6.0 ; # Version aliases. .version-alias-6 = 6.0 ; @@ -1274,6 +1278,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] .version-alias-7 = 7.0 ; .version-alias-8 = 8.0 ; .version-alias-9 = 9.0 ; +.version-alias-10 = 10.0 ; # Names of registry keys containing the Visual C++ installation path (relative # to "HKEY_LOCAL_MACHINE\SOFTWARE\\Microsoft"). @@ -1284,6 +1289,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] .version-8.0express-reg = "VCExpress\\8.0\\Setup\\VC" ; .version-9.0-reg = "VisualStudio\\9.0\\Setup\\VC" ; .version-9.0express-reg = "VCExpress\\9.0\\Setup\\VC" ; +.version-10.0-reg = "VisualStudio\\10.0\\Setup\\VC" ; # Visual C++ Toolkit 2003 does not store its installation path in the registry. # The environment variable 'VCToolkitInstallDir' and the default installation From a46911ae7fb309979b026524b4403fecdfeeb125 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 29 May 2009 08:34:04 +0000 Subject: [PATCH 012/198] Implement warnings control for sun. Patch from John W. Bito. Closes #2966. [SVN r53381] --- v2/tools/sun.jam | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v2/tools/sun.jam b/v2/tools/sun.jam index 8f10d6a44..7690d3acc 100644 --- a/v2/tools/sun.jam +++ b/v2/tools/sun.jam @@ -52,6 +52,10 @@ flags sun.compile OPTIONS on : -xprofile=tcov ; flags sun.compile OPTIONS speed : -xO4 ; flags sun.compile OPTIONS space : -xO2 -xspace ; flags sun.compile OPTIONS multi : -mt ; +flags sun.compile OPTIONS off : -erroff ; +flags sun.compile OPTIONS on : -erroff=%none ; +flags sun.compile OPTIONS all : -erroff=%none +flags sun.compile OPTIONS on : -errwarn ; flags sun.compile.c++ OPTIONS off : +d ; From 027298943561a8886200efcb5c47775afd948a55 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 29 May 2009 08:47:03 +0000 Subject: [PATCH 013/198] Catch more macros. Patch from John W. Bito [SVN r53383] --- historic/jam/src/jam.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/historic/jam/src/jam.h b/historic/jam/src/jam.h index 660c58faf..73a7a04c5 100644 --- a/historic/jam/src/jam.h +++ b/historic/jam/src/jam.h @@ -453,6 +453,7 @@ #if defined( _i386_ ) || \ defined( __i386__ ) || \ + defined( __i386 ) || \ defined( _M_IX86 ) #define OSPLAT "OSPLAT=X86" #endif @@ -470,7 +471,8 @@ #endif -#ifdef __sparc__ +#if defined( __sparc__ ) || \ + defined( __sparc ) #define OSPLAT "OSPLAT=SPARC" #endif From 9a98265e69459bc5c1fe6efbfc3606d4c85ccda6 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 29 May 2009 08:48:44 +0000 Subject: [PATCH 014/198] Properly tag libraries with QCC toolset. Fixes #2191. [SVN r53384] --- v2/tools/common.jam | 1 + 1 file changed, 1 insertion(+) diff --git a/v2/tools/common.jam b/v2/tools/common.jam index 20f52d52e..901175ce2 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -846,6 +846,7 @@ local rule toolset-tag ( name : type ? : property-set ) #case mingw* : tag += mgw ; case mipspro* : tag += mp ; case msvc* : tag += vc ; + case qcc* : tag += qcc ; case sun* : tag += sw ; case tru64cxx* : tag += tru ; case vacpp* : tag += xlc ; From 69a2449e5e31634bd277701a10a2bf6e756af8dd Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 30 May 2009 11:51:46 +0000 Subject: [PATCH 015/198] Generate import libs on cygwin, and fix naming to match conventions. Patch from Ronald Landheer-Cieslak. [SVN r53446] --- v2/tools/gcc.jam | 23 +++++++++++++++++++++++ v2/tools/types/lib.jam | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 41cb58f86..eb818030e 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -542,6 +542,28 @@ generators.register generators.override gcc.mingw.link : gcc.link ; generators.override gcc.mingw.link.dll : gcc.link.dll ; +# Cygwin is similar to msvc and mingw in that it uses import libraries. +# While in simple cases, it can directly link to a shared library, +# it is believed to be slower, and not always possible. Define cygwin-specific +# generators here. + +g = [ new gcc-linking-generator gcc.cygwin.link + : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB + : EXE + : gcc cygwin ] ; +$(g).set-rule-name gcc.link ; +generators.register $(g) ; + +g = [ new gcc-linking-generator gcc.cygwin.link.dll + : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB + : IMPORT_LIB SHARED_LIB + : gcc cygwin ] ; +$(g).set-rule-name gcc.link.dll ; +generators.register $(g) ; + +generators.override gcc.cygwin.link : gcc.link ; +generators.override gcc.cygwin.link.dll : gcc.link.dll ; + # Declare flags for linking. # First, the common flags. toolset.flags gcc.link OPTIONS on : -g ; @@ -553,6 +575,7 @@ toolset.flags gcc.link FINDLIBS-SA ; toolset.flags gcc.link LIBRARIES ; toolset.flags gcc.link.dll .IMPLIB-COMMAND windows : "-Wl,--out-implib," ; +toolset.flags gcc.link.dll .IMPLIB-COMMAND cygwin : "-Wl,--out-implib," ; # For static we made sure there are no dynamic libraries in the # link. On HP-UX not all system libraries exist as archived libraries (for diff --git a/v2/tools/types/lib.jam b/v2/tools/types/lib.jam index c343b788a..345385f85 100644 --- a/v2/tools/types/lib.jam +++ b/v2/tools/types/lib.jam @@ -9,7 +9,7 @@ type.register LIB ; type.set-generated-target-prefix LIB : : "lib" ; type.set-generated-target-prefix LIB : windows : "" ; -type.set-generated-target-prefix LIB : cygwin : "" ; +type.set-generated-target-prefix LIB : cygwin : "cyg" ; # FIXME: should not register both extensions on both # platforms. From 2ad4a8daa478b2df638f814786874d62b337eb4e Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 30 May 2009 13:24:26 +0000 Subject: [PATCH 016/198] On Cygwin, install DLLs to $(prefix)/bin. [SVN r53456] --- v2/tools/package.jam | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/v2/tools/package.jam b/v2/tools/package.jam index 25dd02565..e2bda20f9 100644 --- a/v2/tools/package.jam +++ b/v2/tools/package.jam @@ -83,8 +83,26 @@ rule install ( name : requirements * : binaries * : libraries * : headers * ) stage.install $(name)-bin : $(binaries) : $(requirements) $(bin-locate) ; - stage.install $(name)-lib : $(binaries) $(libraries) : $(requirements) - $(lib-locate) on LIB ; + alias $(name)-lib : $(name)-lib-shared $(name)-lib-static ; + + # Since the install location of shared libraries differs on universe + # and cygwin, use target alternatives to make different targets. + # We should have used indirection conditioanl requirements, but it's + # awkward to pass bin-locate and lib-locate from there to another rule. + alias $(name)-lib-shared : $(name)-lib-shared-universe ; + alias $(name)-lib-shared : $(name)-lib-shared-cygwin : cygwin ; + + # For shared libraries, we install both explicitly specified one and the + # shared libraries that the installed executables depend on. + stage.install $(name)-lib-shared-universe : $(binaries) $(libraries) : $(requirements) + $(lib-locate) on SHARED_LIB ; + stage.install $(name)-lib-shared-cygwin : $(binaries) $(libraries) : $(requirements) + $(bin-locate) on SHARED_LIB ; + + # For static libraries, we do not care about executable dependencies, since + # static libraries are already incorporated into them. + stage.install $(name)-lib-static : $(libraries) : $(requirements) + $(lib-locate) on STATIC_LIB ; stage.install $(name)-headers : $(headers) : $(requirements) $(include-locate)$(install-header-subdir) $(install-source-root) ; @@ -94,6 +112,7 @@ rule install ( name : requirements * : binaries * : libraries * : headers * ) local project-module = [ $(c).project-module ] ; module $(project-module) { - explicit $(1)-bin $(1)-lib $(1)-headers $(1) ; + explicit $(1)-bin $(1)-lib $(1)-headers $(1) $(1)-lib-shared $(1)-lib-static + $(1)-lib-shared-universe $(1)-lib-shared-cygwin ; } } From 7db5ac7eedf82924b51f83f2a512273cbb1e68ec Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 31 May 2009 18:49:11 +0000 Subject: [PATCH 017/198] Make address-model take effect even if architecture is not specified. It seems more ultimately helpful to directly translate address-model to -m32/-m64 on gcc, since that option works fine even if -march is not specified. [SVN r53510] --- v2/tools/gcc.jam | 50 ++++++++++++++++++++++++++++++++-------- v2/tools/intel-linux.jam | 6 +++++ 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index eb818030e..78d961acf 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -349,6 +349,40 @@ rule setup-fpic ( targets * : sources * : properties * ) } } +rule setup-address-model ( targets * : sources * : properties * ) +{ + local model = [ feature.get-values address-model : $(properties) ] ; + if $(model) + { + local option ; + local os = [ feature.get-values target-os : $(properties) ] ; + if $(os) = aix + { + if $(model) = 32 + { + option = -maix32 ; + } + else + { + option = -maix64 ; + } + } + else + { + if $(model) = 32 + { + option = -m32 ; + } + else + { + option = -m64 ; + } + } + OPTIONS on $(targets) += $(option) ; + } +} + + # FIXME: this should not use os.name. if [ os.name ] != NT && [ os.name ] != OSF && [ os.name ] != HPUX && [ os.name ] != AIX { @@ -369,6 +403,7 @@ rule compile.c++.pch ( targets * : sources * : properties * ) { setup-threading $(targets) : $(sources) : $(properties) ; setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; } actions compile.c++.pch @@ -380,6 +415,7 @@ rule compile.c.pch ( targets * : sources * : properties * ) { setup-threading $(targets) : $(sources) : $(properties) ; setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; } actions compile.c.pch @@ -391,6 +427,7 @@ rule compile.c++ ( targets * : sources * : properties * ) { setup-threading $(targets) : $(sources) : $(properties) ; setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; # Some extensions are compiled as C++ by default. For others, we need to # pass -x c++. We could always pass -x c++ but distcc does not work with it. @@ -420,6 +457,7 @@ rule compile.c ( targets * : sources * : properties * ) { setup-threading $(targets) : $(sources) : $(properties) ; setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; # If we use the name g++ then default file suffix -> language mapping does # not work. So have to pass -x option. Maybe, we can work around this by @@ -745,6 +783,7 @@ rule init-link-flags ( toolset linker condition ) rule link ( targets * : sources * : properties * ) { setup-threading $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; SPACE on $(targets) = " " ; # Serialize execution of the 'link' action, since running N links in # parallel is just slower. For now, serialize only gcc links, it might be a @@ -809,6 +848,7 @@ actions piecemeal archive rule link.dll ( targets * : sources * : properties * ) { setup-threading $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; SPACE on $(targets) = " " ; JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; } @@ -896,8 +936,6 @@ local rule cpu-flags ( toolset variable : architecture : instruction-set + : val # Set architecture/instruction-set options. # # x86 and compatible -toolset.flags gcc OPTIONS x86/32 : -m32 ; -toolset.flags gcc OPTIONS x86/64 : -m64 ; cpu-flags gcc OPTIONS : x86 : i386 : -march=i386 : default ; cpu-flags gcc OPTIONS : x86 : i486 : -march=i486 ; cpu-flags gcc OPTIONS : x86 : i586 : -march=i586 ; @@ -931,8 +969,6 @@ cpu-flags gcc OPTIONS : x86 : winchip2 : -march=winchip2 ; cpu-flags gcc OPTIONS : x86 : c3 : -march=c3 ; cpu-flags gcc OPTIONS : x86 : c3-2 : -march=c3-2 ; # Sparc -toolset.flags gcc OPTIONS sparc/32 : -m32 ; -toolset.flags gcc OPTIONS sparc/64 : -m64 ; cpu-flags gcc OPTIONS : sparc : c3 : -mcpu=c3 : default ; cpu-flags gcc OPTIONS : sparc : v7 : -mcpu=v7 ; cpu-flags gcc OPTIONS : sparc : cypress : -mcpu=cypress ; @@ -949,8 +985,6 @@ cpu-flags gcc OPTIONS : sparc : v9 : -mcpu=v9 ; cpu-flags gcc OPTIONS : sparc : ultrasparc : -mcpu=ultrasparc ; cpu-flags gcc OPTIONS : sparc : ultrasparc3 : -mcpu=ultrasparc3 ; # RS/6000 & PowerPC -toolset.flags gcc OPTIONS power/32 : -m32 ; -toolset.flags gcc OPTIONS power/64 : -m64 ; cpu-flags gcc OPTIONS : power : 403 : -mcpu=403 ; cpu-flags gcc OPTIONS : power : 505 : -mcpu=505 ; cpu-flags gcc OPTIONS : power : 601 : -mcpu=601 ; @@ -984,6 +1018,4 @@ cpu-flags gcc OPTIONS : power : rios2 : -mcpu=rios2 ; cpu-flags gcc OPTIONS : power : rsc : -mcpu=rsc ; cpu-flags gcc OPTIONS : power : rs64a : -mcpu=rs64 ; # AIX variant of RS/6000 & PowerPC -toolset.flags gcc OPTIONS power/32/aix : -maix32 ; -toolset.flags gcc OPTIONS power/64/aix : -maix64 ; -toolset.flags gcc AROPTIONS power/64/aix : "-X 64" ; +toolset.flags gcc AROPTIONS 64/aix : "-X 64" ; diff --git a/v2/tools/intel-linux.jam b/v2/tools/intel-linux.jam index de5bf8c7c..82c498c3a 100644 --- a/v2/tools/intel-linux.jam +++ b/v2/tools/intel-linux.jam @@ -112,6 +112,7 @@ rule compile.c++ ( targets * : sources * : properties * ) { gcc.setup-threading $(targets) : $(sources) : $(properties) ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; } @@ -124,6 +125,7 @@ rule compile.c ( targets * : sources * : properties * ) { gcc.setup-threading $(targets) : $(sources) : $(properties) ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; } @@ -136,6 +138,7 @@ rule compile.c++.pch ( targets * : sources * : properties * ) { gcc.setup-threading $(targets) : $(sources) : $(properties) ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; } # # Compiling a pch first deletes any existing *.pchi file, as Intel's compiler @@ -151,6 +154,7 @@ rule compile.c.pch ( targets * : sources * : properties * ) { gcc.setup-threading $(targets) : $(sources) : $(properties) ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; } actions compile.c.pch @@ -161,6 +165,7 @@ actions compile.c.pch rule link ( targets * : sources * : properties * ) { gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; SPACE on $(targets) = " " ; JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; } @@ -173,6 +178,7 @@ actions link bind LIBRARIES rule link.dll ( targets * : sources * : properties * ) { gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; SPACE on $(targets) = " " ; JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; } From ccb45abd32c78d2211ef277cacc022488a1bb38e Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 31 May 2009 19:03:43 +0000 Subject: [PATCH 018/198] Mention address-model in the 'Invocation' section [SVN r53511] --- v2/doc/src/advanced.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/v2/doc/src/advanced.xml b/v2/doc/src/advanced.xml index 2a30bc8ff..7049d8fe9 100644 --- a/v2/doc/src/advanced.xml +++ b/v2/doc/src/advanced.xml @@ -736,6 +736,17 @@ bjam toolset=gcc variant=debug optimization=space Cause the produced binaries to be thread-safe. This requires proper support in the source code itself. + + address-model + + 32,64 + + Explicitly request either 32-bit or 64-bit code generation. This typically + requires that your compiler is appropriately configured. Please refer to + and your compiler documentation + in case of problems. + + toolset From 54ce7d9e44d30f627bf9839ebff19025281938f9 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 4 Jun 2009 06:14:55 +0000 Subject: [PATCH 019/198] Add new 'convert' main target [SVN r53616] --- v2/tools/builtin.jam | 1 + v2/tools/convert.jam | 62 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 v2/tools/convert.jam diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index d55f676b2..f0109e6cb 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -30,6 +30,7 @@ import types/register ; import utility ; import virtual-target ; import message ; +import convert ; # FIXME: the following generate module import is not needed here but removing it # too hastly will break using code (e.g. the main Boost library Jamroot file) diff --git a/v2/tools/convert.jam b/v2/tools/convert.jam new file mode 100644 index 000000000..ac1d70101 --- /dev/null +++ b/v2/tools/convert.jam @@ -0,0 +1,62 @@ +# Copyright (c) 2009 Vladimir Prus +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +# Implements 'convert' target that takes a bunch of source and +# tries to convert each one to the specified type. +# +# For example: +# +# convert objects obj : a.cpp b.cpp ; +# + +import targets ; +import generators ; +import project ; +import type ; +import "class" : new ; + +class convert-target-class : typed-target +{ + rule __init__ ( name : project : type + : sources * : requirements * : default-build * : usage-requirements * ) + { + typed-target.__init__ $(name) : $(project) : $(type) + : $(sources) : $(requirements) : $(default-build) : $(usage-requirements) ; + } + + rule construct ( name : source-targets * : property-set ) + { + local r = [ generators.construct $(self.project) : $(self.type) + : [ property-set.create [ $(property-set).raw ] # [ feature.expand + $(self.type) ] + # ] + : $(source-targets) ] ; + if ! $(r) + { + errors.error "unable to construct" [ full-name ] ; + } + + return $(r) ; + } + +} + +rule convert ( name type : sources * : requirements * : default-build * + : usage-requirements * ) +{ + local project = [ project.current ] ; + + # This is a circular module dependency, so it must be imported here + modules.import targets ; + targets.main-target-alternative + [ new convert-target-class $(name) : $(project) : [ type.type-from-rule-name $(type) ] + : [ targets.main-target-sources $(sources) : $(name) ] + : [ targets.main-target-requirements $(requirements) : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) ] + : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ] + ] ; +} +IMPORT $(__name__) : convert : : convert ; From 29e7992e17983ba519232c313f8eb4731d741664 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Fri, 5 Jun 2009 20:13:47 +0000 Subject: [PATCH 020/198] Fix typo in example usage [SVN r53658] --- v2/tools/python.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/tools/python.jam b/v2/tools/python.jam index 498a2ad89..9e05c03ac 100644 --- a/v2/tools/python.jam +++ b/v2/tools/python.jam @@ -91,8 +91,8 @@ feature.feature pythonpath : : free optional path ; # # Example usage: # -# using python 2.3 ; -# using python 2.3 : /usr/local/bin/python ; +# using python : 2.3 ; +# using python : 2.3 : /usr/local/bin/python ; # rule init ( version ? : cmd-or-prefix ? : includes * : libraries ? : condition * : extension-suffix ? ) From 4d0d03f5186789f9ea26bb8062b3c21d53a1ea3f Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 7 Jun 2009 08:57:11 +0000 Subject: [PATCH 021/198] Clarify that Boost.Jam is low-level engine for Boost.Build. This includes big warning that Boost.Build docs are better be read, explanation that Boost.Jam is not meant as classic Jam replacement, and removal of Jam-as-standalone-build-system features. [SVN r53704] --- historic/jam/doc/bjam.qbk | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index b61b49caa..6a47e4884 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -46,7 +46,15 @@ [section:intro Introduction] -Boost.Jam (BJam) is a build tool based on FTJam, which in turn is based on :Perforce_Jam:. It contains significant improvements made to facilitate its use in the Boost Build System, but should be backward compatible with :Perforce_Jam:. +[warning Most probably, you are looking for [@http://boost.org/boost-build2/doc/html/index.html Boost.Build +manual]. This document is not meant to be read standalone and will only +confuse you. Boost.Build manual refers to specific sections when necessary.] + +Boost.Jam (BJam) is the low-level build engine tool for +[@http://boost.org/boost-build2 Boost.Build]. Historically, Boost.Jam is based +on on FTJam and on :Perforce_Jam: but has grown a number of significant +features and is now developed independently, with no merge back expected to +happen, and little use outside Boost.Build. This is version :version: of BJam and is based on version 2.4 of Jam/MR: @@ -61,24 +69,6 @@ are clearly marked. ALL WARRANTIES ARE HEREBY DISCLAIMED. ] -[section:features Features] - -Jam is a make(1) replacement that makes building simple things simple and building complicated things manageable. - -Jam's language is expressive, making Jamfiles (c.f. Makefiles) compact. - -Jam handles header file dependencies automatically and on-the-fly. - -Jam is very portable: it runs on UNIX, VMS, Mac, and NT. Most Jamfiles themselves are portable. - -Jam is unintrusive: it is small, it has negligible CPU overhead, and it doesn't create any of its own funny files (c.f. Odin, nmake, SunOS make). - -Jam can build large projects spread across many directories in one pass, without recursing, tracking the relationships among all files. Jam can do this with multiple, concurrent processes. - -Jam isn't under the blinkin GNU copyright, so you can incorporate it into commercial products. - -[endsect] - [endsect] [section:building Building BJam] From 9e3b4ca9fca137e45e1609266e357b4214cf46b0 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 7 Jun 2009 16:30:55 +0000 Subject: [PATCH 022/198] Add warning to "Using BJam" section. This is apparently the first hit for "bjam" search in google, and we want to redirect users to Boost.Build docs. [SVN r53725] --- historic/jam/doc/bjam.qbk | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index 6a47e4884..52f974a9c 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -408,6 +408,13 @@ The arguments starting with the "=--option=" forms are passed to the =build.jam= [section:usage Using BJam] +[warning Most probably, you are looking for +[@http://boost.org/boost-build2/doc/html/index.html Boost.Build +manual] or [@http://www.boost.org/boost-build2/doc/html/bbv2/advanced/invocation.html Boost.Build +command-line syntax]. This section documents only low-level options used by +the Boost.Jam build engine, and does not mention any high-level syntax of Boost.Build] + + If /target/ is provided on the command line, =bjam= builds /target/; otherwise =bjam= builds the target =all=. [pre From e7225b9b885dba1593c8be3654803e644df5c3ad Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Mon, 8 Jun 2009 22:16:47 +0000 Subject: [PATCH 023/198] Add missing semicolon [SVN r53762] --- v2/tools/sun.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/sun.jam b/v2/tools/sun.jam index 7690d3acc..0ca927d3e 100644 --- a/v2/tools/sun.jam +++ b/v2/tools/sun.jam @@ -54,7 +54,7 @@ flags sun.compile OPTIONS space : -xO2 -xspace ; flags sun.compile OPTIONS multi : -mt ; flags sun.compile OPTIONS off : -erroff ; flags sun.compile OPTIONS on : -erroff=%none ; -flags sun.compile OPTIONS all : -erroff=%none +flags sun.compile OPTIONS all : -erroff=%none ; flags sun.compile OPTIONS on : -errwarn ; flags sun.compile.c++ OPTIONS off : +d ; From c54370bd76a99eaae84096f03071500d1133743d Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 16 Jun 2009 05:16:20 +0000 Subject: [PATCH 024/198] CMake: BOOST_PLATFORM fix, from Zach Laine [SVN r53968] --- CMake/BoostConfig.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMake/BoostConfig.cmake b/CMake/BoostConfig.cmake index 1b2aef80e..ada1888da 100644 --- a/CMake/BoostConfig.cmake +++ b/CMake/BoostConfig.cmake @@ -102,7 +102,7 @@ message(STATUS "Boost compiler: ${BOOST_COMPILER}") message(STATUS "Boost toolset: ${BOOST_TOOLSET}") # create cache entry -set(BOOST_PLATFORM "unknown" CACHE STRING "Boost platform name") +set(BOOST_PLATFORM "unknown") # Multi-threading support if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") @@ -150,6 +150,9 @@ else() set(BOOST_PLATFORM "unknown") endif() +# create cache entry +set(BOOST_PLATFORM ${BOOST_PLATFORM} CACHE STRING "Boost platform name") + message(STATUS "Boost platform: ${BOOST_PLATFORM}") # Setup DEBUG_COMPILE_FLAGS, RELEASE_COMPILE_FLAGS, DEBUG_LINK_FLAGS and From 61f55cb9f4b202465567361d2d0f3a8b7571ba82 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Jun 2009 08:08:23 +0000 Subject: [PATCH 025/198] Make common.format-name don't check target type. The use in Jamroot calls it only for library targets, and other projects might want to call the function for other target types. [SVN r54298] --- v2/tools/common.jam | 46 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/v2/tools/common.jam b/v2/tools/common.jam index 901175ce2..8df9d53d6 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -751,37 +751,34 @@ actions hard-link # rule format-name ( format * : name : type ? : property-set ) { - if [ type.is-derived $(type) LIB ] + local result = "" ; + for local f in $(format) { - local result = "" ; - for local f in $(format) + switch $(f:G) { - switch $(f:G) - { - case : + case : result += $(name:B) ; - - case : + + case : result += [ join-tag $(f:G=) : [ toolset-tag $(name) : $(type) : - $(property-set) ] ] ; - - case : + $(property-set) ] ] ; + + case : result += [ join-tag $(f:G=) : [ threading-tag $(name) : $(type) - : $(property-set) ] ] ; - - case : + : $(property-set) ] ] ; + + case : result += [ join-tag $(f:G=) : [ runtime-tag $(name) : $(type) : - $(property-set) ] ] ; - - case : + $(property-set) ] ] ; + + case : local key = [ MATCH : $(f:G) ] ; local version = [ $(property-set).get <$(key)> ] ; version ?= $(key) ; - version = [ MATCH "^([^.]+)[.]([^.]+)[.]?([^.]*)" : $(version) ] - ; + version = [ MATCH "^([^.]+)[.]([^.]+)[.]?([^.]*)" : $(version) ] ; result += [ join-tag $(f:G=) : $(version[1])_$(version[2]) ] ; - case : + case : local key = [ MATCH : $(f:G) ] ; local p0 = [ MATCH <($(key))> : [ $(property-set).raw ] ] ; if $(p0) @@ -793,14 +790,13 @@ rule format-name ( format * : name : type ? : property-set ) } } - case * : + case * : result += $(f:G=) ; - } } - result = [ virtual-target.add-prefix-and-suffix $(result:J=) : $(type) : - $(property-set) ] ; - return $(result) ; } + result = [ virtual-target.add-prefix-and-suffix $(result:J=) : $(type) : + $(property-set) ] ; + return $(result) ; } From 4717c579af4c5cec70cad6beb5c4d6d50625b10c Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Jun 2009 10:01:10 +0000 Subject: [PATCH 026/198] Quote environement variable assignements. [SVN r54299] --- v2/tools/common.jam | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/tools/common.jam b/v2/tools/common.jam index 8df9d53d6..35c887cfc 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -501,10 +501,12 @@ if "\n" = "n" # see below. nl = " " ; + q = "" ; } else { nl = "\n" ; + q = "\"" ; } # Returns the command needed to set an environment variable on the current @@ -526,7 +528,7 @@ rule variable-setting-command ( variable : value ) # sequence that messes up the executed export command which then reports # that the passed variable name is incorrect. # But we have a check for cygwin in kernel/bootstrap.jam already. - return "$(variable)=$(value)$(nl)export $(variable)$(nl)" ; + return "$(variable)=$(q)$(value)$(nl)export $(variable)$(q)$(nl)" ; } } From 15ac3aad53d67c8a0f1f336bcb3db09d94785bdd Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Jun 2009 13:49:46 +0000 Subject: [PATCH 027/198] Fix quoting [SVN r54302] --- v2/tools/common.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/common.jam b/v2/tools/common.jam index 35c887cfc..7e52ddaa2 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -528,7 +528,7 @@ rule variable-setting-command ( variable : value ) # sequence that messes up the executed export command which then reports # that the passed variable name is incorrect. # But we have a check for cygwin in kernel/bootstrap.jam already. - return "$(variable)=$(q)$(value)$(nl)export $(variable)$(q)$(nl)" ; + return "$(variable)=$(q)$(value)$(q)$(nl)export $(variable)$(nl)" ; } } From 6e9d2b029c2892e7abdc8d1810f6909067c28c58 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Jun 2009 15:29:35 +0000 Subject: [PATCH 028/198] Add MD5 builtin [SVN r54306] --- historic/jam/src/build.bat | 2 +- historic/jam/src/build.jam | 2 +- historic/jam/src/build.sh | 2 +- historic/jam/src/builtins.c | 29 +++ historic/jam/src/builtins.h | 1 + historic/jam/src/md5.c | 381 ++++++++++++++++++++++++++++++++++++ historic/jam/src/md5.h | 91 +++++++++ 7 files changed, 505 insertions(+), 3 deletions(-) create mode 100644 historic/jam/src/md5.c create mode 100644 historic/jam/src/md5.h diff --git a/historic/jam/src/build.bat b/historic/jam/src/build.bat index 6ec1bb662..7065c0ac9 100644 --- a/historic/jam/src/build.bat +++ b/historic/jam/src/build.bat @@ -417,7 +417,7 @@ set BJAM_SOURCES=%BJAM_SOURCES% command.c compile.c debug.c execnt.c expand.c fi set BJAM_SOURCES=%BJAM_SOURCES% hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c set BJAM_SOURCES=%BJAM_SOURCES% newstr.c option.c output.c parse.c pathunix.c regexp.c set BJAM_SOURCES=%BJAM_SOURCES% rules.c scan.c search.c subst.c timestamp.c variable.c modules.c -set BJAM_SOURCES=%BJAM_SOURCES% strings.c filesys.c builtins.c pwd.c class.c w32_getreg.c native.c +set BJAM_SOURCES=%BJAM_SOURCES% strings.c filesys.c builtins.c md5.c pwd.c class.c w32_getreg.c native.c set BJAM_SOURCES=%BJAM_SOURCES% modules/set.c modules/path.c modules/regex.c set BJAM_SOURCES=%BJAM_SOURCES% modules/property-set.c modules/sequence.c modules/order.c diff --git a/historic/jam/src/build.jam b/historic/jam/src/build.jam index 680bd803b..322d0c189 100644 --- a/historic/jam/src/build.jam +++ b/historic/jam/src/build.jam @@ -465,7 +465,7 @@ jam.source = option.c output.c parse.c regexp.c rules.c scan.c search.c subst.c w32_getreg.c timestamp.c variable.c modules.c strings.c filesys.c - builtins.c pwd.c class.c native.c modules/set.c + builtins.c pwd.c class.c native.c md5.c modules/set.c modules/path.c modules/regex.c modules/property-set.c modules/sequence.c modules/order.c ; diff --git a/historic/jam/src/build.sh b/historic/jam/src/build.sh index 9fb037036..ce97ff940 100755 --- a/historic/jam/src/build.sh +++ b/historic/jam/src/build.sh @@ -240,7 +240,7 @@ BJAM_SOURCES="\ hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c\ newstr.c option.c output.c parse.c pathunix.c pathvms.c regexp.c\ rules.c scan.c search.c subst.c timestamp.c variable.c modules.c\ - strings.c filesys.c builtins.c pwd.c class.c native.c w32_getreg.c\ + strings.c filesys.c builtins.c pwd.c class.c native.c md5.c w32_getreg.c\ modules/set.c modules/path.c modules/regex.c modules/property-set.c\ modules/sequence.c modules/order.c" case $BOOST_JAM_TOOLSET in diff --git a/historic/jam/src/builtins.c b/historic/jam/src/builtins.c index 9e12d7a14..27eb31251 100644 --- a/historic/jam/src/builtins.c +++ b/historic/jam/src/builtins.c @@ -24,6 +24,7 @@ #include "native.h" #include "variable.h" #include "timestamp.h" +#include "md5.h" #include @@ -342,6 +343,12 @@ void load_builtins() builtin_shell, 0, args ) ); } + { + char * args[] = { "string", 0 }; + bind_builtin( "MD5", + builtin_md5, 0, args ) ; + } + /* Initialize builtin modules. */ init_set(); init_path(); @@ -1488,6 +1495,28 @@ LIST * builtin_check_if_file( PARSE * parse, FRAME * frame ) } +LIST * builtin_md5( PARSE * parse, FRAME * frame ) +{ + LIST * l = lol_get( frame->args, 0 ); + char* s = l->string; + + md5_state_t state; + md5_byte_t digest[16]; + char hex_output[16*2 + 1]; + + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)s, strlen(s)); + md5_finish(&state, digest); + + for (di = 0; di < 16; ++di) + sprintf(hex_output + di * 2, "%02x", digest[di]); + + return list_new (0, newstr(hex_output)); +} + + #ifdef HAVE_PYTHON LIST * builtin_python_import_rule( PARSE * parse, FRAME * frame ) diff --git a/historic/jam/src/builtins.h b/historic/jam/src/builtins.h index 84de8e438..8be6249d9 100644 --- a/historic/jam/src/builtins.h +++ b/historic/jam/src/builtins.h @@ -54,6 +54,7 @@ LIST *builtin_nearest_user_location( PARSE *parse, FRAME *frame ); LIST *builtin_check_if_file( PARSE *parse, FRAME *frame ); LIST *builtin_python_import_rule( PARSE *parse, FRAME *frame ); LIST *builtin_shell( PARSE *parse, FRAME *frame ); +LIST *builtin_md5( PARSE *parse, FRAME *frame ); void backtrace( FRAME *frame ); diff --git a/historic/jam/src/md5.c b/historic/jam/src/md5.c new file mode 100644 index 000000000..c35d96c5e --- /dev/null +++ b/historic/jam/src/md5.c @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/historic/jam/src/md5.h b/historic/jam/src/md5.h new file mode 100644 index 000000000..698c995d8 --- /dev/null +++ b/historic/jam/src/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ From 86a27f842416ee744c9499819d9021be07a25b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Hunold?= Date: Fri, 26 Jun 2009 09:16:31 +0000 Subject: [PATCH 029/198] Add tag rules for and [SVN r54371] --- v2/tools/common.jam | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/v2/tools/common.jam b/v2/tools/common.jam index 7e52ddaa2..feacfa46c 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -773,6 +773,14 @@ rule format-name ( format * : name : type ? : property-set ) result += [ join-tag $(f:G=) : [ runtime-tag $(name) : $(type) : $(property-set) ] ] ; + case : + result += [ join-tag $(f:G=) : [ qt-tag $(name) : $(type) : + $(property-set) ] ] ; + + case : + result += [ join-tag $(f:G=) : [ address-model-tag $(name) : $(type) : + $(property-set) ] ] ; + case : local key = [ MATCH : $(f:G) ] ; local version = [ $(property-set).get <$(key)> ] ; @@ -921,6 +929,25 @@ local rule runtime-tag ( name : type ? : property-set ) return $(tag:J=) ; } +# Create a tag for the Qt library version +# "4.6.0" will result in tag "qt460" +local rule qt-tag ( name : type ? : property-set ) +{ + local properties = [ $(property-set).get ] ; + local version = [ MATCH "([0123456789]+)[.]?([0123456789]*)[.]?([0123456789]*)" + : $(properties) ] ; + local tag = "qt"$(version:J=) ; + return $(tag) ; +} + +# Create a tag for the address-model +# 64 will simply generate "64" +local rule address-model-tag ( name : type ? : property-set ) +{ + local tag = ; + local version = [ $(property-set).get ] ; + return $(version) ; +} rule __test__ ( ) { From 7d8c0f709faebb6eb0ba384158988762b58f6415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Hunold?= Date: Fri, 26 Jun 2009 11:25:24 +0000 Subject: [PATCH 030/198] Specify target machine type to avoid linker warnings. [SVN r54375] --- v2/tools/msvc.jam | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index 4f5afd3dd..fac62b978 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -254,6 +254,11 @@ rule configure-version-specific ( toolset : version : conditions ) # 8.0 and above only has multi-threaded static RTL. toolset.flags $(toolset).compile CFLAGS $(conditions)/off/static/single : /MT ; toolset.flags $(toolset).compile CFLAGS $(conditions)/on/static/single : /MTd ; + + # Specify target machine type so the linker will not need to guess. + toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-amd64) : /MACHINE:X64 ; + toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-i386) : /MACHINE:X86 ; + toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-ia64) : /MACHINE:IA64 ; } toolset.pop-checking-for-flags-module ; } From 244044d5617756e910c03e26d3624b0b134a7ffe Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 30 Jun 2009 12:41:37 +0000 Subject: [PATCH 031/198] When starting building each metatarget, ignore active generators up the stack. [SVN r54535] --- v2/build/generators.jam | 24 ++++++++++++++++++++++-- v2/build/targets.jam | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/v2/build/generators.jam b/v2/build/generators.jam index 2d5531277..d086bb006 100644 --- a/v2/build/generators.jam +++ b/v2/build/generators.jam @@ -1215,6 +1215,10 @@ rule find-viable-generators ( target-type : property-set ) { viable-generators += $(g) ; } + else + { + generators.dout [ indent ] " generator " [ $(g).id ] "is active, discaring" ; + } } # Generators which override 'all'. @@ -1320,8 +1324,20 @@ local rule construct-really ( project name ? : target-type : property-set : # 'construct' in stack, returns only targets of requested 'target-type', # otherwise, returns also unused sources and additionally generated targets. # -rule construct ( project name ? : target-type : property-set * : sources * ) +# If 'top-level' is set, does not suppress generators that are already +# used in the stack. This may be useful in cases where a generator +# has to build a metatargets -- for example a target corresponding to +# built tool. +# +rule construct ( project name ? : target-type : property-set * : sources * : top-level ? ) { + local saved-stack ; + if $(top-level) + { + saved-active = $(.active-generators) ; + .active-generators = ; + } + if (.construct-stack) { ensure-type $(sources) ; @@ -1348,11 +1364,15 @@ rule construct ( project name ? : target-type : property-set * : sources * ) decrease-indent ; .construct-stack = $(.construct-stack[2-]) ; + + if $(top-level) + { + .active-generators = $(saved-active) ; + } return $(result) ; } - # Given 'result', obtained from some generator or generators.construct, adds # 'raw-properties' as usage requirements to it. If result already contains usage # requirements -- that is the first element of result of an instance of the diff --git a/v2/build/targets.jam b/v2/build/targets.jam index a7aa2c008..f924a20e6 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -1434,7 +1434,7 @@ class typed-target : basic-target local r = [ generators.construct $(self.project) $(name:S=) : $(self.type) : [ property-set.create [ $(property-set).raw ] $(self.type) ] - : $(source-targets) ] ; + : $(source-targets) : true ] ; if ! $(r) { ECHO "warn: Unable to construct" [ full-name ] ; From 33312e9564ad6493c5afc8ba6a923651897aa8ae Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 30 Jun 2009 15:41:42 +0000 Subject: [PATCH 032/198] Add 'elf' value of target-os. [SVN r54536] --- v2/tools/builtin.jam | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index f0109e6cb..02f2c3f9c 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -39,7 +39,13 @@ import generate ; .os-names = aix bsd cygwin darwin freebsd hpux iphone linux netbsd - openbsd osf qnx qnxnto sgi solaris unix unixware windows ; + openbsd osf qnx qnxnto sgi solaris unix unixware windows + elf # Not actually an OS -- used for targeting bare metal where + # object format is ELF. This catches both -elf and -eabi gcc + # targets and well as other compilers targeting ELF. It is not + # clear how often do we need to key of ELF specifically as opposed + # to other bare metal targets, but let's stick with gcc naming. + ; # Feature used to determine which OS we're on. New and # features should be used instead. From c8985cef5cd9ea9d984082bd793d140c00eff62f Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 7 Jul 2009 07:11:34 +0000 Subject: [PATCH 033/198] Document linkflags and fixes "includes" typo. [SVN r54763] --- v2/doc/src/advanced.xml | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/v2/doc/src/advanced.xml b/v2/doc/src/advanced.xml index 7049d8fe9..3350c273d 100644 --- a/v2/doc/src/advanced.xml +++ b/v2/doc/src/advanced.xml @@ -755,6 +755,22 @@ bjam toolset=gcc variant=debug optimization=space The C++ compiler to use. See for a detailed list. + + include + + (Arbitrary string) + + Additional include paths for C and C++ compilers. + + + + define + + (Arbitrary string) + + Additional macro definitions for C and C++ compilers. + + cxxflags @@ -772,19 +788,11 @@ bjam toolset=gcc variant=debug optimization=space - includes + linkflags (Arbitrary string) - Additional include paths for C and C++ compilers. - - - - define - - (Arbitrary string) - - Additional macro definitions for C and C++ compilers. + Custom options to pass to the C++ linker. From 62c8f1a1f313961284907eb65e6edcd6d44efb40 Mon Sep 17 00:00:00 2001 From: David Dean Date: Thu, 9 Jul 2009 16:46:13 +0000 Subject: [PATCH 034/198] Correcting Borland default compiler options -Ve and -Vx are *NOT* enabled by default in bcc32 [SVN r54833] --- v2/tools/borland.jam | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/v2/tools/borland.jam b/v2/tools/borland.jam index 77bd69d4a..c9bf01acc 100644 --- a/v2/tools/borland.jam +++ b/v2/tools/borland.jam @@ -108,10 +108,6 @@ flags borland NEED_IMPLIB LIB/shared : "" ; # -q no banner # -c compile to object # -P C++ code regardless of file extention -# -Ve zero sized empty base classes, this option is on in the IDE by default -# and effects binary compatibility. -# -Vx zero sized empty members, this option is on in the IDE by default -# and effects binary compatibility. # -a8 8 byte alignment, this option is on in the IDE by default # and effects binary compatibility. # @@ -121,13 +117,13 @@ flags borland NEED_IMPLIB LIB/shared : "" ; actions compile.c++ { - "$(CONFIG_COMMAND)" -j5 -g255 -q -c -P -Ve -Vx -a8 -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" + "$(CONFIG_COMMAND)" -j5 -g255 -q -c -P -a8 -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" } # For C, we don't pass -P flag actions compile.c { - "$(CONFIG_COMMAND)" -j5 -g255 -q -c -Ve -Vx -a8 -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" + "$(CONFIG_COMMAND)" -j5 -g255 -q -c -a8 -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" } From 01c6829832e15e4fe48e7aa61c0dae17ab1147be Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 11 Jul 2009 14:24:26 +0000 Subject: [PATCH 035/198] Approximate -rpath-link for acc. [SVN r54889] --- v2/tools/acc.jam | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/v2/tools/acc.jam b/v2/tools/acc.jam index 0f8c4c113..f04c9dc87 100644 --- a/v2/tools/acc.jam +++ b/v2/tools/acc.jam @@ -66,6 +66,10 @@ flags acc LINKFLAGS on : -pg ; flags acc CFLAGS 64 : +DD64 ; flags acc LINKFLAGS 64 : +DD64 ; +# It is unknown if there's separate option for rpath used only +# at link time, similar to -rpath-link in GNU. We'll use -L. +flags acc RPATH_LINK : ; + flags acc CFLAGS ; flags acc C++FLAGS ; flags acc DEFINES ; @@ -89,13 +93,13 @@ flags acc.compile.c++ TEMPLATE_DEPTH ; actions acc.link bind NEEDLIBS { - $(CONFIG_COMMAND) -AA $(LINKFLAGS) -o "$(<[1])" -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) $(OPTIONS) + $(CONFIG_COMMAND) -AA $(LINKFLAGS) -o "$(<[1])" -L"$(RPATH_LINK)" -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) $(OPTIONS) } SPACE = " " ; actions acc.link.dll bind NEEDLIBS { - $(CONFIG_COMMAND) -AA -b $(LINKFLAGS) -o "$(<[1])" -Wl,+h$(SPACE)-Wl,$(<[-1]:D=) -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) $(OPTIONS) + $(CONFIG_COMMAND) -AA -b $(LINKFLAGS) -o "$(<[1])" -L"$(RPATH_LINK)" -Wl,+h$(<[-1]:D=) -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) $(OPTIONS) } actions acc.compile.c From 2a28528f51ad304be38b44b40e5954f4bee8183a Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 11 Jul 2009 14:33:20 +0000 Subject: [PATCH 036/198] Remove obsolete unused parameter [SVN r54890] --- v2/tools/boostbook.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/boostbook.jam b/v2/tools/boostbook.jam index bdb14b78e..92296050a 100644 --- a/v2/tools/boostbook.jam +++ b/v2/tools/boostbook.jam @@ -305,7 +305,7 @@ class boostbook-generator : generator generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; } - rule run ( project name ? : property-set : sources * : multiple ? ) + rule run ( project name ? : property-set : sources * ) { # Generate the catalog, but only once... local global-catalog = [ boostbook.xml-catalog ] ; From 0448c06c2e52a3cb36dbf6ac2bd25371d8587808 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 11 Jul 2009 14:42:30 +0000 Subject: [PATCH 037/198] Add support for options with implied values. [SVN r54891] --- v2/util/option.jam | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/v2/util/option.jam b/v2/util/option.jam index 20f062f5b..41b11afa4 100644 --- a/v2/util/option.jam +++ b/v2/util/option.jam @@ -13,21 +13,29 @@ rule set ( name : value ) .option.$(name) = $(value) ; } -rule get ( name : default-value ? ) +rule get ( name : default-value ? : implied-value ? ) { local m = [ MATCH --$(name)=(.*) : [ modules.peek : ARGV ] ] ; if $(m) { return $(m[1]) ; } - else if $(.option.$(name)) - { - return $(.option.$(name)) ; - } - else - { - return $(default-value) ; - } + else + { + m = [ MATCH (--$(name)) : [ modules.peek : ARGV ] ] ; + if $(m) && $(implied-value) + { + return $(implied-value) ; + } + else if $(.option.$(name)) + { + return $(.option.$(name)) ; + } + else + { + return $(default-value) ; + } + } } From a92e1458c594ee61de7f15a297bb13e6b78e5459 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 11 Jul 2009 14:43:12 +0000 Subject: [PATCH 038/198] Add supposed for hashed target paths. [SVN r54892] --- v2/build/property-set.jam | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/v2/build/property-set.jam b/v2/build/property-set.jam index cb2e79917..4874deaa9 100644 --- a/v2/build/property-set.jam +++ b/v2/build/property-set.jam @@ -10,7 +10,7 @@ import project ; import property ; import sequence ; import set ; - +import option ; # Class for storing a set of properties. # @@ -37,7 +37,7 @@ class property-set import property ; import property-set ; import set ; - + rule __init__ ( raw-properties * ) { self.raw = $(raw-properties) ; @@ -262,6 +262,8 @@ class property-set else { local p = [ as-path ] ; + p = [ property-set.hash-maybe $(p) ] ; + # A real ugly hack. Boost regression test system requires # specific target paths, and it seems that changing it to handle # other directory layout is really hard. For that reason, we @@ -460,3 +462,20 @@ rule empty ( ) } return $(.empty) ; } + +if [ option.get hash : : yes ] = yes +{ + rule hash-maybe ( path ) + { + path ?= "" ; + return [ MD5 $(path) ] ; + } +} +else +{ + rule hash-maybe ( path ) + { + return $(path) ; + } +} + From b796e2af314540203ef320e1565883ce8c54f9b4 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 11 Jul 2009 15:08:54 +0000 Subject: [PATCH 039/198] Account for the fact that path may be empty. [SVN r54893] --- v2/build/property-set.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/build/property-set.jam b/v2/build/property-set.jam index 4874deaa9..70fd90cde 100644 --- a/v2/build/property-set.jam +++ b/v2/build/property-set.jam @@ -465,7 +465,7 @@ rule empty ( ) if [ option.get hash : : yes ] = yes { - rule hash-maybe ( path ) + rule hash-maybe ( path ? ) { path ?= "" ; return [ MD5 $(path) ] ; @@ -473,7 +473,7 @@ if [ option.get hash : : yes ] = yes } else { - rule hash-maybe ( path ) + rule hash-maybe ( path ? ) { return $(path) ; } From ef885109378a23a59b52fa7bdd9cbf6400fe7700 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 11 Jul 2009 15:39:48 +0000 Subject: [PATCH 040/198] Document darwin, and fat binaries. [SVN r54894] --- v2/doc/src/reference.xml | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/v2/doc/src/reference.xml b/v2/doc/src/reference.xml index 9f8302a22..f0009fea2 100644 --- a/v2/doc/src/reference.xml +++ b/v2/doc/src/reference.xml @@ -746,9 +746,10 @@ using gcc : 4.0 : g++-4.0 ; The gcc module supports the GNU C++ compiler - on Linux, a number of Unix-like system including MacOS X, SunOS and - BeOS, and on Windows (either Cygwin - or MinGW). + on Linux, a number of Unix-like system including SunOS and on Windows + (either Cygwin or + MinGW). On Mac OSX, it is recommended + to use system gcc, see . The gcc module is initialized using the following @@ -811,6 +812,28 @@ using gcc : &toolset_ops; ; +
+ + Apple Darwin gcc + + The darwin module supports the version of gcc that is + modified and provided by Apple. The configuration is essentially identical + to that of the gcc module. + + + + fat binaries + The darwin toolset can generate so called "fat" + binaries—binaries that can run support more than one + architecture, or address mode. To build a binary that can run both + on Intel and PowerPC processors, specify + architecture=combined. To build a binary that can run + both in 32-bit and 64-bit modes, specify + address-model=32_64. If you specify both of those + properties, a "4-way" fat binary will be generated. + + +
From e07fc6e53331f2d37c8816b0b23e6d6d01fa0fe7 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 11 Jul 2009 15:52:41 +0000 Subject: [PATCH 041/198] If --prefix is specified on command line, ignore bin/lib dir from config file. [SVN r54895] --- v2/tools/package.jam | 12 +++++++++++- v2/util/option.jam | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/v2/tools/package.jam b/v2/tools/package.jam index e2bda20f9..36f22c578 100644 --- a/v2/tools/package.jam +++ b/v2/tools/package.jam @@ -36,10 +36,20 @@ import project ; import property ; import stage ; import targets ; - +import modules ; rule install ( name : requirements * : binaries * : libraries * : headers * ) { + if [ MATCH --prefix=(.*) : [ modules.peek : ARGV ] ] + { + # If --prefix is explicitly specified on the command line, + # then we need wipe away any settings of libdir/includir that + # is specified via options in config files. + option.set bindir : ; + option.set libdir : ; + option.set includedir : ; + } + # If is not specified, all headers are installed to # prefix/include, no matter what their relative path is. Sometimes that is # what is needed. diff --git a/v2/util/option.jam b/v2/util/option.jam index 41b11afa4..f6dc37522 100644 --- a/v2/util/option.jam +++ b/v2/util/option.jam @@ -8,7 +8,7 @@ import modules ; # Set a value for a named option, to be used when not overridden on the command # line. -rule set ( name : value ) +rule set ( name : value ? ) { .option.$(name) = $(value) ; } From 8a1a723e829fb8f82e1ba8c8b357a0d755fd0df5 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 15 Jul 2009 18:04:07 +0000 Subject: [PATCH 042/198] Allow 'option' in project config. [SVN r54970] --- v2/build/project.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/build/project.jam b/v2/build/project.jam index 53c32d027..30f4f32b3 100644 --- a/v2/build/project.jam +++ b/v2/build/project.jam @@ -1067,7 +1067,7 @@ module project-rules rule option ( name : value ) { - if $(__name__) != site-config && $(__name__) != user-config + if $(__name__) != site-config && $(__name__) != user-config && $(__name__) != project-config { import errors ; errors.error "The 'option' rule may be used only in site-config or user-config" ; From 1b5b90d27daf5d771bd8db20bcc17da63f98dc70 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 15 Jul 2009 19:06:04 +0000 Subject: [PATCH 043/198] Exclude default_toolset.py [SVN r54972] --- v2/test/test_all.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/v2/test/test_all.py b/v2/test/test_all.py index 26e1019a7..038c2928a 100644 --- a/v2/test/test_all.py +++ b/v2/test/test_all.py @@ -139,7 +139,8 @@ tests = [ "absolute_sources", "custom_generator", "default_build", "default_features", - "default_toolset", +# This test is known to be broken itself. +# "default_toolset", "dependency_property", "dependency_test", "disambiguation", From eff932a571c3b8718a56affd8194a9aa72360cb7 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 15 Jul 2009 19:16:49 +0000 Subject: [PATCH 044/198] Fix unit tests [SVN r54973] --- v2/tools/common.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/tools/common.jam b/v2/tools/common.jam index feacfa46c..05cd392d2 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -960,10 +960,10 @@ rule __test__ ( ) modules.poke os : .name : LINUX ; - assert.result "PATH=foo:bar:baz$(nl)export PATH$(nl)" + assert.result "PATH=\"foo:bar:baz\"$(nl)export PATH$(nl)" : path-variable-setting-command PATH : foo bar baz ; - assert.result "PATH=foo:bar:$PATH$(nl)export PATH$(nl)" + assert.result "PATH=\"foo:bar:$PATH\"$(nl)export PATH$(nl)" : prepend-path-variable-command PATH : foo bar ; modules.poke os : .name : NT ; From 4db1173791ec402b81fcd749f01b15e3e4b27007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Hunold?= Date: Mon, 20 Jul 2009 09:42:00 +0000 Subject: [PATCH 045/198] support building bjam using Visual Studio 2010. [SVN r55041] --- historic/jam/src/build.bat | 27 ++++++++++++++++++++++++++- historic/jam/src/build.jam | 7 +++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/historic/jam/src/build.bat b/historic/jam/src/build.bat index 7065c0ac9..fa14fc453 100644 --- a/historic/jam/src/build.bat +++ b/historic/jam/src/build.bat @@ -28,7 +28,7 @@ ECHO ### You can specify the toolset as the argument, i.e.: ECHO ### .\build.bat msvc ECHO ### ECHO ### Toolsets supported by this script are: borland, como, gcc, gcc-nocygwin, -ECHO ### intel-win32, metrowerks, mingw, msvc, vc7, vc8, vc9 +ECHO ### intel-win32, metrowerks, mingw, msvc, vc7, vc8, vc9, vc10 ECHO ### call :Set_Error endlocal @@ -100,6 +100,16 @@ call :Clear_Error call :Test_Empty %ProgramFiles% if not errorlevel 1 set ProgramFiles=C:\Program Files +call :Clear_Error +if NOT "_%VS100COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET=vc10" + set "BOOST_JAM_TOOLSET_ROOT=%VS100COMNTOOLS%..\..\VC\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual Studio 10.0\VC\VCVARSALL.BAT" ( + set "BOOST_JAM_TOOLSET=vc10" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio 10.0\VC\" + goto :eof) call :Clear_Error if NOT "_%VS90COMNTOOLS%_" == "__" ( set "BOOST_JAM_TOOLSET=vc9" @@ -346,6 +356,21 @@ set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" set "_known_=1" :Skip_VC9 +if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc10_" goto Skip_VC10 +if NOT "_%VS100COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%VS100COMNTOOLS%..\..\VC\" + ) +if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%VCVARSALL.BAT" %BOOST_JAM_ARGS% +if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + if "_%VCINSTALLDIR%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%" + ) ) +set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib" +set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" +set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" +set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" +set "_known_=1" +:Skip_VC10 if NOT "_%BOOST_JAM_TOOLSET%_" == "_borland_" goto Skip_BORLAND if "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( call :Test_Path bcc32.exe ) diff --git a/historic/jam/src/build.jam b/historic/jam/src/build.jam index 322d0c189..782419f8a 100644 --- a/historic/jam/src/build.jam +++ b/historic/jam/src/build.jam @@ -336,6 +336,13 @@ toolset vc9 cl : /Fe /Fe /Fd /Fo : -D [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ] -I$(--python-include) -I$(--extra-include) : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; +## Microsoft Visual C++ 2010 +toolset vc10 cl : /Fe /Fe /Fd /Fo : -D + : /nologo + [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /wd4996 ] + [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ] + -I$(--python-include) -I$(--extra-include) + : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; ## VMS/OpenVMS DEC C toolset vmsdecc cc : /OBJECT= : "/DEFINES=(" "," ")" : /STANDARD=VAXC /PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES From 644b44be44aa40a4ceb39e064505debe722b357e Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 20 Jul 2009 10:10:19 +0000 Subject: [PATCH 046/198] If command fails, print its output even for -d0. [SVN r55042] --- historic/jam/src/output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/historic/jam/src/output.c b/historic/jam/src/output.c index ae20013f8..483c6ca9e 100644 --- a/historic/jam/src/output.c +++ b/historic/jam/src/output.c @@ -78,8 +78,8 @@ void out_action break; } - /* Print out the command output, if requested. */ - if ( action ) + /* Print out the command output, if requested, or if the program failed. */ + if ( action || exit_reason != EXIT_OK) { /* But only output for non-quietly actions. */ if ( ( 0 != out_data ) && From 04c42eea72f4d6e1cdf87b3853d4fed48b03b8e2 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 25 Jul 2009 06:47:43 +0000 Subject: [PATCH 047/198] Properly report failures to add/remove/modify file. [SVN r55152] --- v2/test/BoostBuild.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/test/BoostBuild.py b/v2/test/BoostBuild.py index f2612c28f..1324b154c 100644 --- a/v2/test/BoostBuild.py +++ b/v2/test/BoostBuild.py @@ -572,7 +572,7 @@ class Tester(TestCmd.TestCmd): try: glob_remove(self.unexpected_difference.added_files, name) except: - print "File %s not added as expected" % name + annotation("failure", "File %s not added as expected" % name) self.fail_test(1) def ignore_addition(self, wildcard): @@ -583,7 +583,7 @@ class Tester(TestCmd.TestCmd): try: glob_remove(self.unexpected_difference.removed_files, name) except: - print "File %s not removed as expected" % name + annotation("failure", "File %s not removed as expected" % name) self.fail_test(1) def ignore_removal(self, wildcard): @@ -594,7 +594,7 @@ class Tester(TestCmd.TestCmd): try: glob_remove(self.unexpected_difference.modified_files, name) except: - print "File %s not modified as expected" % name + annotation("failure", "File %s not modified as expected" % name) self.fail_test(1) def ignore_modification(self, wildcard): From c0331da85bfad99596a070f9e45fdaf4d2685e73 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 25 Jul 2009 09:01:23 +0000 Subject: [PATCH 048/198] Initial PDB support and manifest file fixes. Patch from Alexey Pakhunov. The changes are: - Make msvc link generators create targets that correspond to PDB files, so that they can be installed. - Force creation of manifest file if separate manifest is requested - Fix the name of manifest file that boost.build expects to make those produced by the compiler. [SVN r55154] --- v2/tools/msvc.jam | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index fac62b978..caba91254 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -40,6 +40,8 @@ import type ; type.register MANIFEST : manifest ; feature.feature embed-manifest : on off : incidental propagated ; +type.register PDB : pdb ; + ################################################################################ # # Public rules. @@ -259,6 +261,10 @@ rule configure-version-specific ( toolset : version : conditions ) toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-amd64) : /MACHINE:X64 ; toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-i386) : /MACHINE:X86 ; toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-ia64) : /MACHINE:IA64 ; + + # Make sure that manifest will be generated even if there is no + # dependencies to put there. + toolset.flags $(toolset).link LINKFLAGS $(conditions)/off : /MANIFEST ; } toolset.pop-checking-for-flags-module ; } @@ -1005,12 +1011,23 @@ class msvc-linking-generator : linking-generator { local name = [ $(result[0]).name ] ; local action = [ $(result[0]).action ] ; + + if [ $(property-set).get ] = "on" + { + local target = [ class.new file-target $(name) : PDB : $(project) : $(action) ] ; + lcoal registered-target = [ virtual-target.register $(target) ] ; + if $(target) != $(registered-target) + { + $(action).replace-targets $(target) : $(registered-target) ; + } + result += $(registered-target) ; + } if [ $(property-set).get ] = "off" { local target = [ class.new file-target $(name) : MANIFEST : $(project) : $(action) ] ; local registered-target = [ virtual-target.register $(target) ] ; - if $(action) && ( $(target) != $(registered-target) ) + if $(target) != $(registered-target) { $(action).replace-targets $(target) : $(registered-target) ; } From ccfc9b40d181f960fa3a7475d0ca43953e17d800 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 25 Jul 2009 09:23:24 +0000 Subject: [PATCH 049/198] Use suitable names for PDB files for static libs. Patch from Michael Weber. [SVN r55155] --- v2/tools/msvc.jam | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index caba91254..13f53344d 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -347,15 +347,27 @@ toolset.flags msvc YLOPTION : "-Yl" ; # Action for running the C/C++ compiler without using precompiled headers. # # WARNING: Synchronize any changes this in action with intel-win -actions compile-c-c++ +# +# Notes regarding PDB generation, for when we use on/database +# +# 1. PDB_CFLAG is only set for on/database, ensuring that the /Fd flag is dropped if PDB_CFLAG is empty +# +# 2. When compiling executables's source files, PDB_NAME is set on a per-source file basis by rule compile-c-c++. +# The linker will pull these into the executable's PDB +# +# 3. When compiling library's source files, PDB_NAME is updated to .pdb for each source file by rule archive, +# as in this case the compiler must be used to create a single PDB for our library. +# +actions compile-c-c++ bind PDB_NAME { - $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -Fo"$(<[1]:W)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" $(.CC.FILTER) + $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -Fo"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" $(.CC.FILTER) } rule compile-c-c++ ( targets + : sources * ) { DEPENDS $(<[1]) : [ on $(<[1]) return $(PCH_HEADER) ] ; DEPENDS $(<[1]) : [ on $(<[1]) return $(PCH_FILE) ] ; + PDB_NAME on $(<) = $(<:S=.pdb) ; } # Action for running the C/C++ compiler using precompiled headers. In addition @@ -507,6 +519,16 @@ else } } +# this rule sets up the pdb file that will be used when generating static +# libraries and the debug-store option is database, so that the compiler +# puts all debug info into a single .pdb file named after the library +# +# Poking at source targets this way is probably not clean, but it's the +# easiest approach. +rule archive ( targets + : sources * : properties * ) +{ + PDB_NAME on $(>) = $(<:S=.pdb) ; +} ################################################################################ # @@ -1146,7 +1168,7 @@ local rule register-toolset-really ( ) toolset.flags msvc.compile.c OPTIONS : ; toolset.flags msvc.compile.c++ OPTIONS : ; - toolset.flags msvc.compile PDB_CFLAG on/database : /Fd ; # not used yet + toolset.flags msvc.compile PDB_CFLAG on/database : /Fd ; toolset.flags msvc.compile DEFINES ; toolset.flags msvc.compile UNDEFS ; From 5ece412ab00de5529010879b1b24379803936c32 Mon Sep 17 00:00:00 2001 From: "Troy D. Straszheim" Date: Sun, 26 Jul 2009 00:49:56 +0000 Subject: [PATCH 050/198] Copyrights on CMakeLists.txt to keep them from clogging up the inspect reports. This is essentially the same commit as r55095 on the release branch. [SVN r55159] --- CMake/CompileTest/CMakeLists.txt | 6 ++++++ CMake/LinkTest/CMakeLists.txt | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/CMake/CompileTest/CMakeLists.txt b/CMake/CompileTest/CMakeLists.txt index e14abec14..d5a590842 100644 --- a/CMake/CompileTest/CMakeLists.txt +++ b/CMake/CompileTest/CMakeLists.txt @@ -1,3 +1,9 @@ +# +# Copyright Troy D. Straszheim +# +# Distributed under the Boost Software License, Version 1.0. +# See http://www.boost.org/LICENSE_1_0.txt +# cmake_minimum_required(VERSION 2.6) project(CompileTest) add_library(compile-test STATIC ${SOURCE}) diff --git a/CMake/LinkTest/CMakeLists.txt b/CMake/LinkTest/CMakeLists.txt index f2c434387..45327091b 100644 --- a/CMake/LinkTest/CMakeLists.txt +++ b/CMake/LinkTest/CMakeLists.txt @@ -1,3 +1,9 @@ +# +# Copyright Troy D. Straszheim +# +# Distributed under the Boost Software License, Version 1.0. +# See http://www.boost.org/LICENSE_1_0.txt +# cmake_minimum_required(VERSION 2.6) file(GLOB BOOST_EXPORTS "${BOOST_EXPORTS_DIR}/*.cmake") From 4328127c219ba3f9bc128f433f9e5326fb3be97b Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Sun, 26 Jul 2009 13:15:23 +0000 Subject: [PATCH 051/198] Fix typo from 55154 [SVN r55173] --- v2/tools/msvc.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index 13f53344d..c1fcd8eca 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -1037,7 +1037,7 @@ class msvc-linking-generator : linking-generator if [ $(property-set).get ] = "on" { local target = [ class.new file-target $(name) : PDB : $(project) : $(action) ] ; - lcoal registered-target = [ virtual-target.register $(target) ] ; + local registered-target = [ virtual-target.register $(target) ] ; if $(target) != $(registered-target) { $(action).replace-targets $(target) : $(registered-target) ; From f47d83789a303d6139efba85d7739a73bfbca9bd Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 27 Jul 2009 07:20:34 +0000 Subject: [PATCH 052/198] Attempt at fixing generators_test on windows [SVN r55198] --- v2/test/generators-test/extra.jam | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/v2/test/generators-test/extra.jam b/v2/test/generators-test/extra.jam index 01f086a88..88e142dd5 100644 --- a/v2/test/generators-test/extra.jam +++ b/v2/test/generators-test/extra.jam @@ -6,6 +6,7 @@ import type ; import generators ; import "class" : new ; +import common ; type.register WHL : whl ; type.register DLP : dlp ; @@ -53,17 +54,18 @@ class wd-to-cpp : generator generators.override extra.wd-to-cpp : extra.whale ; generators.override extra.wd-to-cpp : extra.dolphin ; - generators.register [ new wd-to-cpp extra.wd-to-cpp : : CPP ] ; rule whale ( targets * : sources * : properties * ) { } +TOUCH = [ common.file-touch-command ] ; + actions whale { echo "Whale consuming " $(>) - touch $(<) + $(TOUCH) $(<) } rule dolphin ( targets * : source * : properties * ) @@ -73,7 +75,7 @@ rule dolphin ( targets * : source * : properties * ) actions dolphin { echo "Dolphin consuming" $(>) - touch $(<) + $(TOUCH) $(<) } rule wd ( targets * : source * : properties * ) @@ -83,7 +85,7 @@ rule wd ( targets * : source * : properties * ) actions wd { echo "WD consuming" $(>) - touch $(<) + $(TOUCH) $(<) } rule x ( target * : source * : properties * ) @@ -94,7 +96,7 @@ rule x ( target * : source * : properties * ) actions x { echo "X: source is " $(>) - touch $(<) + $(TOUCH) $(<) } rule x_pro ( target * : source * : properties * ) @@ -105,7 +107,7 @@ rule x_pro ( target * : source * : properties * ) actions x_pro { echo "X_PRO: source is " $(>) - touch $(<) + $(TOUCH) $(<) } From cda57d4290dcccbbbb905994252ccd20088780e7 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 27 Jul 2009 07:24:41 +0000 Subject: [PATCH 053/198] Prevent doxygen's html (not docbook) generation from using './html'. Now, 'html' directory is created in the build dir of the Jamfile where 'doxygen' target appears. [SVN r55200] --- v2/tools/doxygen.jam | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/v2/tools/doxygen.jam b/v2/tools/doxygen.jam index e3398befe..a3e3a2561 100644 --- a/v2/tools/doxygen.jam +++ b/v2/tools/doxygen.jam @@ -354,9 +354,20 @@ rule doxygen ( target : sources * : requirements * : default-build * : usage-req { # Build an HTML directory from the sources. local html-location = [ feature.get-values : $(requirements) ] ; - local output-dir = [ path.root - [ path.join [ $(project).get build-dir ] $(html-location:E=html) ] - [ path.pwd ] ] ; + local output-dir ; + if [ $(project).get build-dir ] + { + # Explicitly specified build dir. Add html at the end. + output-dir = [ path.join [ $(project).build-dir ] $(html-location:E=html) ] ; + } + else + { + # Trim 'bin' from implicit build dir, for no other reason that backward + # compatibility. + output-dir = [ path.join [ path.parent [ $(project).build-dir ] ] + $(html-location:E=html) ] ; + } + output-dir = [ path.root $(output-dir) [ path.pwd ] ] ; local output-dir-native = [ path.native $(output-dir) ] ; requirements = [ property.change $(requirements) : ] ; From 4e9f60428473fc894881afb1aa4bccf6850ab9ee Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 27 Jul 2009 08:52:41 +0000 Subject: [PATCH 054/198] Add experimental Python port [SVN r55201] --- v2/build/__init__.py | 0 v2/build/alias.py | 62 ++ v2/build/build_request.py | 211 ++++++ v2/build/engine.py | 159 +++++ v2/build/errors.py | 122 ++++ v2/build/feature.py | 891 +++++++++++++++++++++++++ v2/build/generators.py | 967 +++++++++++++++++++++++++++ v2/build/project.ann.py | 996 ++++++++++++++++++++++++++++ v2/build/project.py | 996 ++++++++++++++++++++++++++++ v2/build/property.py | 636 ++++++++++++++++++ v2/build/property_set.py | 368 +++++++++++ v2/build/scanner.py | 157 +++++ v2/build/targets.py | 1264 ++++++++++++++++++++++++++++++++++++ v2/build/toolset.py | 402 ++++++++++++ v2/build/type.py | 292 +++++++++ v2/build/virtual_target.py | 1051 ++++++++++++++++++++++++++++++ v2/build_system.py | 437 +++++++++++++ v2/exceptions.py | 44 ++ v2/kernel/bootstrap.jam | 113 +++- v2/manager.py | 132 ++++ v2/tools/__init__.py | 0 v2/tools/builtin.py | 722 ++++++++++++++++++++ v2/tools/common.py | 817 +++++++++++++++++++++++ v2/tools/darwin.py | 57 ++ v2/tools/gcc.py | 796 +++++++++++++++++++++++ v2/tools/make.py | 55 ++ v2/tools/pch.py | 83 +++ v2/tools/rc.py | 189 ++++++ v2/tools/types/__init__.py | 18 + v2/tools/types/asm.py | 13 + v2/tools/types/cpp.py | 10 + v2/tools/types/exe.py | 11 + v2/tools/types/html.py | 10 + v2/tools/types/lib.py | 23 + v2/tools/types/obj.py | 11 + v2/tools/types/rsp.py | 10 + v2/tools/unix.py | 150 +++++ v2/util/__init__.py | 0 v2/util/logger.py | 46 ++ v2/util/order.py | 121 ++++ v2/util/path.py | 922 ++++++++++++++++++++++++++ v2/util/regex.py | 25 + v2/util/sequence.py | 52 ++ v2/util/set.py | 42 ++ v2/util/utility.py | 155 +++++ 45 files changed, 13632 insertions(+), 6 deletions(-) create mode 100644 v2/build/__init__.py create mode 100755 v2/build/alias.py create mode 100644 v2/build/build_request.py create mode 100644 v2/build/engine.py create mode 100644 v2/build/errors.py create mode 100644 v2/build/feature.py create mode 100644 v2/build/generators.py create mode 100644 v2/build/project.ann.py create mode 100644 v2/build/project.py create mode 100644 v2/build/property.py create mode 100644 v2/build/property_set.py create mode 100644 v2/build/scanner.py create mode 100644 v2/build/targets.py create mode 100644 v2/build/toolset.py create mode 100644 v2/build/type.py create mode 100644 v2/build/virtual_target.py create mode 100644 v2/build_system.py create mode 100644 v2/exceptions.py create mode 100644 v2/manager.py create mode 100644 v2/tools/__init__.py create mode 100644 v2/tools/builtin.py create mode 100644 v2/tools/common.py create mode 100644 v2/tools/darwin.py create mode 100644 v2/tools/gcc.py create mode 100644 v2/tools/make.py create mode 100644 v2/tools/pch.py create mode 100644 v2/tools/rc.py create mode 100644 v2/tools/types/__init__.py create mode 100644 v2/tools/types/asm.py create mode 100644 v2/tools/types/cpp.py create mode 100644 v2/tools/types/exe.py create mode 100644 v2/tools/types/html.py create mode 100644 v2/tools/types/lib.py create mode 100644 v2/tools/types/obj.py create mode 100644 v2/tools/types/rsp.py create mode 100644 v2/tools/unix.py create mode 100644 v2/util/__init__.py create mode 100644 v2/util/logger.py create mode 100644 v2/util/order.py create mode 100644 v2/util/path.py create mode 100644 v2/util/regex.py create mode 100644 v2/util/sequence.py create mode 100644 v2/util/set.py create mode 100644 v2/util/utility.py diff --git a/v2/build/__init__.py b/v2/build/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/v2/build/alias.py b/v2/build/alias.py new file mode 100755 index 000000000..d182cad29 --- /dev/null +++ b/v2/build/alias.py @@ -0,0 +1,62 @@ +# Copyright 2003, 2004, 2006 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# Status: ported (danielw) +# Base revision: 40480 + +# This module defines the 'alias' rule and associated class. +# +# Alias is just a main target which returns its source targets without any +# processing. For example:: +# +# alias bin : hello test_hello ; +# alias lib : helpers xml_parser ; +# +# Another important use of 'alias' is to conveniently group source files:: +# +# alias platform-src : win.cpp : NT ; +# alias platform-src : linux.cpp : LINUX ; +# exe main : main.cpp platform-src ; +# +# Lastly, it's possible to create local alias for some target, with different +# properties:: +# +# alias big_lib : : @/external_project/big_lib/static ; +# + +import targets +import property_set +from b2.manager import get_manager + +class AliasTarget(targets.BasicTarget): + + def __init__(self, *args): + targets.BasicTarget.__init__(self, *args) + + def construct(self, name, source_targets, properties): + return [property_set.empty(), source_targets] + + def compute_usage_requirements(self, subvariant): + base = targets.BasicTarget.compute_usage_requirements(self, subvariant) + # Add source's usage requirement. If we don't do this, "alias" does not + # look like 100% alias. + return base.add(subvariant.sources_usage_requirements()) + +def alias(name, sources, requirements=None, default_build=None, usage_requirements=None): + project = get_manager().projects().current() + targets = get_manager().targets() + + if default_build: + default_build = default_build[0] + + targets.main_target_alternative(AliasTarget( + name[0], project, + targets.main_target_sources(sources, name), + targets.main_target_requirements(requirements or [], project), + targets.main_target_default_build(default_build, project), + targets.main_target_usage_requirements(usage_requirements or [], project))) + +# Declares the 'alias' target. It will build sources, and return them unaltered. +get_manager().projects().add_rule("alias", alias) + diff --git a/v2/build/build_request.py b/v2/build/build_request.py new file mode 100644 index 000000000..9fd29e974 --- /dev/null +++ b/v2/build/build_request.py @@ -0,0 +1,211 @@ +# Status: being ported by Vladimir Prus +# TODO: need to re-compare with mainline of .jam +# Base revision: 40480 +# +# (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +import feature +from b2.util import set +from b2.util.utility import * + +def expand_no_defaults (property_sets): + """ Expand the given build request by combining all property_sets which don't + specify conflicting non-free features. + """ + # First make all features and subfeatures explicit + expanded_property_sets = [ __apply_to_property_set (feature.expand_subfeatures, x) for x in property_sets ] + + # Now combine all of the expanded property_sets + product = __x_product (expanded_property_sets) + + return product + +def __apply_to_property_set (f, property_set): + """ Transform property_set by applying f to each component property. + """ + properties = feature.split (property_set) + return '/'.join (f (properties)) + + + +def __x_product (property_sets): + """ Return the cross-product of all elements of property_sets, less any + that would contain conflicting values for single-valued features. + """ + x_product_seen = [] + x_product_used = [] + feature_space = [] + return __x_product_aux (property_sets, x_product_seen, x_product_used, feature_space) + +def __x_product_aux (property_sets, x_product_seen, x_product_used, feature_space): + """ Implementation of __x_product. + """ + result = [] + + if property_sets: + p = feature.split (property_sets [0]) + else: + p = [] + + f = set.difference (get_grist (p), feature.free_features ()) + + seen = [] + # No conflict with things used at a higher level? + if not set.intersection (f, x_product_used): + # don't mix in any conflicting features + local_x_product_used = x_product_used + f + local_x_product_seen = [] + + if len (property_sets) > 1: + rest = __x_product_aux (property_sets [1:], local_x_product_seen, local_x_product_used, feature_space) + result = [ property_sets [0] + '/' + x for x in rest] + + if not result and property_sets: + result = [property_sets [0]] + + # If we didn't encounter a conflicting feature lower down, + # don't recurse again. + if not set.intersection (f, local_x_product_seen): + property_sets = [] + + seen = local_x_product_seen + + if len (property_sets) > 1: + result.extend (__x_product_aux (property_sets [1:], x_product_seen, x_product_used, feature_space)) + x_product_seen += f + seen + + # Note that we've seen these features so that higher levels will + # recurse again without them set. + + return result + +def looks_like_implicit_value(v): + """Returns true if 'v' is either implicit value, or + the part before the first '-' symbol is implicit value.""" + if feature.is_implicit_value(v): + return 1 + else: + split = v.split("-") + if feature.is_implicit_value(split[0]): + return 1 + + return 0 + +def from_command_line(command_line): + """Takes the command line tokens (such as taken from ARGV rule) + and constructs build request from it. Returns a list of two + lists. First is the set of targets specified in the command line, + and second is the set of requested build properties.""" + + targets = [] + properties = [] + + for e in command_line: + if e[0] != "-": + # Build request spec either has "=" in it, or completely + # consists of implicit feature values. + if e.find("=") != -1 or looks_like_implicit_value(e.split("/")[0]): + properties += convert_command_line_element(e) + else: + targets.append(e) + + return [targets, properties] + +# Converts one element of command line build request specification into +# internal form. +def convert_command_line_element(e): + + result = None + parts = e.split("/") + for p in parts: + m = p.split("=") + if len(m) > 1: + feature = m[0] + values = m[1].split(",") + lresult = [("<%s>%s" % (feature, v)) for v in values] + else: + lresult = p.split(",") + + if p.find('-') == -1: + # FIXME: first port property.validate + # property.validate cannot handle subfeatures, + # so we avoid the check here. + #for p in lresult: + # property.validate(p) + pass + + if not result: + result = lresult + else: + result = [e1 + "/" + e2 for e1 in result for e2 in lresult] + + return result + +### +### rule __test__ ( ) +### { +### import assert feature ; +### +### feature.prepare-test build-request-test-temp ; +### +### import build-request ; +### import build-request : expand_no_defaults : build-request.expand_no_defaults ; +### import errors : try catch ; +### import feature : feature subfeature ; +### +### feature toolset : gcc msvc borland : implicit ; +### subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 +### 3.0 3.0.1 3.0.2 : optional ; +### +### feature variant : debug release : implicit composite ; +### feature inlining : on off ; +### feature "include" : : free ; +### +### feature stdlib : native stlport : implicit ; +### +### feature runtime-link : dynamic static : symmetric ; +### +### +### local r ; +### +### r = [ build-request.from-command-line bjam debug runtime-link=dynamic ] ; +### assert.equal [ $(r).get-at 1 ] : ; +### assert.equal [ $(r).get-at 2 ] : debug dynamic ; +### +### try ; +### { +### +### build-request.from-command-line bjam gcc/debug runtime-link=dynamic/static ; +### } +### catch \"static\" is not a value of an implicit feature ; +### +### +### r = [ build-request.from-command-line bjam -d2 --debug debug target runtime-link=dynamic ] ; +### assert.equal [ $(r).get-at 1 ] : target ; +### assert.equal [ $(r).get-at 2 ] : debug dynamic ; +### +### r = [ build-request.from-command-line bjam debug runtime-link=dynamic,static ] ; +### assert.equal [ $(r).get-at 1 ] : ; +### assert.equal [ $(r).get-at 2 ] : debug dynamic static ; +### +### r = [ build-request.from-command-line bjam debug gcc/runtime-link=dynamic,static ] ; +### assert.equal [ $(r).get-at 1 ] : ; +### assert.equal [ $(r).get-at 2 ] : debug gcc/dynamic +### gcc/static ; +### +### r = [ build-request.from-command-line bjam msvc gcc,borland/runtime-link=static ] ; +### assert.equal [ $(r).get-at 1 ] : ; +### assert.equal [ $(r).get-at 2 ] : msvc gcc/static +### borland/static ; +### +### r = [ build-request.from-command-line bjam gcc-3.0 ] ; +### assert.equal [ $(r).get-at 1 ] : ; +### assert.equal [ $(r).get-at 2 ] : gcc-3.0 ; +### +### feature.finish-test build-request-test-temp ; +### } +### +### diff --git a/v2/build/engine.py b/v2/build/engine.py new file mode 100644 index 000000000..246420fb4 --- /dev/null +++ b/v2/build/engine.py @@ -0,0 +1,159 @@ +# Copyright Pedro Ferreira 2005. +# Copyright Vladimir Prus 2007. +# 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) + +bjam_interface = __import__('bjam') + +import operator + +class BjamAction: + """Class representing bjam action defined from Python.""" + + def __init__(self, action_name, function): + self.action_name = action_name + self.function = function + + def __call__(self, targets, sources, property_set): + if self.function: + self.function(targets, sources, property_set) + # Bjam actions defined from Python have only the command + # to execute, and no associated jam procedural code. So + # passing 'property_set' to it is not necessary. + bjam_interface.call("set-update-action", self.action_name, + targets, sources, []) + +class BjamNativeAction: + """Class representing bjam action fully defined by Jam code.""" + + def __init__(self, action_name): + self.action_name = action_name + + def __call__(self, targets, sources, property_set): + if property_set: + bjam_interface.call("set-update-action", self.action_name, + targets, sources, property_set.raw()) + else: + bjam_interface.call("set-update-action", self.action_name, + targets, sources, []) + +action_modifiers = {"updated": 0x01, + "together": 0x02, + "ignore": 0x04, + "quietly": 0x08, + "piecemeal": 0x10, + "existing": 0x20} + +class Engine: + """ The abstract interface to a build engine. + + For now, the naming of targets, and special handling of some + target variables like SEARCH and LOCATE make this class coupled + to bjam engine. + """ + def __init__ (self): + self.actions = {} + + def add_dependency (self, targets, sources): + """Adds a dependency from 'targets' to 'sources' + + Both 'targets' and 'sources' can be either list + of target names, or a single target name. + """ + if isinstance (targets, str): + targets = [targets] + if isinstance (sources, str): + sources = [sources] + + for target in targets: + for source in sources: + self.do_add_dependency (target, source) + + def set_target_variable (self, targets, variable, value, append=0): + """ Sets a target variable. + + The 'variable' will be available to bjam when it decides + where to generate targets, and will also be available to + updating rule for that 'taret'. + """ + if isinstance (targets, str): + targets = [targets] + + for target in targets: + self.do_set_target_variable (target, variable, value, append) + + def set_update_action (self, action_name, targets, sources, properties): + """ Binds a target to the corresponding update action. + If target needs to be updated, the action registered + with action_name will be used. + The 'action_name' must be previously registered by + either 'register_action' or 'register_bjam_action' + method. + """ + if isinstance (targets, str): + targets = [targets] + self.do_set_update_action (action_name, targets, sources, properties) + + def register_action (self, action_name, command, bound_list = [], flags = [], + function = None): + """Creates a new build engine action. + + Creates on bjam side an action named 'action_name', with + 'command' as the command to be executed, 'bound_variables' + naming the list of variables bound when the command is executed + and specified flag. + If 'function' is not None, it should be a callable taking three + parameters: + - targets + - sources + - instance of the property_set class + This function will be called by set_update_action, and can + set additional target variables. + """ + if self.actions.has_key(action_name): + raise "Bjam action %s is already defined" % action_name + + assert(isinstance(flags, list)) + + bjam_flags = reduce(operator.or_, + (action_modifiers[flag] for flag in flags), 0) + + bjam_interface.define_action(action_name, command, bound_list, bjam_flags) + + self.actions[action_name] = BjamAction(action_name, function) + + def register_bjam_action (self, action_name): + """Informs self that 'action_name' is declared in bjam. + + From this point, 'action_name' is a valid argument to the + set_update_action method. The action_name should be callable + in the global module of bjam. + """ + + # We allow duplicate calls to this rule for the same + # action name. This way, jamfile rules that take action names + # can just register them without specially checking if + # action is already registered. + if not self.actions.has_key(action_name): + self.actions[action_name] = BjamNativeAction(action_name) + + # Overridables + + + def do_set_update_action (self, action_name, targets, sources, property_set): + action = self.actions.get(action_name) + if not action: + raise "No action %s was registered" % action_name + action(targets, sources, property_set) + + def do_set_target_variable (self, target, variable, value, append): + if append: + bjam_interface.call("set-target-variable", target, variable, value, "true") + else: + bjam_interface.call("set-target-variable", target, variable, value) + + def do_add_dependency (self, target, source): + bjam_interface.call("DEPENDS", target, source) + + diff --git a/v2/build/errors.py b/v2/build/errors.py new file mode 100644 index 000000000..1a85a9177 --- /dev/null +++ b/v2/build/errors.py @@ -0,0 +1,122 @@ +# Status: being written afresh by Vladimir Prus + +# Copyright 2007 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# This file is supposed to implement error reporting for Boost.Build. +# Experience with jam version has shown that printing full backtrace +# on each error is buffling. Further, for errors printed after parsing -- +# during target building, the stacktrace does not even mention what +# target is being built. + +# This module implements explicit contexts -- where other code can +# communicate which projects/targets are being built, and error +# messages will show those contexts. For programming errors, +# Python assertions are to be used. + +import bjam +import traceback +import sys + +def format(message, prefix=""): + parts = message.split("\n") + return "\n".join(prefix+p for p in parts) + + +class Context: + + def __init__(self, message, nested=None): + self.message_ = message + self.nested_ = nested + + def report(self, indent=""): + print indent + " -", self.message_ + if self.nested_: + print indent + " declared at:" + for n in self.nested_: + n.report(indent + " ") + +class JamfileContext: + + def __init__(self): + raw = bjam.backtrace() + self.raw_ = raw + + def report(self, indent=""): + for r in self.raw_: + print indent + " - %s:%s" % (r[0], r[1]) + +class ExceptionWithUserContext(Exception): + + def __init__(self, message, context, + original_exception=None, original_tb=None, stack=None): + Exception.__init__(self, message) + self.context_ = context + self.original_exception_ = original_exception + self.original_tb_ = original_tb + self.stack_ = stack + + def report(self): + print "error:", self.message + if self.original_exception_: + print format(self.original_exception_.message, " ") + print + print " error context (most recent first):" + for c in self.context_[::-1]: + c.report() + print + if "--stacktrace" in bjam.variable("ARGV"): + if self.original_tb_: + traceback.print_tb(self.original_tb_) + elif self.stack_: + for l in traceback.format_list(self.stack_): + print l, + else: + print " use the '--stacktrace' option to get Python stacktrace" + print + +def user_error_checkpoint(callable): + def wrapper(self, *args): + errors = self.manager().errors() + try: + return callable(self, *args) + except ExceptionWithUserContext, e: + raise + except Exception, e: + errors.handle_stray_exception(e) + finally: + errors.pop_user_context() + + return wrapper + +class Errors: + + def __init__(self): + self.contexts_ = [] + + def push_user_context(self, message, nested=None): + self.contexts_.append(Context(message, nested)) + + def pop_user_context(self): + del self.contexts_[-1] + + def push_jamfile_context(self): + self.contexts_.append(JamfileContext()) + + def pop_jamfile_context(self): + del self.contexts_[-1] + + def capture_user_context(self): + return self.contexts_[:] + + def handle_stray_exception(self, e): + raise ExceptionWithUserContext("unexpected exception", self.contexts_[:], + e, sys.exc_info()[2]) + def __call__(self, message): + raise ExceptionWithUserContext(message, self.contexts_[:], + stack=traceback.extract_stack()) + + + + diff --git a/v2/build/feature.py b/v2/build/feature.py new file mode 100644 index 000000000..43701c29d --- /dev/null +++ b/v2/build/feature.py @@ -0,0 +1,891 @@ +# Status: mostly ported. +# TODO: carry over tests. +# Base revision: 40480 +# +# Copyright 2001, 2002, 2003 Dave Abrahams +# Copyright 2002, 2006 Rene Rivera +# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# TODO: stop using grists to identify the name of features? +# create a class for Features and Properties? +# represent conditions using object trees, composite pattern? + +import re + +from b2.util import set, utility +from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, to_seq +from b2.exceptions import * + +__re_split_subfeatures = re.compile ('<(.*):(.*)>') +__re_no_hyphen = re.compile ('^([^:]+)$') +__re_slash_or_backslash = re.compile (r'[\\/]') + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __all_attributes, __all_features, __implicit_features, __composite_properties + global __features_with_attributes, __subfeature_value_to_name, __all_top_features, __free_features + global __all_subfeatures + + # The list with all attribute names. + __all_attributes = [ 'implicit', + 'executed', + 'composite', + 'optional', + 'symmetric', + 'free', + 'incidental', + 'path', + 'dependency', + 'propagated', + 'link-incompatible', + 'subfeature', + 'order-sensitive' + ] + + # A map containing all features. The key is the gristed feature name. The value is a map with: + # 'values': [], + # 'attributes': [], + # 'subfeatures': [], + # 'default': None + __all_features = {} + + # All non-subfeatures. + __all_top_features = [] + + # Maps valus to the corresponding implicit feature + __implicit_features = {} + + # A map containing all composite properties. The key is the name of the property. The value is a map with: + # 'components': [] + __composite_properties = {} + + __features_with_attributes = {} + for attribute in __all_attributes: + __features_with_attributes [attribute] = [] + + # Maps a value to the corresponding subfeature name. + __subfeature_value_to_name = {} + + # All free features + __free_features = [] + + __all_subfeatures = [] + +reset () + +def enumerate (): + """ Returns an iterator to the features map. + """ + return __all_features.iteritems () + +# FIXME: prepare-test/finish-test? + +def feature (name, values, attributes = []): + """ Declares a new feature with the given name, values, and attributes. + name: the feature name + values: a sequence of the allowable values - may be extended later with feature.extend + attributes: a sequence of the feature's attributes (e.g. implicit, free, propagated, ...) + """ + name = add_grist (name) + + __validate_feature_attributes (name, attributes) + + feature = { + 'values': [], + 'attributes': attributes, + 'subfeatures': [], + 'default': None + } + __all_features [name] = feature + + feature ['attributes'] = attributes + + for attribute in attributes: + __features_with_attributes [attribute].append (name) + + if 'subfeature' in attributes: + __all_subfeatures.append(name) + else: + __all_top_features.append(name) + + extend (name, values) + + # FIXME: why his is needed. + if 'free' in attributes: + __free_features.append (name) + +def set_default (feature, value): + """ Sets the default value of the given feature, overriding any previous default. + feature: the name of the feature + value: the default value to assign + """ + + if isinstance(feature, list): + feature = feature[0] + + feature = add_grist (feature) + f = __all_features [feature] + + if isinstance(value, list): + value = value[0] + + values = f['values'] + if not value in values: + raise InvalidValue ("The specified default value, '%s' is invalid.\n" % value + "allowed values are: %s" % values) + + f ['default'] = value + +def defaults (features): + """ Returns the default property values for the given features. + """ + result = [] + for f in features: + attributes = __all_features [f]['attributes'] + if not 'free' in attributes and not 'optional' in attributes: + defaults = __all_features [f]['default'] + if defaults: + result.append (replace_grist (defaults, f)) + + return result + +def valid (names): + """ Returns true iff all elements of names are valid features. + """ + def valid_one (name): return __all_features.has_key (name) + + if isinstance (names, str): + return valid_one (names) + else: + return [ valid_one (name) for name in names ] + +def attributes (feature): + """ Returns the attributes of the given feature. + """ + return __all_features [feature]['attributes'] + +def values (feature): + """ Return the values of the given feature. + """ + validate_feature (feature) + return __all_features [feature]['values'] + +def is_implicit_value (value_string): + """ Returns true iff 'value_string' is a value_string + of an implicit feature. + """ + v = value_string.split('-') + + if not __implicit_features.has_key(v[0]): + return False + + feature = __implicit_features[v[0]] + + for subvalue in (v[1:]): + if not __find_implied_subfeature(feature, subvalue, v[0]): + return False + + return True + +def implied_feature (implicit_value): + """ Returns the implicit feature associated with the given implicit value. + """ + components = implicit_value.split('-') + + if not __implicit_features.has_key(components[0]): + raise InvalidValue ("'%s' is not a value of an implicit feature" % implicit_value) + + return __implicit_features[components[0]] + +def __find_implied_subfeature (feature, subvalue, value_string): + feature = add_grist (feature) + if value_string == None: value_string = '' + + if not __subfeature_value_to_name.has_key (feature) \ + or not __subfeature_value_to_name [feature].has_key (value_string) \ + or not __subfeature_value_to_name [feature][value_string].has_key (subvalue): + return None + + return __subfeature_value_to_name[feature][value_string][subvalue] + +# Given a feature and a value of one of its subfeatures, find the name +# of the subfeature. If value-string is supplied, looks for implied +# subfeatures that are specific to that value of feature +# feature # The main feature name +# subvalue # The value of one of its subfeatures +# value-string # The value of the main feature + +def implied_subfeature (feature, subvalue, value_string): + result = __find_implied_subfeature (feature, subvalue, value_string) + if not result: + raise InvalidValue ("'%s' is not a known subfeature value of '%s%s'" % (subvalue, feature, value_string)) + + return result + +def validate_feature (name): + """ Checks if all name is a valid feature. Otherwise, raises an exception. + """ + x = valid (name) + if not x: + raise InvalidFeature ("'%s' is not a valid feature name" % name) + +def valid (names): + """ Returns true iff all elements of names are valid features. + """ + def valid_one (name): return __all_features.has_key (name) + + if isinstance (names, str): + return valid_one (names) + else: + return [ valid_one (name) for name in names ] + +def __expand_subfeatures_aux (feature, value, dont_validate = False): + """ Helper for expand_subfeatures. + Given a feature and value, or just a value corresponding to an + implicit feature, returns a property set consisting of all component + subfeatures and their values. For example: + + expand_subfeatures gcc-2.95.2-linux-x86 + -> gcc 2.95.2 linux x86 + equivalent to: + expand_subfeatures gcc-2.95.2-linux-x86 + + feature: The name of the feature, or empty if value corresponds to an implicit property + value: The value of the feature. + dont_validate: If True, no validation of value string will be done. + """ + if not feature: + feature = implied_feature(value) + else: + validate_feature(feature) + + if not dont_validate: + validate_value_string(feature, value) + + components = value.split ("-") + + # get the top-level feature's value + value = replace_grist(components[0], '') + + result = [ replace_grist(components[0], feature) ] + + subvalues = components[1:] + + while len(subvalues) > 0: + subvalue = subvalues [0] # pop the head off of subvalues + subvalues = subvalues [1:] + + subfeature = __find_implied_subfeature (feature, subvalue, value) + + # If no subfeature was found, reconstitute the value string and use that + if not subfeature: + result = '-'.join(components) + result = replace_grist (result, feature) + return [result] + + f = ungrist (feature) + # FIXME: why grist includes '<>'? + result.append (replace_grist (subvalue, '<' + f + '-' + subfeature + '>')) + + return result + +def expand_subfeatures (properties, dont_validate = False): + """ + Make all elements of properties corresponding to implicit features + explicit, and express all subfeature values as separate properties + in their own right. For example, the property + + gcc-2.95.2-linux-x86 + + might expand to + + gcc 2.95.2 linux x86 + + properties: A sequence with elements of the form + value-string or just value-string in the + case of implicit features. + : dont_validate: If True, no validation of value string will be done. + """ + result = [] + for p in properties: + p_grist = get_grist (p) + # Don't expand subfeatures in subfeatures + if ':' in p_grist: + result.append (p) + else: + result.extend (__expand_subfeatures_aux (p_grist, replace_grist (p, ''), dont_validate)) + + return result + + + +# rule extend was defined as below: + # Can be called three ways: + # + # 1. extend feature : values * + # 2. extend subfeature : values * + # 3. extend value-string subfeature : values * + # + # * Form 1 adds the given values to the given feature + # * Forms 2 and 3 add subfeature values to the given feature + # * Form 3 adds the subfeature values as specific to the given + # property value-string. + # + #rule extend ( feature-or-property subfeature ? : values * ) +# +# Now, the specific rule must be called, depending on the desired operation: +# extend_feature +# extend_subfeature + +def extend (name, values): + """ Adds the given values to the given feature. + """ + name = add_grist (name) + __validate_feature (name) + feature = __all_features [name] + + if 'implicit' in feature ['attributes']: + for v in values: + if __implicit_features.has_key (v): + raise BaseException ("'%s' is already associated with the feature '%s'" % (v, __implicit_features [v])) + + __implicit_features[v] = name + + if len (feature ['values']) == 0 and len (values) > 0: + # This is the first value specified for this feature, + # take it as default value + feature ['default'] = values[0] + + feature['values'].extend (values) + +def validate_value_string (feature, value_string): + """ Checks that value-string is a valid value-string for the given feature. + """ + f = __all_features [feature] + if 'free' in f ['attributes'] or value_string in f ['values']: + return + + values = [value_string] + + if f['subfeatures']: + values = value_string.split('-') + + # An empty value is allowed for optional features + if not values[0] in f['values'] and \ + (values[0] or not 'optional' in f['attributes']): + raise InvalidValue ("'%s' is not a known value of feature '%s'\nlegal values: '%s'" % (values [0], feature, f ['values'])) + + for v in values [1:]: + # this will validate any subfeature values in value-string + implied_subfeature(feature, v, values[0]) + + +""" Extends the given subfeature with the subvalues. If the optional + value-string is provided, the subvalues are only valid for the given + value of the feature. Thus, you could say that + mingw is specifc to gcc-2.95.2 as follows: + + extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ; + + feature: The feature whose subfeature is being extended. + + value-string: If supplied, specifies a specific value of the + main feature for which the new subfeature values + are valid. + + subfeature: The name of the subfeature. + + subvalues: The additional values of the subfeature being defined. +""" +def extend_subfeature (feature, value_string, subfeature, subvalues): + feature = add_grist (feature) + validate_feature (feature) + + if value_string: + validate_value_string (feature, value_string) + + subfeature_name = __get_subfeature_name (subfeature, value_string) + + f = ungrist (feature) + extend (f + '-' + subfeature_name, subvalues) ; + + __add_to_subfeature_value_to_name_map (feature, value_string, subfeature_name, subvalues) + +def subfeature (feature_name, value_string, subfeature, subvalues, attributes = []): + """ Declares a subfeature. + feature_name: Root feature that is not a subfeature. + value_string: An optional value-string specifying which feature or + subfeature values this subfeature is specific to, + if any. + subfeature: The name of the subfeature being declared. + subvalues: The allowed values of this subfeature. + attributes: The attributes of the subfeature. + """ + feature_name = add_grist (feature_name) + validate_feature (feature_name) + + # Add grist to the subfeature name if a value-string was supplied + subfeature_name = __get_subfeature_name (subfeature, value_string) + + if subfeature_name in __all_features [feature_name]['subfeatures']: + message = "'%s' already declared as a subfeature of '%s'" % (subfeature, feature_name) + message += " specific to '%s'" % value_string + raise BaseException (message) + + __all_features [feature_name]['subfeatures'].append (subfeature_name) + + # First declare the subfeature as a feature in its own right + f = ungrist (feature_name) + feature (f + '-' + subfeature_name, subvalues, attributes + ['subfeature']) + + # Now make sure the subfeature values are known. + extend_subfeature (feature_name, value_string, subfeature, subvalues) + +def compose (composite_property, component_properties): + """ Sets the components of the given composite property. + """ + component_properties = to_seq (component_properties) + + feature = get_grist (composite_property) + if not 'composite' in attributes (feature): + raise BaseException ("'%s' is not a composite feature" % feature) + + if __composite_properties.has_key (composite_property): + raise BaseException ('components of "%s" already set: %s' % (composite_property, str (__composite_properties [composite_property]['components']))) + + if composite_property in component_properties: + raise BaseException ('composite property "%s" cannot have itself as a component' % composite_property) + + entry = { 'components': component_properties } + __composite_properties [composite_property] = entry + + +def expand_composite (property): + result = [ property ] + if __composite_properties.has_key (property): + for p in __composite_properties [property]['components']: + result.extend (expand_composite (p)) + return result + + +def get_values (feature, properties): + """ Returns all values of the given feature specified by the given property set. + """ + result = [] + for p in properties: + if get_grist (p) == feature: + result.append (replace_grist (p, '')) + + return result + +def free_features (): + """ Returns all free features. + """ + return __free_features + +def expand_composites (properties): + """ Expand all composite properties in the set so that all components + are explicitly expressed. + """ + explicit_features = get_grist (properties) + + result = [] + + # now expand composite features + for p in properties: + expanded = expand_composite (p) + + for x in expanded: + if not x in result: + f = get_grist (x) + + if f in __free_features: + result.append (x) + elif not x in properties: # x is the result of expansion + if not f in explicit_features: # not explicitly-specified + if f in get_grist (result): + raise FeatureConflict ("expansions of composite features result in " + "conflicting values for '%s'\nvalues: '%s'\none contributing composite property was '%s'" % (f, + get_values (f, result) + [replace_grist (x, '')], p)) + else: + result.append (x) + elif f in get_grist (result): + raise FeatureConflict ("explicitly-specified values of non-free feature '%s' conflict\n" + "existing values: '%s'\nvalue from expanding '%s': '%s'" % (f, + get_values (f, properties), p, replace_grist (x, ''))) + else: + result.append (x) + + return result + +def is_subfeature_of (parent_property, f): + """ Return true iff f is an ordinary subfeature of the parent_property's + feature, or if f is a subfeature of the parent_property's feature + specific to the parent_property's value. + """ + if not valid (f) or not 'subfeature' in __all_features [f]['attributes']: + return False + + specific_subfeature = __re_split_subfeatures.match (f) + + if specific_subfeature: + # The feature has the form + # , + # e.g. + feature_value = split_top_feature(specific_subfeature.group(1)) + if replace_grist (feature_value [1], '<' + feature_value [0] + '>') == parent_property: + return True + else: + # The feature has the form , + # e.g. + top_sub = split_top_feature (ungrist (f)) + + if top_sub [1] and add_grist (top_sub [0]) == get_grist (parent_property): + return True + + return False + +def __is_subproperty_of (parent_property, p): + """ As is_subfeature_of, for subproperties. + """ + return is_subfeature_of (parent_property, get_grist (p)) + + +# Returns true iff the subvalue is valid for the feature. When the +# optional value-string is provided, returns true iff the subvalues +# are valid for the given value of the feature. +def is_subvalue(feature, value_string, subfeature, subvalue): + + if not value_string: + value_string = '' + + if not __subfeature_value_to_name.has_key(feature): + return False + + if not __subfeature_value_to_name[feature].has_key(value_string): + return False + + if not __subfeature_value_to_name[feature][value_string].has_key(subvalue): + return False + + if __subfeature_value_to_name[feature][value_string][subvalue]\ + != subfeature: + return False + + return True + + + + +def implied_subfeature (feature, subvalue, value_string): + result = __find_implied_subfeature (feature, subvalue, value_string) + if not result: + raise InvalidValue ("'%s' is not a known subfeature value of '%s%s'" % (subvalue, feature, value_string)) + + return result + + +def expand (properties): + """ Given a property set which may consist of composite and implicit + properties and combined subfeature values, returns an expanded, + normalized property set with all implicit features expressed + explicitly, all subfeature values individually expressed, and all + components of composite properties expanded. Non-free features + directly expressed in the input properties cause any values of + those features due to composite feature expansion to be dropped. If + two values of a given non-free feature are directly expressed in the + input, an error is issued. + """ + expanded = expand_subfeatures (properties) + return expand_composites (expanded) + + +def split_top_feature (feature_plus): + """ Given an ungristed string, finds the longest prefix which is a + top-level feature name followed by a dash, and return a pair + consisting of the parts before and after that dash. More + interesting than a simple split because feature names can contain + dashes. + """ + e = feature_plus.split ('-') + f = e [0] + + v = None + while e: + if add_grist (f) in __all_top_features: + if len (e) > 1: + after = '-'.join (e [1:]) + else: + after = '' + + v = (f, after) + + e = e [1:] + f = f + '-' + if len (e): f += e [0] + + return v + +def add_defaults (properties): + """ Given a set of properties, add default values for features not + represented in the set. + Note: if there's there's ordinary feature F1 and composite feature + F2, which includes some value for F1, and both feature have default values, + then the default value of F1 will be added, not the value in F2. This might + not be right idea: consider + + feature variant : debug ... ; + debug : .... on + feature : off on ; + + Here, when adding default for an empty property set, we'll get + + debug off + + and that's kind of strange. + """ + result = [ x for x in properties ] + + for v in replace_grist (properties, ''): + if v in properties: + raise BaseException ("'add_defaults' requires explicitly specified features, but '%s' appears to be the value of an un-expanded implicit feature" % v) + + # We don't add default for elements with ":" inside. This catches: + # 1. Conditional properties --- we don't want debug:DEBUG + # to be takes as specified value for + # 2. Free properties with ":" in values. We don't care, since free properties + # don't have defaults. + xproperties = [ property for property in properties if __re_no_hyphen.match (property) ] + missing_top = set.difference (__all_top_features, get_grist (xproperties)) + more = defaults (missing_top) + result += more + xproperties += more + + # Add defaults for subfeatures of features which are present + for p in xproperties: + gp = get_grist (p) + s = [] + if __all_features.has_key (gp): + s = __all_features [gp]['subfeatures'] + f = ungrist (gp) + + xbase = ['<%s-%s>' % (f, xs) for xs in s] + + missing_subs = set.difference (xbase, get_grist (result)) + result += defaults (__select_subfeatures (p, missing_subs)) + + return result + +def minimize (properties): + """ Given an expanded property set, eliminate all redundancy: properties + which are elements of other (composite) properties in the set will + be eliminated. Non-symmetric properties equal to default values will be + eliminated, unless the override a value from some composite property. + Implicit properties will be expressed without feature + grist, and sub-property values will be expressed as elements joined + to the corresponding main property. + """ +# FXIME: the code below was in the original feature.jam file, however 'p' is not defined. +# # Precondition checking +# local implicits = [ set.intersection $(p:G=) : $(p:G) ] ; +# if $(implicits) +# { +# error minimize requires an expanded property set, but \"$(implicits[1])\" +# appears to be the value of an un-expanded implicit feature ; +# } + + # remove properties implied by composite features + components = [] + for property in properties: + if __composite_properties.has_key (property): + components.extend (__composite_properties [property]['components']) + + x = set.difference (properties, components) + + # handle subfeatures and implicit features + x = __move_subfeatures_to_the_end (x) + + result = [] + while x: + fullp = x [0] + p = fullp + f = get_grist (p) + v = replace_grist (p, '') + + # eliminate features in implicit properties. + if 'implicit' in __all_features [f]['attributes']: + p = v + + # locate all subproperties of $(x[1]) in the property set + subproperties = __select_subproperties (fullp, x) + + if subproperties: + # reconstitute the joined property name + subproperties.sort () + joined = p + '-' + '-'.join (replace_grist (subproperties, '')) + result.append (joined) + + x = set.difference (x [1:], subproperties) + + else: + # eliminate properties whose value is equal to feature's + # default and which are not symmetric and which do not + # contradict values implied by composite properties. + + # since all component properties of composites in the set + # have been eliminated, any remaining property whose + # feature is the same as a component of a composite in the + # set must have a non-redundant value. + if [fullp] != defaults ([f]) or 'symmetric' in attributes (f)\ + or get_grist (fullp) in get_grist (components): + result.append (p) + + x = x [1:] + + return result + + +def split (properties): + """ Given a property-set of the form + v1/v2/...vN-1/vN/vN+1/...vM + + Returns + v1 v2 ... vN-1 vN vN+1 ... vM + + Note that vN...vM may contain slashes. This is resilient to the + substitution of backslashes for slashes, since Jam, unbidden, + sometimes swaps slash direction on NT. + """ + + def split_one (properties): + pieces = re.split (__re_slash_or_backslash, properties) + result = [] + + for x in pieces: + if not get_grist (x) and len (result) > 0 and get_grist (result [-1]): + result = result [0:-1] + [ result [-1] + '/' + x ] + else: + result.append (x) + + return result + + if isinstance (properties, str): + return split_one (properties) + + result = [] + for p in properties: + result += split_one (p) + return result + + +def compress_subproperties (properties): + """ Combine all subproperties into their parent properties + + Requires: for every subproperty, there is a parent property. All + features are explicitly expressed. + + This rule probably shouldn't be needed, but + build-request.expand-no-defaults is being abused for unintended + purposes and it needs help + """ + result = [] + matched_subs = [] + for p in properties: + pg = get_grist (p) + if not pg: + raise BaseException ("Gristed variable exppected. Got '%s'." % p) + + if not 'subfeature' in __all_features [pg]['attributes']: + subs = __select_subproperties (p, properties) + + matched_subs.extend (subs) + + subvalues = '-'.join (get_value (subs)) + if subvalues: subvalues = '-' + subvalues + + result.append (p + subvalues) + + else: + all_subs.append (p) + + # TODO: this variables are used just for debugging. What's the overhead? + assert (set.equal (all_subs, matched_subs)) + + return result + +###################################################################################### +# Private methods + +def __select_subproperties (parent_property, properties): + return [ x for x in properties if __is_subproperty_of (parent_property, x) ] + +def __move_subfeatures_to_the_end (properties): + """ Helper for minimize, below - returns the list with + the same properties, but where all subfeatures + are in the end of the list + """ + x1 = [] + x2 = [] + for p in properties: + if 'subfeature' in __all_features [get_grist (p)]['attributes']: + x2.append (p) + + else: + x1.append (p) + + return x1 + x2 + +def __get_subfeature_name (subfeature, value_string): + if value_string == None: + prefix = '' + else: + prefix = value_string + ':' + + return prefix + subfeature + + +def __validate_feature_attributes (name, attributes): + for attribute in attributes: + if not attribute in __all_attributes: + raise InvalidAttribute ("unknown attributes: '%s' in feature declaration: '%s'" % (str (set.difference (attributes, __all_attributes)), name)) + + if name in __all_features: + raise AlreadyDefined ("feature '%s' already defined" % name) + elif 'implicit' in attributes and 'free' in attributes: + raise InvalidAttribute ("free features cannot also be implicit (in declaration of feature '%s')" % name) + elif 'free' in attributes and 'propagated' in attributes: + raise InvalidAttribute ("free features cannot also be propagated (in declaration of feature '%s')" % name) + + +def __validate_feature (feature): + """ Generates an error if the feature is unknown. + """ + if not __all_features.has_key (feature): + raise BaseException ('unknown feature "%s"' % feature) + +def __add_to_subfeature_value_to_name_map (feature, value_string, subfeature_name, subvalues): + # provide a way to get from the given feature or property and + # subfeature value to the subfeature name. + if value_string == None: value_string = '' + + if not __subfeature_value_to_name.has_key (feature): + __subfeature_value_to_name [feature] = {} + + if not __subfeature_value_to_name [feature].has_key (value_string): + __subfeature_value_to_name [feature][value_string] = {} + + for subvalue in subvalues: + __subfeature_value_to_name [feature][value_string][subvalue] = subfeature_name + + +def __select_subfeatures (parent_property, features): + """ Given a property, return the subset of features consisting of all + ordinary subfeatures of the property's feature, and all specific + subfeatures of the property's feature which are conditional on the + property's value. + """ + return [f for f in features if is_subfeature_of (parent_property, f)] + +# FIXME: copy over tests. diff --git a/v2/build/generators.py b/v2/build/generators.py new file mode 100644 index 000000000..6c02a6359 --- /dev/null +++ b/v2/build/generators.py @@ -0,0 +1,967 @@ +# Status: being ported by Vladimir Prus +# Base revision: 41557 +# TODO: replace the logging with dout + +# Copyright Vladimir Prus 2002. +# Copyright Rene Rivera 2006. +# +# 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) + +# Manages 'generators' --- objects which can do transformation between different +# target types and contain algorithm for finding transformation from sources +# to targets. +# +# The main entry point to this module is generators.construct rule. It is given +# a list of source targets, desired target type and a set of properties. +# It starts by selecting 'viable generators', which have any chances of producing +# the desired target type with the required properties. Generators are ranked and +# a set of most specific ones is selected. +# +# The most specific generators have their 'run' methods called, with the properties +# and list of sources. Each one selects target which can be directly consumed, and +# tries to convert the remaining ones to the types it can consume. This is done +# by recursively calling 'construct' with all consumable types. +# +# If the generator has collected all the targets it needs, it creates targets +# corresponding to result, and returns it. When all generators have been run, +# results of one of them are selected and returned as result. +# +# It's quite possible that 'construct' returns more targets that it was asked for. +# For example, it was asked to target type EXE, but the only found generators produces +# both EXE and TDS (file with debug) information. The extra target will be returned. +# +# Likewise, when generator tries to convert sources to consumable types, it can get +# more targets that it was asked for. The question is what to do with extra targets. +# Boost.Build attempts to convert them to requested types, and attempts as early as +# possible. Specifically, this is done after invoking each generator. (Later I'll +# document the rationale for trying extra target conversion at that point). +# +# That early conversion is not always desirable. Suppose a generator got a source of +# type Y and must consume one target of type X_1 and one target of type X_2. +# When converting Y to X_1 extra target of type Y_2 is created. We should not try to +# convert it to type X_1, because if we do so, the generator will get two targets +# of type X_1, and will be at loss as to which one to use. Because of that, the +# 'construct' rule has a parameter, telling if multiple targets can be returned. If +# the parameter is false, conversion of extra targets is not performed. + + +import re +import cStringIO +import os.path + +from virtual_target import Subvariant +import virtual_target, type, property_set, property +from b2.util.logger import * +from b2.util.utility import * +from b2.util import set +from b2.util.sequence import unique +import b2.util.sequence as sequence +from b2.manager import get_manager + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __generators, __type_to_generators, __generators_for_toolset, __construct_stack + global __overrides, __active_generators + global __viable_generators_cache, __viable_source_types_cache + + __generators = {} + __type_to_generators = {} + __generators_for_toolset = {} + __overrides = {} + + # TODO: can these be global? + __construct_stack = [] + __viable_generators_cache = {} + __viable_source_types_cache = {} + __active_generators = [] + +reset () + +_re_separate_types_prefix_and_postfix = re.compile ('([^\\(]*)(\\((.*)%(.*)\\))?') +_re_match_type = re.compile('([^\\(]*)(\\(.*\\))?') + + +__debug = None +__indent = "" + +def debug(): + global __debug + if __debug is None: + __debug = "--debug-generators" in bjam.variable("ARGV") + return __debug + +def increase_indent(): + global __indent + __indent += " " + +def decrease_indent(): + global __indent + __indent = __indent[0:-4] + +def dout(message): + if debug(): + print __indent + message + +def normalize_target_list (targets): + """ Takes a vector of 'virtual-target' instances and makes a normalized + representation, which is the same for given set of targets, + regardless of their order. + """ + return (targets[0], targets[1].sort ()) + + +class Generator: + """ Creates a generator. + manager: the build manager. + id: identifies the generator + + rule: the rule which sets up build actions. + + composing: whether generator processes each source target in + turn, converting it to required types. + Ordinary generators pass all sources together to + recusrive generators.construct_types call. + + source_types (optional): types that this generator can handle + + target_types_and_names: types the generator will create and, optionally, names for + created targets. Each element should have the form + type["(" name-pattern ")"] + for example, obj(%_x). Name of generated target will be found + by replacing % with the name of source, provided explicit name + was not specified. + + requirements (optional) + + NOTE: all subclasses must have a similar signature for clone to work! + """ + def __init__ (self, id, composing, source_types, target_types_and_names, requirements): + assert(not isinstance(source_types, str)) + assert(not isinstance(target_types_and_names, str)) + self.id_ = id + self.composing_ = composing + self.source_types_ = source_types + self.target_types_and_names_ = target_types_and_names + self.requirements_ = requirements + + self.target_types_ = [] + self.name_prefix_ = [] + self.name_postfix_ = [] + + for e in target_types_and_names: + # Create three parallel lists: one with the list of target types, + # and two other with prefixes and postfixes to be added to target + # name. We use parallel lists for prefix and postfix (as opposed + # to mapping), because given target type might occur several times, + # for example "H H(%_symbols)". + m = _re_separate_types_prefix_and_postfix.match (e) + + if not m: + raise BaseException ("Invalid type and name '%s' in declaration of type '%s'" % (e, id)) + + target_type = m.group (1) + if not target_type: target_type = '' + prefix = m.group (3) + if not prefix: prefix = '' + postfix = m.group (4) + if not postfix: postfix = '' + + self.target_types_.append (target_type) + self.name_prefix_.append (prefix) + self.name_postfix_.append (postfix) + + for x in self.source_types_: + type.validate (x) + + for x in self.target_types_: + type.validate (x) + + def clone (self, new_id, new_toolset_properties): + """ Returns another generator which differers from $(self) in + - id + - value to feature in properties + """ + return self.__class__ (new_id, + self.composing_, + self.source_types_, + self.target_types_and_names_, + # Note: this does not remove any subfeatures of + # which might cause problems + property.change (self.requirements_, '') + new_toolset_properties) + + def clone_and_change_target_type(self, base, type): + """Creates another generator that is the same as $(self), except that + if 'base' is in target types of $(self), 'type' will in target types + of the new generator.""" + target_types = [] + for t in self.target_types_and_names_: + m = _re_match_type.match(t) + assert m + + if m.group(1) == base: + if m.group(2): + target_types.append(type + m.group(2)) + else: + target_types.append(type) + else: + target_types.append(t) + + return self.__class__(self.id_, self.composing_, + self.source_types_, + target_types, + self.requirements_) + + + def id (self): + return self.id_ + + def source_types (self): + """ Returns the list of target type the generator accepts. + """ + return self.source_types_ + + def target_types (self): + """ Returns the list of target types that this generator produces. + It is assumed to be always the same -- i.e. it cannot change depending + list of sources. + """ + return self.target_types_ + + def requirements (self): + """ Returns the required properties for this generator. Properties + in returned set must be present in build properties if this + generator is to be used. If result has grist-only element, + that build properties must include some value of that feature. + """ + return self.requirements_ + + def match_rank (self, property_set_to_match): + """ Returns true if the generator can be run with the specified + properties. + """ + # See if generator's requirements are satisfied by + # 'properties'. Treat a feature name in requirements + # (i.e. grist-only element), as matching any value of the + # feature. + all_requirements = self.requirements () + + property_requirements = [] + feature_requirements = [] + for r in all_requirements: + if get_value (r): + property_requirements.append (r) + + else: + feature_requirements.append (r) + + properties_to_match = property_set_to_match.raw () + + return set.contains (property_requirements, properties_to_match) \ + and set.contains (feature_requirements, get_grist (properties_to_match)) + + def run (self, project, name, prop_set, sources): + """ Tries to invoke this generator on the given sources. Returns a + list of generated targets (instances of 'virtual-target'). + + project: Project for which the targets are generated. + + name: Determines the name of 'name' attribute for + all generated targets. See 'generated_targets' method. + + prop_set: Desired properties for generated targets. + + sources: Source targets. + """ + + if project.manager ().logger ().on (): + project.manager ().logger ().log (__name__, " generator '%s'" % self.id_) + project.manager ().logger ().log (__name__, " composing: '%s'" % self.composing_) + + if not self.composing_ and len (sources) > 1 and len (self.source_types_) > 1: + raise BaseException ("Unsupported source/source_type combination") + + # We don't run composing generators if no name is specified. The reason + # is that composing generator combines several targets, which can have + # different names, and it cannot decide which name to give for produced + # target. Therefore, the name must be passed. + # + # This in effect, means that composing generators are runnable only + # at top-level of transofrmation graph, or if name is passed explicitly. + # Thus, we dissallow composing generators in the middle. For example, the + # transofrmation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE won't be allowed + # (the OBJ -> STATIC_LIB generator is composing) + if not self.composing_ or name: + return self.run_really (project, name, prop_set, sources) + else: + return [] + + def run_really (self, project, name, prop_set, sources): + + # consumed: Targets that this generator will consume directly. + # bypassed: Targets that can't be consumed and will be returned as-is. + + if self.composing_: + (consumed, bypassed) = self.convert_multiple_sources_to_consumable_types (project, prop_set, sources) + else: + (consumed, bypassed) = self.convert_to_consumable_types (project, name, prop_set, sources) + + result = [] + if consumed: + result = self.construct_result (consumed, project, name, prop_set) + result.extend (bypassed) + + if result: + if project.manager ().logger ().on (): + project.manager ().logger ().log (__name__, " SUCCESS: ", result) + + else: + project.manager ().logger ().log (__name__, " FAILURE") + + return result + + def construct_result (self, consumed, project, name, prop_set): + """ Constructs the dependency graph that will be returned by this + generator. + consumed: Already prepared list of consumable targets + If generator requires several source files will contain + exactly len $(self.source_types_) targets with matching types + Otherwise, might contain several targets with the type of + self.source_types_ [0] + project: + name: + prop_set: Properties to be used for all actions create here + """ + result = [] + # If this is 1->1 transformation, apply it to all consumed targets in order. + if len (self.source_types_) < 2 and not self.composing_: + + for r in consumed: + result.extend (self.generated_targets ([r], prop_set, project, name)) + + else: + + if consumed: + result.extend (self.generated_targets (consumed, prop_set, project, name)) + + return result + + def determine_output_name(self, sources): + """Determine the name of the produced target from the + names of the sources.""" + + # The simple case if when a name + # of source has single dot. Then, we take the part before + # dot. Several dots can be caused by: + # - Using source file like a.host.cpp + # - A type which suffix has a dot. Say, we can + # type 'host_cpp' with extension 'host.cpp'. + # In the first case, we want to take the part till the last + # dot. In the second case -- no sure, but for now take + # the part till the last dot too. + name = os.path.splitext(sources[0].name())[0] + + for s in sources[1:]: + n2 = os.path.splitext(s.name()) + if n2 != name: + get_manager().errors()( + "%s: source targets have different names: cannot determine target name" + % (self.id_)) + + # Names of sources might include directory. We should strip it. + return os.path.basename(name) + + + def generated_targets (self, sources, prop_set, project, name): + """ Constructs targets that are created after consuming 'sources'. + The result will be the list of virtual-target, which the same length + as 'target_types' attribute and with corresponding types. + + When 'name' is empty, all source targets must have the same value of + the 'name' attribute, which will be used instead of the 'name' argument. + + The value of 'name' attribute for each generated target will be equal to + the 'name' parameter if there's no name pattern for this type. Otherwise, + the '%' symbol in the name pattern will be replaced with the 'name' parameter + to obtain the 'name' attribute. + + For example, if targets types are T1 and T2(with name pattern "%_x"), suffixes + for T1 and T2 are .t1 and t2, and source if foo.z, then created files would + be "foo.t1" and "foo_x.t2". The 'name' attribute actually determined the + basename of a file. + + Note that this pattern mechanism has nothing to do with implicit patterns + in make. It's a way to produce target which name is different for name of + source. + """ + if not name: + name = self.determine_output_name(sources) + + # Assign an action for each target + action = self.action_class() + a = action (project.manager(), sources, self.id_, prop_set) + + # Create generated target for each target type. + targets = [] + pre = self.name_prefix_ + post = self.name_postfix_ + for t in self.target_types_: + generated_name = pre[0] + name + post[0] + pre = pre[1:] + post = post[1:] + + targets.append(virtual_target.FileTarget(generated_name, False, t, project, a)) + + return [ project.manager().virtual_targets().register(t) for t in targets ] + + def convert_to_consumable_types (self, project, name, prop_set, sources, only_one=False): + """ Attempts to convert 'source' to the types that this generator can + handle. The intention is to produce the set of targets can should be + used when generator is run. + only_one: convert 'source' to only one of source types + if there's more that one possibility, report an + error. + + Returns a pair: + consumed: all targets that can be consumed. + bypassed: all targets that cannot be consumed. + """ + consumed = [] + bypassed = [] + missing_types = [] + + if len (sources) > 1: + # Don't know how to handle several sources yet. Just try + # to pass the request to other generator + missing_types = self.source_types_ + + else: + (c, m) = self.consume_directly (sources [0]) + consumed += c + missing_types += m + + # No need to search for transformation if + # some source type has consumed source and + # no more source types are needed. + if only_one and consumed: + missing_types = [] + + #TODO: we should check that only one source type + #if create of 'only_one' is true. + # TODO: consider if consuned/bypassed separation should + # be done by 'construct_types'. + + if missing_types: + transformed = construct_types (project, name, missing_types, prop_set, sources) + + # Add targets of right type to 'consumed'. Add others to + # 'bypassed'. The 'generators.construct' rule has done + # its best to convert everything to the required type. + # There's no need to rerun it on targets of different types. + + # NOTE: ignoring usage requirements + for t in transformed[1]: + if t.type() in missing_types: + consumed.append(t) + + else: + bypassed.append(t) + + consumed = unique(consumed) + bypassed = unique(bypassed) + + # remove elements of 'bypassed' that are in 'consumed' + + # Suppose the target type of current generator, X is produced from + # X_1 and X_2, which are produced from Y by one generator. + # When creating X_1 from Y, X_2 will be added to 'bypassed' + # Likewise, when creating X_2 from Y, X_1 will be added to 'bypassed' + # But they are also in 'consumed'. We have to remove them from + # bypassed, so that generators up the call stack don't try to convert + # them. + + # In this particular case, X_1 instance in 'consumed' and X_1 instance + # in 'bypassed' will be the same: because they have the same source and + # action name, and 'virtual-target.register' won't allow two different + # instances. Therefore, it's OK to use 'set.difference'. + + bypassed = set.difference(bypassed, consumed) + + return (consumed, bypassed) + + + def convert_multiple_sources_to_consumable_types (self, project, prop_set, sources): + """ Converts several files to consumable types. + """ + consumed = [] + bypassed = [] + + # We process each source one-by-one, trying to convert it to + # a usable type. + for s in sources: + # TODO: need to check for failure on each source. + (c, b) = self.convert_to_consumable_types (project, None, prop_set, [s], True) + if not c: + project.manager ().logger ().log (__name__, " failed to convert ", s) + + consumed.extend (c) + bypassed.extend (b) + + return (consumed, bypassed) + + def consume_directly (self, source): + real_source_type = source.type () + + # If there are no source types, we can consume anything + source_types = self.source_types + if not source_types: + source_types = [real_source_type] + + consumed = [] + missing_types = [] + for st in self.source_types_: + # The 'source' if of right type already) + if real_source_type == st or type.is_derived (real_source_type, st): + consumed.append (source) + + else: + missing_types.append (st) + + return (consumed, missing_types) + + def action_class (self): + """ Returns the class to be used to actions. Default implementation + returns "action". + """ + return virtual_target.Action + + +def find (id): + """ Finds the generator with id. Returns None if not found. + """ + return __generators.get (id, None) + +def register (g): + """ Registers new generator instance 'g'. + """ + id = g.id () + + __generators [id] = g + + # A generator can produce several targets of the + # same type. We want unique occurence of that generator + # in .generators.$(t) in that case, otherwise, it will + # be tried twice and we'll get false ambiguity. + for t in sequence.unique(g.target_types()): + __type_to_generators.setdefault(t, []).append(g) + + # Update the set of generators for toolset + + # TODO: should we check that generator with this id + # is not already registered. For example, the fop.jam + # module intentionally declared two generators with the + # same id, so such check will break it. + + # Some generators have multiple periods in their name, so the + # normal $(id:S=) won't generate the right toolset name. + # e.g. if id = gcc.compile.c++, then + # .generators-for-toolset.$(id:S=) will append to + # .generators-for-toolset.gcc.compile, which is a separate + # value from .generators-for-toolset.gcc. Correcting this + # makes generator inheritance work properly. + # See also inherit-generators in module toolset + base = id.split ('.', 100) [0] + + __generators_for_toolset.setdefault(base, []).append(g) + +def register_standard (id, source_types, target_types, requirements = []): + """ Creates new instance of the 'generator' class and registers it. + Returns the creates instance. + Rationale: the instance is returned so that it's possible to first register + a generator and then call 'run' method on that generator, bypassing all + generator selection. + """ + g = Generator (id, False, source_types, target_types, requirements) + register (g) + return g + +def register_composing (id, source_types, target_types, requirements = []): + g = Generator (id, True, source_types, target_types, requirements) + register (g) + return g + +def generators_for_toolset (toolset): + """ Returns all generators which belong to 'toolset'. + """ + return __generators_for_toolset.get(toolset, []) + +def override (overrider_id, overridee_id): + """Make generator 'overrider-id' be preferred to + 'overridee-id'. If, when searching for generators + that could produce a target of certain type, + both those generators are amoung viable generators, + the overridden generator is immediately discarded. + + The overridden generators are discarded immediately + after computing the list of viable generators, before + running any of them.""" + + __overrides.get(overrider_id, []).append(overridee_id) + +def __viable_source_types_real (target_type): + """ Returns a list of source type which can possibly be converted + to 'target_type' by some chain of generator invocation. + + More formally, takes all generators for 'target_type' and + returns union of source types for those generators and result + of calling itself recusrively on source types. + """ + generators = [] + + t = type.all_bases (target_type) + + result = [] + # 't' is the list of types which are not yet processed + while t: + # Find all generators for current type. + # Unlike 'find_viable_generators' we don't care about prop_set. + generators = __type_to_generators.get (t [0], []) + t = t[1:] + + for g in generators: + if not g.source_types(): + # Empty source types -- everything can be accepted + result = "*" + # This will terminate outer loop. + t = None + break + + for source_type in g.source_types (): + if not source_type in result: + # If generator accepts 'source_type' it + # will happily accept any type derived from it + all = type.all_derived (source_type) + for n in all: + if not n in result: + t.append (n) + result.append (n) + + result = unique (result) + + return result + + +def viable_source_types (target_type): + """ Helper rule, caches the result of '__viable_source_types_real'. + """ + if not __viable_source_types_cache.has_key (target_type): + __viable_source_types_cache [target_type] = __viable_source_types_real (target_type) + return __viable_source_types_cache [target_type] + +def viable_source_types_for_generator_real (generator): + """ Returns the list of source types, which, when passed to 'run' + method of 'generator', has some change of being eventually used + (probably after conversion by other generators) + """ + source_types = generator.source_types () + + if not source_types: + # If generator does not specify any source types, + # it might be special generator like builtin.lib-generator + # which just relays to other generators. Return '*' to + # indicate that any source type is possibly OK, since we don't + # know for sure. + return ['*'] + + else: + result = [] + for s in source_types: + result += type.all_derived (s) + viable_source_types (s) + result = unique (result) + if "*" in result: + result = ["*"] + return result + +def viable_source_types_for_generator (generator): + """ Caches the result of 'viable_source_types_for_generator'. + """ + key = str (generator) + if not __viable_source_types_cache.has_key (key): + __viable_source_types_cache [key] = viable_source_types_for_generator_real (generator) + + return __viable_source_types_cache [key] + +def try_one_generator_really (project, name, generator, target_type, properties, sources): + """ Returns usage requirements + list of created targets. + """ + targets = generator.run (project, name, properties, sources) + + usage_requirements = [] + success = False + + dout("returned " + str(targets)) + + if targets: + success = True; + + if isinstance (targets[0], property_set.PropertySet): + usage_requirements = targets [0] + targets = targets [1] + + else: + usage_requirements = property_set.empty () + + dout( " generator" + generator.id() + " spawned ") + # generators.dout [ indent ] " " $(targets) ; +# if $(usage-requirements) +# { +# generators.dout [ indent ] " with usage requirements:" $(x) ; +# } + + if success: + return (usage_requirements, targets) + else: + return None + +def try_one_generator (project, name, generator, target_type, properties, sources): + """ Checks if generator invocation can be pruned, because it's guaranteed + to fail. If so, quickly returns empty list. Otherwise, calls + try_one_generator_really. + """ + source_types = [] + + for s in sources: + source_types.append (s.type ()) + + viable_source_types = viable_source_types_for_generator (generator) + + if source_types and viable_source_types != ['*'] and\ + not set.intersection (source_types, viable_source_types): + if project.manager ().logger ().on (): + id = generator.id () + project.manager ().logger ().log (__name__, "generator '%s' pruned" % id) + project.manager ().logger ().log (__name__, "source_types" '%s' % source_types) + project.manager ().logger ().log (__name__, "viable_source_types '%s'" % viable_source_types) + + return [] + + else: + return try_one_generator_really (project, name, generator, target_type, properties, sources) + + +def construct_types (project, name, target_types, prop_set, sources): + + result = [] + usage_requirements = property_set.empty() + + for t in target_types: + r = construct (project, name, t, prop_set, sources) + + if r: + (ur, targets) = r + usage_requirements = usage_requirements.add(ur) + result.extend(targets) + + # TODO: have to introduce parameter controlling if + # several types can be matched and add appropriate + # checks + + # TODO: need to review the documentation for + # 'construct' to see if it should return $(source) even + # if nothing can be done with it. Currents docs seem to + # imply that, contrary to the behaviour. + if result: + return (usage_requirements, result) + + else: + return (usage_requirements, sources) + +def __ensure_type (targets): + """ Ensures all 'targets' have types. If this is not so, exists with + error. + """ + for t in targets: + if not t.type (): + raise BaseException ("target '%s' has no type" % str (t)) + +def find_viable_generators_aux (target_type, prop_set): + """ Returns generators which can be used to construct target of specified type + with specified properties. Uses the following algorithm: + - iterates over requested target_type and all it's bases (in the order returned bt + type.all-bases. + - for each type find all generators that generate that type and which requirements + are satisfied by properties. + - if the set of generators is not empty, returns that set. + + Note: this algorithm explicitly ignores generators for base classes if there's + at least one generator for requested target_type. + """ + # Select generators that can create the required target type. + viable_generators = [] + initial_generators = [] + + import type + + # Try all-type generators first. Assume they have + # quite specific requirements. + all_bases = type.all_bases(target_type) + + for t in all_bases: + + initial_generators = __type_to_generators.get(t, []) + + if initial_generators: + dout("there are generators for this type") + if t != target_type: + # We're here, when no generators for target-type are found, + # but there are some generators for a base type. + # We'll try to use them, but they will produce targets of + # base type, not of 'target-type'. So, we clone the generators + # and modify the list of target types. + generators2 = [] + for g in initial_generators[:]: + # generators.register adds generator to the list of generators + # for toolsets, which is a bit strange, but should work. + # That list is only used when inheriting toolset, which + # should have being done before generators are run. + ng = g.clone_and_change_target_type(t, target_type) + generators2.append(ng) + register(ng) + + initial_generators = generators2 + break + + for g in initial_generators: + dout("trying generator " + g.id() + + "(" + str(g.source_types()) + "->" + str(g.target_types()) + ")") + + m = g.match_rank(prop_set) + if m: + dout(" is viable") + viable_generators.append(g) + + return viable_generators + +def find_viable_generators (target_type, prop_set): + key = target_type + '.' + str (prop_set) + + l = __viable_generators_cache.get (key, None) + + if not l: + l = find_viable_generators_aux (target_type, prop_set) + + __viable_generators_cache [key] = l + + viable_generators = [] + for g in l: + # Avoid trying the same generator twice on different levels. + # TODO: is this really used? + if not g in __active_generators: + viable_generators.append (g) + + # Generators which override 'all'. + all_overrides = [] + + # Generators which are overriden + overriden_ids = [] + + for g in viable_generators: + id = g.id () + + this_overrides = __overrides.get (id, []) + + if this_overrides: + overriden_ids.extend (this_overrides) + if 'all' in this_overrides: + all_overrides.append (g) + + if all_overrides: + viable_generators = all_overrides + + result = [] + for g in viable_generators: + if not g.id () in overriden_ids: + result.append (g) + + return result + +def __construct_really (project, name, target_type, prop_set, sources): + """ Attempts to construct target by finding viable generators, running them + and selecting the dependency graph. + """ + viable_generators = find_viable_generators (target_type, prop_set) + + result = [] + + project.manager ().logger ().log (__name__, "*** %d viable generators" % len (viable_generators)) + + generators_that_succeeded = [] + + for g in viable_generators: + __active_generators.append(g) + r = try_one_generator (project, name, g, target_type, prop_set, sources) + del __active_generators[-1] + + if r: + generators_that_succeeded.append(g) + if result: + output = cStringIO.StringIO() + print >>output, "ambiguity found when searching for best transformation" + print >>output, "Trying to produce type '%s' from: " % (target_type) + for s in sources: + print >>output, " - " + s.str() + print >>output, "Generators that succeeded:" + for g in generators_that_succeeded: + print >>output, " - " + g.id() + print >>output, "First generator produced: " + for t in result[1:]: + print >>output, " - " + str(t) + print >>output, "Second generator produced:" + for t in r[1:]: + print >>output, " - " + str(t) + get_manager().errors()(output.getvalue()) + else: + result = r; + + return result; + + +def construct (project, name, target_type, prop_set, sources): + """ Attempts to create target of 'target-type' with 'properties' + from 'sources'. The 'sources' are treated as a collection of + *possible* ingridients -- i.e. it is not required to consume + them all. If 'multiple' is true, the rule is allowed to return + several targets of 'target-type'. + + Returns a list of target. When this invocation is first instance of + 'construct' in stack, returns only targets of requested 'target-type', + otherwise, returns also unused sources and additionally generated + targets. + """ + # TODO: Why is global needed here? + global __construct_stack + if __construct_stack: + __ensure_type (sources) + + __construct_stack.append (1) + + if project.manager().logger().on(): + increase_indent () + + dout( "*** construct " + target_type) + + for s in sources: + dout(" from " + str(s)) + + project.manager().logger().log (__name__, " properties: ", prop_set.raw ()) + + result = __construct_really(project, name, target_type, prop_set, sources) + + project.manager().logger().decrease_indent() + + __construct_stack = __construct_stack [1:] + + return result + diff --git a/v2/build/project.ann.py b/v2/build/project.ann.py new file mode 100644 index 000000000..349f54955 --- /dev/null +++ b/v2/build/project.ann.py @@ -0,0 +1,996 @@ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 1) # Status: being ported by Vladimir Prus +ddc17f01 (vladimir_prus 2007-10-26 14:57:56 +0000 2) # Base revision: 40480 +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 3) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 4) # Copyright 2002, 2003 Dave Abrahams +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 5) # Copyright 2002, 2005, 2006 Rene Rivera +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 6) # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 7) # Distributed under the Boost Software License, Version 1.0. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 8) # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 9) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 10) # Implements project representation and loading. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 11) # Each project is represented by +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 12) # - a module where all the Jamfile content live. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 13) # - an instance of 'project-attributes' class. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 14) # (given module name, can be obtained by 'attributes' rule) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 15) # - an instance of 'project-target' class (from targets.jam) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 16) # (given a module name, can be obtained by 'target' rule) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 17) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 18) # Typically, projects are created as result of loading Jamfile, which is +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 19) # do by rules 'load' and 'initialize', below. First, module for Jamfile +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 20) # is loaded and new project-attributes instance is created. Some rules +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 21) # necessary for project are added to the module (see 'project-rules' module) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 22) # at the bottom of this file. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 23) # Default project attributes are set (inheriting attributes of parent project, if +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 24) # it exists). After that, Jamfile is read. It can declare its own attributes, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 25) # via 'project' rule, which will be combined with already set attributes. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 26) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 27) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 28) # The 'project' rule can also declare project id, which will be associated with +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 29) # the project module. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 30) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 31) # There can also be 'standalone' projects. They are created by calling 'initialize' +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 32) # on arbitrary module, and not specifying location. After the call, the module can +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 33) # call 'project' rule, declare main target and behave as regular projects. However, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 34) # since it's not associated with any location, it's better declare only prebuilt +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 35) # targets. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 36) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 37) # The list of all loaded Jamfile is stored in variable .project-locations. It's possible +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 38) # to obtain module name for a location using 'module-name' rule. The standalone projects +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 39) # are not recorded, the only way to use them is by project id. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 40) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 41) import b2.util.path +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 42) from b2.build import property_set, property +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 43) from b2.build.errors import ExceptionWithUserContext +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 44) import b2.build.targets +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 45) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 46) import bjam +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 47) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 48) import re +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 49) import sys +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 50) import os +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 51) import string +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 52) import imp +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 53) import traceback +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 54) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 55) class ProjectRegistry: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 56) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 57) def __init__(self, manager, global_build_dir): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 58) self.manager = manager +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 59) self.global_build_dir = None +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 60) self.project_rules_ = ProjectRules(self) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 61) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 62) # The target corresponding to the project being loaded now +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 63) self.current_project = None +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 64) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 65) # The set of names of loaded project modules +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 66) self.jamfile_modules = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 67) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 68) # Mapping from location to module name +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 69) self.location2module = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 70) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 71) # Mapping from project id to project module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 72) self.id2module = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 73) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 74) # Map from Jamfile directory to parent Jamfile/Jamroot +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 75) # location. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 76) self.dir2parent_jamfile = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 77) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 78) # Map from directory to the name of Jamfile in +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 79) # that directory (or None). +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 80) self.dir2jamfile = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 81) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 82) # Map from project module to attributes object. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 83) self.module2attributes = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 84) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 85) # Map from project module to target for the project +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 86) self.module2target = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 87) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 88) # Map from names to Python modules, for modules loaded +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 89) # via 'using' and 'import' rules in Jamfiles. +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 90) self.loaded_tool_modules_ = {} +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 91) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 92) # Map from project target to the list of +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 93) # (id,location) pairs corresponding to all 'use-project' +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 94) # invocations. +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 95) # TODO: should not have a global map, keep this +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 96) # in ProjectTarget. +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 97) self.used_projects = {} +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 98) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 99) self.saved_current_project = [] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 100) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 101) self.JAMROOT = self.manager.getenv("JAMROOT"); +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 102) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 103) # Note the use of character groups, as opposed to listing +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 104) # 'Jamroot' and 'jamroot'. With the latter, we'd get duplicate +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 105) # matches on windows and would have to eliminate duplicates. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 106) if not self.JAMROOT: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 107) self.JAMROOT = ["project-root.jam", "[Jj]amroot", "[Jj]amroot.jam"] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 108) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 109) # Default patterns to search for the Jamfiles to use for build +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 110) # declarations. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 111) self.JAMFILE = self.manager.getenv("JAMFILE") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 112) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 113) if not self.JAMFILE: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 114) self.JAMFILE = ["[Bb]uild.jam", "[Jj]amfile.v2", "[Jj]amfile", +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 115) "[Jj]amfile.jam"] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 116) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 117) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 118) def load (self, jamfile_location): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 119) """Loads jamfile at the given location. After loading, project global +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 120) file and jamfile needed by the loaded one will be loaded recursively. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 121) If the jamfile at that location is loaded already, does nothing. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 122) Returns the project module for the Jamfile.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 123) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 124) absolute = os.path.join(os.getcwd(), jamfile_location) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 125) absolute = os.path.normpath(absolute) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 126) jamfile_location = b2.util.path.relpath(os.getcwd(), absolute) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 127) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 128) if "--debug-loading" in self.manager.argv(): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 129) print "Loading Jamfile at '%s'" % jamfile_location +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 130) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 131) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 132) mname = self.module_name(jamfile_location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 133) # If Jamfile is already loaded, don't try again. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 134) if not mname in self.jamfile_modules: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 135) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 136) self.load_jamfile(jamfile_location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 137) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 138) # We want to make sure that child project are loaded only +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 139) # after parent projects. In particular, because parent projects +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 140) # define attributes whch are inherited by children, and we don't +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 141) # want children to be loaded before parents has defined everything. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 142) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 143) # While "build-project" and "use-project" can potentially refer +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 144) # to child projects from parent projects, we don't immediately +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 145) # load child projects when seing those attributes. Instead, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 146) # we record the minimal information that will be used only later. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 147) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 148) self.load_used_projects(mname) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 149) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 150) return mname +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 151) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 152) def load_used_projects(self, module_name): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 153) # local used = [ modules.peek $(module-name) : .used-projects ] ; +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 154) used = self.used_projects[module_name] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 155) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 156) location = self.attribute(module_name, "location") +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 157) for u in used: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 158) id = u[0] +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 159) where = u[1] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 160) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 161) self.use(id, os.path.join(location, where)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 162) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 163) def load_parent(self, location): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 164) """Loads parent of Jamfile at 'location'. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 165) Issues an error if nothing is found.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 166) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 167) found = b2.util.path.glob_in_parents( +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 168) location, self.JAMROOT + self.JAMFILE) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 169) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 170) if not found: +1674e2d9 (jhunold 2008-08-08 19:52:05 +0000 171) print "error: Could not find parent for project at '%s'" % location +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 172) print "error: Did not find Jamfile or project-root.jam in any parent directory." +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 173) sys.exit(1) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 174) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 175) return self.load(os.path.dirname(found[0])) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 176) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 177) def act_as_jamfile(self, module, location): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 178) """Makes the specified 'module' act as if it were a regularly loaded Jamfile +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 179) at 'location'. If Jamfile is already located for that location, it's an +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 180) error.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 181) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 182) if self.module_name(location) in self.jamfile_modules: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 183) self.manager.errors()( +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 184) "Jamfile was already loaded for '%s'" % location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 185) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 186) # Set up non-default mapping from location to module. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 187) self.location2module[location] = module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 188) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 189) # Add the location to the list of project locations +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 190) # so that we don't try to load Jamfile in future +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 191) self.jamfile_modules.append(location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 192) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 193) self.initialize(module, location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 194) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 195) def find(self, name, current_location): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 196) """Given 'name' which can be project-id or plain directory name, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 197) return project module corresponding to that id or directory. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 198) Returns nothing of project is not found.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 199) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 200) project_module = None +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 201) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 202) # Try interpreting name as project id. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 203) if name[0] == '/': +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 204) project_module = self.id2module.get(name) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 205) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 206) if not project_module: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 207) location = os.path.join(current_location, name) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 208) # If no project is registered for the given location, try to +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 209) # load it. First see if we have Jamfile. If not we might have project +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 210) # root, willing to act as Jamfile. In that case, project-root +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 211) # must be placed in the directory referred by id. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 212) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 213) project_module = self.module_name(location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 214) if not project_module in self.jamfile_modules and \ +49c03622 (jhunold 2008-07-23 09:57:41 +0000 215) b2.util.path.glob([location], self.JAMROOT + self.JAMFILE): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 216) project_module = self.load(location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 217) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 218) return project_module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 219) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 220) def module_name(self, jamfile_location): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 221) """Returns the name of module corresponding to 'jamfile-location'. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 222) If no module corresponds to location yet, associates default +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 223) module name with that location.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 224) module = self.location2module.get(jamfile_location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 225) if not module: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 226) # Root the path, so that locations are always umbiguious. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 227) # Without this, we can't decide if '../../exe/program1' and '.' +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 228) # are the same paths, or not. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 229) jamfile_location = os.path.realpath( +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 230) os.path.join(os.getcwd(), jamfile_location)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 231) module = "Jamfile<%s>" % jamfile_location +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 232) self.location2module[jamfile_location] = module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 233) return module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 234) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 235) def find_jamfile (self, dir, parent_root=0, no_errors=0): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 236) """Find the Jamfile at the given location. This returns the +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 237) exact names of all the Jamfiles in the given directory. The optional +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 238) parent-root argument causes this to search not the given directory +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 239) but the ones above it up to the directory given in it.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 240) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 241) # Glob for all the possible Jamfiles according to the match pattern. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 242) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 243) jamfile_glob = None +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 244) if parent_root: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 245) parent = self.dir2parent_jamfile.get(dir) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 246) if not parent: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 247) parent = b2.util.path.glob_in_parents(dir, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 248) self.JAMFILE) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 249) self.dir2parent_jamfile[dir] = parent +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 250) jamfile_glob = parent +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 251) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 252) jamfile = self.dir2jamfile.get(dir) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 253) if not jamfile: +49c03622 (jhunold 2008-07-23 09:57:41 +0000 254) jamfile = b2.util.path.glob([dir], self.JAMFILE) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 255) self.dir2jamfile[dir] = jamfile +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 256) jamfile_glob = jamfile +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 257) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 258) if len(jamfile_glob): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 259) # Multiple Jamfiles found in the same place. Warn about this. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 260) # And ensure we use only one of them. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 261) # As a temporary convenience measure, if there's Jamfile.v2 amount +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 262) # found files, suppress the warning and use it. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 263) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 264) pattern = "(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 265) v2_jamfiles = [x for x in jamfile_glob if re.match(pattern, x)] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 266) if len(v2_jamfiles) == 1: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 267) jamfile_glob = v2_jamfiles +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 268) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 269) print """warning: Found multiple Jamfiles at '%s'! +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 270) Loading the first one: '%s'.""" % (dir, jamfile_glob[0]) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 271) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 272) # Could not find it, error. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 273) if not no_errors and not jamfile_glob: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 274) self.manager.errors()( +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 275) """Unable to load Jamfile. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 276) Could not find a Jamfile in directory '%s' +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 277) Attempted to find it with pattern '%s'. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 278) Please consult the documentation at 'http://boost.org/b2.'.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 279) % (dir, string.join(self.JAMFILE))) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 280) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 281) return jamfile_glob[0] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 282) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 283) def load_jamfile(self, dir): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 284) """Load a Jamfile at the given directory. Returns nothing. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 285) Will attempt to load the file as indicated by the JAMFILE patterns. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 286) Effect of calling this rule twice with the same 'dir' is underfined.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 287) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 288) # See if the Jamfile is where it should be. +49c03622 (jhunold 2008-07-23 09:57:41 +0000 289) jamfile_to_load = b2.util.path.glob([dir], self.JAMROOT) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 290) if not jamfile_to_load: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 291) jamfile_to_load = self.find_jamfile(dir) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 292) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 293) jamfile_to_load = jamfile_to_load[0] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 294) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 295) # The module of the jamfile. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 296) dir = os.path.realpath(os.path.dirname(jamfile_to_load)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 297) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 298) jamfile_module = self.module_name (dir) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 299) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 300) # Initialize the jamfile module before loading. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 301) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 302) self.initialize(jamfile_module, dir, os.path.basename(jamfile_to_load)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 303) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 304) saved_project = self.current_project +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 305) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 306) self.used_projects[jamfile_module] = [] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 307) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 308) # Now load the Jamfile in it's own context. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 309) # Initialization might have load parent Jamfiles, which might have +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 310) # loaded the current Jamfile with use-project. Do a final check to make +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 311) # sure it's not loaded already. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 312) if not jamfile_module in self.jamfile_modules: +49c03622 (jhunold 2008-07-23 09:57:41 +0000 313) self.jamfile_modules[jamfile_module] = True +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 314) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 315) # FIXME: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 316) # mark-as-user $(jamfile-module) ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 317) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 318) bjam.call("load", jamfile_module, jamfile_to_load) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 319) basename = os.path.basename(jamfile_to_load) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 320) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 321) # Now do some checks +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 322) if self.current_project != saved_project: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 323) self.manager.errors()( +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 324) """The value of the .current-project variable +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 325) has magically changed after loading a Jamfile. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 326) This means some of the targets might be defined a the wrong project. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 327) after loading %s +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 328) expected value %s +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 329) actual value %s""" % (jamfile_module, saved_project, self.current_project)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 330) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 331) if self.global_build_dir: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 332) id = self.attribute(jamfile_module, "id") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 333) project_root = self.attribute(jamfile_module, "project-root") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 334) location = self.attribute(jamfile_module, "location") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 335) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 336) if location and project_root == dir: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 337) # This is Jamroot +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 338) if not id: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 339) # FIXME: go via errors module, so that contexts are +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 340) # shown? +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 341) print "warning: the --build-dir option was specified" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 342) print "warning: but Jamroot at '%s'" % dir +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 343) print "warning: specified no project id" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 344) print "warning: the --build-dir option will be ignored" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 345) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 346) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 347) def load_standalone(self, jamfile_module, file): +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 348) """Loads 'file' as standalone project that has no location +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 349) associated with it. This is mostly useful for user-config.jam, +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 350) which should be able to define targets, but although it has +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 351) some location in filesystem, we don't want any build to +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 352) happen in user's HOME, for example. +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 353) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 354) The caller is required to never call this method twice on +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 355) the same file. +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 356) """ +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 357) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 358) self.initialize(jamfile_module) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 359) self.used_projects[jamfile_module] = [] +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 360) bjam.call("load", jamfile_module, file) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 361) self.load_used_projects(jamfile_module) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 362) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 363) def is_jamroot(self, basename): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 364) match = [ pat for pat in self.JAMROOT if re.match(pat, basename)] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 365) if match: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 366) return 1 +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 367) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 368) return 0 +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 369) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 370) def initialize(self, module_name, location=None, basename=None): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 371) """Initialize the module for a project. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 372) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 373) module-name is the name of the project module. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 374) location is the location (directory) of the project to initialize. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 375) If not specified, stanalone project will be initialized +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 376) """ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 377) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 378) if "--debug-loading" in self.manager.argv(): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 379) print "Initializing project '%s'" % module_name +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 380) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 381) # TODO: need to consider if standalone projects can do anything but defining +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 382) # prebuilt targets. If so, we need to give more sensible "location", so that +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 383) # source paths are correct. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 384) if not location: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 385) location = "" +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 386) else: +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 387) location = b2.util.path.relpath(os.getcwd(), location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 388) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 389) attributes = ProjectAttributes(self.manager, location, module_name) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 390) self.module2attributes[module_name] = attributes +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 391) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 392) if location: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 393) attributes.set("source-location", location, exact=1) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 394) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 395) attributes.set("source-location", "", exact=1) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 396) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 397) attributes.set("requirements", property_set.empty(), exact=True) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 398) attributes.set("usage-requirements", property_set.empty(), exact=True) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 399) attributes.set("default-build", [], exact=True) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 400) attributes.set("projects-to-build", [], exact=True) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 401) attributes.set("project-root", None, exact=True) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 402) attributes.set("build-dir", None, exact=True) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 403) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 404) self.project_rules_.init_project(module_name) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 405) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 406) jamroot = False +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 407) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 408) parent_module = None; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 409) if module_name == "site-config": +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 410) # No parent +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 411) pass +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 412) elif module_name == "user-config": +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 413) parent_module = "site-config" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 414) elif location and not self.is_jamroot(basename): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 415) # We search for parent/project-root only if jamfile was specified +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 416) # --- i.e +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 417) # if the project is not standalone. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 418) parent_module = self.load_parent(location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 419) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 420) # It's either jamroot, or standalone project. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 421) # If it's jamroot, inherit from user-config. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 422) if location: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 423) parent_module = "user-config" ; +49c03622 (jhunold 2008-07-23 09:57:41 +0000 424) jamroot = True ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 425) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 426) if parent_module: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 427) self.inherit_attributes(module_name, parent_module) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 428) attributes.set("parent-module", parent_module, exact=1) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 429) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 430) if jamroot: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 431) attributes.set("project-root", location, exact=1) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 432) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 433) parent = None +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 434) if parent_module: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 435) parent = self.target(parent_module) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 436) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 437) if not self.module2target.has_key(module_name): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 438) target = b2.build.targets.ProjectTarget(self.manager, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 439) module_name, module_name, parent, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 440) self.attribute(module_name,"requirements"), +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 441) # FIXME: why we need to pass this? It's not +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 442) # passed in jam code. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 443) self.attribute(module_name, "default-build")) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 444) self.module2target[module_name] = target +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 445) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 446) self.current_project = self.target(module_name) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 447) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 448) def inherit_attributes(self, project_module, parent_module): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 449) """Make 'project-module' inherit attributes of project +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 450) root and parent module.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 451) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 452) attributes = self.module2attributes[project_module] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 453) pattributes = self.module2attributes[parent_module] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 454) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 455) # Parent module might be locationless user-config. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 456) # FIXME: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 457) #if [ modules.binding $(parent-module) ] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 458) #{ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 459) # $(attributes).set parent : [ path.parent +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 460) # [ path.make [ modules.binding $(parent-module) ] ] ] ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 461) # } +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 462) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 463) attributes.set("project-root", pattributes.get("project-root"), exact=True) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 464) attributes.set("default-build", pattributes.get("default-build"), exact=True) +49c03622 (jhunold 2008-07-23 09:57:41 +0000 465) attributes.set("requirements", pattributes.get("requirements"), exact=True) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 466) attributes.set("usage-requirements", +cde6f09a (vladimir_prus 2007-10-19 23:12:33 +0000 467) pattributes.get("usage-requirements"), exact=1) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 468) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 469) parent_build_dir = pattributes.get("build-dir") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 470) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 471) if parent_build_dir: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 472) # Have to compute relative path from parent dir to our dir +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 473) # Convert both paths to absolute, since we cannot +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 474) # find relative path from ".." to "." +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 475) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 476) location = attributes.get("location") +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 477) parent_location = pattributes.get("location") +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 478) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 479) our_dir = os.path.join(os.getcwd(), location) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 480) parent_dir = os.path.join(os.getcwd(), parent_location) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 481) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 482) build_dir = os.path.join(parent_build_dir, +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 483) b2.util.path.relpath(parent_dir, +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 484) our_dir)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 485) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 486) def register_id(self, id, module): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 487) """Associate the given id with the given project module.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 488) self.id2module[id] = module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 489) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 490) def current(self): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 491) """Returns the project which is currently being loaded.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 492) return self.current_project +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 493) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 494) def push_current(self, project): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 495) """Temporary changes the current project to 'project'. Should +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 496) be followed by 'pop-current'.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 497) self.saved_current_project.append(self.current_project) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 498) self.current_project = project +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 499) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 500) def pop_current(self): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 501) self.current_project = self.saved_current_project[-1] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 502) del self.saved_current_project[-1] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 503) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 504) def attributes(self, project): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 505) """Returns the project-attribute instance for the +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 506) specified jamfile module.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 507) return self.module2attributes[project] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 508) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 509) def attribute(self, project, attribute): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 510) """Returns the value of the specified attribute in the +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 511) specified jamfile module.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 512) return self.module2attributes[project].get(attribute) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 513) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 514) def target(self, project_module): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 515) """Returns the project target corresponding to the 'project-module'.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 516) if not self.module2target[project_module]: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 517) self.module2target[project_module] = \ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 518) ProjectTarget(project_module, project_module, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 519) self.attribute(project_module, "requirements")) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 520) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 521) return self.module2target[project_module] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 522) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 523) def use(self, id, location): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 524) # Use/load a project. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 525) saved_project = self.current_project +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 526) project_module = self.load(location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 527) declared_id = self.attribute(project_module, "id") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 528) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 529) if not declared_id or declared_id != id: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 530) # The project at 'location' either have no id or +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 531) # that id is not equal to the 'id' parameter. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 532) if self.id2module[id] and self.id2module[id] != project_module: +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 533) self.manager.errors()( +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 534) """Attempt to redeclare already existing project id '%s'""" % id) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 535) self.id2module[id] = project_module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 536) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 537) self.current_module = saved_project +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 538) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 539) def add_rule(self, name, callable): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 540) """Makes rule 'name' available to all subsequently loaded Jamfiles. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 541) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 542) Calling that rule wil relay to 'callable'.""" +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 543) self.project_rules_.add_rule(name, callable) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 544) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 545) def project_rules(self): +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 546) return self.project_rules_ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 547) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 548) def glob_internal(self, project, wildcards, excludes, rule_name): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 549) location = project.get("source-location") +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 550) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 551) result = [] +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 552) callable = b2.util.path.__dict__[rule_name] +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 553) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 554) paths = callable(location, wildcards, excludes) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 555) has_dir = 0 +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 556) for w in wildcards: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 557) if os.path.dirname(w): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 558) has_dir = 1 +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 559) break +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 560) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 561) if has_dir or rule_name != "glob": +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 562) # The paths we've found are relative to current directory, +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 563) # but the names specified in sources list are assumed to +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 564) # be relative to source directory of the corresponding +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 565) # prject. So, just make the name absolute. +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 566) result = [os.path.join(os.getcwd(), p) for p in paths] +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 567) else: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 568) # There were not directory in wildcard, so the files are all +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 569) # in the source directory of the project. Just drop the +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 570) # directory, instead of making paths absolute. +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 571) result = [os.path.basename(p) for p in paths] +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 572) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 573) return result +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 574) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 575) def load_module(self, name, extra_path=None): +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 576) """Classic Boost.Build 'modules' are in fact global variables. +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 577) Therefore, try to find an already loaded Python module called 'name' in sys.modules. +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 578) If the module ist not loaded, find it Boost.Build search +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 579) path and load it. The new module is not entered in sys.modules. +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 580) The motivation here is to have disjoint namespace of modules +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 581) loaded via 'import/using' in Jamfile, and ordinary Python +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 582) modules. We don't want 'using foo' in Jamfile to load ordinary +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 583) Python module 'foo' which is going to not work. And we +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 584) also don't want 'import foo' in regular Python module to +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 585) accidentally grab module named foo that is internal to +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 586) Boost.Build and intended to provide interface to Jamfiles.""" +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 587) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 588) existing = self.loaded_tool_modules_.get(name) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 589) if existing: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 590) return existing +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 591) +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 592) modules = sys.modules +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 593) for class_name in modules: +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 594) if name in class_name: +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 595) module = modules[class_name] +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 596) self.loaded_tool_modules_[name] = module +53b0faa2 (jhunold 2008-08-10 18:25:50 +0000 597) return module +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 598) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 599) path = extra_path +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 600) if not path: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 601) path = [] +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 602) path.extend(self.manager.b2.path()) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 603) location = None +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 604) for p in path: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 605) l = os.path.join(p, name + ".py") +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 606) if os.path.exists(l): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 607) location = l +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 608) break +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 609) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 610) if not location: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 611) self.manager.errors()("Cannot find module '%s'" % name) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 612) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 613) mname = "__build_build_temporary__" +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 614) file = open(location) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 615) try: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 616) # TODO: this means we'll never make use of .pyc module, +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 617) # which might be a problem, or not. +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 618) module = imp.load_module(mname, file, os.path.basename(location), +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 619) (".py", "r", imp.PY_SOURCE)) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 620) del sys.modules[mname] +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 621) self.loaded_tool_modules_[name] = module +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 622) return module +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 623) finally: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 624) file.close() +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 625) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 626) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 627) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 628) # FIXME: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 629) # Defines a Boost.Build extension project. Such extensions usually +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 630) # contain library targets and features that can be used by many people. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 631) # Even though extensions are really projects, they can be initialize as +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 632) # a module would be with the "using" (project.project-rules.using) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 633) # mechanism. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 634) #rule extension ( id : options * : * ) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 635) #{ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 636) # # The caller is a standalone module for the extension. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 637) # local mod = [ CALLER_MODULE ] ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 638) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 639) # # We need to do the rest within the extension module. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 640) # module $(mod) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 641) # { +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 642) # import path ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 643) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 644) # # Find the root project. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 645) # local root-project = [ project.current ] ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 646) # root-project = [ $(root-project).project-module ] ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 647) # while +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 648) # [ project.attribute $(root-project) parent-module ] && +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 649) # [ project.attribute $(root-project) parent-module ] != user-config +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 650) # { +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 651) # root-project = [ project.attribute $(root-project) parent-module ] ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 652) # } +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 653) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 654) # # Create the project data, and bring in the project rules +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 655) # # into the module. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 656) # project.initialize $(__name__) : +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 657) # [ path.join [ project.attribute $(root-project) location ] ext $(1:L) ] ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 658) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 659) # # Create the project itself, i.e. the attributes. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 660) # # All extensions are created in the "/ext" project space. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 661) # project /ext/$(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 662) # local attributes = [ project.attributes $(__name__) ] ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 663) # +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 664) # # Inherit from the root project of whomever is defining us. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 665) # project.inherit-attributes $(__name__) : $(root-project) ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 666) # $(attributes).set parent-module : $(root-project) : exact ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 667) # } +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 668) #} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 669) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 670) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 671) class ProjectAttributes: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 672) """Class keeping all the attributes of a project. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 673) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 674) The standard attributes are 'id', "location", "project-root", "parent" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 675) "requirements", "default-build", "source-location" and "projects-to-build". +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 676) """ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 677) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 678) def __init__(self, manager, location, project_module): +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 679) self.manager = manager +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 680) self.location = location +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 681) self.project_module = project_module +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 682) self.attributes = {} +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 683) self.usage_requirements = None +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 684) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 685) def set(self, attribute, specification, exact): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 686) """Set the named attribute from the specification given by the user. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 687) The value actually set may be different.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 688) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 689) if exact: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 690) self.__dict__[attribute] = specification +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 691) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 692) elif attribute == "requirements": +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 693) self.requirements = property_set.refine_from_user_input( +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 694) self.requirements, specification, +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 695) self.project_module, self.location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 696) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 697) elif attribute == "usage-requirements": +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 698) unconditional = [] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 699) for p in specification: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 700) split = property.split_conditional(p) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 701) if split: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 702) unconditional.append(split[1]) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 703) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 704) unconditional.append(p) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 705) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 706) non_free = property.remove("free", unconditional) +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 707) if non_free: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 708) pass +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 709) # FIXME: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 710) #errors.error "usage-requirements" $(specification) "have non-free properties" $(non-free) ; +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 711) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 712) t = property.translate_paths(specification, self.location) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 713) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 714) existing = self.__dict__.get("usage-requirements") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 715) if existing: +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 716) new = property_set.create(existing.raw() + t) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 717) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 718) new = property_set.create(t) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 719) self.__dict__["usage-requirements"] = new +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 720) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 721) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 722) elif attribute == "default-build": +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 723) self.__dict__["default-build"] = property_set.create(specification) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 724) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 725) elif attribute == "source-location": +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 726) source_location = [] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 727) for path in specification: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 728) source_location += os.path.join(self.location, path) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 729) self.__dict__["source-location"] = source_location +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 730) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 731) elif attribute == "build-dir": +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 732) self.__dict__["build-dir"] = os.path.join(self.location, specification) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 733) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 734) elif not attribute in ["id", "default-build", "location", +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 735) "source-location", "parent", +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 736) "projects-to-build", "project-root"]: +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 737) self.manager.errors()( +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 738) """Invalid project attribute '%s' specified +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 739) for project at '%s'""" % (attribute, self.location)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 740) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 741) self.__dict__[attribute] = specification +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 742) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 743) def get(self, attribute): +cde6f09a (vladimir_prus 2007-10-19 23:12:33 +0000 744) return self.__dict__[attribute] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 745) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 746) def dump(self): +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 747) """Prints the project attributes.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 748) id = self.get("id") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 749) if not id: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 750) id = "(none)" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 751) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 752) id = id[0] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 753) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 754) parent = self.get("parent") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 755) if not parent: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 756) parent = "(none)" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 757) else: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 758) parent = parent[0] +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 759) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 760) print "'%s'" % id +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 761) print "Parent project:%s", parent +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 762) print "Requirements:%s", self.get("requirements") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 763) print "Default build:%s", string.join(self.get("debuild-build")) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 764) print "Source location:%s", string.join(self.get("source-location")) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 765) print "Projects to build:%s", string.join(self.get("projects-to-build").sort()); +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 766) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 767) class ProjectRules: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 768) """Class keeping all rules that are made available to Jamfile.""" +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 769) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 770) def __init__(self, registry): +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 771) self.registry = registry +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 772) self.manager_ = registry.manager +38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 773) self.rules = {} +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 774) self.local_names = [x for x in self.__class__.__dict__ +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 775) if x not in ["__init__", "init_project", "add_rule", +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 776) "error_reporting_wrapper", "add_rule_for_type"]] +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 777) self.all_names_ = [x for x in self.local_names] +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 778) +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 779) def add_rule_for_type(self, type): +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 780) rule_name = type.lower(); +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 781) +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 782) def xpto (name, sources, requirements = [], default_build = None, usage_requirements = []): +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 783) return self.manager_.targets().create_typed_target( +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 784) type, self.registry.current(), name[0], sources, +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 785) requirements, default_build, usage_requirements) +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 786) +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 787) self.add_rule(type.lower(), xpto) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 788) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 789) def add_rule(self, name, callable): +38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 790) self.rules[name] = callable +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 791) self.all_names_.append(name) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 792) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 793) def all_names(self): +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 794) return self.all_names_ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 795) +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 796) def call_and_report_errors(self, callable, *args): +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 797) result = None +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 798) try: +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 799) self.manager_.errors().push_jamfile_context() +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 800) result = callable(*args) +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 801) except ExceptionWithUserContext, e: +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 802) e.report() +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 803) except Exception, e: +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 804) try: +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 805) self.manager_.errors().handle_stray_exception (e) +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 806) except ExceptionWithUserContext, e: +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 807) e.report() +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 808) finally: +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 809) self.manager_.errors().pop_jamfile_context() +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 810) +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 811) return result +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 812) +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 813) def make_wrapper(self, callable): +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 814) """Given a free-standing function 'callable', return a new +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 815) callable that will call 'callable' and report all exceptins, +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 816) using 'call_and_report_errors'.""" +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 817) def wrapper(*args): +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 818) self.call_and_report_errors(callable, *args) +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 819) return wrapper +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 820) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 821) def init_project(self, project_module): +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 822) +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 823) for n in self.local_names: +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 824) # Using 'getattr' here gives us a bound method, +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 825) # while using self.__dict__[r] would give unbound one. +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 826) v = getattr(self, n) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 827) if callable(v): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 828) if n == "import_": +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 829) n = "import" +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 830) else: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 831) n = string.replace(n, "_", "-") +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 832) +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 833) bjam.import_rule(project_module, n, +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 834) self.make_wrapper(v)) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 835) +38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 836) for n in self.rules: +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 837) bjam.import_rule(project_module, n, +0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 838) self.make_wrapper(self.rules[n])) +38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 839) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 840) def project(self, *args): +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 841) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 842) jamfile_module = self.registry.current().project_module() +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 843) attributes = self.registry.attributes(jamfile_module) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 844) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 845) id = None +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 846) if args and args[0]: +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 847) id = args[0][0] +092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 848) args = args[1:] +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 849) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 850) if id: +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 851) if id[0] != '/': +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 852) id = '/' + id +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 853) self.registry.register_id (id, jamfile_module) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 854) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 855) explicit_build_dir = None +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 856) for a in args: +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 857) if a: +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 858) attributes.set(a[0], a[1:], exact=0) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 859) if a[0] == "build-dir": +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 860) explicit_build_dir = a[1] +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 861) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 862) # If '--build-dir' is specified, change the build dir for the project. +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 863) if self.registry.global_build_dir: +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 864) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 865) location = attributes.get("location") +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 866) # Project with empty location is 'standalone' project, like +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 867) # user-config, or qt. It has no build dir. +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 868) # If we try to set build dir for user-config, we'll then +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 869) # try to inherit it, with either weird, or wrong consequences. +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 870) if location and location == attributes.get("project-root"): +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 871) # This is Jamroot. +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 872) if id: +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 873) if explicit_build_dir and os.path.isabs(explicit_build_dir): +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 874) self.register.manager.errors()( +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 875) """Absolute directory specified via 'build-dir' project attribute +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 876) Don't know how to combine that with the --build-dir option.""") +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 877) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 878) rid = id +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 879) if rid[0] == '/': +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 880) rid = rid[1:] +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 881) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 882) p = os.path.join(self.registry.global_build_dir, +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 883) rid, explicit_build_dir) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 884) attributes.set("build-dir", p, exact=1) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 885) elif explicit_build_dir: +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 886) self.registry.manager.errors()( +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 887) """When --build-dir is specified, the 'build-project' +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 888) attribute is allowed only for top-level 'project' invocations""") +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 889) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 890) def constant(self, name, value): +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 891) """Declare and set a project global constant. +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 892) Project global constants are normal variables but should +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 893) not be changed. They are applied to every child Jamfile.""" +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 894) m = "Jamfile" +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 895) self.registry.current().add_constant(name[0], value) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 896) +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 897) def path_constant(self, name, value): +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 898) """Declare and set a project global constant, whose value is a path. The +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 899) path is adjusted to be relative to the invocation directory. The given +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 900) value path is taken to be either absolute, or relative to this project +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 901) root.""" +0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 902) self.registry.current().add_constant(name[0], value, path=1) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 903) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 904) def use_project(self, id, where): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 905) # See comment in 'load' for explanation why we record the +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 906) # parameters as opposed to loading the project now. +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 907) m = self.registry.current().project_module(); +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 908) self.registry.used_projects[m].append((id, where)) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 909) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 910) def build_project(self, dir): +1674e2d9 (jhunold 2008-08-08 19:52:05 +0000 911) assert(isinstance(dir, list)) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 912) jamfile_module = self.registry.current().project_module() +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 913) attributes = self.registry.attributes(jamfile_module) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 914) now = attributes.get("projects-to-build") +1674e2d9 (jhunold 2008-08-08 19:52:05 +0000 915) attributes.set("projects-to-build", now + dir, exact=True) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 916) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 917) def explicit(self, target_names): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 918) t = self.registry.current() +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 919) for n in target_names: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 920) t.mark_target_as_explicit(n) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 921) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 922) def glob(self, wildcards, excludes=None): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 923) return self.registry.glob_internal(self.registry.current(), +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 924) wildcards, excludes, "glob") +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 925) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 926) def glob_tree(self, wildcards, excludes=None): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 927) bad = 0 +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 928) for p in wildcards: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 929) if os.path.dirname(p): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 930) bad = 1 +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 931) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 932) if excludes: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 933) for p in excludes: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 934) if os.path.dirname(p): +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 935) bad = 1 +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 936) +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 937) if bad: +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 938) self.registry.manager().errors()( +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 939) "The patterns to 'glob-tree' may not include directory") +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 940) return self.registry.glob_internal(self.registry.current(), +2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 941) wildcards, excludes, "glob_tree") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 942) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 943) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 944) def using(self, toolset, *args): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 945) # The module referred by 'using' can be placed in +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 946) # the same directory as Jamfile, and the user +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 947) # will expect the module to be found even though +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 948) # the directory is not in BOOST_BUILD_PATH. +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 949) # So temporary change the search path. +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 950) jamfile_module = self.registry.current().project_module() +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 951) attributes = self.registry.attributes(jamfile_module) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 952) location = attributes.get("location") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 953) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 954) m = self.registry.load_module(toolset[0], [location]) +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 955) if not m.__dict__.has_key("init"): +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 956) self.registry.manager.errors()( +7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 957) "Tool module '%s' does not define the 'init' method" % toolset[0]) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 958) m.init(*args) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 959) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 960) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 961) def import_(self, name, names_to_import=None, local_names=None): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 962) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 963) name = name[0] +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 964) jamfile_module = self.registry.current().project_module() +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 965) attributes = self.registry.attributes(jamfile_module) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 966) location = attributes.get("location") +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 967) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 968) m = self.registry.load_module(name, [location]) +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 969) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 970) for f in m.__dict__: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 971) v = m.__dict__[f] +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 972) if callable(v): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 973) bjam.import_rule(jamfile_module, name + "." + f, v) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 974) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 975) if names_to_import: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 976) if not local_names: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 977) local_names = names_to_import +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 978) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 979) if len(names_to_import) != len(local_names): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 980) self.registry.manager.errors()( +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 981) """The number of names to import and local names do not match.""") +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 982) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 983) for n, l in zip(names_to_import, local_names): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 984) bjam.import_rule(jamfile_module, l, m.__dict__[n]) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 985) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 986) def conditional(self, condition, requirements): +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 987) """Calculates conditional requirements for multiple requirements +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 988) at once. This is a shorthand to be reduce duplication and to +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 989) keep an inline declarative syntax. For example: +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 990) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 991) lib x : x.cpp : [ conditional gcc debug : +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 992) DEBUG_EXCEPTION DEBUG_TRACE ] ; +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 993) """ +f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 994) +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 995) c = string.join(condition, ",") +f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 996) return [c + ":" + r for r in requirements] diff --git a/v2/build/project.py b/v2/build/project.py new file mode 100644 index 000000000..4e7486c20 --- /dev/null +++ b/v2/build/project.py @@ -0,0 +1,996 @@ +# Status: being ported by Vladimir Prus +# Base revision: 40480 + +# Copyright 2002, 2003 Dave Abrahams +# Copyright 2002, 2005, 2006 Rene Rivera +# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# Implements project representation and loading. +# Each project is represented by +# - a module where all the Jamfile content live. +# - an instance of 'project-attributes' class. +# (given module name, can be obtained by 'attributes' rule) +# - an instance of 'project-target' class (from targets.jam) +# (given a module name, can be obtained by 'target' rule) +# +# Typically, projects are created as result of loading Jamfile, which is +# do by rules 'load' and 'initialize', below. First, module for Jamfile +# is loaded and new project-attributes instance is created. Some rules +# necessary for project are added to the module (see 'project-rules' module) +# at the bottom of this file. +# Default project attributes are set (inheriting attributes of parent project, if +# it exists). After that, Jamfile is read. It can declare its own attributes, +# via 'project' rule, which will be combined with already set attributes. +# +# +# The 'project' rule can also declare project id, which will be associated with +# the project module. +# +# There can also be 'standalone' projects. They are created by calling 'initialize' +# on arbitrary module, and not specifying location. After the call, the module can +# call 'project' rule, declare main target and behave as regular projects. However, +# since it's not associated with any location, it's better declare only prebuilt +# targets. +# +# The list of all loaded Jamfile is stored in variable .project-locations. It's possible +# to obtain module name for a location using 'module-name' rule. The standalone projects +# are not recorded, the only way to use them is by project id. + +import b2.util.path +from b2.build import property_set, property +from b2.build.errors import ExceptionWithUserContext +import b2.build.targets + +import bjam + +import re +import sys +import os +import string +import imp +import traceback + +class ProjectRegistry: + + def __init__(self, manager, global_build_dir): + self.manager = manager + self.global_build_dir = None + self.project_rules_ = ProjectRules(self) + + # The target corresponding to the project being loaded now + self.current_project = None + + # The set of names of loaded project modules + self.jamfile_modules = {} + + # Mapping from location to module name + self.location2module = {} + + # Mapping from project id to project module + self.id2module = {} + + # Map from Jamfile directory to parent Jamfile/Jamroot + # location. + self.dir2parent_jamfile = {} + + # Map from directory to the name of Jamfile in + # that directory (or None). + self.dir2jamfile = {} + + # Map from project module to attributes object. + self.module2attributes = {} + + # Map from project module to target for the project + self.module2target = {} + + # Map from names to Python modules, for modules loaded + # via 'using' and 'import' rules in Jamfiles. + self.loaded_tool_modules_ = {} + + # Map from project target to the list of + # (id,location) pairs corresponding to all 'use-project' + # invocations. + # TODO: should not have a global map, keep this + # in ProjectTarget. + self.used_projects = {} + + self.saved_current_project = [] + + self.JAMROOT = self.manager.getenv("JAMROOT"); + + # Note the use of character groups, as opposed to listing + # 'Jamroot' and 'jamroot'. With the latter, we'd get duplicate + # matches on windows and would have to eliminate duplicates. + if not self.JAMROOT: + self.JAMROOT = ["project-root.jam", "[Jj]amroot", "[Jj]amroot.jam"] + + # Default patterns to search for the Jamfiles to use for build + # declarations. + self.JAMFILE = self.manager.getenv("JAMFILE") + + if not self.JAMFILE: + self.JAMFILE = ["[Bb]uild.jam", "[Jj]amfile.v2", "[Jj]amfile", + "[Jj]amfile.jam"] + + + def load (self, jamfile_location): + """Loads jamfile at the given location. After loading, project global + file and jamfile needed by the loaded one will be loaded recursively. + If the jamfile at that location is loaded already, does nothing. + Returns the project module for the Jamfile.""" + + absolute = os.path.join(os.getcwd(), jamfile_location) + absolute = os.path.normpath(absolute) + jamfile_location = b2.util.path.relpath(os.getcwd(), absolute) + + if "--debug-loading" in self.manager.argv(): + print "Loading Jamfile at '%s'" % jamfile_location + + + mname = self.module_name(jamfile_location) + # If Jamfile is already loaded, don't try again. + if not mname in self.jamfile_modules: + + self.load_jamfile(jamfile_location) + + # We want to make sure that child project are loaded only + # after parent projects. In particular, because parent projects + # define attributes whch are inherited by children, and we don't + # want children to be loaded before parents has defined everything. + # + # While "build-project" and "use-project" can potentially refer + # to child projects from parent projects, we don't immediately + # load child projects when seing those attributes. Instead, + # we record the minimal information that will be used only later. + + self.load_used_projects(mname) + + return mname + + def load_used_projects(self, module_name): + # local used = [ modules.peek $(module-name) : .used-projects ] ; + used = self.used_projects[module_name] + + location = self.attribute(module_name, "location") + for u in used: + id = u[0] + where = u[1] + + self.use(id, os.path.join(location, where)) + + def load_parent(self, location): + """Loads parent of Jamfile at 'location'. + Issues an error if nothing is found.""" + + found = b2.util.path.glob_in_parents( + location, self.JAMROOT + self.JAMFILE) + + if not found: + print "error: Could not find parent for project at '%s'" % location + print "error: Did not find Jamfile or project-root.jam in any parent directory." + sys.exit(1) + + return self.load(os.path.dirname(found[0])) + + def act_as_jamfile(self, module, location): + """Makes the specified 'module' act as if it were a regularly loaded Jamfile + at 'location'. If Jamfile is already located for that location, it's an + error.""" + + if self.module_name(location) in self.jamfile_modules: + self.manager.errors()( + "Jamfile was already loaded for '%s'" % location) + + # Set up non-default mapping from location to module. + self.location2module[location] = module + + # Add the location to the list of project locations + # so that we don't try to load Jamfile in future + self.jamfile_modules.append(location) + + self.initialize(module, location) + + def find(self, name, current_location): + """Given 'name' which can be project-id or plain directory name, + return project module corresponding to that id or directory. + Returns nothing of project is not found.""" + + project_module = None + + # Try interpreting name as project id. + if name[0] == '/': + project_module = self.id2module.get(name) + + if not project_module: + location = os.path.join(current_location, name) + # If no project is registered for the given location, try to + # load it. First see if we have Jamfile. If not we might have project + # root, willing to act as Jamfile. In that case, project-root + # must be placed in the directory referred by id. + + project_module = self.module_name(location) + if not project_module in self.jamfile_modules and \ + b2.util.path.glob([location], self.JAMROOT + self.JAMFILE): + project_module = self.load(location) + + return project_module + + def module_name(self, jamfile_location): + """Returns the name of module corresponding to 'jamfile-location'. + If no module corresponds to location yet, associates default + module name with that location.""" + module = self.location2module.get(jamfile_location) + if not module: + # Root the path, so that locations are always umbiguious. + # Without this, we can't decide if '../../exe/program1' and '.' + # are the same paths, or not. + jamfile_location = os.path.realpath( + os.path.join(os.getcwd(), jamfile_location)) + module = "Jamfile<%s>" % jamfile_location + self.location2module[jamfile_location] = module + return module + + def find_jamfile (self, dir, parent_root=0, no_errors=0): + """Find the Jamfile at the given location. This returns the + exact names of all the Jamfiles in the given directory. The optional + parent-root argument causes this to search not the given directory + but the ones above it up to the directory given in it.""" + + # Glob for all the possible Jamfiles according to the match pattern. + # + jamfile_glob = None + if parent_root: + parent = self.dir2parent_jamfile.get(dir) + if not parent: + parent = b2.util.path.glob_in_parents(dir, + self.JAMFILE) + self.dir2parent_jamfile[dir] = parent + jamfile_glob = parent + else: + jamfile = self.dir2jamfile.get(dir) + if not jamfile: + jamfile = b2.util.path.glob([dir], self.JAMFILE) + self.dir2jamfile[dir] = jamfile + jamfile_glob = jamfile + + if len(jamfile_glob): + # Multiple Jamfiles found in the same place. Warn about this. + # And ensure we use only one of them. + # As a temporary convenience measure, if there's Jamfile.v2 amount + # found files, suppress the warning and use it. + # + pattern = "(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)" + v2_jamfiles = [x for x in jamfile_glob if re.match(pattern, x)] + if len(v2_jamfiles) == 1: + jamfile_glob = v2_jamfiles + else: + print """warning: Found multiple Jamfiles at '%s'! +Loading the first one: '%s'.""" % (dir, jamfile_glob[0]) + + # Could not find it, error. + if not no_errors and not jamfile_glob: + self.manager.errors()( + """Unable to load Jamfile. +Could not find a Jamfile in directory '%s' +Attempted to find it with pattern '%s'. +Please consult the documentation at 'http://boost.org/boost-build2'.""" + % (dir, string.join(self.JAMFILE))) + + return jamfile_glob[0] + + def load_jamfile(self, dir): + """Load a Jamfile at the given directory. Returns nothing. + Will attempt to load the file as indicated by the JAMFILE patterns. + Effect of calling this rule twice with the same 'dir' is underfined.""" + + # See if the Jamfile is where it should be. + jamfile_to_load = b2.util.path.glob([dir], self.JAMROOT) + if not jamfile_to_load: + jamfile_to_load = self.find_jamfile(dir) + else: + jamfile_to_load = jamfile_to_load[0] + + # The module of the jamfile. + dir = os.path.realpath(os.path.dirname(jamfile_to_load)) + + jamfile_module = self.module_name (dir) + + # Initialize the jamfile module before loading. + # + self.initialize(jamfile_module, dir, os.path.basename(jamfile_to_load)) + + saved_project = self.current_project + + self.used_projects[jamfile_module] = [] + + # Now load the Jamfile in it's own context. + # Initialization might have load parent Jamfiles, which might have + # loaded the current Jamfile with use-project. Do a final check to make + # sure it's not loaded already. + if not jamfile_module in self.jamfile_modules: + self.jamfile_modules[jamfile_module] = True + + # FIXME: + # mark-as-user $(jamfile-module) ; + + bjam.call("load", jamfile_module, jamfile_to_load) + basename = os.path.basename(jamfile_to_load) + + # Now do some checks + if self.current_project != saved_project: + self.manager.errors()( +"""The value of the .current-project variable +has magically changed after loading a Jamfile. +This means some of the targets might be defined a the wrong project. +after loading %s +expected value %s +actual value %s""" % (jamfile_module, saved_project, self.current_project)) + + if self.global_build_dir: + id = self.attribute(jamfile_module, "id") + project_root = self.attribute(jamfile_module, "project-root") + location = self.attribute(jamfile_module, "location") + + if location and project_root == dir: + # This is Jamroot + if not id: + # FIXME: go via errors module, so that contexts are + # shown? + print "warning: the --build-dir option was specified" + print "warning: but Jamroot at '%s'" % dir + print "warning: specified no project id" + print "warning: the --build-dir option will be ignored" + + + def load_standalone(self, jamfile_module, file): + """Loads 'file' as standalone project that has no location + associated with it. This is mostly useful for user-config.jam, + which should be able to define targets, but although it has + some location in filesystem, we don't want any build to + happen in user's HOME, for example. + + The caller is required to never call this method twice on + the same file. + """ + + self.initialize(jamfile_module) + self.used_projects[jamfile_module] = [] + bjam.call("load", jamfile_module, file) + self.load_used_projects(jamfile_module) + + def is_jamroot(self, basename): + match = [ pat for pat in self.JAMROOT if re.match(pat, basename)] + if match: + return 1 + else: + return 0 + + def initialize(self, module_name, location=None, basename=None): + """Initialize the module for a project. + + module-name is the name of the project module. + location is the location (directory) of the project to initialize. + If not specified, stanalone project will be initialized + """ + + if "--debug-loading" in self.manager.argv(): + print "Initializing project '%s'" % module_name + + # TODO: need to consider if standalone projects can do anything but defining + # prebuilt targets. If so, we need to give more sensible "location", so that + # source paths are correct. + if not location: + location = "" + else: + location = b2.util.path.relpath(os.getcwd(), location) + + attributes = ProjectAttributes(self.manager, location, module_name) + self.module2attributes[module_name] = attributes + + if location: + attributes.set("source-location", location, exact=1) + else: + attributes.set("source-location", "", exact=1) + + attributes.set("requirements", property_set.empty(), exact=True) + attributes.set("usage-requirements", property_set.empty(), exact=True) + attributes.set("default-build", [], exact=True) + attributes.set("projects-to-build", [], exact=True) + attributes.set("project-root", None, exact=True) + attributes.set("build-dir", None, exact=True) + + self.project_rules_.init_project(module_name) + + jamroot = False + + parent_module = None; + if module_name == "site-config": + # No parent + pass + elif module_name == "user-config": + parent_module = "site-config" + elif location and not self.is_jamroot(basename): + # We search for parent/project-root only if jamfile was specified + # --- i.e + # if the project is not standalone. + parent_module = self.load_parent(location) + else: + # It's either jamroot, or standalone project. + # If it's jamroot, inherit from user-config. + if location: + parent_module = "user-config" ; + jamroot = True ; + + if parent_module: + self.inherit_attributes(module_name, parent_module) + attributes.set("parent-module", parent_module, exact=1) + + if jamroot: + attributes.set("project-root", location, exact=1) + + parent = None + if parent_module: + parent = self.target(parent_module) + + if not self.module2target.has_key(module_name): + target = b2.build.targets.ProjectTarget(self.manager, + module_name, module_name, parent, + self.attribute(module_name,"requirements"), + # FIXME: why we need to pass this? It's not + # passed in jam code. + self.attribute(module_name, "default-build")) + self.module2target[module_name] = target + + self.current_project = self.target(module_name) + + def inherit_attributes(self, project_module, parent_module): + """Make 'project-module' inherit attributes of project + root and parent module.""" + + attributes = self.module2attributes[project_module] + pattributes = self.module2attributes[parent_module] + + # Parent module might be locationless user-config. + # FIXME: + #if [ modules.binding $(parent-module) ] + #{ + # $(attributes).set parent : [ path.parent + # [ path.make [ modules.binding $(parent-module) ] ] ] ; + # } + + attributes.set("project-root", pattributes.get("project-root"), exact=True) + attributes.set("default-build", pattributes.get("default-build"), exact=True) + attributes.set("requirements", pattributes.get("requirements"), exact=True) + attributes.set("usage-requirements", + pattributes.get("usage-requirements"), exact=1) + + parent_build_dir = pattributes.get("build-dir") + + if parent_build_dir: + # Have to compute relative path from parent dir to our dir + # Convert both paths to absolute, since we cannot + # find relative path from ".." to "." + + location = attributes.get("location") + parent_location = pattributes.get("location") + + our_dir = os.path.join(os.getcwd(), location) + parent_dir = os.path.join(os.getcwd(), parent_location) + + build_dir = os.path.join(parent_build_dir, + b2.util.path.relpath(parent_dir, + our_dir)) + + def register_id(self, id, module): + """Associate the given id with the given project module.""" + self.id2module[id] = module + + def current(self): + """Returns the project which is currently being loaded.""" + return self.current_project + + def push_current(self, project): + """Temporary changes the current project to 'project'. Should + be followed by 'pop-current'.""" + self.saved_current_project.append(self.current_project) + self.current_project = project + + def pop_current(self): + self.current_project = self.saved_current_project[-1] + del self.saved_current_project[-1] + + def attributes(self, project): + """Returns the project-attribute instance for the + specified jamfile module.""" + return self.module2attributes[project] + + def attribute(self, project, attribute): + """Returns the value of the specified attribute in the + specified jamfile module.""" + return self.module2attributes[project].get(attribute) + + def target(self, project_module): + """Returns the project target corresponding to the 'project-module'.""" + if not self.module2target[project_module]: + self.module2target[project_module] = \ + ProjectTarget(project_module, project_module, + self.attribute(project_module, "requirements")) + + return self.module2target[project_module] + + def use(self, id, location): + # Use/load a project. + saved_project = self.current_project + project_module = self.load(location) + declared_id = self.attribute(project_module, "id") + + if not declared_id or declared_id != id: + # The project at 'location' either have no id or + # that id is not equal to the 'id' parameter. + if self.id2module[id] and self.id2module[id] != project_module: + self.manager.errors()( +"""Attempt to redeclare already existing project id '%s'""" % id) + self.id2module[id] = project_module + + self.current_module = saved_project + + def add_rule(self, name, callable): + """Makes rule 'name' available to all subsequently loaded Jamfiles. + + Calling that rule wil relay to 'callable'.""" + self.project_rules_.add_rule(name, callable) + + def project_rules(self): + return self.project_rules_ + + def glob_internal(self, project, wildcards, excludes, rule_name): + location = project.get("source-location") + + result = [] + callable = b2.util.path.__dict__[rule_name] + + paths = callable(location, wildcards, excludes) + has_dir = 0 + for w in wildcards: + if os.path.dirname(w): + has_dir = 1 + break + + if has_dir or rule_name != "glob": + # The paths we've found are relative to current directory, + # but the names specified in sources list are assumed to + # be relative to source directory of the corresponding + # prject. So, just make the name absolute. + result = [os.path.join(os.getcwd(), p) for p in paths] + else: + # There were not directory in wildcard, so the files are all + # in the source directory of the project. Just drop the + # directory, instead of making paths absolute. + result = [os.path.basename(p) for p in paths] + + return result + + def load_module(self, name, extra_path=None): + """Classic Boost.Build 'modules' are in fact global variables. + Therefore, try to find an already loaded Python module called 'name' in sys.modules. + If the module ist not loaded, find it Boost.Build search + path and load it. The new module is not entered in sys.modules. + The motivation here is to have disjoint namespace of modules + loaded via 'import/using' in Jamfile, and ordinary Python + modules. We don't want 'using foo' in Jamfile to load ordinary + Python module 'foo' which is going to not work. And we + also don't want 'import foo' in regular Python module to + accidentally grab module named foo that is internal to + Boost.Build and intended to provide interface to Jamfiles.""" + + existing = self.loaded_tool_modules_.get(name) + if existing: + return existing + + modules = sys.modules + for class_name in modules: + if name is class_name: + module = modules[class_name] + self.loaded_tool_modules_[name] = module + return module + + path = extra_path + if not path: + path = [] + path.extend(self.manager.boost_build_path()) + location = None + for p in path: + l = os.path.join(p, name + ".py") + if os.path.exists(l): + location = l + break + + if not location: + self.manager.errors()("Cannot find module '%s'" % name) + + mname = "__build_build_temporary__" + file = open(location) + try: + # TODO: this means we'll never make use of .pyc module, + # which might be a problem, or not. + module = imp.load_module(mname, file, os.path.basename(location), + (".py", "r", imp.PY_SOURCE)) + del sys.modules[mname] + self.loaded_tool_modules_[name] = module + return module + finally: + file.close() + + + +# FIXME: +# Defines a Boost.Build extension project. Such extensions usually +# contain library targets and features that can be used by many people. +# Even though extensions are really projects, they can be initialize as +# a module would be with the "using" (project.project-rules.using) +# mechanism. +#rule extension ( id : options * : * ) +#{ +# # The caller is a standalone module for the extension. +# local mod = [ CALLER_MODULE ] ; +# +# # We need to do the rest within the extension module. +# module $(mod) +# { +# import path ; +# +# # Find the root project. +# local root-project = [ project.current ] ; +# root-project = [ $(root-project).project-module ] ; +# while +# [ project.attribute $(root-project) parent-module ] && +# [ project.attribute $(root-project) parent-module ] != user-config +# { +# root-project = [ project.attribute $(root-project) parent-module ] ; +# } +# +# # Create the project data, and bring in the project rules +# # into the module. +# project.initialize $(__name__) : +# [ path.join [ project.attribute $(root-project) location ] ext $(1:L) ] ; +# +# # Create the project itself, i.e. the attributes. +# # All extensions are created in the "/ext" project space. +# project /ext/$(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; +# local attributes = [ project.attributes $(__name__) ] ; +# +# # Inherit from the root project of whomever is defining us. +# project.inherit-attributes $(__name__) : $(root-project) ; +# $(attributes).set parent-module : $(root-project) : exact ; +# } +#} + + +class ProjectAttributes: + """Class keeping all the attributes of a project. + + The standard attributes are 'id', "location", "project-root", "parent" + "requirements", "default-build", "source-location" and "projects-to-build". + """ + + def __init__(self, manager, location, project_module): + self.manager = manager + self.location = location + self.project_module = project_module + self.attributes = {} + self.usage_requirements = None + + def set(self, attribute, specification, exact): + """Set the named attribute from the specification given by the user. + The value actually set may be different.""" + + if exact: + self.__dict__[attribute] = specification + + elif attribute == "requirements": + self.requirements = property_set.refine_from_user_input( + self.requirements, specification, + self.project_module, self.location) + + elif attribute == "usage-requirements": + unconditional = [] + for p in specification: + split = property.split_conditional(p) + if split: + unconditional.append(split[1]) + else: + unconditional.append(p) + + non_free = property.remove("free", unconditional) + if non_free: + pass + # FIXME: + #errors.error "usage-requirements" $(specification) "have non-free properties" $(non-free) ; + + t = property.translate_paths(specification, self.location) + + existing = self.__dict__.get("usage-requirements") + if existing: + new = property_set.create(existing.raw() + t) + else: + new = property_set.create(t) + self.__dict__["usage-requirements"] = new + + + elif attribute == "default-build": + self.__dict__["default-build"] = property_set.create(specification) + + elif attribute == "source-location": + source_location = [] + for path in specification: + source_location += os.path.join(self.location, path) + self.__dict__["source-location"] = source_location + + elif attribute == "build-dir": + self.__dict__["build-dir"] = os.path.join(self.location, specification) + + elif not attribute in ["id", "default-build", "location", + "source-location", "parent", + "projects-to-build", "project-root"]: + self.manager.errors()( +"""Invalid project attribute '%s' specified +for project at '%s'""" % (attribute, self.location)) + else: + self.__dict__[attribute] = specification + + def get(self, attribute): + return self.__dict__[attribute] + + def dump(self): + """Prints the project attributes.""" + id = self.get("id") + if not id: + id = "(none)" + else: + id = id[0] + + parent = self.get("parent") + if not parent: + parent = "(none)" + else: + parent = parent[0] + + print "'%s'" % id + print "Parent project:%s", parent + print "Requirements:%s", self.get("requirements") + print "Default build:%s", string.join(self.get("debuild-build")) + print "Source location:%s", string.join(self.get("source-location")) + print "Projects to build:%s", string.join(self.get("projects-to-build").sort()); + +class ProjectRules: + """Class keeping all rules that are made available to Jamfile.""" + + def __init__(self, registry): + self.registry = registry + self.manager_ = registry.manager + self.rules = {} + self.local_names = [x for x in self.__class__.__dict__ + if x not in ["__init__", "init_project", "add_rule", + "error_reporting_wrapper", "add_rule_for_type"]] + self.all_names_ = [x for x in self.local_names] + + def add_rule_for_type(self, type): + rule_name = type.lower(); + + def xpto (name, sources, requirements = [], default_build = None, usage_requirements = []): + return self.manager_.targets().create_typed_target( + type, self.registry.current(), name[0], sources, + requirements, default_build, usage_requirements) + + self.add_rule(type.lower(), xpto) + + def add_rule(self, name, callable): + self.rules[name] = callable + self.all_names_.append(name) + + def all_names(self): + return self.all_names_ + + def call_and_report_errors(self, callable, *args): + result = None + try: + self.manager_.errors().push_jamfile_context() + result = callable(*args) + except ExceptionWithUserContext, e: + e.report() + except Exception, e: + try: + self.manager_.errors().handle_stray_exception (e) + except ExceptionWithUserContext, e: + e.report() + finally: + self.manager_.errors().pop_jamfile_context() + + return result + + def make_wrapper(self, callable): + """Given a free-standing function 'callable', return a new + callable that will call 'callable' and report all exceptins, + using 'call_and_report_errors'.""" + def wrapper(*args): + self.call_and_report_errors(callable, *args) + return wrapper + + def init_project(self, project_module): + + for n in self.local_names: + # Using 'getattr' here gives us a bound method, + # while using self.__dict__[r] would give unbound one. + v = getattr(self, n) + if callable(v): + if n == "import_": + n = "import" + else: + n = string.replace(n, "_", "-") + + bjam.import_rule(project_module, n, + self.make_wrapper(v)) + + for n in self.rules: + bjam.import_rule(project_module, n, + self.make_wrapper(self.rules[n])) + + def project(self, *args): + + jamfile_module = self.registry.current().project_module() + attributes = self.registry.attributes(jamfile_module) + + id = None + if args and args[0]: + id = args[0][0] + args = args[1:] + + if id: + if id[0] != '/': + id = '/' + id + self.registry.register_id (id, jamfile_module) + + explicit_build_dir = None + for a in args: + if a: + attributes.set(a[0], a[1:], exact=0) + if a[0] == "build-dir": + explicit_build_dir = a[1] + + # If '--build-dir' is specified, change the build dir for the project. + if self.registry.global_build_dir: + + location = attributes.get("location") + # Project with empty location is 'standalone' project, like + # user-config, or qt. It has no build dir. + # If we try to set build dir for user-config, we'll then + # try to inherit it, with either weird, or wrong consequences. + if location and location == attributes.get("project-root"): + # This is Jamroot. + if id: + if explicit_build_dir and os.path.isabs(explicit_build_dir): + self.register.manager.errors()( +"""Absolute directory specified via 'build-dir' project attribute +Don't know how to combine that with the --build-dir option.""") + + rid = id + if rid[0] == '/': + rid = rid[1:] + + p = os.path.join(self.registry.global_build_dir, + rid, explicit_build_dir) + attributes.set("build-dir", p, exact=1) + elif explicit_build_dir: + self.registry.manager.errors()( +"""When --build-dir is specified, the 'build-project' +attribute is allowed only for top-level 'project' invocations""") + + def constant(self, name, value): + """Declare and set a project global constant. + Project global constants are normal variables but should + not be changed. They are applied to every child Jamfile.""" + m = "Jamfile" + self.registry.current().add_constant(name[0], value) + + def path_constant(self, name, value): + """Declare and set a project global constant, whose value is a path. The + path is adjusted to be relative to the invocation directory. The given + value path is taken to be either absolute, or relative to this project + root.""" + self.registry.current().add_constant(name[0], value, path=1) + + def use_project(self, id, where): + # See comment in 'load' for explanation why we record the + # parameters as opposed to loading the project now. + m = self.registry.current().project_module(); + self.registry.used_projects[m].append((id, where)) + + def build_project(self, dir): + assert(isinstance(dir, list)) + jamfile_module = self.registry.current().project_module() + attributes = self.registry.attributes(jamfile_module) + now = attributes.get("projects-to-build") + attributes.set("projects-to-build", now + dir, exact=True) + + def explicit(self, target_names): + t = self.registry.current() + for n in target_names: + t.mark_target_as_explicit(n) + + def glob(self, wildcards, excludes=None): + return self.registry.glob_internal(self.registry.current(), + wildcards, excludes, "glob") + + def glob_tree(self, wildcards, excludes=None): + bad = 0 + for p in wildcards: + if os.path.dirname(p): + bad = 1 + + if excludes: + for p in excludes: + if os.path.dirname(p): + bad = 1 + + if bad: + self.registry.manager().errors()( +"The patterns to 'glob-tree' may not include directory") + return self.registry.glob_internal(self.registry.current(), + wildcards, excludes, "glob_tree") + + + def using(self, toolset, *args): + # The module referred by 'using' can be placed in + # the same directory as Jamfile, and the user + # will expect the module to be found even though + # the directory is not in BOOST_BUILD_PATH. + # So temporary change the search path. + jamfile_module = self.registry.current().project_module() + attributes = self.registry.attributes(jamfile_module) + location = attributes.get("location") + + m = self.registry.load_module(toolset[0], [location]) + if not m.__dict__.has_key("init"): + self.registry.manager.errors()( + "Tool module '%s' does not define the 'init' method" % toolset[0]) + m.init(*args) + + + def import_(self, name, names_to_import=None, local_names=None): + + name = name[0] + jamfile_module = self.registry.current().project_module() + attributes = self.registry.attributes(jamfile_module) + location = attributes.get("location") + + m = self.registry.load_module(name, [location]) + + for f in m.__dict__: + v = m.__dict__[f] + if callable(v): + bjam.import_rule(jamfile_module, name + "." + f, v) + + if names_to_import: + if not local_names: + local_names = names_to_import + + if len(names_to_import) != len(local_names): + self.registry.manager.errors()( +"""The number of names to import and local names do not match.""") + + for n, l in zip(names_to_import, local_names): + bjam.import_rule(jamfile_module, l, m.__dict__[n]) + + def conditional(self, condition, requirements): + """Calculates conditional requirements for multiple requirements + at once. This is a shorthand to be reduce duplication and to + keep an inline declarative syntax. For example: + + lib x : x.cpp : [ conditional gcc debug : + DEBUG_EXCEPTION DEBUG_TRACE ] ; + """ + + c = string.join(condition, ",") + return [c + ":" + r for r in requirements] diff --git a/v2/build/property.py b/v2/build/property.py new file mode 100644 index 000000000..bc91cc243 --- /dev/null +++ b/v2/build/property.py @@ -0,0 +1,636 @@ +# Status: ported, except for tests and --abbreviate-paths. +# Base revision: 40480 +# +# Copyright 2001, 2002, 2003 Dave Abrahams +# Copyright 2006 Rene Rivera +# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +import re +from b2.util.utility import * +from b2.build import feature +from b2.util import sequence, set + +__re_two_ampersands = re.compile ('&&') +__re_comma = re.compile (',') +__re_split_condition = re.compile ('(.*):(<.*)') +__re_toolset_feature = re.compile ('^(|)') +__re_os_feature = re.compile ('^()') +__re_split_conditional = re.compile (r'(.+):<(.+)') +__re_colon = re.compile (':') +__re_has_condition = re.compile (r':<') +__re_separate_condition_and_property = re.compile (r'(.*):(<.*)') + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __results + + # A cache of results from as_path + __results = {} + +reset () + + +def path_order (x, y): + """ Helper for as_path, below. Orders properties with the implicit ones + first, and within the two sections in alphabetical order of feature + name. + """ + if x == y: + return 0 + + xg = get_grist (x) + yg = get_grist (y) + + if yg and not xg: + return -1 + + elif xg and not yg: + return 1 + + else: + if not xg: + x = feature.expand_subfeatures([x]) + y = feature.expand_subfeatures([y]) + + if x < y: + return -1 + elif x > y: + return 1 + else: + return 0 + +def abbreviate_dashed(string): + # FIXME: string.abbreviate? + return [string.abbreviate(part) for part in string.split('-')].join('-') + +def identify(string): + return string + +# FIXME: --abbreviate-paths + +def as_path (properties): + """ Returns a path which represents the given expanded property set. + """ + key = '-'.join (properties) + + if not __results.has_key (key): + # trim redundancy + properties = feature.minimize (properties) + + # sort according to path_order + properties.sort (path_order) + + components = [] + for p in properties: + pg = get_grist (p) + # FIXME: abbrev? + if pg: + f = ungrist (pg) + components.append (f + '-' + replace_grist (p, '')) + + else: + components.append (p) + + __results [key] = '/'.join (components) + + return __results [key] + +def refine (properties, requirements): + """ Refines 'properties' by overriding any non-free properties + for which a different value is specified in 'requirements'. + Conditional requirements are just added without modification. + Returns the resulting list of properties. + """ + # The result has no duplicates, so we store it in a map + # TODO: use a set from Python 2.4? + result = {} + + # Records all requirements. + required = {} + + # All the elements of requirements should be present in the result + # Record them so that we can handle 'properties'. + for r in requirements: + # Don't consider conditional requirements. + if not is_conditional (r): + # Note: cannot use local here, so take an ugly name + required [get_grist (r)] = replace_grist (r, '') + + for p in properties: + # Skip conditional properties + if is_conditional (p): + result [p] = None + # No processing for free properties + elif 'free' in feature.attributes (get_grist (p)): + result [p] = None + else: + if required.has_key (get_grist (p)): + required_value = required [get_grist (p)] + + value = replace_grist (p, '') + + if value != required_value: + result [replace_grist (required_value, get_grist (p))] = None + else: + result [p] = None + else: + result [p] = None + + return result.keys () + requirements + +def translate_paths (properties, path): + """ Interpret all path properties in 'properties' as relative to 'path' + The property values are assumed to be in system-specific form, and + will be translated into normalized form. + """ + result = [] + + for p in properties: + split = split_conditional (p) + + condition = '' + + if split: + condition = split [0] + p = split [1] + + if get_grist (p) and 'path' in feature.attributes (get_grist (p)): + values = __re_two_ampersands.split (forward_slashes (get_grist (p))) + + t = [os.path.join(path, v) for v in values] + t = '&&'.join (t) + tp = backslashes_to_slashes (replace_grist (t, get_grist (p))) + result.append (condition + tp) + + else: + result.append (condition + p) + + return result + +def translate_indirect(specification, context_module): + """Assumes that all feature values that start with '@' are + names of rules, used in 'context-module'. Such rules can be + either local to the module or global. Qualified local rules + with the name of the module.""" + result = [] + for p in specification: + if p[0] == '@': + m = p[1:] + if not '.' in p: + # This is unqualified rule name. The user might want + # to set flags on this rule name, and toolset.flag + # auto-qualifies the rule name. Need to do the same + # here so set flag setting work. + # We can arrange for toolset.flag to *not* auto-qualify + # the argument, but then two rules defined in two Jamfiles + # will conflict. + m = context_module + "." + m + + result.append(get_grist(p) + "@" + m) + else: + result.append(p) + + return result + +def validate (properties): + """ Exit with error if any of the properties is not valid. + properties may be a single property or a sequence of properties. + """ + + if isinstance (properties, str): + __validate1 (properties) + else: + for p in properties: + __validate1 (p) + +def expand_subfeatures_in_conditions (properties): + + result = [] + for p in properties: + s = __re_split_condition.match (p) + + if not s: + result.append (p) + + else: + condition = s.group (1) + + # Condition might include several elements + condition = __re_comma.split (condition) + + value = s.group (2) + + e = [] + for c in condition: + + cg = get_grist (c) + if __re_toolset_feature.match (cg) or __re_os_feature.match (cg): + # It common that condition includes a toolset which + # was never defined, or mentiones subfeatures which + # were never defined. In that case, validation will + # only produce an spirious error, so don't validate. + e.append (feature.expand_subfeatures (c, True)) + + else: + e.append (feature.expand_subfeatures (c)) + + if e == condition: + result.append (p) + + else: + individual_subfeatures = Set.difference (e, condition) + result.append (','.join (individual_subfeatures) + ':' + value) + + return result + +def make (specification): + """ Converts implicit values into full properties. + """ + result = [] + for e in specification: + if get_grist (e): + result.append (e) + + elif feature.is_implicit_value (e): + f = feature.implied_feature (e) + result.append (f + e) + + else: + raise InvalidProperty ("'%s' is not a valid for property specification" % e) + + return result + + +def split_conditional (property): + """ If 'property' is conditional property, returns + condition and the property, e.g + debug,gcc:full will become + debug,gcc full. + Otherwise, returns empty string. + """ + m = __re_split_conditional.match (property) + + if m: + return (m.group (1), '<' + m.group (2)) + + return None + +def is_conditional (property): + """ Returns True if a property is conditional. + """ + if __re_colon.search (replace_grist (property, '')): + return True + else: + return False + +def select (features, properties): + """ Selects properties which correspond to any of the given features. + """ + result = [] + + # add any missing angle brackets + features = add_grist (features) + + return [p for p in properties if get_grist(p) in features] + +def validate_property_sets (sets): + for s in sets: + validate(feature.split(s)) + + +def evaluate_conditionals_in_context (properties, context): + """ Removes all conditional properties which conditions are not met + For those with met conditions, removes the condition. Properies + in conditions are looked up in 'context' + """ + base = [] + conditionals = [] + + for p in properties: + if __re_has_condition.search (p): + conditionals.append (p) + else: + base.append (p) + + result = base + for p in conditionals: + + # Separate condition and property + s = __re_separate_condition_and_property.match (p) + + # Split condition into individual properties + conditions = s.group (1).split (',') + + # Evaluate condition + if set.contains (c, context): + result.append (s.group (2)) + + return result + +def expand_subfeatures_in_conditions(properties): + + result = [] + for p in properties: + + s = __re_separate_condition_and_property.match(p) + if not s: + result.append(p) + else: + condition = s.group(1) + # Condition might include several elements + condition = condition.split(",") + value = s.group(2) + + e = [] + for c in condition: + # It common that condition includes a toolset which + # was never defined, or mentiones subfeatures which + # were never defined. In that case, validation will + # only produce an spirious error, so prevent + # validation by passing 'true' as second parameter. + e.extend(feature.expand_subfeatures(c, dont_validate=True)) + + if e == condition: + result.append(p) + else: + individual_subfeatures = set.difference(e, condition) + result.append(",".join(individual_subfeatures) + ":" + value) + + return result + +def change (properties, feature, value = None): + """ Returns a modified version of properties with all values of the + given feature replaced by the given value. + If 'value' is None the feature will be removed. + """ + result = [] + + feature = add_grist (feature) + + for p in properties: + if get_grist (p) == feature: + if value: + result.append (replace_grist (value, feature)) + + else: + result.append (p) + + return result + + +################################################################ +# Private functions + +def __validate1 (property): + """ Exit with error if property is not valid. + """ + msg = None + + f = get_grist (property) + if f: + value = get_value (property) + + if not feature.valid (f): + f = ungrist (get_grist (property)) # Ungrist for better error messages + msg = "Unknown feature '%s'" % f + + elif value and not 'free' in feature.attributes (f): + feature.validate_value_string (f, value) + + elif not value: + f = ungrist (get_grist (property)) # Ungrist for better error messages + msg = "No value specified for feature '%s'" % f + + else: + f = feature.implied_feature (property) + feature.validate_value_string (f, property) + + if msg: + # FIXME: don't use globals like this. Import here to + # break circular dependency. + from b2.manager import get_manager + get_manager().errors()("Invalid property '%s': %s" % (property, msg)) + + +################################################################### +# Still to port. +# Original lines are prefixed with "# " +# +# +# import utility : ungrist ; +# import sequence : unique ; +# import errors : error ; +# import feature ; +# import regex ; +# import sequence ; +# import set ; +# import path ; +# import assert ; +# +# + + +# rule validate-property-sets ( property-sets * ) +# { +# for local s in $(property-sets) +# { +# validate [ feature.split $(s) ] ; +# } +# } +# + +def remove(attributes, properties): + """Returns a property sets which include all the elements + in 'properties' that do not have attributes listed in 'attributes'.""" + + result = [] + for e in properties: + attributes_new = feature.attributes(get_grist(e)) + has_common_features = 0 + for a in attributes_new: + if a in attributes: + has_common_features = 1 + break + + if not has_common_features: + result += e + + return result + + +def take(attributes, properties): + """Returns a property set which include all + properties in 'properties' that have any of 'attributes'.""" + result = [] + for e in properties: + if set.intersection(attributes, feature.attributes(get_grist(e))): + result.append(e) + return result + + +class PropertyMap: + """ Class which maintains a property set -> string mapping. + """ + def __init__ (self): + self.__properties = [] + self.__values = [] + + def insert (self, properties, value): + """ Associate value with properties. + """ + self.__properties.append(properties) + self.__values.append(value) + + def find (self, properties): + """ Return the value associated with properties + or any subset of it. If more than one + subset has value assigned to it, return the + value for the longest subset, if it's unique. + """ + return self.find_replace (properties) + + def find_replace(self, properties, value=None): + matches = [] + match_ranks = [] + + for i in range(0, len(self.__properties)): + p = self.__properties[i] + + if set.contains (p, properties): + matches.append (i) + match_ranks.append(len(p)) + + best = sequence.select_highest_ranked (matches, match_ranks) + + if not best: + return None + + if len (best) > 1: + raise NoBestMatchingAlternative () + + best = best [0] + + original = self.__values[best] + + if value: + self.__values[best] = value + + return original + +# local rule __test__ ( ) +# { +# import errors : try catch ; +# import feature ; +# import feature : feature subfeature compose ; +# +# # local rules must be explicitly re-imported +# import property : path-order ; +# +# feature.prepare-test property-test-temp ; +# +# feature toolset : gcc : implicit symmetric ; +# subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 +# 3.0 3.0.1 3.0.2 : optional ; +# feature define : : free ; +# feature runtime-link : dynamic static : symmetric link-incompatible ; +# feature optimization : on off ; +# feature variant : debug release : implicit composite symmetric ; +# feature rtti : on off : link-incompatible ; +# +# compose debug : _DEBUG off ; +# compose release : NDEBUG on ; +# +# import assert ; +# import "class" : new ; +# +# validate gcc gcc-3.0.1 : $(test-space) ; +# +# assert.result gcc off FOO +# : refine gcc off +# : FOO +# : $(test-space) +# ; +# +# assert.result gcc on +# : refine gcc off +# : on +# : $(test-space) +# ; +# +# assert.result gcc off +# : refine gcc : off : $(test-space) +# ; +# +# assert.result gcc off off:FOO +# : refine gcc : off off:FOO +# : $(test-space) +# ; +# +# assert.result gcc:foo gcc:bar +# : refine gcc:foo : gcc:bar +# : $(test-space) +# ; +# +# assert.result MY_RELEASE +# : evaluate-conditionals-in-context +# release,off:MY_RELEASE +# : gcc release off +# +# ; +# +# try ; +# validate value : $(test-space) ; +# catch "Invalid property 'value': unknown feature 'feature'." ; +# +# try ; +# validate default : $(test-space) ; +# catch \"default\" is not a known value of feature ; +# +# validate WHATEVER : $(test-space) ; +# +# try ; +# validate : $(test-space) ; +# catch "Invalid property '': No value specified for feature 'rtti'." ; +# +# try ; +# validate value : $(test-space) ; +# catch "value" is not a value of an implicit feature ; +# +# +# assert.result on +# : remove free implicit : gcc foo on : $(test-space) ; +# +# assert.result a +# : select include : a gcc ; +# +# assert.result a +# : select include bar : a gcc ; +# +# assert.result a gcc +# : select include : a gcc ; +# +# assert.result kylix a +# : change gcc a : kylix ; +# +# # Test ordinary properties +# assert.result +# : split-conditional gcc +# ; +# +# # Test properties with ":" +# assert.result +# : split-conditional FOO=A::B +# ; +# +# # Test conditional feature +# assert.result gcc,3.0 FOO +# : split-conditional gcc,3.0:FOO +# ; +# +# feature.finish-test property-test-temp ; +# } +# + diff --git a/v2/build/property_set.py b/v2/build/property_set.py new file mode 100644 index 000000000..5ccafd404 --- /dev/null +++ b/v2/build/property_set.py @@ -0,0 +1,368 @@ +# Status: ported. +# Base revision: 40480 + +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +from b2.util.utility import * +import property, feature, string +from b2.exceptions import * +from b2.util.sequence import unique +from b2.util.set import difference + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __cache + + # A cache of property sets + # TODO: use a map of weak refs? + __cache = {} + +reset () + + +def create (raw_properties = []): + """ Creates a new 'PropertySet' instance for the given raw properties, + or returns an already existing one. + """ + raw_properties.sort () + raw_properties = unique (raw_properties) + + key = '-'.join (raw_properties) + + if not __cache.has_key (key): + __cache [key] = PropertySet (raw_properties) + + return __cache [key] + +def create_with_validation (raw_properties): + """ Creates new 'PropertySet' instances after checking + that all properties are valid and converting incidental + properties into gristed form. + """ + property.validate (raw_properties) + + return create (property.make (raw_properties)) + +def empty (): + """ Returns PropertySet with empty set of properties. + """ + return create () + +def create_from_user_input(raw_properties, jamfile_module, location): + """Creates a property-set from the input given by the user, in the + context of 'jamfile-module' at 'location'""" + + property.validate(raw_properties) + + specification = property.translate_paths(raw_properties, location) + specification = property.translate_indirect(specification, jamfile_module) + specification = property.expand_subfeatures_in_conditions(specification) + specification = property.make(specification) + return create(specification) + + +def refine_from_user_input(parent_requirements, specification, jamfile_module, + location): + """Refines requirements with requirements provided by the user. + Specially handles "-value" syntax in specification + to remove given requirements. + - parent-requirements -- property-set object with requirements + to refine + - specification -- string list of requirements provided by the use + - project-module -- the module to which context indirect features + will be bound. + - location -- the path to which path features are relative.""" + + + if not specification: + return parent_requirements + + + add_requirements = [] + remove_requirements = [] + + for r in specification: + if r[0] == '-': + remove_requirements.append(r[1:]) + else: + add_requirements.append(r) + + if remove_requirements: + # Need to create property set, so that path features + # and indirect features are translated just like they + # are in project requirements. + ps = create_from_user_input(remove_requirements, + jamfile_module, location) + + parent_requirements = create(difference(parent_requirements.raw(), + ps.raw())) + specification = add_requirements + + requirements = create_from_user_input(specification, + jamfile_module, location) + + return parent_requirements.refine(requirements) + +class PropertySet: + """ Class for storing a set of properties. + - there's 1<->1 correspondence between identity and value. No + two instances of the class are equal. To maintain this property, + the 'PropertySet.create' rule should be used to create new instances. + Instances are immutable. + + - each property is classified with regard to it's effect on build + results. Incidental properties have no effect on build results, from + Boost.Build point of view. Others are either free, or non-free, which we + call 'base'. Each property belong to exactly one of those categories and + it's possible to get list of properties in each category. + + In addition, it's possible to get list of properties with specific + attribute. + + - several operations, like and refine and as_path are provided. They all use + caching whenever possible. + """ + def __init__ (self, raw_properties = []): + + self.raw_ = raw_properties + + self.incidental_ = [] + self.free_ = [] + self.base_ = [] + self.dependency_ = [] + self.non_dependency_ = [] + self.conditional_ = [] + self.non_conditional_ = [] + self.propagated_ = [] + self.link_incompatible = [] + + # A cache of refined properties. + self.refined_ = {} + + # A cache of property sets created by adding properties to this one. + self.added_ = {} + + # Cache for the default properties. + self.defaults_ = None + + # Cache for the expanded properties. + self.expanded_ = None + + # Cache for the expanded composite properties + self.composites_ = None + + # Cache for the property set containing propagated properties. + self.propagated_ps_ = None + + # A map of features to its values. + self.feature_map_ = None + + # A tuple (target path, is relative to build directory) + self.target_path_ = None + + self.as_path_ = None + + # A cache for already evaluated sets. + self.evaluated_ = {} + + for p in raw_properties: + if not get_grist (p): + raise BaseException ("Invalid property: '%s'" % p) + + att = feature.attributes (get_grist (p)) + + # A feature can be both incidental and free, + # in which case we add it to incidental. + if 'incidental' in att: + self.incidental_.append (p) + elif 'free' in att: + self.free_.append (p) + else: + self.base_.append (p) + + if 'dependency' in att: + self.dependency_.append (p) + else: + self.non_dependency_.append (p) + + if property.is_conditional (p): + self.conditional_.append (p) + else: + self.non_conditional_.append (p) + + if 'propagated' in att: + self.propagated_.append (p) + + if 'link_incompatible' in att: + self.link_incompatible.append (p) + + def raw (self): + """ Returns the list of stored properties. + """ + return self.raw_ + + def __str__(self): + return string.join(self.raw_) + + def base (self): + """ Returns properties that are neither incidental nor free. + """ + return self.base_ + + def free (self): + """ Returns free properties which are not dependency properties. + """ + return self.free_ + + def dependency (self): + """ Returns dependency properties. + """ + return self.dependency_ + + def non_dependency (self): + """ Returns properties that are not dependencies. + """ + return self.non_dependency_ + + def conditional (self): + """ Returns conditional properties. + """ + return self.conditional_ + + def non_conditional (self): + """ Returns properties that are not conditional. + """ + return self.non_conditional_ + + def incidental (self): + """ Returns incidental properties. + """ + return self.incidental_ + + def refine (self, requirements): + """ Refines this set's properties using the requirements passed as an argument. + """ + str_req = str (requirements) + if not self.refined_.has_key (str_req): + r = property.refine (self.raw (), requirements.raw ()) + + self.refined_ [str_req] = create (r) + + return self.refined_ [str_req] + + def expand (self): + if not self.expanded_: + expanded = feature.expand (self.raw_) + self.expanded_ = create (expanded) + return self.expanded_ + + def expand_componsite(self): + if not self.componsites_: + self.composites_ = create(feature.expand_composires(self.raw_)) + return self.composites_ + + def evaluate_conditionals(self, context=None): + if not context: + context = self + + if not self.evaluated_.has_key(context): + self.evaluated_[context] = create( + property.evaluate_conditionals_in_context(self.raw_, + context.raw())) + + return self.evaluated_[context] + + def propagated (self): + if not self.propagated_ps_: + self.propagated_ps_ = create (self.propagated_) + return self.propagated_ps_ + + def add_defaults (self): + if not self.defaults_: + expanded = feature.add_defaults(self.raw_) + self.defaults_ = create(expanded) + return self.defaults_ + + def as_path (self): + if not self.as_path_: + self.as_path_ = property.as_path(self.base_) + + return self.as_path_ + + def target_path (self): + """ Computes the target path that should be used for + target with these properties. + Returns a tuple of + - the computed path + - if the path is relative to build directory, a value of + 'true'. + """ + if not self.target_path_: + # The feature can be used to explicitly + # change the location of generated targets + l = self.get ('') + if l: + computed = l + is_relative = False + + else: + p = self.as_path () + + # Really, an ugly hack. Boost regression test system requires + # specific target paths, and it seems that changing it to handle + # other directory layout is really hard. For that reason, + # we teach V2 to do the things regression system requires. + # The value o '' is predended to the path. + prefix = self.get ('') + + if prefix: + if len (prefix) > 1: + raise AlreadyDefined ("Two properties specified: '%s'" % prefix) + + computed = os.path.join(prefix[0], p) + + else: + computed = p + + if not computed: + computed = "." + + is_relative = True + + self.target_path_ = (computed, is_relative) + + return self.target_path_ + + def add (self, ps): + """ Creates a new property set containing the properties in this one, + plus the ones of the property set passed as argument. + """ + if not self.added_.has_key (str (ps)): + self.added_ [str (ps)] = create (self.raw_ + ps.raw ()) + return self.added_ [str (ps)] + + def add_raw (self, properties): + """ Creates a new property set containing the properties in this one, + plus the ones passed as argument. + """ + return self.add (create (properties)) + + + def get (self, feature): + """ Returns all values of 'feature'. + """ + if not self.feature_map_: + self.feature_map_ = {} + + for v in self.raw_: + key = get_grist (v) + if not self.feature_map_.has_key (key): + self.feature_map_ [key] = [] + self.feature_map_ [get_grist (v)].append (replace_grist (v, '')) + + return self.feature_map_.get (feature, []) + diff --git a/v2/build/scanner.py b/v2/build/scanner.py new file mode 100644 index 000000000..63f286994 --- /dev/null +++ b/v2/build/scanner.py @@ -0,0 +1,157 @@ +# Status: ported. +# Base revision: 45462 +# +# Copyright 2003 Dave Abrahams +# Copyright 2002, 2003, 2004, 2005 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# Implements scanners: objects that compute implicit dependencies for +# files, such as includes in C++. +# +# Scanner has a regular expression used to find dependencies, some +# data needed to interpret those dependencies (for example, include +# paths), and a code which actually established needed relationship +# between actual jam targets. +# +# Scanner objects are created by actions, when they try to actualize +# virtual targets, passed to 'virtual-target.actualize' method and are +# then associated with actual targets. It is possible to use +# several scanners for a virtual-target. For example, a single source +# might be used by to compile actions, with different include paths. +# In this case, two different actual targets will be created, each +# having scanner of its own. +# +# Typically, scanners are created from target type and action's +# properties, using the rule 'get' in this module. Directly creating +# scanners is not recommended, because it might create many equvivalent +# but different instances, and lead in unneeded duplication of +# actual targets. However, actions can also create scanners in a special +# way, instead of relying on just target type. + +import property +import bjam +from b2.exceptions import * +from b2.manager import get_manager + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __scanners, __rv_cache, __scanner_cache + + # Maps registered scanner classes to relevant properties + __scanners = {} + + # A cache of scanners. + # The key is: class_name.properties_tag, where properties_tag is the concatenation + # of all relevant properties, separated by '-' + __scanner_cache = {} + +reset () + + +def register(scanner_class, relevant_properties): + """ Registers a new generator class, specifying a set of + properties relevant to this scanner. Ctor for that class + should have one parameter: list of properties. + """ + __scanners[str(scanner_class)] = relevant_properties + +def registered(scanner_class): + """ Returns true iff a scanner of that class is registered + """ + return __scanners.has_key(str(scanner_class)) + +def get(scanner_class, properties): + """ Returns an instance of previously registered scanner + with the specified properties. + """ + scanner_name = str(scanner_class) + + if not registered(scanner_name): + raise BaseException ("attempt to get unregisted scanner: %s" % scanner_name) + + relevant_properties = __scanners[scanner_name] + r = property.select(relevant_properties, properties) + + scanner_id = scanner_name + '.' + '-'.join(r) + + if not __scanner_cache.has_key(scanner_name): + __scanner_cache[scanner_name] = scanner_class(r) + + return __scanner_cache[scanner_name] + +class Scanner: + """ Base scanner class. + """ + def __init__ (self): + pass + + def pattern (self): + """ Returns a pattern to use for scanning. + """ + raise BaseException ("method must be overriden") + + def process (self, target, matches): + """ Establish necessary relationship between targets, + given actual target beeing scanned, and a list of + pattern matches in that file. + """ + raise BaseException ("method must be overriden") + + +# Common scanner class, which can be used when there's only one +# kind of includes (unlike C, where "" and <> includes have different +# search paths). +def CommonScanner(Scanner): + + def __init__ (self, includes): + Scanner.__init__(self) + self.includes = includes + + def process(self, target, matches, binding): + + target_path = os.path.normpath(os.path.dirname(binding[0])) + bjam.call("mark-included", target, matches) + + engine.set_target_variable(matches, "SEARCH", + [target_path] + self.includes_) + get_manager().scanners().propagate(self, matches) + +class ScannerRegistry: + + def __init__ (self, manager): + self.manager_ = manager + self.count_ = 0 + self.exported_scanners_ = {} + + def install (self, scanner, target, vtarget): + """ Installs the specified scanner on actual target 'target'. + vtarget: virtual target from which 'target' was actualized. + """ + engine = self.manager_.engine() + engine.set_target_variable(target, "HDRSCAN", scanner.pattern()) + if not self.exported_scanners_.has_key(scanner): + exported_name = "scanner_" + str(self.count_) + self.count_ = self.count_ + 1 + self.exported_scanners_[scanner] = exported_name + bjam.import_rule("", exported_name, scanner.process) + else: + exported_name = self.exported_scanners_[scanner] + + engine.set_target_variable(target, "HDRRULE", exported_name) + + # scanner reflects difference in properties affecting + # binding of 'target', which will be known when processing + # includes for it, will give information on how to + # interpret quoted includes. + engine.set_target_variable(target, "HDRGRIST", str(id(scanner))) + pass + + def propagate(self, scanner, targets): + engine = self.manager_.engine() + engine.set_target_variable(targets, "HDRSCAN", scanner.pattern()) + engine.set_target_variable(targets, "HDRRULE", + self.exported_scanners_[scanner]) + engine.set_target_variable(targets, "HDRGRIST", str(id(scanner))) + diff --git a/v2/build/targets.py b/v2/build/targets.py new file mode 100644 index 000000000..b147009d1 --- /dev/null +++ b/v2/build/targets.py @@ -0,0 +1,1264 @@ +# Status: being ported by Vladimir Prus +# Still to do: call toolset.requirements when those are ported. +# Remember the location of target. +# Base revision: 40480 + +# Copyright Vladimir Prus 2002-2007. +# Copyright Rene Rivera 2006. +# +# 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) + +# Supports 'abstract' targets, which are targets explicitly defined in Jamfile. +# +# Abstract targets are represented by classes derived from 'AbstractTarget' class. +# The first abstract target is 'project_target', which is created for each +# Jamfile, and can be obtained by the 'target' rule in the Jamfile's module. +# (see project.jam). +# +# Project targets keep a list of 'MainTarget' instances. +# A main target is what the user explicitly defines in a Jamfile. It is +# possible to have several definitions for a main target, for example to have +# different lists of sources for different platforms. So, main targets +# keep a list of alternatives. +# +# Each alternative is an instance of 'AbstractTarget'. When a main target +# subvariant is defined by some rule, that rule will decide what class to +# use, create an instance of that class and add it to the list of alternatives +# for the main target. +# +# Rules supplied by the build system will use only targets derived +# from 'BasicTarget' class, which will provide some default behaviour. +# There will be two classes derived from it, 'make-target', created by the +# 'make' rule, and 'TypedTarget', created by rules such as 'exe' and 'dll'. + +# +# +------------------------+ +# |AbstractTarget | +# +========================+ +# |name | +# |project | +# | | +# |generate(properties) = 0| +# +-----------+------------+ +# | +# ^ +# / \ +# +-+-+ +# | +# | +# +------------------------+------+------------------------------+ +# | | | +# | | | +# +----------+-----------+ +------+------+ +------+-------+ +# | project_target | | MainTarget | | BasicTarget | +# +======================+ 1 * +=============+ alternatives +==============+ +# | generate(properties) |o-----------+ generate |<>------------->| generate | +# | main-target | +-------------+ | construct = 0| +# +----------------------+ +--------------+ +# | +# ^ +# / \ +# +-+-+ +# | +# | +# ...--+----------------+------------------+----------------+---+ +# | | | | +# | | | | +# ... ---+-----+ +------+-------+ +------+------+ +--------+-----+ +# | | TypedTarget | | make-target | | stage-target | +# . +==============+ +=============+ +==============+ +# . | construct | | construct | | construct | +# +--------------+ +-------------+ +--------------+ + +import re +import os.path +import sys + +from b2.util.utility import * +import property, project, virtual_target, property_set, feature, generators +from virtual_target import Subvariant +from b2.exceptions import * +from b2.util.sequence import unique +from b2.util import set, path +from b2.build.errors import user_error_checkpoint + +_re_separate_target_from_properties = re.compile (r'^([^<]*)(/(<.*))?$') + +class TargetRegistry: + + def __init__ (self): + # All targets that are currently being built. + # Only the key is id (target), the value is the actual object. + self.targets_being_built_ = {} + + # Current indent for debugging messages + self.indent_ = "" + + self.debug_building_ = "--debug-building" in bjam.variable("ARGV") + + def main_target_alternative (self, target): + """ Registers the specified target as a main target alternatives. + Returns 'target'. + """ + target.project ().add_alternative (target) + return target + + def main_target_sources (self, sources, main_target_name, no_remaning=0): + """Return the list of sources to use, if main target rule is invoked + with 'sources'. If there are any objects in 'sources', they are treated + as main target instances, and the name of such targets are adjusted to + be '__'. Such renaming + is disabled is non-empty value is passed for 'no-renaming' parameter.""" + result = [] + + for t in sources: + if isinstance (t, AbstractTarget): + name = t.name () + + if not no_renaming: + new_name = main_target_name + '__' + name + t.rename (new_name) + + # Inline targets are not built by default. + p = t.project() + p.mark_target_as_explicit(name) + result.append (new_name) + + else: + result.append (t) + + return result + + + def main_target_requirements(self, specification, project): + """Returns the requirement to use when declaring a main target, + which are obtained by + - translating all specified property paths, and + - refining project requirements with the one specified for the target + + 'specification' are the properties xplicitly specified for a + main target + 'project' is the project where the main taret is to be declared.""" + + # FIXME: revive after toolset.requirements are ported. + #specification.append(toolset.requirements) + + requirements = property_set.refine_from_user_input( + project.get("requirements"), specification, + project.project_module, project.get("location")) + + return requirements + + def main_target_usage_requirements (self, specification, project): + """ Returns the use requirement to use when declaraing a main target, + which are obtained by + - translating all specified property paths, and + - adding project's usage requirements + specification: Use-properties explicitly specified for a main target + project: Project where the main target is to be declared + """ + project_usage_requirements = project.get ('usage-requirements') + + # We don't use 'refine-from-user-input' because I'm not sure if: + # - removing of parent's usage requirements makes sense + # - refining of usage requirements is not needed, since usage requirements + # are always free. + usage_requirements = property_set.create_from_user_input( + specification, project.project_module(), project.get("location")) + + return project_usage_requirements.add (usage_requirements) + + def main_target_default_build (self, specification, project): + """ Return the default build value to use when declaring a main target, + which is obtained by using specified value if not empty and parent's + default build attribute otherwise. + specification: Default build explicitly specified for a main target + project: Project where the main target is to be declared + """ + if specification: + result = specification + + else: + result = project.get ('default-build') + + return property_set.create_with_validation (result) + + def start_building (self, main_target_instance): + """ Helper rules to detect cycles in main target references. + """ + if self.targets_being_built_.has_key(id(main_target_instance)): + names = [] + for t in self.targets_being_built_.values(): + names.append (t.full_name()) + + raise Recursion ("Recursion in main target references" + "the following target are being built currently: '%s'" % names) + + self.targets_being_built_[id(main_target_instance)] = main_target_instance + + def end_building (self, main_target_instance): + assert (self.targets_being_built_.has_key (id (main_target_instance))) + del self.targets_being_built_ [id (main_target_instance)] + + def create_typed_target (self, type, project, name, sources, requirements, default_build, usage_requirements): + """ Creates a TypedTarget with the specified properties. + The 'name', 'sources', 'requirements', 'default_build' and + 'usage_requirements' are assumed to be in the form specified + by the user in Jamfile corresponding to 'project'. + """ + return self.main_target_alternative (TypedTarget (name, project, type, + self.main_target_sources (sources, name), + self.main_target_requirements (requirements, project), + self.main_target_default_build (default_build, project), + self.main_target_usage_requirements (usage_requirements, project))) + + def increase_indent(self): + self.indent_ += " " + + def decrease_indent(self): + self.indent_ = self.indent_[0:-4] + + def logging(self): + return self.debug_building_ + + def log(self, message): + if self.debug_building_: + print self.indent_ + message + +class GenerateResult: + + def __init__ (self, ur=None, targets=None): + if not targets: + targets = [] + + self.__usage_requirements = ur + self.__targets = targets + + if not self.__usage_requirements: + self.__usage_requirements = property_set.empty () + + def usage_requirements (self): + return self.__usage_requirements + + def targets (self): + return self.__targets + + def extend (self, other): + assert (isinstance (other, GenerateResult)) + + self.__usage_requirements = self.__usage_requirements.add (other.usage_requirements ()) + self.__targets.extend (other.targets ()) + +class AbstractTarget: + """ Base class for all abstract targets. + """ + def __init__ (self, name, project, manager = None): + """ manager: the Manager object + name: name of the target + project: the project target to which this one belongs + manager:the manager object. If none, uses project.manager () + """ + assert (isinstance (project, ProjectTarget)) + # Note: it might seem that we don't need either name or project at all. + # However, there are places where we really need it. One example is error + # messages which should name problematic targets. Another is setting correct + # paths for sources and generated files. + + # Why allow manager to be specified? Because otherwise project target could not derive + # from this class. + if manager: + self.manager_ = manager + else: + self.manager_ = project.manager () + + self.name_ = name + self.project_ = project + + def manager (self): + return self.manager_ + + def name (self): + """ Returns the name of this target. + """ + return self.name_ + + def project (self): + """ Returns the project for this target. + """ + return self.project_ + + def location (self): + """ Return the location where the target was declared. + """ + return self.location_ + + def full_name (self): + """ Returns a user-readable name for this target. + """ + location = self.project ().get ('location') + return location + '/' + self.name_ + + def generate (self, property_set): + """ Takes a property set. Generates virtual targets for this abstract + target, using the specified properties, unless a different value of some + feature is required by the target. + On success, returns a GenerateResult instance with: + - a property_set with the usage requirements to be + applied to dependents + - a list of produced virtual targets, which may be + empty. + If 'property_set' is empty, performs default build of this + target, in a way specific to derived class. + """ + raise BaseException ("method should be defined in derived classes") + + def rename (self, new_name): + self.name_ = new_name + +class ProjectTarget (AbstractTarget): + """ Project target class (derived from 'AbstractTarget') + + This class these responsibilities: + - maintaining a list of main target in this project and + building it + + Main targets are constructed in two stages: + - When Jamfile is read, a number of calls to 'add_alternative' is made. + At that time, alternatives can also be renamed to account for inline + targets. + - The first time 'main-target' or 'has-main-target' rule is called, + all alternatives are enumerated an main targets are created. + """ + def __init__ (self, manager, name, project_module, parent_project, requirements, default_build): + AbstractTarget.__init__ (self, name, self, manager) + + self.project_module_ = project_module + self.location_ = manager.projects().attribute (project_module, 'location') + self.requirements_ = requirements + self.default_build_ = default_build + + self.build_dir_ = None + + if parent_project: + self.inherit (parent_project) + + # A cache of IDs + self.ids_cache_ = {} + + # True is main targets have already been built. + self.built_main_targets_ = False + + # A list of the registered alternatives for this project. + self.alternatives_ = [] + + # A map from main target name to the target corresponding + # to it. + self.main_target_ = {} + + # Targets marked as explicit. + self.explicit_targets_ = [] + + # The constants defined for this project. + self.constants_ = {} + + # Whether targets for all main target are already created. + self.built_main_targets_ = 0 + + # TODO: This is needed only by the 'make' rule. Need to find the + # way to make 'make' work without this method. + def project_module (self): + return self.project_module_ + + def get (self, attribute): + return self.manager().projects().attribute( + self.project_module_, attribute) + + def build_dir (self): + if not self.build_dir_: + self.build_dir_ = self.get ('build-dir') + if not self.build_dir_: + self.build_dir_ = os.path.join (os.path.dirname( + self.project_.get ('location')), 'bin') + + return self.build_dir_ + + def generate (self, ps): + """ Generates all possible targets contained in this project. + """ + self.manager_.targets().log( + "Building project '%s' with '%s'" % (self.name (), ps.raw ())) + self.manager_.targets().increase_indent () + + result = GenerateResult () + + for t in self.targets_to_build (): + g = t.generate (ps) + result.extend (g) + + self.manager_.targets().decrease_indent () + return result + + def targets_to_build (self): + """ Computes and returns a list of AbstractTarget instances which + must be built when this project is built. + """ + result = [] + + if not self.built_main_targets_: + self.build_main_targets () + + # Collect all main targets here, except for "explicit" ones. + for n, t in self.main_target_.iteritems (): + if not t.name () in self.explicit_targets_: + result.append (t) + + # Collect all projects referenced via "projects-to-build" attribute. + self_location = self.get ('location') + for pn in self.get ('projects-to-build'): + result.append (self.find(pn)) + + return result + + def mark_target_as_explicit (self, target_name): + """Add 'target' to the list of targets in this project + that should be build only by explicit request.""" + + # Record the name of the target, not instance, since this + # rule is called before main target instaces are created. + self.explicit_.append(target_name) + + def add_alternative (self, target_instance): + """ Add new target alternative. + """ + if self.built_main_targets_: + raise IllegalOperation ("add-alternative called when main targets are already created for project '%s'" % self.full_name ()) + + self.alternatives_.append (target_instance) + + def main_target (self, name): + if not self.built_main_targets_: + self.build_main_targets() + + return self.main_target_[name] + + def has_main_target (self, name): + """Tells if a main target with the specified name exists.""" + if not self.built_main_targets_: + self.build_main_targets() + + return self.main_target_.has_key(name) + + def create_main_target (self, name): + """ Returns a 'MainTarget' class instance corresponding to the 'name'. + """ + if not self.built_main_targets_: + self.build_main_targets () + + return self.main_targets_.get (name, None) + + + def find_really(self, id): + """ Find and return the target with the specified id, treated + relative to self. + """ + result = None + current_location = self.get ('location') + + __re_split_project_target = re.compile (r'(.*)//(.*)') + split = __re_split_project_target.match (id) + + project_part = None + target_part = None + + if split: + project_part = split.group (1) + target_part = split.group (2) + + project_registry = self.project_.manager ().projects () + + extra_error_message = '' + if project_part: + # There's explicit project part in id. Looks up the + # project and pass the request to it. + pm = project_registry.find (project_part, current_location) + + if pm: + project_target = project_registry.target (pm) + result = project_target.find (target_part, no_error=1) + + else: + extra_error_message = "error: could not find project '$(project_part)'" + + else: + # Interpret target-name as name of main target + # Need to do this before checking for file. Consider this: + # + # exe test : test.cpp ; + # install s : test : . ; + # + # After first build we'll have target 'test' in Jamfile and file + # 'test' on the disk. We need target to override the file. + + result = None + if self.has_main_target(id): + result = self.main_target(id) + + if not result: + result = FileReference (self.manager_, id, self.project_) + if not result.exists (): + # File actually does not exist. + # Reset 'target' so that an error is issued. + result = None + + + if not result: + # Interpret id as project-id + project_module = project_registry.find (id, current_location) + if project_module: + result = project_registry.target (project_module) + + return result + + def find (self, id, no_error = False): + v = self.ids_cache_.get (id, None) + + if not v: + v = self.find_really (id) + self.ids_cache_ [id] = v + + if v or no_error: + return v + + raise BaseException ("Unable to find file or target named '%s'\nreferred from project at '%s'" % (id, self.get ('location'))) + + + def build_main_targets (self): + self.built_main_targets_ = True + + for a in self.alternatives_: + name = a.name () + if not self.main_target_.has_key (name): + t = MainTarget (name, self.project_) + self.main_target_ [name] = t + + self.main_target_ [name].add_alternative (a) + + def add_constant(self, name, value, path=0): + """Adds a new constant for this project. + + The constant will be available for use in Jamfile + module for this project. If 'path' is true, + the constant will be interpreted relatively + to the location of project. + """ + + if path: + value = os.path.join(self.location_, value) + # Now make the value absolute path + value = os.path.join(os.getcwd(), value) + + self.constants_[name] = value + bjam.call("set-variable", self.project_module(), name, value) + + def inherit(self, parent_project): + for c in parent_project.constants_: + # No need to pass the type. Path constants were converted to + # absolute paths already by parent. + self.add-constant(parent_project.constants_[c]) + + # Import rules from parent + this_module = self.project_module() + parent_module = parent_project.project_module() + + rules = bjam.call("RULENAMES", parent_module) + if not rules: + rules = [] + user_rules = [x for x in rules + if x not in self.manager().projects().project_rules()] + if user_rules: + bjam.call("import-rules-from-parent", parent_module, this_module, user_rules) + +class MainTarget (AbstractTarget): + """ A named top-level target in Jamfile. + """ + def __init__ (self, name, project): + AbstractTarget.__init__ (self, name, project) + self.alternatives_ = [] + self.default_build_ = property_set.empty () + + def add_alternative (self, target): + """ Add a new alternative for this target. + """ + d = target.default_build () + + if self.alternatives_ and self.default_build_ != d: + raise BaseException ("Default build must be identical in all alternatives\n" + "main target is '%s'\n" + "with '%s'\n" + "differing from previous default build: '%s'" % (full_name (), d.raw (), self.default_build_.raw ())) + + else: + self.default_build_ = d + + self.alternatives_.append (target) + + def __select_alternatives (self, property_set, debug): + """ Returns the best viable alternative for this property_set + See the documentation for selection rules. + # TODO: shouldn't this be 'alternative' (singular)? + """ + # When selecting alternatives we have to consider defaults, + # for example: + # lib l : l.cpp : debug ; + # lib l : l_opt.cpp : release ; + # won't work unless we add default value debug. + property_set = property_set.add_defaults () + + # The algorithm: we keep the current best viable alternative. + # When we've got new best viable alternative, we compare it + # with the current one. + best = None + best_properties = None + + if len (self.alternatives_) == 0: + return None + + if len (self.alternatives_) == 1: + return self.alternatives_ [0] + + for v in self.alternatives_: + properties = v.match (property_set, debug) + + if properties: + if not best: + best = v + best_properties = properties + + else: + if set.equal (properties, best_properties): + return None + + elif set.contains (properties, best_properties): + # Do nothing, this alternative is worse + pass + + elif set.contains (best_properties, properties): + best = v + best_properties = properties + + else: + return None + + return best + + def apply_default_build (self, property_set): + # 1. First, see what properties from default_build + # are already present in property_set. + + raw = property_set.raw () + specified_features = get_grist (raw) + + defaults_to_apply = [] + for d in self.default_build_.raw (): + if not get_grist (d) in specified_features: + defaults_to_apply.append (d) + + # 2. If there's any defaults to be applied, form the new + # build request. Pass it throw 'expand-no-defaults', since + # default_build might contain "release debug", which will + # result in two property_sets. + result = [] + if defaults_to_apply: + + # We have to compress subproperties here to prevent + # property lists like: + # + # msvc 7.1 multi + # + # from being expanded into: + # + # 7.1/multi + # msvc/7.1/multi + # + # due to cross-product property combination. That may + # be an indication that + # build_request.expand-no-defaults is the wrong rule + # to use here. + compressed = feature.compress-subproperties (raw) + + properties = build_request.expand_no_defaults (compressed, defaults_to_apply) + + if properties: + for p in properties: + result.append (property_set.create (feature.expand (feature.split (p)))) + + else: + result .append (property_set.empty ()) + + else: + result.append (property_set) + + return result + + def generate (self, ps): + """ Select an alternative for this main target, by finding all alternatives + which requirements are satisfied by 'properties' and picking the one with + longest requirements set. + Returns the result of calling 'generate' on that alternative. + """ + self.manager_.targets ().start_building (self) + + # We want composite properties in build request act as if + # all the properties it expands too are explicitly specified. + ps = ps.expand () + + all_property_sets = self.apply_default_build (ps) + + result = GenerateResult () + + for p in all_property_sets: + result.extend (self.__generate_really (p)) + + self.manager_.targets ().end_building (self) + + return result + + def __generate_really (self, prop_set): + """ Generates the main target with the given property set + and returns a list which first element is property_set object + containing usage_requirements of generated target and with + generated virtual target in other elements. It's possible + that no targets are generated. + """ + best_alternative = self.__select_alternatives (prop_set, debug=0) + + if not best_alternative: + self.__select_alternatives(prop_set, debug=1) + raise NoBestMatchingAlternative ( + "Failed to build '%s'\n" + "with properties '%s'\n" + "because no best-matching alternative could be found." + % (full_name, prop_set.raw ())) + + result = best_alternative.generate (prop_set) + + # Now return virtual targets for the only alternative + return result + + def rename(self, new_name): + AbstractTarget.rename(self, new_name) + for a in self.alternatives_: + a.rename(new_name) + +class FileReference (AbstractTarget): + """ Abstract target which refers to a source file. + This is artificial creature; it's usefull so that sources to + a target can be represented as list of abstract target instances. + """ + def __init__ (self, manager, file, project): + AbstractTarget.__init__ (self, file, project) + self.file_location_ = None + + def generate (self, properties): + return GenerateResult (None, [ + self.manager_.virtual_targets ().from_file ( + self.name_, self.location(), self.project_) ]) + + def exists (self): + """ Returns true if the referred file really exists. + """ + if self.location (): + return True + else: + return False + + def location (self): + # Returns the location of target. Needed by 'testing.jam' + if not self.file_location_: + source_location = self.project_.get ('source-location') + + for src_dir in source_location: + location = os.path.join(src_dir, self.name()) + if os.path.isfile(location): + self.file_location_ = src_dir + self.file_path = location + break + + return self.file_location_ + +class BasicTarget (AbstractTarget): + """ Implements the most standard way of constructing main target + alternative from sources. Allows sources to be either file or + other main target and handles generation of those dependency + targets. + """ + def __init__ (self, name, project, sources, requirements = None, default_build = None, usage_requirements = None): + AbstractTarget.__init__ (self, name, project) + + for s in sources: + if get_grist (s): + raise InvalidSource ("property '%s' found in the 'sources' parameter for '%s'" (s, name)) + + self.sources_ = sources + + if not requirements: requirements = property_set.empty () + self.requirements_ = requirements + + if not default_build: default_build = property_set.empty () + self.default_build_ = default_build + + if not usage_requirements: usage_requirements = property_set.empty () + self.usage_requirements_ = usage_requirements + + # A cache for resolved references + self.source_targets_ = None + + # A cache for generated targets + self.generated_ = {} + + # A cache for build requests + self.request_cache = {} + + self.user_context_ = self.manager_.errors().capture_user_context() + + def sources (self): + """ Returns the list of AbstractTargets which are used as sources. + The extra properties specified for sources are not represented. + The only used of this rule at the moment is the '--dump-test' + feature of the test system. + """ + if self.source_targets_ == None: + self.source_targets_ = [] + for s in self.sources_: + self.source_targets_.append (self.resolve_reference (s, self.project_)) + + return self.source_targets_ + + def requirements (self): + return self.requirements_ + + def default_build (self): + return self.default_build_ + + def resolve_reference (self, target_reference, project): + """ Given a target_reference, made in context of 'project', + returns the AbstractTarget instance that is referred to, as well + as properties explicitly specified for this reference. + """ + # Separate target name from properties override + split = _re_separate_target_from_properties.match (target_reference) + if not split: + raise BaseException ("Invalid reference: '%s'" % target_reference) + + id = split.group (1) + + sproperties = [] + + if split.group (3): + sproperties = property.make (feature.split (split.group (3))) + sproperties = self.manager.features ().expand_composites (sproperties) + + # Find the target + target = project.find (id) + + return (target, property_set.create (sproperties)) + + def common_properties (self, build_request, requirements): + """ Given build request and requirements, return properties + common to dependency build request and target build + properties. + """ + # For optimization, we add free requirements directly, + # without using complex algorithsm. + # This gives the complex algorithm better chance of caching results. + free = requirements.free () + non_free = property_set.create (requirements.base () + requirements.incidental ()) + + key = str (build_request) + '-' + str (non_free) + if not self.request_cache.has_key (key): + self.request_cache [key] = self.__common_properties2 (build_request, non_free) + + return self.request_cache [key].add_raw (free) + + # Given 'context' -- a set of already present properties, and 'requirements', + # decide which extra properties should be applied to 'context'. + # For conditional requirements, this means evaluating condition. For + # indirect conditional requirements, this means calling a rule. Ordinary + # requirements are always applied. + # + # Handles situation where evaluating one conditional requirements affects + # condition of another conditional requirements, for example: + # + # gcc:release release:RELEASE + # + # If 'what' is 'refined' returns context refined with new requirements. + # If 'what' is 'added' returns just the requirements that must be applied. + def evaluate_requirements(self, requirements, context, what): + # Apply non-conditional requirements. + # It's possible that that further conditional requirement change + # a value set by non-conditional requirements. For example: + # + # exe a : a.cpp : single foo:multi ; + # + # I'm not sure if this should be an error, or not, especially given that + # + # single + # + # might come from project's requirements. + + unconditional = feature.expand(requirements.non_conditional()) + + raw = context.raw() + raw = property.refine(raw, unconditional) + + # We've collected properties that surely must be present in common + # properties. We now try to figure out what other properties + # should be added in order to satisfy rules (4)-(6) from the docs. + + conditionals = requirements.conditional() + + # It's supposed that #conditionals iterations + # should be enough for properties to propagate along conditions in any + # direction. + max_iterations = len(conditionals) +\ + len(requirements.get("")) + 1 + + added_requirements = [] + current = raw + + # It's assumed that ordinary conditional requirements can't add + # properties, and that rules referred + # by properties can't add new + # properties. So the list of indirect conditionals + # does not change. + indirect = requirements.get("") + indirect = [s[1:] for s in indirect] + + ok = 0 + for i in range(0, max_iterations): + + e = property.evaluate_conditionals_in_context(conditionals, current) + + # Evaluate indirect conditionals. + for i in indirect: + e.extend(bjam.call(i, current)) + + if e == added_requirements: + # If we got the same result, we've found final properties. + ok = 1 + break + else: + # Oops, results of evaluation of conditionals has changed. + # Also 'current' contains leftover from previous evaluation. + # Recompute 'current' using initial properties and conditional + # requirements. + added_requirements = e + current = property.refine(raw, feature.expand(e)) + + if not ok: + self.manager().errors()("Can't evaluate conditional properties " + + str(conditionals)) + + + if what == "added": + return property_set.create(unconditional + added_requirements) + elif what == "refined": + return property_set.create(current) + else: + self.manager().errors("Invalid value of the 'what' parameter") + + def __common_properties2(self, build_request, requirements): + # This guarantees that default properties are present + # in result, unless they are overrided by some requirement. + # TODO: There is possibility that we've added bar, which is composite + # and expands to bar2, but default value of is not bar2, + # in which case it's not clear what to do. + # + build_request = build_request.add_defaults() + # Featured added by 'add-default' can be composite and expand + # to features without default values -- so they are not added yet. + # It could be clearer/faster to expand only newly added properties + # but that's not critical. + build_request = build_request.expand() + + return self.evaluate_requirements(requirements, build_request, + "refined") + + def match (self, property_set, debug): + """ Returns the alternative condition for this alternative, if + the condition is satisfied by 'property_set'. + """ + # The condition is composed of all base non-conditional properties. + # It's not clear if we should expand 'self.requirements_' or not. + # For one thing, it would be nice to be able to put + # msvc-6.0 + # in requirements. + # On the other hand, if we have release in condition it + # does not make sense to require full to be in + # build request just to select this variant. + bcondition = self.requirements_.base () + ccondition = self.requirements_.conditional () + condition = set.difference (bcondition, ccondition) + + if debug: + print " next alternative: required properties:", str(condition) + + if set.contains (condition, property_set.raw ()): + + if debug: + print " matched" + + return condition + + else: + return None + + def generate_dependencies (self, dependencies, property_set): + """ Takes a target reference, which might be either target id + or a dependency property, and generates that target using + 'property_set' as build request. + + Returns a tuple (result, usage_requirements). + """ + result_var = [] + usage_requirements = [] + for dependency in dependencies: + grist = get_grist (dependency) + id = replace_grist (dependency, '') + + result = self.generate_from_reference (id, self.project_, property_set) + + # FIXME: + # TODO: this is a problem: the grist must be kept and the value + # is the object itself. This won't work in python. + targets = [ self.manager_.register_object (x) for x in result.targets () ] + + result_var += replace_grist (targets, grist) + usage_requirements += result.usage_requirements ().raw () + + return (result_var, usage_requirements) + + @user_error_checkpoint + def generate (self, ps): + """ Determines final build properties, generates sources, + and calls 'construct'. This method should not be + overridden. + """ + self.manager_.errors().push_user_context( + "Generating target " + self.full_name(), self.user_context_) + + if self.manager().targets().logging(): + self.manager().targets().log( + "Building target '%s'" % self.name_) + self.manager().targets().increase_indent () + self.manager().targets().log( + "Build request: '%s'" % str (ps.raw ())) + cf = self.manager().command_line_free_features() + self.manager().targets().log( + "Command line free features: '%s'" % str (cf.raw ())) + self.manager().targets().log( + "Target requirements: %s'" % str (self.requirements().raw ())) + + if not self.generated_.has_key (str (ps)): + + # Apply free features form the command line. If user + # said + # define=FOO + # he most likely want this define to be set for all compiles. + ps = ps.refine(self.manager().command_line_free_features()) + rproperties = self.common_properties (ps, self.requirements_) + + self.manager().targets().log( + "Common properties are '%s'" % str (rproperties.raw ())) + + if rproperties.get("") != "no": + + result = GenerateResult () + + properties = rproperties.non_dependency () + + (p, u) = self.generate_dependencies (rproperties.dependency (), rproperties) + properties += p + usage_requirements = u + + (source_targets, u) = self.generate_dependencies (self.sources_, rproperties) + usage_requirements += u + + self.manager_.targets().log( + "Usage requirements for '%s' are '%s'" % (self.name_, usage_requirements)) + + rproperties = property_set.create (properties + usage_requirements) + usage_requirements = property_set.create (usage_requirements) + + self.manager_.targets().log( + "Build properties: '%s'" % str(rproperties.raw())) + + extra = rproperties.get ('') + source_targets += replace_grist (extra, '') + source_targets = replace_references_by_objects (self.manager (), source_targets) + + # We might get duplicate sources, for example if + # we link to two library which have the same in + # usage requirements. + source_targets = unique (source_targets) + + result = self.construct (self.name_, source_targets, rproperties) + if result: + assert len(result) == 2 + gur = result [0] + result = result [1] + + s = self.create_subvariant ( + result, + self.manager().virtual_targets().recent_targets(), ps, + source_targets, rproperties, usage_requirements) + self.manager().virtual_targets().clear_recent_targets() + + ur = self.compute_usage_requirements (s) + ur = ur.add (gur) + s.set_usage_requirements (ur) + + self.manager_.targets().log ( + "Usage requirements from '%s' are '%s'" % + (self.name, str(rproperties.raw()))) + + self.generated_ [str (ps)] = GenerateResult (ur, result) + else: + self.generated_ [str (ps)] = GenerateResult (property_set.empty(), []) + else: + self.manager().targets().log( + "Skipping build: no in common properties") + + # We're here either because there's error computing + # properties, or there's no in properties. + # In the latter case we don't want any diagnostic. + # In the former case, we need diagnostics. TODOo + self.generated_ [str (ps)] = GenerateResult (rproperties, []) + else: + self.manager().targets().log ("Already built") + + self.manager().targets().decrease_indent() + + return self.generated_ [str (ps)] + + def generate_from_reference (self, target_reference, project, property_set): + """ Attempts to generate the target given by target reference, which + can refer both to a main target or to a file. + Returns a list consisting of + - usage requirements + - generated virtual targets, if any + target_reference: Target reference + project: Project where the reference is made + property_set: Properties of the main target that makes the reference + """ + target, sproperties = self.resolve_reference (target_reference, project) + + # Take properties which should be propagated and refine them + # with source-specific requirements. + propagated = property_set.propagated () + rproperties = propagated.refine (sproperties) + + return target.generate (rproperties) + + def compute_usage_requirements (self, subvariant): + """ Given the set of generated targets, and refined build + properties, determines and sets appripriate usage requirements + on those targets. + """ + rproperties = subvariant.build_properties () + xusage_requirements =self.evaluate_requirements( + self.usage_requirements_, rproperties, "added") + + # We generate all dependency properties and add them, + # as well as their usage requirements, to result. + (r1, r2) = self.generate_dependencies (xusage_requirements.dependency (), rproperties) + extra = r1 + r2 + + result = property_set.create (xusage_requirements.non_dependency () + extra) + + # Propagate usage requirements we've got from sources, except + # for the and features. + # + # That feature specifies which pch file to use, and should apply + # only to direct dependents. Consider: + # + # pch pch1 : ... + # lib lib1 : ..... pch1 ; + # pch pch2 : + # lib lib2 : pch2 lib1 ; + # + # Here, lib2 should not get property from pch1. + # + # Essentially, when those two features are in usage requirements, + # they are propagated only to direct dependents. We might need + # a more general mechanism, but for now, only those two + # features are special. + raw = subvariant.sources_usage_requirements().raw() + raw = property.change(raw, "", None); + raw = property.change(raw, "", None); + result = result.add(property_set.create(raw)) + + return result + + def create_subvariant (self, root_targets, all_targets, + build_request, sources, + rproperties, usage_requirements): + """Creates a new subvariant-dg instances for 'targets' + - 'root-targets' the virtual targets will be returned to dependents + - 'all-targets' all virtual + targets created while building this main target + - 'build-request' is property-set instance with + requested build properties""" + + for e in root_targets: + e.root (True) + + s = Subvariant (self, build_request, sources, + rproperties, usage_requirements, all_targets) + + for v in all_targets: + if not v.creating_subvariant(): + v.creating_subvariant(s) + + return s + + def construct (self, name, source_targets, properties): + """ Constructs the virtual targets for this abstract targets and + the dependecy graph. Returns a tuple consisting of the properties and the list of virtual targets. + Should be overrided in derived classes. + """ + raise BaseException ("method should be defined in derived classes") + + +class TypedTarget (BasicTarget): + import generators + + def __init__ (self, name, project, type, sources, requirements, default_build, usage_requirements): + BasicTarget.__init__ (self, name, project, sources, requirements, default_build, usage_requirements) + self.type_ = type + + def type (self): + return self.type_ + + def construct (self, name, source_targets, prop_set): + r = generators.construct (self.project_, name, self.type_, + property_set.create (prop_set.raw () + ['' + self.type_]), + source_targets) + + if not r: + print "warning: Unable to construct '%s'" % self.full_name () + + # Are there any top-level generators for this type/property set. + if not generators.find_viable_generators (self.type_, prop_set): + print "error: no generators were found for type '$(self.type)'" + print "error: and the requested properties" + print "error: make sure you've configured the needed tools" + print "See http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" + + print "To debug this problem, try the --debug-generators option." + sys.exit(1) + + return r + diff --git a/v2/build/toolset.py b/v2/build/toolset.py new file mode 100644 index 000000000..8485f0afe --- /dev/null +++ b/v2/build/toolset.py @@ -0,0 +1,402 @@ +# Status: being ported by Vladimir Prus +# Base revision: 40958 +# +# Copyright 2003 Dave Abrahams +# Copyright 2005 Rene Rivera +# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +""" Support for toolset definition. +""" + +import feature, property, generators +from b2.util.utility import * +from b2.util import set + +__re_split_last_segment = re.compile (r'^(.+)\.([^\.])*') +__re_two_ampersands = re.compile ('(&&)') +__re_first_segment = re.compile ('([^.]*).*') +__re_first_group = re.compile (r'[^.]*\.(.*)') + +# Flag is a mechanism to set a value +# A single toolset flag. Specifies that when certain +# properties are in build property set, certain values +# should be appended to some variable. +# +# A flag applies to a specific action in specific module. +# The list of all flags for a module is stored, and each +# flag further contains the name of the rule it applies +# for, +class Flag: + + def __init__(self, variable_name, values, condition, rule = None): + self.variable_name = variable_name + self.values = values + self.condition = condition + self.rule = rule + + def __str__(self): + return("Flag(" + str(self.variable_name) + ", " + str(self.values) +\ + ", " + str(self.condition) + ", " + str(self.rule) + ")") + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __module_flags, __flags, __stv + + # Mapping from module name to a list of all flags that apply + # to either that module directly, or to any rule in that module. + # Each element of the list is Flag instance. + # So, for module named xxx this might contain flags for 'xxx', + # for 'xxx.compile', for 'xxx.compile.c++', etc. + __module_flags = {} + + # Mapping from specific rule or module name to a list of Flag instances + # that apply to that name. + # Say, it might contain flags for 'xxx.compile.c++'. If there are + # entries for module name 'xxx', they are flags for 'xxx' itself, + # not including any rules in that module. + __flags = {} + + # A cache for varaible settings. The key is generated from the rule name and the properties. + __stv = {} + +reset () + +# FIXME: --ignore-toolset-requirements +# FIXME: using + +def normalize_condition (property_sets): + """ Expands subfeatures in each property set. + e.g + gcc-3.2 + will be converted to + gcc/3.2 + + TODO: does this one belong here or in feature? + """ + result = [] + for p in property_sets: + split = feature.split (p) + expanded = feature.expand_subfeatures (split) + result.append ('/'.join (expanded)) + + return result + +# FIXME push-checking-for-flags-module .... +# FIXME: investigate existing uses of 'hack-hack' parameter +# in jam code. + +def flags (rule_or_module, variable_name, condition, values = []): + """ Specifies the flags (variables) that must be set on targets under certain + conditions, described by arguments. + rule_or_module: If contains dot, should be a rule name. + The flags will be applied when that rule is + used to set up build actions. + + If does not contain dot, should be a module name. + The flags will be applied for all rules in that + module. + If module for rule is different from the calling + module, an error is issued. + + variable_name: Variable that should be set on target + + condition A condition when this flag should be applied. + Should be set of property sets. If one of + those property sets is contained in build + properties, the flag will be used. + Implied values are not allowed: + "gcc" should be used, not just + "gcc". Subfeatures, like in "gcc-3.2" + are allowed. If left empty, the flag will + always used. + + Propery sets may use value-less properties + ('' vs. 'value') to match absent + properties. This allows to separately match + + /64 + ia64/ + + Where both features are optional. Without this + syntax we'd be forced to define "default" value. + + values: The value to add to variable. If + is specified, then the value of 'feature' + will be added. + """ + if condition and not replace_grist (condition, ''): + # We have condition in the form '', that is, without + # value. That's a previous syntax: + # + # flags gcc.link RPATH ; + # for compatibility, convert it to + # flags gcc.link RPATH : ; + values = [ condition ] + condition = None + + if condition: + property.validate_property_sets (condition) + condition = normalize_condition ([condition]) + + __add_flag (rule_or_module, variable_name, condition, values) + +def set_target_variables (manager, rule_or_module, targets, properties): + """ + """ + key = rule_or_module + '.' + str (properties) + settings = __stv.get (key, None) + if not settings: + settings = __set_target_variables_aux (manager, rule_or_module, properties) + + __stv [key] = settings + + if settings: + for s in settings: + for target in targets: + manager.engine ().set_target_variable (target, s [0], s[1], True) + +def find_property_subset (property_sets, properties): + """Returns the first element of 'property-sets' which is a subset of + 'properties', or an empty list if no such element exists.""" + + prop_keys = get_grist(properties) + + for s in property_sets: + # Handle value-less properties like '' (compare with + # 'x86'). + + set = feature.split(s) + + # Find the set of features that + # - have no property specified in required property set + # - are omitted in build property set + default_props = [] + for i in set: + # If $(i) is a value-less property it should match default + # value of an optional property. See the first line in the + # example below: + # + # property set properties result + # foo foo match + # foo foo foo no match + # foo foo foo no match + # foo foo foo foo match + if not (get_value(i) or get_grist(i) in prop_keys): + default_props.append(i) + + # FIXME: can this be expressed in a more pythonic way? + has_all = 1 + for i in set: + if i not in (properties + default_props): + has_all = 0 + break + if has_all: + return s + + return None + + +def register (toolset): + """ Registers a new toolset. + """ + feature.extend('toolset', [toolset]) + +def inherit_generators (toolset, properties, base, generators_to_ignore = []): + if not properties: + properties = [replace_grist (toolset, '')] + + base_generators = generators.generators_for_toolset(base) + + for g in base_generators: + id = g.id() + + if not id in generators_to_ignore: + # Some generator names have multiple periods in their name, so + # $(id:B=$(toolset)) doesn't generate the right new_id name. + # e.g. if id = gcc.compile.c++, $(id:B=darwin) = darwin.c++, + # which is not what we want. Manually parse the base and suffix + # (if there's a better way to do this, I'd love to see it.) + # See also register in module generators. + (base, suffix) = split_action_id(id) + + new_id = toolset + '.' + suffix + + generators.register(g.clone(new_id, properties)) + +def inherit_flags(toolset, base, prohibited_properties = []): + """Brings all flag definitions from the 'base' toolset into the 'toolset' + toolset. Flag definitions whose conditions make use of properties in + 'prohibited-properties' are ignored. Don't confuse property and feature, for + example on and off, so blocking one of them does + not block the other one. + + The flag conditions are not altered at all, so if a condition includes a name, + or version of a base toolset, it won't ever match the inheriting toolset. When + such flag settings must be inherited, define a rule in base toolset module and + call it as needed.""" + for f in __module_flags.get(base, []): + + if not f.condition or set.difference(f.condition, prohibited_properties): + match = __re_first_group.match(f.rule) + rule_ = None + if match: + rule_ = match.group(1) + + new_rule_or_module = '' + + if rule_: + new_rule_or_module = toolset + '.' + rule_ + else: + new_rule_or_module = toolset + + __add_flag (new_rule_or_module, f.variable_name, f.condition, f.values) + +def inherit_rules (toolset, base): + pass + # FIXME: do something about this. +# base_generators = generators.generators_for_toolset (base) + +# import action + +# ids = [] +# for g in base_generators: +# (old_toolset, id) = split_action_id (g.id ()) +# ids.append (id) ; + +# new_actions = [] + +# engine = get_manager().engine() + # FIXME: do this! +# for action in engine.action.values(): +# pass +# (old_toolset, id) = split_action_id(action.action_name) +# +# if old_toolset == base: +# new_actions.append ((id, value [0], value [1])) +# +# for a in new_actions: +# action.register (toolset + '.' + a [0], a [1], a [2]) + + # TODO: how to deal with this? +# IMPORT $(base) : $(rules) : $(toolset) : $(rules) : localized ; +# # Import the rules to the global scope +# IMPORT $(toolset) : $(rules) : : $(toolset).$(rules) ; +# } +# + +###################################################################################### +# Private functions + +def __set_target_variables_aux (manager, rule_or_module, properties): + """ Given a rule name and a property set, returns a list of tuples of + variables names and values, which must be set on targets for that + rule/properties combination. + """ + result = [] + + for f in __flags.get(rule_or_module, []): + + if not f.condition or find_property_subset (f.condition, properties): + processed = [] + for v in f.values: + # The value might be so needs special + # treatment. + processed += __handle_flag_value (manager, v, properties) + + for r in processed: + result.append ((f.variable_name, r)) + + # strip away last dot separated part and recurse. + next = __re_split_last_segment.match(rule_or_module) + + if next: + result.extend(__set_target_variables_aux( + manager, next.group(1), properties)) + + return result + +def __handle_flag_value (manager, value, properties): + result = [] + + if get_grist (value): + matches = property.select (value, properties) + for p in matches: + att = feature.attributes (get_grist (p)) + + ungristed = replace_grist (p, '') + + if 'dependency' in att: + # the value of a dependency feature is a target + # and must be actualized + # FIXME: verify that 'find' actually works, ick! + result.append (manager.targets ().find (ungristed).actualize ()) + + elif 'path' in att or 'free' in att: + values = [] + + # Treat features with && in the value + # specially -- each &&-separated element is considered + # separate value. This is needed to handle searched + # libraries, which must be in specific order. + if not __re_two_ampersands.search (ungristed): + values.append (ungristed) + + else: + values.extend(value.split ('&&')) + + result.extend(values) + else: + result.append (ungristed) + else: + result.append (value) + + return result + +def __add_flag (rule_or_module, variable_name, condition, values): + """ Adds a new flag setting with the specified values. + Does no checking. + """ + f = Flag(variable_name, values, condition, rule_or_module) + + # Grab the name of the module + m = __re_first_segment.match (rule_or_module) + assert m + module = m.group(1) + + __module_flags.setdefault(m, []).append(f) + __flags.setdefault(rule_or_module, []).append(f) + +def requirements(): + """Return the list of global 'toolset requirements'. + Those requirements will be automatically added to the requirements of any main target.""" + return __requirements + +def add_requirements(requirements): + """Adds elements to the list of global 'toolset requirements'. The requirements + will be automatically added to the requirements for all main targets, as if + they were specified literally. For best results, all requirements added should + be conditional or indirect conditional.""" + + # FIXME: + #if ! $(.ignore-requirements) + #{ + __requirements.extend(requirements) + #} + +# Make toolset 'toolset', defined in a module of the same name, +# inherit from 'base' +# 1. The 'init' rule from 'base' is imported into 'toolset' with full +# name. Another 'init' is called, which forwards to the base one. +# 2. All generators from 'base' are cloned. The ids are adjusted and +# property in requires is adjusted too +# 3. All flags are inherited +# 4. All rules are imported. +def inherit(toolset, base): + get_manager().projects().load_module(base, []); + + inherit_generators(toolset, [], base) + inherit_flags(toolset, base) + inherit_rules(toolset, base) diff --git a/v2/build/type.py b/v2/build/type.py new file mode 100644 index 000000000..62c7d7275 --- /dev/null +++ b/v2/build/type.py @@ -0,0 +1,292 @@ +# Status: ported. +# Base revision: 45462. + +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + + + +import re +import os +import os.path +from b2.util.utility import replace_grist, os_name +from b2.exceptions import * +from b2.build import feature, property, scanner + +__re_hyphen = re.compile ('-') + +def __register_features (): + """ Register features need by this module. + """ + # The feature is optional so that it is never implicitly added. + # It's used only for internal purposes, and in all cases we + # want to explicitly use it. + feature.feature ('target-type', [], ['composite', 'optional']) + feature.feature ('main-target-type', [], ['optional', 'incidental']) + feature.feature ('base-target-type', [], ['composite', 'optional', 'free']) + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + Note that this must be called _after_ resetting the module 'feature'. + """ + global __prefixes_suffixes, __suffixes_to_types, __types, __rule_names_to_types, __target_suffixes_cache + + __register_features () + + # Stores suffixes for generated targets. + __prefixes_suffixes = [property.PropertyMap(), property.PropertyMap()] + + # Maps suffixes to types + __suffixes_to_types = {} + + # A map with all the registered types, indexed by the type name + # Each entry is a dictionary with following values: + # 'base': the name of base type or None if type has no base + # 'derived': a list of names of type which derive from this one + # 'scanner': the scanner class registered for this type, if any + __types = {} + + # Caches suffixes for targets with certain properties. + __target_suffixes_cache = {} + +reset () + + +def register (type, suffixes = [], base_type = None): + """ Registers a target type, possibly derived from a 'base-type'. + If 'suffixes' are provided, they list all the suffixes that mean a file is of 'type'. + Also, the first element gives the suffix to be used when constructing and object of + 'type'. + type: a string + suffixes: None or a sequence of strings + base_type: None or a string + """ + # Type names cannot contain hyphens, because when used as + # feature-values they will be interpreted as composite features + # which need to be decomposed. + if __re_hyphen.search (type): + raise BaseException ('type name "%s" contains a hyphen' % type) + + if __types.has_key (type): + raise BaseException ('Type "%s" is already registered.' % type) + + entry = {} + entry ['base'] = base_type + entry ['derived'] = [] + entry ['scanner'] = None + __types [type] = entry + + if base_type: + __types [base_type]['derived'].append (type) + + if len (suffixes) > 0: + # Generated targets of 'type' will use the first of 'suffixes' + # (this may be overriden) + set_generated_target_suffix (type, [], suffixes [0]) + + # Specify mapping from suffixes to type + register_suffixes (suffixes, type) + + feature.extend('target-type', [type]) + feature.extend('main-target-type', [type]) + feature.extend('base-target-type', [type]) + + if base_type: + feature.compose ('' + type, replace_grist (base_type, '')) + feature.compose ('' + type, '' + base_type) + + # FIXME: resolving recursive dependency. + from b2.manager import get_manager + get_manager().projects().project_rules().add_rule_for_type(type) + +def register_suffixes (suffixes, type): + """ Specifies that targets with suffix from 'suffixes' have the type 'type'. + If a different type is already specified for any of syffixes, issues an error. + """ + for s in suffixes: + if __suffixes_to_types.has_key (s): + old_type = __suffixes_to_types [s] + if old_type != type: + raise BaseException ('Attempting to specify type for suffix "%s"\nOld type: "%s", New type "%s"' % (s, old_type, type)) + else: + __suffixes_to_types [s] = type + +def registered (type): + """ Returns true iff type has been registered. + """ + return __types.has_key (type) + +def validate (type): + """ Issues an error if 'type' is unknown. + """ + if not registered (type): + raise BaseException ("Unknown target type '%s'" % type) + +def set_scanner (type, scanner): + """ Sets a scanner class that will be used for this 'type'. + """ + validate (type) + __types [type]['scanner'] = scanner + +def get_scanner (type, prop_set): + """ Returns a scanner instance appropriate to 'type' and 'property_set'. + """ + if registered (type): + scanner_type = __types [type]['scanner'] + if scanner_type: + return scanner.get (scanner_type, prop_set.raw ()) + pass + + return None + +def all_bases (type): + """ Returns type and all of its bases, in the order of their distance from type. + """ + result = [] + while type: + result.append (type) + type = __types [type]['base'] + + return result + +def all_derived (type): + """ Returns type and all classes that derive from it, in the order of their distance from type. + """ + result = [type] + for d in __types [type]['derived']: + result.extend (all_derived (d)) + + return result + +def is_derived (type, base): + """ Returns true if 'type' is 'base' or has 'base' as its direct or indirect base. + """ + # TODO: this isn't very efficient, especially for bases close to type + if base in all_bases (type): + return True + else: + return False + +def is_subtype (type, base): + """ Same as is_derived. Should be removed. + """ + # TODO: remove this method + return is_derived (type, base) + +def set_generated_target_suffix (type, properties, suffix): + """ Sets a target suffix that should be used when generating target + of 'type' with the specified properties. Can be called with + empty properties if no suffix for 'type' was specified yet. + This does not automatically specify that files 'suffix' have + 'type' --- two different types can use the same suffix for + generating, but only one type should be auto-detected for + a file with that suffix. User should explicitly specify which + one. + + The 'suffix' parameter can be empty string ("") to indicate that + no suffix should be used. + """ + set_generated_target_ps(1, type, properties, suffix) + + + +def change_generated_target_suffix (type, properties, suffix): + """ Change the suffix previously registered for this type/properties + combination. If suffix is not yet specified, sets it. + """ + change_generated_target_ps(1, type, properties, suffix) + +def generated_target_suffix(type, properties): + return generated_target_ps(1, type, properties) + +# Sets a target prefix that should be used when generating targets of 'type' +# with the specified properties. Can be called with empty properties if no +# prefix for 'type' has been specified yet. +# +# The 'prefix' parameter can be empty string ("") to indicate that no prefix +# should be used. +# +# Usage example: library names use the "lib" prefix on unix. +def set_generated_target_prefix(type, properties, prefix): + set_generated_target_ps(0, type, properties, prefix) + +# Change the prefix previously registered for this type/properties combination. +# If prefix is not yet specified, sets it. +def change_generated_target_prefix(type, properties, prefix): + change_generated_target_ps(0, type, properties, prefix) + +def generated_target_prefix(type, properties): + return generated_target_ps(0, type, properties) + +def set_generated_target_ps(is_suffix, type, properties, val): + properties.append ('' + type) + __prefixes_suffixes[is_suffix].insert (properties, val) + +def change_generated_target_ps(is_suffix, type, properties, val): + properties.append ('' + type) + prev = __prefixes_suffixes[is_suffix].find_replace(properties, val) + if not prev: + set_generated_target_ps(is_suffix, type, properties, val) + +# Returns either prefix or suffix (as indicated by 'is_suffix') that should be used +# when generating a target of 'type' with the specified properties. +# If no prefix/suffix is specified for 'type', returns prefix/suffix for +# base type, if any. +def generated_target_ps_real(is_suffix, type, properties): + + result = '' + found = False + while type and not found: + result = __prefixes_suffixes[is_suffix].find (['' + type] + properties) + + # Note that if the string is empty (""), but not null, we consider + # suffix found. Setting prefix or suffix to empty string is fine. + if result: + found = True + + type = __types [type]['base'] + + if not result: + result = '' + return result + +def generated_target_ps(is_suffix, type, prop_set): + """ Returns suffix that should be used when generating target of 'type', + with the specified properties. If not suffix were specified for + 'type', returns suffix for base type, if any. + """ + key = str(is_suffix) + type + str(prop_set) + v = __target_suffixes_cache.get (key, None) + + if not v: + v = generated_target_ps_real(is_suffix, type, prop_set.raw()) + __target_suffixes_cache [key] = v + + return v + +def type(filename): + """ Returns file type given it's name. If there are several dots in filename, + tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and + "so" will be tried. + """ + while 1: + filename, suffix = os.path.splitext (filename) + if not suffix: return None + suffix = suffix[1:] + + if __suffixes_to_types.has_key(suffix): + return __suffixes_to_types[suffix] + +# NOTE: moved from tools/types/register +def register_type (type, suffixes, base_type = None, os = []): + """ Register the given type on the specified OSes, or on remaining OSes + if os is not specified. This rule is injected into each of the type + modules for the sake of convenience. + """ + if registered (type): + return + + if not os or os_name () in os: + register (type, suffixes, base_type) diff --git a/v2/build/virtual_target.py b/v2/build/virtual_target.py new file mode 100644 index 000000000..b4af3a771 --- /dev/null +++ b/v2/build/virtual_target.py @@ -0,0 +1,1051 @@ +# Status: being ported by Vladimir Prus +# Essentially ported, minor fixme remain. +# Base revision: 40480 +# +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +# Implements virtual targets, which correspond to actual files created during +# build, but are not yet targets in Jam sense. They are needed, for example, +# when searching for possible transormation sequences, when it's not known +# if particular target should be created at all. +# +# +# +--------------------------+ +# | VirtualTarget | +# +==========================+ +# | actualize | +# +--------------------------+ +# | actualize_action() = 0 | +# | actualize_location() = 0 | +# +----------------+---------+ +# | +# ^ +# / \ +# +-+-+ +# | +# +---------------------+ +-------+--------------+ +# | Action | | AbstractFileTarget | +# +=====================| * +======================+ +# | action_name | +--+ action | +# | properties | | +----------------------+ +# +---------------------+--+ | actualize_action() | +# | actualize() |0..1 +-----------+----------+ +# | path() | | +# | adjust_properties() | sources | +# | actualize_sources() | targets | +# +------+--------------+ ^ +# | / \ +# ^ +-+-+ +# / \ | +# +-+-+ +-------------+-------------+ +# | | | +# | +------+---------------+ +--------+-------------+ +# | | FileTarget | | SearchedLibTarget | +# | +======================+ +======================+ +# | | actualize-location() | | actualize-location() | +# | +----------------------+ +----------------------+ +# | +# +-+------------------------------+ +# | | +# +----+----------------+ +---------+-----------+ +# | CompileAction | | LinkAction | +# +=====================+ +=====================+ +# | adjust_properties() | | adjust_properties() | +# +---------------------+ | actualize_sources() | +# +---------------------+ +# +# The 'CompileAction' and 'LinkAction' classes are defined not here, +# but in builtin.jam modules. They are shown in the diagram to give +# the big picture. + +import re +import os.path +import string + +from b2.util import path, utility, set +from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, get_value +from b2.util.sequence import unique +from b2.tools import common +from b2.exceptions import * +import b2.build.type +import type + +__re_starts_with_at = re.compile ('^@(.*)') + +class VirtualTargetRegistry: + def __init__ (self, manager): + self.manager_ = manager + + # A cache for FileTargets + self.files_ = {} + + # A cache for targets. + self.cache_ = {} + + # A map of actual names to virtual targets. + # Used to make sure we don't associate same + # actual target to two virtual targets. + self.actual_ = {} + + self.recent_targets_ = [] + + # All targets ever registed + self.all_targets_ = [] + + self.next_id_ = 0 + + def register (self, target): + """ Registers a new virtual target. Checks if there's already registered target, with the same + name, type, project and subvariant properties, and also with the same sources + and equal action. If such target is found it is retured and 'target' is not registered. + Otherwise, 'target' is registered and returned. + """ + signature = target.path() + "-" + target.name() + + result = None + if not self.cache_.has_key (signature): + self.cache_ [signature] = [] + + for t in self.cache_ [signature]: + a1 = t.action () + a2 = target.action () + + # TODO: why are we checking for not result? + if not result: + if not a1 and not a2: + result = t + else: + if a1 and a2 and a1.action_name () == a2.action_name () and a1.sources () == a2.sources (): + ps1 = a1.properties () + ps2 = a2.properties () + p1 = ps1.base () + ps1.free () + ps1.dependency () + p2 = ps2.base () + ps2.free () + ps2.dependency () + if p1 == p2: + result = t + + if not result: + self.cache_ [signature].append (target) + result = target + + # TODO: Don't append if we found pre-existing target? + self.recent_targets_.append(result) + self.all_targets_.append(result) + + result.set_id(self.next_id_) + self.next_id_ = self.next_id_+1 + + return result + + def from_file (self, file, file_location, project): + """ Creates a virtual target with appropriate name and type from 'file'. + If a target with that name in that project was already created, returns that already + created target. + TODO: more correct way would be to compute path to the file, based on name and source location + for the project, and use that path to determine if the target was already created. + TODO: passing project with all virtual targets starts to be annoying. + """ + # Check if we've created a target corresponding to this file. + path = os.path.join(os.getcwd(), file_location, file) + path = os.path.normpath(path) + + if self.files_.has_key (path): + return self.files_ [path] + + file_type = type.type (file) + + result = FileTarget (file, False, file_type, project, + None, file_location) + self.files_ [path] = result + + result.set_id(self.next_id_) + self.next_id_ = self.next_id_+1 + + return result + + def recent_targets(self): + """Each target returned by 'register' is added to a list of + 'recent-target', returned by this function. So, this allows + us to find all targets created when building a given main + target, even if the target.""" + + return self.recent_targets_ + + def clear_recent_targets(self): + self.recent_targets_ = [] + + def all_targets(self): + # Returns all virtual targets ever created + return self.all_targets_ + + # Returns all targets from 'targets' with types + # equal to 'type' or derived from it. + def select_by_type(self, type, targets): + return [t for t in targets if type.is_sybtype(t.type(), type)] + + def register_actual_name (self, actual_name, virtual_target): + if self.actual_.has_key (actual_name): + cs1 = self.actual_ [actual_name].creating_subvariant () + cs2 = virtual_target.creating_subvariant () + cmt1 = cs1.main_target () + cmt2 = cs2.main_target () + + action1 = self.actual_ [actual_name].action () + action2 = virtual_target.action () + + properties_added = [] + properties_removed = [] + if action1 and action2: + p1 = action1.properties () + p1 = p1.raw () + p2 = action2.properties () + p2 = p2.raw () + + properties_removed = set.difference (p1, p2) + if not properties_removed: properties_removed = "none" + + properties_added = set.difference (p2, p1) + if not properties_added: properties_added = "none" + + # FIXME: Revive printing of real location. + raise BaseException ("Duplicate name of actual target: '%s'\n" + "previous virtual target '%s'\n" + "created from '%s'\n" + "another virtual target '%s'\n" + "created from '%s'\n" + "added properties: '%s'\n" + "removed properties: '%s'\n" % (actual_name, + self.actual_ [actual_name], "loc", #cmt1.location (), + virtual_target, + "loc", #cmt2.location (), + properties_added, properties_removed)) + + else: + self.actual_ [actual_name] = virtual_target + + + def add_suffix (self, specified_name, file_type, prop_set): + """ Appends the suffix appropriate to 'type/property_set' combination + to the specified name and returns the result. + """ + suffix = type.generated_target_suffix (file_type, prop_set) + + if suffix: + return specified_name + '.' + suffix + + else: + return specified_name + +class VirtualTarget: + """ Potential target. It can be converted into jam target and used in + building, if needed. However, it can be also dropped, which allows + to search for different transformation and select only one. + name: name of this target. + project: project to which this target belongs. + """ + def __init__ (self, name, project): + self.name_ = name + self.project_ = project + self.dependencies_ = [] + + # Caches if dapendencies for scanners have already been set. + self.made_ = {} + + def manager(self): + return self.project_.manager() + + def virtual_targets(self): + return self.manager().virtual_targets() + + def name (self): + """ Name of this target. + """ + return self.name_ + + def project (self): + """ Project of this target. + """ + return self.project_ + + def set_id(self, id): + self.id_ = id + + def __hash__(self): + return self.id_ + + def __cmp__(self, other): + return self.id_ - other.id_ + + def depends (self, d): + """ Adds additional instances of 'VirtualTarget' that this + one depends on. + """ + self.dependencies_ = unique (self.dependencies_ + d).sort () + + def dependencies (self): + return self.dependencies_ + + def actualize (self, scanner = None): + """ Generates all the actual targets and sets up build actions for + this target. + + If 'scanner' is specified, creates an additional target + with the same location as actual target, which will depend on the + actual target and be associated with 'scanner'. That additional + target is returned. See the docs (#dependency_scanning) for rationale. + Target must correspond to a file if 'scanner' is specified. + + If scanner is not specified, then actual target is returned. + """ + actual_name = self.actualize_no_scanner () + + if not scanner: + return actual_name + + else: + # Add the scanner instance to the grist for name. + g = '-'.join ([ungrist(get_grist(actual_name)), str(id(scanner))]) + + name = replace_grist (actual_name, '<' + g + '>') + + if not self.made_.has_key (name): + self.made_ [name] = True + + self.project_.manager ().engine ().add_dependency (name, actual_name) + + self.actualize_location (name) + + self.project_.manager ().scanners ().install (scanner, name, str (self)) + + return name + +# private: (overridables) + + def actualize_action (self, target): + """ Sets up build actions for 'target'. Should call appropriate rules + and set target variables. + """ + raise BaseException ("method should be defined in derived classes") + + def actualize_location (self, target): + """ Sets up variables on 'target' which specify its location. + """ + raise BaseException ("method should be defined in derived classes") + + def path (self): + """ If the target is generated one, returns the path where it will be + generated. Otherwise, returns empty list. + """ + raise BaseException ("method should be defined in derived classes") + + def actual_name (self): + """ Return that actual target name that should be used + (for the case where no scanner is involved) + """ + raise BaseException ("method should be defined in derived classes") + + +class AbstractFileTarget (VirtualTarget): + """ Target which correspond to a file. The exact mapping for file + is not yet specified in this class. (TODO: Actually, the class name + could be better...) + + May be a source file (when no action is specified), or + derived file (otherwise). + + The target's grist is concatenation of project's location, + properties of action (for derived files), and, optionally, + value identifying the main target. + + exact: If non-empty, the name is exactly the name + created file should have. Otherwise, the '__init__' + method will add suffix obtained from 'type' by + calling 'type.generated-target-suffix'. + + type: optional type of this target. + """ + def __init__ (self, name, exact, type, project, action = None): + VirtualTarget.__init__ (self, name, project) + + self.type_ = type + + self.action_ = action + self.exact_ = exact + + if action: + action.add_targets ([self]) + + if self.type and not exact: + self.__adjust_name (name) + + + self.actual_name_ = None + self.path_ = None + self.intermediate_ = False + self.creating_subvariant_ = None + + # True if this is a root target. + self.root_ = False + + def type (self): + return self.type_ + + def set_path (self, path): + """ Sets the path. When generating target name, it will override any path + computation from properties. + """ + self.path_ = path + + def action (self): + """ Returns the action. + """ + return self.action_ + + def root (self, set = None): + """ Sets/gets the 'root' flag. Target is root is it directly correspods to some + variant of a main target. + """ + if set: + self.root_ = True + return self.root_ + + def creating_subvariant (self, s = None): + """ Gets or sets the subvariant which created this target. Subvariant + is set when target is brought into existance, and is never changed + after that. In particual, if target is shared by subvariant, only + the first is stored. + s: If specified, specified the value to set, + which should be instance of 'subvariant' class. + """ + if s and not self.creating_subvariant (): + if self.creating_subvariant (): + raise BaseException ("Attempt to change 'dg'") + + else: + self.creating_subvariant_ = s + + return self.creating_subvariant_ + + def actualize_action (self, target): + if self.action_: + self.action_.actualize () + + # Return a human-readable representation of this target + # + # If this target has an action, that's: + # + # { -. ... } + # + # otherwise, it's: + # + # { . } + # + def str(self): + a = self.action() + + name_dot_type = self.name_ + "." + self.type_ + + if a: + action_name = a.action_name() + ss = [ s.str() for s in a.sources()] + + return "{ %s-%s %s}" % (action_name, name_dot_type, str(ss)) + else: + return "{ " + name_dot_type + " }" + +# private: + + def actual_name (self): + if not self.actual_name_: + self.actual_name_ = '<' + self.grist() + '>' + self.name_ + + return self.actual_name_ + + def grist (self): + """Helper to 'actual_name', above. Compute unique prefix used to distinguish + this target from other targets with the same name which create different + file. + """ + # Depending on target, there may be different approaches to generating + # unique prefixes. We'll generate prefixes in the form + # + path = self.path () + + if path: + # The target will be generated to a known path. Just use the path + # for identification, since path is as unique as it can get. + return 'p' + path + + else: + # File is either source, which will be searched for, or is not a file at + # all. Use the location of project for distinguishing. + project_location = self.project_.get ('location') + path_components = b2.util.path.split(project_location) + location_grist = '!'.join (path_components) + + if self.action_: + ps = self.action_.properties () + property_grist = ps.as_path () + # 'property_grist' can be empty when 'ps' is an empty + # property set. + if property_grist: + location_grist = location_grist + '/' + property_grist + + return 'l' + location_grist + + def __adjust_name(self, specified_name): + """Given the target name specified in constructor, returns the + name which should be really used, by looking at the properties. + The tag properties come in two flavour: + - value, + - @rule-name + In the first case, value is just added to name + In the second case, the specified rule is called with specified name, + target type and properties and should return the new name. + If not property is specified, or the rule specified by + returns nothing, returns the result of calling + virtual-target.add-suffix""" + + if self.action_: + ps = self.action_.properties() + else: + ps = property_set.empty() + + # FIXME: I'm not sure how this is used, need to check with + # Rene to figure out how to implement + #~ We add ourselves to the properties so that any tag rule can get + #~ more direct information about the target than just that available + #~ through the properties. This is useful in implementing + #~ name changes based on the sources of the target. For example to + #~ make unique names of object files based on the source file. + #~ --grafik + #ps = property_set.create(ps.raw() + ["%s" % "XXXX"]) + #ps = [ property-set.create [ $(ps).raw ] $(__name__) ] ; + + tag = ps.get("") + + if tag: + + rule_names = [t[:1] for t in tag if t[0] == '@'] + if rule_names: + if len(tag) > 1: + self.manager_.errors()( +"""@rulename is present but is not the only feature""") + + self.name_ = bjam.call(rule_names[0], specified_name, self.type_, ps) + else: + self.manager_.errors()( +"""The value of the feature must be '@rule-nane'""") + + # If there's no tag or the tag rule returned nothing. + if not tag or not self.name_: + self.name_ = add_prefix_and_suffix(specified_name, self.type_, ps) + + def actualize_no_scanner(self): + name = self.actual_name() + + # Do anything only on the first invocation + if not self.made_: + self.made_[name] = True + + if self.action_: + # For non-derived target, we don't care if there + # are several virtual targets that refer to the same name. + # One case when this is unavoidable is when file name is + # main.cpp and two targets have types CPP (for compiling) + # and MOCCABLE_CPP (for convertion to H via Qt tools). + self.virtual_targets().register_actual_name(name, self) + + for i in self.dependencies_: + self.manager_.engine().add_dependency(name, i.actualize()) + + self.actualize_location(name) + self.actualize_action(name) + + return name + +def add_prefix_and_suffix(specified_name, type, property_set): + """Appends the suffix appropriate to 'type/property-set' combination + to the specified name and returns the result.""" + + suffix = b2.build.type.generated_target_suffix(type, property_set) + + # Handle suffixes for which no leading dot is desired. Those are + # specified by enclosing them in <...>. Needed by python so it + # can create "_d.so" extensions, for example. + if get_grist(suffix): + suffix = ungrist(suffix) + elif suffix: + suffix = "." + suffix + + prefix = b2.build.type.generated_target_prefix(type, property_set) + + if specified_name.startswith(prefix): + prefix = "" + + if not prefix: + prefix = "" + if not suffix: + suffix = "" + return prefix + specified_name + suffix + + +class FileTarget (AbstractFileTarget): + """ File target with explicitly known location. + + The file path is determined as + - value passed to the 'set_path' method, if any + - for derived files, project's build dir, joined with components + that describe action's properties. If the free properties + are not equal to the project's reference properties + an element with name of main target is added. + - for source files, project's source dir + + The file suffix is + - the value passed to the 'suffix' method, if any, or + - the suffix which correspond to the target's type. + """ + def __init__ (self, name, exact, type, project, action = None, path=None): + AbstractFileTarget.__init__ (self, name, exact, type, project, action) + + self.path_ = path + + def clone_with_different_type(self, new_type): + return FileTarget(self.name_, 1, new_type, self.project_, + self.action_, self.path_) + + def actualize_location (self, target): + engine = self.project_.manager_.engine () + + if self.action_: + # This is a derived file. + path = self.path () + engine.set_target_variable (target, 'LOCATE', path) + + # Make sure the path exists. + engine.add_dependency (target, path) + common.mkdir(engine, path) + + # It's possible that the target name includes a directory + # too, for example when installing headers. Create that + # directory. + d = os.path.dirname(get_value(target)) + if d: + d = os.path.join(path, d) + engine.add_dependency(target, d) + common.mkdir(engine, d) + + # For real file target, we create a fake target that + # depends on the real target. This allows to run + # + # bjam hello.o + # + # without trying to guess the name of the real target. + # Note the that target has no directory name, and a special + # grist . + # + # First, that means that "bjam hello.o" will build all + # known hello.o targets. + # Second, the grist makes sure this target won't be confused + # with other targets, for example, if we have subdir 'test' + # with target 'test' in it that includes 'test.o' file, + # then the target for directory will be just 'test' the target + # for test.o will be test.o and the target + # we create below will be test.o + engine.add_dependency("%s" % get_value(target), target) + + else: + # This is a source file. + engine.set_target_variable (target, 'SEARCH', self.project_.get ('source-location')) + + + def path (self): + """ Returns the directory for this target. + """ + if not self.path_: + if self.action_: + p = self.action_.properties () + target_path = p.target_path () + + if target_path [1] == True: + # Indicates that the path is relative to + # build dir. + target_path = os.path.join (self.project_.build_dir (), target_path [0]) + + # Store the computed path, so that it's not recomputed + # any more + self.path_ = target_path + + return self.path_ + + +class NotFileTarget(AbstractFileTarget): + + def __init__(self, name, project): + AbstractFileTarget.__init__(name, project) + + def path(self): + """Returns nothing, to indicate that target path is not known.""" + return None + + def actualize_location(self, target): + bjam.call("NOTFILE", target) + bjam.call("ALWAYS", taget) + + +class Action: + """ Class which represents an action. + Both 'targets' and 'sources' should list instances of 'VirtualTarget'. + Action name should name a rule with this prototype + rule action_name ( targets + : sources * : properties * ) + Targets and sources are passed as actual jam targets. The rule may + not establish dependency relationship, but should do everything else. + """ + def __init__ (self, manager, sources, action_name, prop_set): + self.sources_ = sources + self.action_name_ = action_name + if not prop_set: + prop_set = property_set.empty() + self.properties_ = prop_set + + self.manager_ = manager + self.engine_ = self.manager_.engine () + self.targets_ = [] + + # Indicates whether this has been actualized or not. + self.actualized_ = False + + self.dependency_only_sources_ = [] + self.actual_sources_ = [] + + + def add_targets (self, targets): + self.targets_ += targets + + def targets (self): + return self.targets_ + + def sources (self): + return self.sources_ + + def action_name (self): + return self.action_name_ + + def properties (self): + return self.properties_ + + def actualize (self): + """ Generates actual build instructions. + """ + if self.actualized_: + return + + self.actualized_ = True + + ps = self.properties () + properties = self.adjust_properties (ps) + actual_targets = [] + + for i in self.targets (): + actual_targets.append (i.actualize ()) + + self.actualize_sources (self.sources (), properties) + + self.engine_.add_dependency (actual_targets, self.actual_sources_ + self.dependency_only_sources_) + + raw_properties = properties.raw () + + # FIXME: check the comment below. Was self.action_name_ [1] + # Action name can include additional argument to rule, which should not + # be passed to 'set-target-variables' + # FIXME: breaking circular dependency + import toolset + toolset.set_target_variables (self.manager_, self.action_name_, actual_targets, raw_properties) + + engine = self.manager_.engine () + + self.manager_.engine ().set_update_action (self.action_name_, actual_targets, self.actual_sources_, + properties) + + # Since we set up creating action here, we also set up + # action for cleaning up + self.manager_.engine ().set_update_action ('common.Clean', 'clean-all', + actual_targets, None) + + return actual_targets + + def actualize_source_type (self, sources, prop_set): + """ Helper for 'actualize_sources'. + For each passed source, actualizes it with the appropriate scanner. + Returns the actualized virtual targets. + """ + result = [] + for i in sources: + scanner = None + +# FIXME: what's this? +# if isinstance (i, str): +# i = self.manager_.get_object (i) + + if i.type (): + scanner = type.get_scanner (i.type (), prop_set) + + result.append (i.actualize (scanner)) + + return result + + def actualize_sources (self, sources, prop_set): + """ Creates actual jam targets for sources. Initializes two member + variables: + 'self.actual_sources_' -- sources which are passed to updating action + 'self.dependency_only_sources_' -- sources which are made dependencies, but + are not used otherwise. + + New values will be *appended* to the variables. They may be non-empty, + if caller wants it. + """ + dependencies = self.properties_.get ('') + + self.dependency_only_sources_ += self.actualize_source_type (dependencies, prop_set) + self.actual_sources_ += self.actualize_source_type (sources, prop_set) + + # This is used to help bjam find dependencies in generated headers + # in other main targets. + # Say: + # + # make a.h : ....... ; + # exe hello : hello.cpp : a.h ; + # + # However, for bjam to find the dependency the generated target must + # be actualized (i.e. have the jam target). In the above case, + # if we're building just hello ("bjam hello"), 'a.h' won't be + # actualized unless we do it here. + implicit = self.properties_.get("") + for i in implicit: + i.actualize() + + def adjust_properties (self, prop_set): + """ Determines real properties when trying building with 'properties'. + This is last chance to fix properties, for example to adjust includes + to get generated headers correctly. Default implementation returns + its argument. + """ + return prop_set + + +class NullAction (Action): + """ Action class which does nothing --- it produces the targets with + specific properties out of nowhere. It's needed to distinguish virtual + targets with different properties that are known to exist, and have no + actions which create them. + """ + def __init__ (self, manager, prop_set): + Action.__init__ (self, manager, None, None, prop_set) + + def actualize (self): + if not self.actualized_: + self.actualized_ = True + + for i in self.targets (): + i.actualize () + +class NonScanningAction(Action): + """Class which acts exactly like 'action', except that the sources + are not scanned for dependencies.""" + + def __init__(self, sources, action_name, property_set): + #FIXME: should the manager parameter of Action.__init__ + #be removed? -- Steven Watanabe + Action.__init__(b2.manager.get_manager(), sources, action_name, property_set) + + def actualize_source_type(self, sources, property_set): + + return [x for source in sources for x in i.actualize()] + +def traverse (target, include_roots = False, include_sources = False): + """ Traverses the dependency graph of 'target' and return all targets that will + be created before this one is created. If root of some dependency graph is + found during traversal, it's either included or not, dependencing of the + value of 'include_roots'. In either case, sources of root are not traversed. + """ + result = [] + + if target.action (): + action = target.action () + + # This includes 'target' as well + result += action.targets () + + for t in action.sources (): + + # FIXME: + # TODO: see comment in Manager.register_object () + #if not isinstance (t, VirtualTarget): + # t = target.project_.manager_.get_object (t) + + if not t.root (): + result += traverse (t, include_roots, include_sources) + + elif include_roots: + result.append (t) + + elif include_sources: + result.append (target) + + return result + +def clone_action (action, new_project, new_action_name, new_properties): + """Takes an 'action' instances and creates new instance of it + and all produced target. The rule-name and properties are set + to 'new-rule-name' and 'new-properties', if those are specified. + Returns the cloned action.""" + + if not new_action_name: + new_action_name = action.action_name() + + if not new_properties: + new_properties = action.properties() + + closed_action = action.__class__(action.sources(), new_action_name, + new_properties) + + cloned_targets = [] + for target in action.targets(): + + n = target.name() + # Don't modify the name of the produced targets. Strip the directory f + cloned_target = FileTarget(n, 1, target.type(), new_project, + cloned_action) + + d = target.dependencies() + if d: + cloned_target.depends(d) + cloned_target.root(target.root()) + cloned_target.creating_subvariant(target.creating_subvariant()) + + cloned_targets.append(cloned_target) + + return cloned_action + +class Subvariant: + + def __init__ (self, main_target, prop_set, sources, build_properties, sources_usage_requirements, created_targets): + """ + main_target: The instance of MainTarget class + prop_set: Properties requested for this target + sources: + build_properties: Actually used properties + sources_usage_requirements: Properties propagated from sources + created_targets: Top-level created targets + """ + self.main_target_ = main_target + self.properties_ = prop_set + self.sources_ = sources + self.build_properties_ = build_properties + self.sources_usage_requirements_ = sources_usage_requirements + self.created_targets_ = created_targets + + self.usage_requirements_ = None + + # Pre-compose the list of other dependency graphs, on which this one + # depends + deps = build_properties.get ('') + + self.other_dg_ = [] + for d in deps: + # FIXME: the property must have the actual object here, not a string. + value = replace_grist (d, '') + self.other_dg_.append (value.creating_subvariant ()) + + self.other_dg_ = unique (self.other_dg_) + + self.implicit_includes_cache_ = {} + self.target_directories_ = None + + def main_target (self): + return self.main_target_ + + def created_targets (self): + return self.created_targets_ + + def requested_properties (self): + return self.properties_ + + def build_properties (self): + return self.build_properties_ + + def sources_usage_requirements (self): + return self.sources_usage_requirements_ + + def set_usage_requirements (self, usage_requirements): + self.usage_requirements_ = usage_requirements + + def usage_requirements (self): + return self.usage_requirements_ + + def all_referenced_targets(self): + """Returns all targets referenced by this subvariant, + either directly or indirectly, and either as sources, + or as dependency properties. Targets referred with + dependency property are returned a properties, not targets.""" + + # Find directly referenced targets. + deps = self.build_properties().dependency() + all_targets = self.sources_ + deps + + # Find other subvariants. + r = [] + for t in all_targets: + r.append(t.creating_subvariant) + r = unique(r) + + for s in r: + if s != self: + all_targets.extend(s.all_referenced_targets()) + + return all_targets + + def implicit_includes (self, feature, target_type): + """ Returns the properties which specify implicit include paths to + generated headers. This traverses all targets in this subvariant, + and subvariants referred by properties. + For all targets which are of type 'target-type' (or for all targets, + if 'target_type' is not specified), the result will contain + <$(feature)>path-to-that-target. + """ + + if not target_type: + key = feature + else: + key = feature + "-" + target_type + + + result = self.implicit_includes_cache_.get(key) + if not result: + target_paths = self.all_target_directories(target_type) + target_paths = unique(target_paths) + result = ["<%s>%s" % (feature, p) for p in target_paths] + self.implicit_includes_cache_[key] = result + + return result + + def all_target_directories(self, target_type = None): + # TODO: does not appear to use target_type in deciding + # if we've computed this already. + if not self.target_directories_: + self.target_directories_ = self.compute_target_directories(target_type) + return self.target_directories_ + + def compute_target_directories(self, target_type=None): + result = [] + for t in self.created_targets(): + if not target_type or type.is_derived(t.type(), target_type): + result.append(t.path()) + + for d in self.other_dg_: + result.extend(d.all_target_directories(target_type)) + + result = unique(result) + return result diff --git a/v2/build_system.py b/v2/build_system.py new file mode 100644 index 000000000..b4589ee72 --- /dev/null +++ b/v2/build_system.py @@ -0,0 +1,437 @@ +# Status: being ported by Vladimir Prus. + +# Copyright 2003, 2005 Dave Abrahams +# Copyright 2006 Rene Rivera +# Copyright 2003, 2004, 2005, 2006, 2007 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +from b2.build.engine import Engine +from b2.manager import Manager +from b2.util.path import glob +from b2.build import feature, property_set +import b2.build.virtual_target +from b2.build.targets import ProjectTarget +from b2.util.sequence import unique +import b2.build.build_request +from b2.build.errors import ExceptionWithUserContext +import b2.tools.common + +import bjam + +import os +import sys + +# FIXME: +# Returns the location of the build system. The primary use case +# is building Boost, where it's sometimes needed to get location +# of other components (like BoostBook files), and it's convenient +# to use location relatively to Boost.Build path. +#rule location ( ) +#{ +# local r = [ modules.binding build-system ] ; +# return $(r:P) ; +#} + +# FIXME: + +def get_boolean_option(name): + match = "--" + name + if match in argv: + return 1 + else: + return 0 + +def get_string_option(name): + match = "--" + name + "=" + + for arg in argv: + if arg.startswith(match): + return arg[len(match):] + return None + +def home_directories(): + if os.name == "nt": + result = set() + try: + result.add(os.environ['HOMEDRIVE'] + os.environ['HOMEPATH']) + result.add(os.environ['HOME']) + result.add(os.environ['USERPROFILE']) + except KeyError: + pass + return list(result) + else: + return [os.environ['HOME']] + +ignore_config = 0 +debug_config = 0 + +def load_config(manager, basename, path): + """Unless ignore-config is set, search configuration + basename.jam in path and loads it. The jamfile module + for that file will be loaded 'basename'.""" + + if not ignore_config: + found = glob(path, [basename + ".jam"]) + if found: + found = found[0] + if debug_config: + print "notice: searching '%s' for '%s.jam'" % (path, basename) + if found: + print "notice: loading %s.jam from %s" % (basename, found) + + manager.projects().load_standalone(basename, found) + +def main(): + + global argv + argv = bjam.variable("ARGV") + + # FIXME: document this option. + if "--profiling" in argv: + import cProfile + import pstats + cProfile.runctx('main_real()', globals(), locals(), "stones.prof") + + stats = pstats.Stats("stones.prof") + stats.strip_dirs() + stats.sort_stats('time', 'calls') + stats.print_callers(20) + else: + main_real() + +def main_real(): + + global ignore_config + global debug_config + + boost_build_path = bjam.variable("BOOST_BUILD_PATH") + + engine = Engine() + + global_build_dir = get_string_option("build-dir") + debug_config = get_boolean_option("debug-configuration") + + manager = Manager(engine, global_build_dir) + + # This module defines types and generator and what not, + # and depends on manager's existence + import b2.tools.builtin + + + # Check if we can load 'test-config.jam'. If we can, load it and + # ignore user configs. + + test_config = glob(boost_build_path, ["test-config.jam"]) + if test_config: + test_config = test_config[0] + + if test_config: + if debug_config: + print "notice: loading testing-config.jam from '%s'" % test_config + print "notice: user-config.jam and site-config.jam will be ignored" + + manager.projects().load_standalone("test-config", test_config) + + + ignore_config = test_config or get_boolean_option("ignore-config") + user_path = home_directories() + boost_build_path + + site_path = ["/etc"] + user_path + if bjam.variable("OS") in ["NT", "CYGWIN"]: + site_path = [os.environ("SystemRoot")] + user_path + + load_config(manager, "site-config", site_path) + + user_config_path = get_string_option("user-config") + if not user_config_path: + user_config_path = os.environ.get("BOOST_BUILD_USER_CONFIG") + + if user_config_path: + if debug_config: + print "Loading explicitly specifier user configuration file:" + print " %s" % user_config_path + + manager.projects().load_standalone("user-config", user_config_path) + + else: + load_config(manager, "user-config", user_path) + + +# FIXME: +## # +## # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or +## # toolset=xx,yy,...zz in the command line +## # +## local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*) : $(argv) ] : "," ] ; +## local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*) : $(argv) ] : "," ] ; + +## # if the user specified --toolset=..., we need to add toolset=... to +## # the build request +## local extra-build-request ; + + extra_build_request = [] + +## if ! $(ignore-config) +## { +## for local t in $(option-toolsets) $(feature-toolsets) +## { +## # Parse toolset-version/properties +## local (t-v,t,v) = [ MATCH (([^-/]+)-?([^/]+)?)/?.* : $(t) ] ; +## local toolset-version = $((t-v,t,v)[1]) ; +## local toolset = $((t-v,t,v)[2]) ; +## local version = $((t-v,t,v)[3]) ; + +## if $(debug-config) +## { +## ECHO notice: [cmdline-cfg] Detected command-line request for +## $(toolset-version): toolset= \"$(toolset)\" "version= \""$(version)\" ; +## } + +## local known ; + +## # if the toolset isn't known, configure it now. +## if $(toolset) in [ feature.values ] +## { +## known = true ; +## } + +## if $(known) && $(version) +## && ! [ feature.is-subvalue toolset : $(toolset) : version : $(version) ] +## { +## known = ; +## } + +## if ! $(known) +## { +## if $(debug-config) +## { +## ECHO notice: [cmdline-cfg] toolset $(toolset-version) +## not previously configured; configuring now ; +## } +## toolset.using $(toolset) : $(version) ; +## } +## else +## { +## if $(debug-config) +## { +## ECHO notice: [cmdline-cfg] toolset $(toolset-version) already configured ; +## } +## } + +## # make sure we get an appropriate property into the build request in +## # case the user used the "--toolset=..." form +## if ! $(t) in $(argv) +## && ! $(t) in $(feature-toolsets) +## { +## if $(debug-config) +## { +## ECHO notice: [cmdline-cfg] adding toolset=$(t) "to build request." ; +## } +## extra-build-request += toolset=$(t) ; +## } +## } +## } + + +# FIXME: +## if USER_MODULE in [ RULENAMES ] +## { +## USER_MODULE site-config user-config ; +## } + + if get_boolean_option("version"): + # FIXME: Move to a separate module. Include bjam + # verision. + print "Boost.Build M15 (Python port in development)" + sys.exit(0) + + b2.tools.common.init(manager) + + # We always load project in "." so that 'use-project' directives has + # any chance of been seen. Otherwise, we won't be able to refer to + # subprojects using target ids. + + current_project = None + projects = manager.projects() + if projects.find(".", "."): + current_project = projects.target(projects.load(".")) + + # FIXME: revive this logic, when loading of gcc works + if not feature.values("") and not ignore_config and 0: + default_toolset = "gcc" ; + if bjam.variable("OS") == "NT": + default_toolset = "msvc" + + print "warning: No toolsets are configured." ; + print "warning: Configuring default toolset '%s'" % default_toolset + print "warning: If the default is wrong, you may not be able to build C++ programs." + print "warning: Use the \"--toolset=xxxxx\" option to override our guess." + print "warning: For more configuration options, please consult" + print "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" + + projects.project_rules().using([default_toolset]) + + (target_ids, properties) = b2.build.build_request.from_command_line( + argv[1:] + extra_build_request) + + if properties: + expanded = b2.build.build_request.expand_no_defaults(properties) + xexpanded = [] + for e in expanded: + xexpanded.append(property_set.create(feature.split(e))) + expanded = xexpanded + else: + expanded = [property_set.empty()] + + targets = [] + + clean = get_boolean_option("clean") + clean_all = get_boolean_option("clean-all") + + + bjam_targets = [] + + # Given a target id, try to find and return corresponding target. + # This is only invoked when there's no Jamfile in "." + # This code somewhat duplicates code in project-target.find but we can't reuse + # that code without project-targets instance. + def find_target (target_id): + split = target_id.split("//") + pm = None + if len(split) > 1: + pm = projects.find(split[0], ".") + else: + pm = projects.find(target_id, ".") + + result = None + if pm: + result = projects.target(pm) + + if len(split) > 1: + result = result.find(split[1]) + + if not current_project and not target_ids: + print "error: no Jamfile in current directory found, and no target references specified." + sys.exit(1) + + for id in target_ids: + if id == "clean": + clean = 1 + else: + t = None + if current_project: + t = current_project.find(id, no_error=1) + else: + t = find_target(id) + + if not t: + print "notice: could not find main target '%s'" % id + print "notice: assuming it's a name of file to create " ; + bjam_targets.append(id) + else: + targets.append(t) + + if not targets: + targets = [projects.target(projects.module_name("."))] + + virtual_targets = [] + + # Virtual targets obtained when building main targets references on + # the command line. When running + # + # bjam --clean main_target + # + # we want to clean the files that belong only to that main target, + # so we need to record which targets are produced. + results_of_main_targets = [] + + for p in expanded: + manager.set_command_line_free_features(property_set.create(p.free())) + + for t in targets: + try: + g = t.generate(p) + if not isinstance(t, ProjectTarget): + results_of_main_targets.extend(g.targets()) + virtual_targets.extend(g.targets()) + except ExceptionWithUserContext, e: + e.report() + except Exception: + raise + + # The cleaning is tricky. Say, if + # user says: + # + # bjam --clean foo + # + # where 'foo' is a directory, then we want to clean targets + # which are in 'foo' or in any children Jamfiles, but not in any + # unrelated Jamfiles. So, we collect the list of project under which + # cleaning is allowed. + # + projects_to_clean = [] + targets_to_clean = [] + if clean or clean_all: + for t in targets: + if isinstance(t, ProjectTarget): + projects_to_clean.append(t.project_module()) + + for t in results_of_main_targets: + # Don't include roots or sources. + targets_to_clean += b2.build.virtual_target.traverse(t) + + targets_to_clean = unique(targets_to_clean) + + is_child_cache_ = {} + + # Returns 'true' if 'project' is a child of 'current-project', + # possibly indirect, or is equal to 'project'. + # Returns 'false' otherwise. + def is_child (project): + + r = is_child_cache_.get(project, None) + if not r: + if project in projects_to_clean: + r = 1 + else: + parent = manager.projects().attribute(project, "parent-module") + if parent and parent != "user-config": + r = is_child(parent) + else: + r = 0 + + is_child_cache_[project] = r + + return r + + actual_targets = [] + for t in virtual_targets: + actual_targets.append(t.actualize()) + + + bjam.call("NOTFILE", "all") + bjam.call("DEPENDS", "all", actual_targets) + + if bjam_targets: + bjam.call("UPDATE", ["%s" % x for x in bjam_targets]) + elif clean_all: + bjam.call("UPDATE", "clean-all") + elif clean: + to_clean = [] + for t in manager.virtual_targets().all_targets(): + p = t.project() + + # Remove only derived targets. + if t.action() and \ + (t in targets_to_clean or is_child(p.project_module())): + to_clean.append(t) + + to_clean_actual = [t.actualize() for t in to_clean] + manager.engine().set_update_action('common.Clean', 'clean', + to_clean_actual, None) + + bjam.call("UPDATE", "clean") + + else: + bjam.call("UPDATE", "all") diff --git a/v2/exceptions.py b/v2/exceptions.py new file mode 100644 index 000000000..5750abfe3 --- /dev/null +++ b/v2/exceptions.py @@ -0,0 +1,44 @@ +# Copyright Pedro Ferreira 2005. 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) + +# TODO: add more exception types? + +class BaseException (Exception): + def __init__ (self, message = ''): Exception.__init__ (self, message) + +class UserError (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class FeatureConflict (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class InvalidSource (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class InvalidFeature (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class InvalidProperty (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class InvalidValue (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class InvalidAttribute (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class AlreadyDefined (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class IllegalOperation (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class Recursion (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class NoBestMatchingAlternative (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) + +class NoAction (BaseException): + def __init__ (self, message = ''): BaseException.__init__ (self, message) diff --git a/v2/kernel/bootstrap.jam b/v2/kernel/bootstrap.jam index 99ecdaba5..fb918e7a8 100644 --- a/v2/kernel/bootstrap.jam +++ b/v2/kernel/bootstrap.jam @@ -111,6 +111,8 @@ IMPORT modules : import : : import ; BOOST_BUILD_PATH += $(whereami:D)/$(subdirs) ; modules.poke .ENVIRON : BOOST_BUILD_PATH : $(BOOST_BUILD_PATH) ; + + modules.poke : EXTRA_PYTHONPATH : $(whereami) ; } # Reload the modules, to clean up things. The modules module can tolerate @@ -129,11 +131,110 @@ local dont-build = [ option.process ] ; # if ! $(dont-build) { - # Allow users to override the build system file from the - # command-line (mostly for testing) - local build-system = [ MATCH --build-system=(.*) : $(ARGV) ] ; - build-system ?= build-system ; + if ! [ MATCH (--python) : $(ARGV) ] + { + # Allow users to override the build system file from the + # command-line (mostly for testing) + local build-system = [ MATCH --build-system=(.*) : $(ARGV) ] ; + build-system ?= build-system ; - # Use last element in case of multiple command-line options - import $(build-system[-1]) ; + # Use last element in case of multiple command-line options + import $(build-system[-1]) ; + } + else + { + ECHO "Boost.Build V2 Python port (experimental)" ; + + # Define additional interface that is exposed to Python code. Python code will + # also have access to select bjam builtins in the 'bjam' module, but some + # things are easier to define outside C. + module python_interface + { + rule load ( module-name : location ) + { + USER_MODULE $(module-name) ; + # Make all rules in the loaded module available in + # the global namespace, so that we don't have + # to bother specifying "right" module when calling + # from Python. + module $(module-name) + { + __name__ = $(1) ; + include $(2) ; + local rules = [ RULENAMES $(1) ] ; + IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ; + } + } + + rule peek ( module-name ? : variables + ) + { + module $(<) + { + return $($(>)) ; + } + } + + rule set-variable ( module-name : name : value * ) + { + module $(<) + { + $(>) = $(3) ; + } + } + + rule set-top-level-targets ( targets * ) + { + DEPENDS all : $(targets) ; + } + + rule set-update-action ( action : targets * : sources * : properties * ) + { + $(action) $(targets) : $(sources) : $(properties) ; + } + + rule set-target-variable ( targets + : variable : value * : append ? ) + { + if $(append) + { + $(variable) on $(targets) += $(value) ; + } + else + { + $(variable) on $(targets) = $(value) ; + } + } + + rule get-target-variable ( target : variable ) + { + return [ on $(target) return $($(variable)) ] ; + } + + rule import-rules-from-parent ( parent-module : this-module : user-rules ) + { + IMPORT $(parent-module) : $(user-rules) : $(this-module) : $(user-rules) ; + EXPORT $(this-module) : $(user-rules) ; + } + + rule mark-included ( targets * : includes * ) { + INCLUDES $(targets) : $(INCLUDES) ; + } + } + + PYTHON_IMPORT_RULE bootstrap : bootstrap : PyBB : bootstrap ; + modules.poke PyBB : root : [ NORMALIZE_PATH $(.bootstrap-file:DT)/.. ] ; + + module PyBB + { + bootstrap $(root) ; + } + + + #PYTHON_IMPORT_RULE boost.build.build_system : main : PyBB : main ; + + #module PyBB + #{ + # main ; + #} + + } } diff --git a/v2/manager.py b/v2/manager.py new file mode 100644 index 000000000..7067dc82c --- /dev/null +++ b/v2/manager.py @@ -0,0 +1,132 @@ +# Copyright Pedro Ferreira 2005. 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) + +import bjam + +# To simplify implementation of tools level, we'll +# have a global variable keeping the current manager. +the_manager = None +def get_manager(): + return the_manager + +class Manager: + """ This class is a facade to the Boost.Build system. + It serves as the root to access all data structures in use. + """ + + def __init__ (self, engine, global_build_dir): + """ Constructor. + engine: the build engine that will actually construct the targets. + """ + from build.virtual_target import VirtualTargetRegistry + from build.targets import TargetRegistry + from build.project import ProjectRegistry + from build.scanner import ScannerRegistry + from build.errors import Errors + from b2.util.logger import NullLogger + from build import build_request, property_set, feature + + self.engine_ = engine + self.virtual_targets_ = VirtualTargetRegistry (self) + self.projects_ = ProjectRegistry (self, global_build_dir) + self.targets_ = TargetRegistry () + self.logger_ = NullLogger () + self.scanners_ = ScannerRegistry (self) + self.argv_ = bjam.variable("ARGV") + self.boost_build_path_ = bjam.variable("BOOST_BUILD_PATH") + self.errors_ = Errors() + self.command_line_free_features_ = property_set.empty() + + # Object Map. + # TODO: This is a kludge: maps object names to the actual instances. + # Sometimes, objects are stored in properties, along with some grist. + # This map is used to store the value and return an id, which can be later on used to retriev it back. + self.object_map_ = {} + + global the_manager + the_manager = self + + def scanners (self): + return self.scanners_ + + def engine (self): + return self.engine_ + + def virtual_targets (self): + return self.virtual_targets_ + + def targets (self): + return self.targets_ + + def projects (self): + return self.projects_ + + def argv (self): + return self.argv_ + + def logger (self): + return self.logger_ + + def set_logger (self, logger): + self.logger_ = logger + + def errors (self): + return self.errors_ + + def getenv(self, name): + return bjam.variable(name) + + def boost_build_path(self): + return self.boost_build_path_ + + def command_line_free_features(self): + return self.command_line_free_features_ + + def set_command_line_free_features(self, v): + self.command_line_free_features_ = v + + def register_object (self, value): + """ Stores an object in a map and returns a key that can be used to retrieve it. + """ + key = 'object_registry_' + str (value) + self.object_map_ [key] = value + return key + + def get_object (self, key): + """ Returns a previously registered object. + """ + if not isinstance (key, str): + # Probably it's the object itself. + return key + + return self.object_map_ [key] + + def construct (self, properties = [], targets = []): + """ Constructs the dependency graph. + properties: the build properties. + targets: the targets to consider. If none is specified, uses all. + """ + if not targets: + for name, project in self.projects ().projects (): + targets.append (project.target ()) + + property_groups = build_request.expand_no_defaults (properties) + + virtual_targets = [] + build_prop_sets = [] + for p in property_groups: + build_prop_sets.append (property_set.create (feature.split (p))) + + if not build_prop_sets: + build_prop_sets = [property_set.empty ()] + + for build_properties in build_prop_sets: + for target in targets: + result = target.generate (build_properties) + virtual_targets.extend (result.targets ()) + + actual_targets = [] + for virtual_target in virtual_targets: + actual_targets.extend (virtual_target.actualize ()) + diff --git a/v2/tools/__init__.py b/v2/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/v2/tools/builtin.py b/v2/tools/builtin.py new file mode 100644 index 000000000..a99682f4b --- /dev/null +++ b/v2/tools/builtin.py @@ -0,0 +1,722 @@ +# Status: minor updates by Steven Watanabe to make gcc work +# +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +""" Defines standard features and rules. +""" + +import sys +from b2.build import feature, property, virtual_target, generators, type, property_set, scanner +from b2.util.utility import * +from b2.util import path, regex +import b2.tools.types +from b2.manager import get_manager + +# Records explicit properties for a variant. +# The key is the variant name. +__variant_explicit_properties = {} + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __variant_explicit_properties + + __variant_explicit_properties = {} + +def variant (name, parents_or_properties, explicit_properties = []): + """ Declares a new variant. + First determines explicit properties for this variant, by + refining parents' explicit properties with the passed explicit + properties. The result is remembered and will be used if + this variant is used as parent. + + Second, determines the full property set for this variant by + adding to the explicit properties default values for all properties + which neither present nor are symmetric. + + Lastly, makes appropriate value of 'variant' property expand + to the full property set. + name: Name of the variant + parents_or_properties: Specifies parent variants, if + 'explicit_properties' are given, + and explicit_properties otherwise. + explicit_properties: Explicit properties. + """ + parents = [] + if not explicit_properties: + if get_grist (parents_or_properties [0]): + explicit_properties = parents_or_properties + + else: + parents = parents_or_properties + + else: + parents = parents_or_properties + + # The problem is that we have to check for conflicts + # between base variants. + if len (parents) > 1: + raise BaseException ("Multiple base variants are not yet supported") + + inherited = [] + # Add explicitly specified properties for parents + for p in parents: + # TODO: the check may be stricter + if not feature.is_implicit_value (p): + raise BaseException ("Invalid base varaint '%s'" % p) + + inherited += __variant_explicit_properties [p] + + property.validate (explicit_properties) + explicit_properties = property.refine (inherited, explicit_properties) + + # Record explicitly specified properties for this variant + # We do this after inheriting parents' properties, so that + # they affect other variants, derived from this one. + __variant_explicit_properties [name] = explicit_properties + + feature.extend('variant', [name]) + feature.compose (replace_grist (name, ''), explicit_properties) + +__os_names = """ + amiga aix bsd cygwin darwin dos emx freebsd hpux iphone linux netbsd + openbsd osf qnx qnxnto sgi solaris sun sunos svr4 sysv ultrix unix unixware + vms windows +""".split() + +# Translates from bjam current OS to the os tags used in host-os and target-os, +# i.e. returns the running host-os. +# +def default_host_os(): + host_os = os_name() + if host_os not in (x.upper() for x in __os_names): + if host_os == 'NT': host_os = 'windows' + elif host_os == 'AS400': host_os = 'unix' + elif host_os == 'MINGW': host_os = 'windows' + elif host_os == 'BSDI': host_os = 'bsd' + elif host_os == 'COHERENT': host_os = 'unix' + elif host_os == 'DRAGONFLYBSD': host_os = 'bsd' + elif host_os == 'IRIX': host_os = 'sgi' + elif host_os == 'MACOSX': host_os = 'darwin' + elif host_os == 'KFREEBSD': host_os = 'freebsd' + elif host_os == 'LINUX': host_os = 'linux' + else: host_os = 'unix' + return host_os.lower() + +def register_globals (): + """ Registers all features and variants declared by this module. + """ + + # This feature is used to determine which OS we're on. + # In future, this may become and + # TODO: check this. Compatibility with bjam names? Subfeature for version? + os = sys.platform + feature.feature ('os', [os], ['propagated', 'link-incompatible']) + + + # The two OS features define a known set of abstract OS names. The host-os is + # the OS under which bjam is running. Even though this should really be a fixed + # property we need to list all the values to prevent unknown 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.feature('host-os', __os_names) + feature.set_default('host-os', default_host_os()) + + feature.feature('target-os', __os_names, ['propagated', 'link-incompatible']) + feature.set_default('target-os', default_host_os()) + + feature.feature ('toolset', [], ['implicit', 'propagated' ,'symmetric']) + + feature.feature ('stdlib', ['native'], ['propagated', 'composite']) + + feature.feature ('link', ['shared', 'static'], ['propagated']) + feature.feature ('runtime-link', ['shared', 'static'], ['propagated']) + feature.feature ('runtime-debugging', ['on', 'off'], ['propagated']) + + + feature.feature ('optimization', ['off', 'speed', 'space'], ['propagated']) + feature.feature ('profiling', ['off', 'on'], ['propagated']) + feature.feature ('inlining', ['off', 'on', 'full'], ['propagated']) + + feature.feature ('threading', ['single', 'multi'], ['propagated']) + feature.feature ('rtti', ['on', 'off'], ['propagated']) + feature.feature ('exception-handling', ['on', 'off'], ['propagated']) + feature.feature ('debug-symbols', ['on', 'off'], ['propagated']) + feature.feature ('define', [], ['free']) + feature.feature ('include', [], ['free', 'path']) #order-sensitive + feature.feature ('cflags', [], ['free']) + feature.feature ('cxxflags', [], ['free']) + feature.feature ('linkflags', [], ['free']) + feature.feature ('archiveflags', [], ['free']) + feature.feature ('version', [], ['free']) + + feature.feature ('location-prefix', [], ['free']) + + feature.feature ('action', [], ['free']) + + + # The following features are incidental, since + # in themself they have no effect on build products. + # Not making them incidental will result in problems in corner + # cases, for example: + # + # unit-test a : a.cpp : b ; + # lib b : a.cpp b ; + # + # Here, if is not incidental, we'll decide we have two + # targets for a.obj with different properties, and will complain. + # + # Note that making feature incidental does not mean it's ignored. It may + # be ignored when creating the virtual target, but the rest of build process + # will use them. + feature.feature ('use', [], ['free', 'dependency', 'incidental']) + feature.feature ('dependency', [], ['free', 'dependency', 'incidental']) + feature.feature ('implicit-dependency', [], ['free', 'dependency', 'incidental']) + + feature.feature('warnings', [ + 'on', # Enable default/"reasonable" warning level for the tool. + 'all', # Enable all possible warnings issued by the tool. + 'off'], # Disable all warnings issued by the tool. + ['incidental', 'propagated']) + + feature.feature('warnings-as-errors', [ + 'off', # Do not fail the compilation if there are warnings. + 'on'], # Fail the compilation if there are warnings. + ['incidental', 'propagated']) + + feature.feature ('source', [], ['free', 'dependency', 'incidental']) + feature.feature ('library', [], ['free', 'dependency', 'incidental']) + feature.feature ('file', [], ['free', 'dependency', 'incidental']) + feature.feature ('find-shared-library', [], ['free']) #order-sensitive ; + feature.feature ('find-static-library', [], ['free']) #order-sensitive ; + feature.feature ('library-path', [], ['free', 'path']) #order-sensitive ; + # Internal feature. + feature.feature ('library-file', [], ['free', 'dependency']) + + feature.feature ('name', [], ['free']) + feature.feature ('tag', [], ['free']) + feature.feature ('search', [], ['free', 'path']) #order-sensitive ; + feature.feature ('location', [], ['free', 'path']) + + feature.feature ('dll-path', [], ['free', 'path']) + feature.feature ('hardcode-dll-paths', ['true', 'false'], ['incidental']) + + + # This is internal feature which 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. + # On Linux, it's needed to add proper -rpath-link command line options. + feature.feature ('xdll-path', [], ['free', 'path']) + + #provides means to specify def-file for windows dlls. + feature.feature ('def-file', [], ['free', 'dependency']) + + # This feature is used to allow specific generators to run. + # For example, QT tools can only be invoked when QT library + # is used. In that case, qt will be in usage requirement + # of the library. + feature.feature ('allow', [], ['free']) + + # The addressing model to generate code for. Currently a limited set only + # specifying the bit size of pointers. + feature.feature('address-model', ['16', '32', '64'], ['propagated', 'optional']) + + # Type of CPU architecture to compile for. + feature.feature('architecture', [ + # x86 and x86-64 + 'x86', + + # ia64 + 'ia64', + + # Sparc + 'sparc', + + # RS/6000 & PowerPC + 'power', + + # MIPS/SGI + 'mips1', 'mips2', 'mips3', 'mips4', 'mips32', 'mips32r2', 'mips64', + + # HP/PA-RISC + 'parisc', + + # Advanced RISC Machines + 'arm', + + # Combined architectures for platforms/toolsets that support building for + # multiple architectures at once. "combined" would be the default multi-arch + # for the toolset. + 'combined', + 'combined-x86-power'], + + ['propagated', 'optional']) + + # The specific instruction set in an architecture to compile. + feature.feature('instruction-set', [ + # x86 and x86-64 + 'i386', 'i486', 'i586', 'i686', 'pentium', 'pentium-mmx', 'pentiumpro', 'pentium2', 'pentium3', + 'pentium3m', 'pentium-m', 'pentium4', 'pentium4m', 'prescott', 'nocona', 'conroe', 'conroe-xe', + 'conroe-l', 'allendale', 'mermon', 'mermon-xe', 'kentsfield', 'kentsfield-xe', 'penryn', 'wolfdale', + 'yorksfield', 'nehalem', 'k6', 'k6-2', 'k6-3', 'athlon', 'athlon-tbird', 'athlon-4', 'athlon-xp', + 'athlon-mp', 'k8', 'opteron', 'athlon64', 'athlon-fx', 'winchip-c6', 'winchip2', 'c3', 'c3-2', + + # ia64 + 'itanium', 'itanium1', 'merced', 'itanium2', 'mckinley', + + # Sparc + 'v7', 'cypress', 'v8', 'supersparc', 'sparclite', 'hypersparc', 'sparclite86x', 'f930', 'f934', + 'sparclet', 'tsc701', 'v9', 'ultrasparc', 'ultrasparc3', + + # RS/6000 & PowerPC + '401', '403', '405', '405fp', '440', '440fp', '505', '601', '602', + '603', '603e', '604', '604e', '620', '630', '740', '7400', + '7450', '750', '801', '821', '823', '860', '970', '8540', + 'power-common', 'ec603e', 'g3', 'g4', 'g5', 'power', 'power2', + 'power3', 'power4', 'power5', 'powerpc', 'powerpc64', 'rios', + 'rios1', 'rsc', 'rios2', 'rs64a', + + # MIPS + '4kc', '4kp', '5kc', '20kc', 'm4k', 'r2000', 'r3000', 'r3900', 'r4000', + 'r4100', 'r4300', 'r4400', 'r4600', 'r4650', + 'r6000', 'r8000', 'rm7000', 'rm9000', 'orion', 'sb1', 'vr4100', + 'vr4111', 'vr4120', 'vr4130', 'vr4300', + 'vr5000', 'vr5400', 'vr5500', + + # HP/PA-RISC + '700', '7100', '7100lc', '7200', '7300', '8000', + + # Advanced RISC Machines + 'armv2', 'armv2a', 'armv3', 'armv3m', 'armv4', 'armv4t', 'armv5', + 'armv5t', 'armv5te', 'armv6', 'armv6j', 'iwmmxt', 'ep9312'], + + ['propagated', 'optional']) + + # Windows-specific features + feature.feature ('user-interface', ['console', 'gui', 'wince', 'native', 'auto'], []) + feature.feature ('variant', [], ['implicit', 'composite', 'propagated', 'symmetric']) + + + variant ('debug', ['off', 'on', 'off', 'on']) + variant ('release', ['speed', 'off', 'full', + 'off', 'NDEBUG']) + variant ('profile', ['release'], ['on', 'on']) + + type.register ('H', ['h']) + type.register ('HPP', ['hpp'], 'H') + type.register ('C', ['c']) + + +reset () +register_globals () + +class SearchedLibTarget (virtual_target.AbstractFileTarget): + def __init__ (self, name, project, shared, real_name, search, action): + virtual_target.AbstractFileTarget.__init__ (self, name, False, 'SEARCHED_LIB', project, action) + + self.shared_ = shared + self.real_name_ = real_name + if not self.real_name_: + self.real_name_ = name + self.search_ = search + + def shared (self): + return self.shared_ + + def real_name (self): + return self.real_name_ + + def search (self): + return self.search_ + + def actualize_location (self, target): + project.manager ().engine ().add_not_file_target (target) + + def path (self): + #FIXME: several functions rely on this not being None + return "" + + +class CScanner (scanner.Scanner): + def __init__ (self, includes): + scanner.Scanner.__init__ (self) + + self.includes_ = includes + + def pattern (self): + return r'#[ \t]*include[ ]*(<(.*)>|"(.*)")' + + def process (self, target, matches, binding): + + angle = regex.transform (matches, "<(.*)>") + quoted = regex.transform (matches, '"(.*)"') + + g = str(id(self)) + b = os.path.normpath(os.path.dirname(binding[0])) + + # Attach binding of including file to included targets. + # When target is directly created from virtual target + # this extra information is unnecessary. But in other + # cases, it allows to distinguish between two headers of the + # same name included from different places. + # We don't need this extra information for angle includes, + # since they should not depend on including file (we can't + # get literal "." in include path). + g2 = g + "#" + b + + g = "<" + g + ">" + g2 = "<" + g2 + ">" + angle = [g + x for x in angle] + quoted = [g2 + x for x in quoted] + + all = angle + quoted + bjam.call("mark-included", target, all) + + engine = get_manager().engine() + engine.set_target_variable(angle, "SEARCH", self.includes_) + engine.set_target_variable(quoted, "SEARCH", self.includes_) + + # Just propagate current scanner to includes, in a hope + # that includes do not change scanners. + get_manager().scanners().propagate(self, angle + quoted) + +scanner.register (CScanner, 'include') +type.set_scanner ('CPP', CScanner) + +# Ported to trunk@47077 +class LibGenerator (generators.Generator): + """ The generator class for libraries (target type LIB). Depending on properties it will + request building of the approapriate specific type -- SHARED_LIB, STATIC_LIB or + SHARED_LIB. + """ + + def __init__(self, id = 'LibGenerator', composing = True, source_types = [], target_types_and_names = ['LIB'], requirements = []): + generators.Generator.__init__(self, id, composing, source_types, target_types_and_names, requirements) + + def run(self, project, name, prop_set, sources): + # The lib generator is composing, and can be only invoked with + # explicit name. This check is present in generator.run (and so in + # builtin.LinkingGenerator), but duplicate it here to avoid doing + # extra work. + if name: + properties = prop_set.raw() + # Determine the needed target type + actual_type = None + properties_grist = get_grist(properties) + if '' not in properties_grist and \ + ('' in properties_grist or '' in properties_grist): + actual_type = 'SEARCHED_LIB' + elif '' in properties_grist: + # The generator for + actual_type = 'LIB' + elif 'shared' in properties: + actual_type = 'SHARED_LIB' + else: + actual_type = 'STATIC_LIB' + + prop_set = prop_set.add_raw(['LIB']) + + # Construct the target. + return generators.construct(project, name, actual_type, prop_set, sources) + + def viable_source_types(self): + return ['*'] + +generators.register(LibGenerator()) + +### # The implementation of the 'lib' rule. Beyond standard syntax that rule allows +### # simplified: +### # lib a b c ; +### # so we need to write code to handle that syntax. +### rule lib ( names + : sources * : requirements * : default-build * +### : usage-requirements * ) +### { +### local project = [ project.current ] ; +### +### # This is a circular module dependency, so it must be imported here +### import targets ; +### +### local result ; +### if ! $(sources) && ! $(requirements) +### && ! $(default-build) && ! $(usage-requirements) +### { +### for local name in $(names) +### { +### result += [ +### targets.main-target-alternative +### [ new typed-target $(name) : $(project) : LIB +### : +### : [ targets.main-target-requirements $(requirements) $(name) : +### $(project) ] +### : [ targets.main-target-default-build $(default-build) : $(project) ] +### : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ] +### ] ] ; +### } +### } +### else +### { +### if $(names[2]) +### { +### errors.user-error "When several names are given to the 'lib' rule" : +### "it's not allowed to specify sources or requirements. " ; +### } +### +### local name = $(names[1]) ; +### result = [ targets.main-target-alternative +### [ new typed-target $(name) : $(project) : LIB +### : [ targets.main-target-sources $(sources) : $(name) ] +### : [ targets.main-target-requirements $(requirements) : $(project) ] +### : [ targets.main-target-default-build $(default-build) : $(project) ] +### : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ] +### ] ] ; +### } +### return $(result) ; +### } +### IMPORT $(__name__) : lib : : lib ; + +# Updated to trunk@47077 +class SearchedLibGenerator (generators.Generator): + def __init__ (self, id = 'SearchedLibGenerator', composing = False, source_types = [], target_types_and_names = ['SEARCHED_LIB'], requirements = []): + # TODO: the comment below looks strange. There are no requirements! + # The requirements cause the generators to be tried *only* when we're building + # lib target and there's 'search' feature. This seems ugly --- all we want + # is make sure SearchedLibGenerator is not invoked deep in transformation + # search. + generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) + + def run(self, project, name, prop_set, sources): + if not name: + return None + + # If name is empty, it means we're called not from top-level. + # In this case, we just fail immediately, because SearchedLibGenerator + # cannot be used to produce intermediate targets. + + properties = prop_set.raw () + shared = 'shared' in properties + + a = virtual_target.NullAction (project.manager(), prop_set) + + real_name = feature.get_values ('', properties) + if real_name: + real_name = real_name[0] + else: + real_nake = name + search = feature.get_values('', properties) + usage_requirements = property_set.create(['' + p for p in search]) + t = SearchedLibTarget(name, project, shared, real_name, search, a) + + # We return sources for a simple reason. If there's + # lib png : z : png ; + # the 'z' target should be returned, so that apps linking to + # 'png' will link to 'z', too. + return(usage_requirements, [b2.manager.get_manager().virtual_targets().register(t)] + sources) + +generators.register (SearchedLibGenerator ()) + +### class prebuilt-lib-generator : generator +### { +### rule __init__ ( * : * ) +### { +### generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; +### } +### +### rule run ( project name ? : prop_set : sources * : multiple ? ) +### { +### local f = [ $(prop_set).get ] ; +### return $(f) $(sources) ; +### } +### } +### +### generators.register +### [ new prebuilt-lib-generator builtin.prebuilt : : LIB : ] ; + + +class CompileAction (virtual_target.Action): + def __init__ (self, manager, sources, action_name, prop_set): + virtual_target.Action.__init__ (self, manager, sources, action_name, prop_set) + + def adjust_properties (self, prop_set): + """ For all virtual targets for the same dependency graph as self, + i.e. which belong to the same main target, add their directories + to include path. + """ + s = self.targets () [0].creating_subvariant () + + return prop_set.add_raw (s.implicit_includes ('include', 'H')) + +class CCompilingGenerator (generators.Generator): + """ Declare a special compiler generator. + The only thing it does is changing the type used to represent + 'action' in the constructed dependency graph to 'CompileAction'. + That class in turn adds additional include paths to handle a case + when a source file includes headers which are generated themselfs. + """ + def __init__ (self, id, composing, source_types, target_types_and_names, requirements): + # TODO: (PF) What to do with optional_properties? It seemed that, in the bjam version, the arguments are wrong. + generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) + + def action_class (self): + return CompileAction + +def register_c_compiler (id, source_types, target_types, requirements, optional_properties = []): + g = CCompilingGenerator (id, False, source_types, target_types, requirements + optional_properties) + return generators.register (g) + + +class LinkingGenerator (generators.Generator): + """ The generator class for handling EXE and SHARED_LIB creation. + """ + def __init__ (self, id, composing, source_types, target_types_and_names, requirements): + generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) + + def run (self, project, name, prop_set, sources): + lib_sources = prop_set.get('') + [ sources.append (project.manager().get_object(x)) for x in lib_sources ] + + # Add properties for all searched libraries + extra = [] + for s in sources: + if s.type () == 'SEARCHED_LIB': + search = s.search() + extra.append(replace_grist(search, '')) + + orig_xdll_path = [] + + if prop_set.get('') == ['true'] and type.is_derived(self.target_types_ [0], 'EXE'): + xdll_path = prop_set.get('') + orig_xdll_path = [ replace_grist(x, '') for x in xdll_path ] + # It's possible that we have libraries in sources which did not came + # from 'lib' target. For example, libraries which are specified + # just as filenames as sources. We don't have xdll-path properties + # for such target, but still need to add proper dll-path properties. + for s in sources: + if type.is_derived (s.type (), 'SHARED_LIB') and not s.action (): + # Unfortunately, we don't have a good way to find the path + # to a file, so use this nasty approach. + p = s.project() + location = path.root(s.name(), p.get('source-location')) + xdll_path.append(path.parent(location)) + + extra += [ replace_grist(x, '') for x in xdll_path ] + + if extra: + prop_set = prop_set.add_raw (extra) + + result = generators.Generator.run(self, project, name, prop_set, sources) + + if result: + ur = self.extra_usage_requirements(result, prop_set) + ur = ur.add(property_set.create(orig_xdll_path)) + else: + return None + + return(ur, result) + + def extra_usage_requirements (self, created_targets, prop_set): + + result = property_set.empty () + extra = [] + + # Add appropriate usage requirements. + raw = prop_set.raw () + if 'shared' in raw: + paths = [] + + # TODO: is it safe to use the current directory? I think we should use + # another mechanism to allow this to be run from anywhere. + pwd = os.getcwd() + + for t in created_targets: + if type.is_derived(t.type(), 'SHARED_LIB'): + paths.append(path.root(path.make(t.path()), pwd)) + + extra += replace_grist(paths, '') + + # We need to pass features that we've got from sources, + # because if shared library is built, exe which uses it must know paths + # to other shared libraries this one depends on, to be able to find them + # all at runtime. + + # Just pass all features in property_set, it's theorically possible + # that we'll propagate features explicitly specified by + # the user, but then the user's to blaim for using internal feature. + values = prop_set.get('') + extra += replace_grist(values, '') + + if extra: + result = property_set.create(extra) + + return result + + def generated_targets (self, sources, prop_set, project, name): + + # sources to pass to inherited rule + sources2 = [] + # properties to pass to inherited rule + properties2 = [] + # sources which are libraries + libraries = [] + + # Searched libraries are not passed as argument to linker + # but via some option. So, we pass them to the action + # via property. + properties2 = prop_set.raw() + fsa = [] + fst = [] + for s in sources: + if type.is_derived(s.type(), 'SEARCHED_LIB'): + name = s.real_name() + if s.shared(): + fsa.append(name) + + else: + fst.append(name) + + else: + sources2.append(s) + + if fsa: + properties2 += [replace_grist('&&'.join(fsa), '')] + if fst: + properties2 += [replace_grist('&&'.join(fst), '')] + + spawn = generators.Generator.generated_targets(self, sources2, property_set.create(properties2), project, name) + + return spawn + + +def register_linker(id, source_types, target_types, requirements): + g = LinkingGenerator(id, True, source_types, target_types, requirements) + generators.register(g) + +class ArchiveGenerator (generators.Generator): + """ The generator class for handling STATIC_LIB creation. + """ + def __init__ (self, id, composing, source_types, target_types_and_names, requirements): + generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) + + def run (self, project, name, prop_set, sources): + sources += prop_set.get ('') + + result = generators.Generator.run (self, project, name, prop_set, sources) + + return result + +### rule register-archiver ( id composing ? : source_types + : target_types + : +### requirements * ) +### { +### local g = [ new ArchiveGenerator $(id) $(composing) : $(source_types) +### : $(target_types) : $(requirements) ] ; +### generators.register $(g) ; +### } +### +### +### IMPORT $(__name__) : register-linker register-archiver +### : : generators.register-linker generators.register-archiver ; +### +### +### diff --git a/v2/tools/common.py b/v2/tools/common.py new file mode 100644 index 000000000..8de091f54 --- /dev/null +++ b/v2/tools/common.py @@ -0,0 +1,817 @@ +# Status: being ported by Steven Watanabe +# Base revision: 47174 +# +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +""" Provides actions common to all toolsets, such as creating directories and + removing files. +""" + +import re +import bjam +import os +import os.path + +from b2.build import feature +from b2.util.utility import * +from b2.util import path + +__re__before_first_dash = re.compile ('([^-]*)-') + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + Note that this must be called _after_ resetting the module 'feature'. + """ + global __had_unspecified_value, __had_value, __declared_subfeature + global __init_loc + global __all_signatures, __debug_configuration, __show_configuration + + # Stores toolsets without specified initialization values. + __had_unspecified_value = {} + + # Stores toolsets with specified initialization values. + __had_value = {} + + # Stores toolsets with declared subfeatures. + __declared_subfeature = {} + + # Stores all signatures of the toolsets. + __all_signatures = {} + + # Stores the initialization locations of each toolset + __init_loc = {} + + __debug_configuration = '--debug-configuration' in bjam.variable('ARGV') + __show_configuration = '--show-configuration' in bjam.variable('ARGV') + +reset() + +# ported from trunk@47174 +class Configurations(object): + """ + This class helps to manage toolset configurations. Each configuration + has a unique ID and one or more parameters. A typical example of a unique ID + is a condition generated by 'common.check-init-parameters' rule. Other kinds + of IDs can be used. Parameters may include any details about the configuration + like 'command', 'path', etc. + + A toolset configuration may be in one of the following states: + + - registered + Configuration has been registered (e.g. by autodetection code) but has + not yet been marked as used, i.e. 'toolset.using' rule has not yet been + called for it. + - used + Once called 'toolset.using' rule marks the configuration as 'used'. + + The main difference between the states above is that while a configuration is + 'registered' its options can be freely changed. This is useful in particular + for autodetection code - all detected configurations may be safely overwritten + by user code. + """ + + def __init__(self): + self.used_ = set() + self.all_ = set() + self.params = {} + + def register(self, id): + """ + Registers a configuration. + + Returns True if the configuration has been added and False if + it already exists. Reports an error if the configuration is 'used'. + """ + if id in self.used_: + #FIXME + errors.error("common: the configuration '$(id)' is in use") + + if id not in self.all_: + self.all_ += [id] + + # Indicate that a new configuration has been added. + return True + else: + return False + + def use(self, id): + """ + Mark a configuration as 'used'. + + Returns True if the state of the configuration has been changed to + 'used' and False if it the state wasn't changed. Reports an error + if the configuration isn't known. + """ + if id not in self.all_: + #FIXME: + errors.error("common: the configuration '$(id)' is not known") + + if id not in self.used_: + self.used_ += [id] + + # indicate that the configuration has been marked as 'used' + return True + else: + return False + + def all(self): + """ Return all registered configurations. """ + return self.all_ + + def used(self): + """ Return all used configurations. """ + return self.used_ + + def get(self, id, param): + """ Returns the value of a configuration parameter. """ + self.params_.getdefault(param, {}).getdefault(id, None) + + def set (self, id, param, value): + """ Sets the value of a configuration parameter. """ + self.params_.setdefault(param, {})[id] = value + +# Ported from trunk@47174 +def check_init_parameters(toolset, requirement, *args): + """ The rule for checking toolset parameters. Trailing parameters should all be + parameter name/value pairs. The rule will check that each parameter either has + a value in each invocation or has no value in each invocation. Also, the rule + will check that the combination of all parameter values is unique in all + invocations. + + Each parameter name corresponds to a subfeature. This rule will declare a + subfeature the first time a non-empty parameter value is passed and will + extend it with all the values. + + The return value from this rule is a condition to be used for flags settings. + """ + # The type checking here is my best guess about + # what the types should be. + assert(isinstance(toolset, str)) + assert(isinstance(requirement, str) or requirement is None) + sig = toolset + condition = replace_grist(toolset, '') + subcondition = [] + + for arg in args: + assert(isinstance(arg, tuple)) + assert(len(arg) == 2) + name = arg[0] + value = arg[1] + assert(isinstance(name, str)) + assert(isinstance(value, str) or value is None) + + str_toolset_name = str((toolset, name)) + + # FIXME: is this the correct translation? + ### if $(value)-is-not-empty + if value is not None: + condition = condition + '-' + value + if __had_unspecified_value.has_key(str_toolset_name): + raise BaseException("'%s' initialization: parameter '%s' inconsistent\n" \ + "no value was specified in earlier initialization\n" \ + "an explicit value is specified now" % (toolset, name)) + + # The logic below is for intel compiler. It calls this rule + # with 'intel-linux' and 'intel-win' as toolset, so we need to + # get the base part of toolset name. + # We can't pass 'intel' as toolset, because it that case it will + # be impossible to register versionles intel-linux and + # intel-win of specific version. + t = toolset + m = __re__before_first_dash.match(toolset) + if m: + t = m.group(1) + + if not __had_value.has_key(str_toolset_name): + if not __declared_subfeature.has_key(str((t, name))): + feature.subfeature('toolset', t, name, [], ['propagated']) + __declared_subfeature[str((t, name))] = True + + __had_value[str_toolset_name] = True + + feature.extend_subfeature('toolset', t, name, [value]) + subcondition += ['' + value ] + + else: + if __had_value.has_key(str_toolset_name): + raise BaseException ("'%s' initialization: parameter '%s' inconsistent\n" \ + "an explicit value was specified in an earlier initialization\n" \ + "no value is specified now" % (toolset, name)) + + __had_unspecified_value[str_toolset_name] = True + + if value == None: value = '' + + sig = sig + value + '-' + + if __all_signatures.has_key(sig): + message = "duplicate initialization of '%s' with the following parameters: " % toolset + + for arg in args: + name = arg[0] + value = arg[1] + if value == None: value = '' + + message += "'%s' = '%s'\n" % (name, value) + + raise BaseException(message) + + __all_signatures[sig] = True + # FIXME + __init_loc[sig] = "User location unknown" #[ errors.nearest-user-location ] ; + + # If we have a requirement, this version should only be applied under that + # condition. To accomplish this we add a toolset requirement that imposes + # the toolset subcondition, which encodes the version. + if requirement: + r = ['' + toolset, requirement] + r = ','.join(r) + toolset.add_requirements([r + ':' + c for c in subcondition]) + + # We add the requirements, if any, to the condition to scope the toolset + # variables and options to this specific version. + condition = [condition] + if requirement: + condition += [requirement] + + if __show_configuration: + print "notice:", condition + return ['/'.join(condition)] + +# Ported from trunk@47077 +def get_invocation_command_nodefault( + toolset, tool, user_provided_command=[], additional_paths=[], path_last=False): + """ + A helper rule to get the command to invoke some tool. If + 'user-provided-command' is not given, tries to find binary named 'tool' in + PATH and in the passed 'additional-path'. Otherwise, verifies that the first + element of 'user-provided-command' is an existing program. + + This rule returns the command to be used when invoking the tool. If we can't + find the tool, a warning is issued. If 'path-last' is specified, PATH is + checked after 'additional-paths' when searching for 'tool'. + """ + assert(isinstance(toolset, str)) + assert(isinstance(tool, str)) + assert(isinstance(user_provided_command, list)) + if additional_paths is not None: + assert(isinstance(additional_paths, list)) + assert(all([isinstance(path, str) for path in additional_paths])) + assert(all(isinstance(path, str) for path in additional_paths)) + assert(isinstance(path_last, bool)) + + if not user_provided_command: + command = find_tool(tool, additional_paths, path_last) + if not command and __debug_configuration: + print "warning: toolset", toolset, "initialization: can't find tool, tool" + #FIXME + #print "warning: initialized from" [ errors.nearest-user-location ] ; + else: + command = check_tool(user_provided_command) + if not command and __debug_configuration: + print "warning: toolset", toolset, "initialization:" + print "warning: can't find user-provided command", user_provided_command + #FIXME + #ECHO "warning: initialized from" [ errors.nearest-user-location ] + + assert(isinstance(command, str)) + + return command + +# ported from trunk@47174 +def get_invocation_command(toolset, tool, user_provided_command = [], + additional_paths = [], path_last = False): + """ Same as get_invocation_command_nodefault, except that if no tool is found, + returns either the user-provided-command, if present, or the 'tool' parameter. + """ + + assert(isinstance(toolset, str)) + assert(isinstance(tool, str)) + assert(isinstance(user_provided_command, list)) + if additional_paths is not None: + assert(isinstance(additional_paths, list)) + assert(all([isinstance(path, str) for path in additional_paths])) + assert(isinstance(path_last, bool)) + + result = get_invocation_command_nodefault(toolset, tool, + user_provided_command, + additional_paths, + path_last) + + if not result: + if user_provided_command: + result = user_provided_command[0] + else: + result = tool + + assert(isinstance(result, str)) + + return result + +# ported from trunk@47281 +def get_absolute_tool_path(command): + """ + Given an invocation command, + return the absolute path to the command. This works even if commnad + has not path element and is present in PATH. + """ + if os.path.dirname(command): + return os.path.dirname(command) + else: + programs = path.programs_path() + m = path.glob(programs, [command, command + '.exe' ]) + if not len(m): + print "Could not find:", command, "in", programs + return os.path.dirname(m[0]) + +# ported from trunk@47174 +def find_tool(name, additional_paths = [], path_last = False): + """ Attempts to find tool (binary) named 'name' in PATH and in + 'additional-paths'. If found in path, returns 'name'. If + found in additional paths, returns full name. If the tool + is found in several directories, returns the first path found. + Otherwise, returns the empty string. If 'path_last' is specified, + path is checked after 'additional_paths'. + """ + assert(isinstance(name, str)) + assert(isinstance(additional_paths, list)) + assert(isinstance(path_last, bool)) + + programs = path.programs_path() + match = path.glob(programs, [name, name + '.exe']) + additional_match = path.glob(additional_paths, [name, name + '.exe']) + + result = [] + if path_last: + result = additional_match + if not result and match: + result = match + + else: + if match: + result = match + + elif additional_match: + result = additional_match + + if result: + return path.native(result[0]) + else: + return '' + +#ported from trunk@47281 +def check_tool_aux(command): + """ Checks if 'command' can be found either in path + or is a full name to an existing file. + """ + assert(isinstance(command, str)) + dirname = os.path.dirname(command) + if dirname: + if os.path.exists(command): + return command + # Both NT and Cygwin will run .exe files by their unqualified names. + elif on_windows() and os.path.exists(command + '.exe'): + return command + # Only NT will run .bat files by their unqualified names. + elif os_name() == 'NT' and os.path.exists(command + '.bat'): + return command + else: + paths = path.programs_path() + if path.glob(paths, [command]): + return command + +# ported from trunk@47281 +def check_tool(command): + """ Checks that a tool can be invoked by 'command'. + If command is not an absolute path, checks if it can be found in 'path'. + If comand is absolute path, check that it exists. Returns 'command' + if ok and empty string otherwise. + """ + assert(isinstance(command, list)) + assert(all(isinstance(c, str) for c in command)) + #FIXME: why do we check the first and last elements???? + if check_tool_aux(command[0]) or check_tool_aux(command[-1]): + return command + +# ported from trunk@47281 +def handle_options(tool, condition, command, options): + """ Handle common options for toolset, specifically sets the following + flag variables: + - CONFIG_COMMAND to 'command' + - OPTIOns for compile to the value of in options + - OPTIONS for compile.c to the value of in options + - OPTIONS for compile.c++ to the value of in options + - OPTIONS for compile.fortran to the value of in options + - OPTIONs for link to the value of in options + """ + from b2.build import toolset + + assert(isinstance(tool, str)) + assert(isinstance(condition, list)) + assert(isinstance(command, str)) + assert(isinstance(options, list)) + assert(command) + toolset.flags(tool, 'CONFIG_COMMAND', condition, [command]) + toolset.flags(tool + '.compile', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.compile.c', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.compile.c++', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.compile.fortran', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.link', 'OPTIONS', condition, feature.get_values('', options)) + +# ported from trunk@47281 +def get_program_files_dir(): + """ returns the location of the "program files" directory on a windows + platform + """ + ProgramFiles = bjam.variable("ProgramFiles") + if ProgramFiles: + ProgramFiles = ' '.join(ProgramFiles) + else: + ProgramFiles = "c:\\Program Files" + return ProgramFiles + +# ported from trunk@47281 +def rm_command(): + return __RM + +# ported from trunk@47281 +def copy_command(): + return __CP + +# ported from trunk@47281 +def variable_setting_command(variable, value): + """ + Returns the command needed to set an environment variable on the current + platform. The variable setting persists through all following commands and is + visible in the environment seen by subsequently executed commands. In other + words, on Unix systems, the variable is exported, which is consistent with the + only possible behavior on Windows systems. + """ + assert(isinstance(variable, str)) + assert(isinstance(value, str)) + + if os_name() == 'NT': + return "set " + variable + "=" + value + os.linesep + else: + # (todo) + # The following does not work on CYGWIN and needs to be fixed. On + # CYGWIN the $(nl) variable holds a Windows new-line \r\n sequence that + # messes up the executed export command which then reports that the + # passed variable name is incorrect. This is most likely due to the + # extra \r character getting interpreted as a part of the variable name. + # + # Several ideas pop to mind on how to fix this: + # * One way would be to separate the commands using the ; shell + # command separator. This seems like the quickest possible + # solution but I do not know whether this would break code on any + # platforms I I have no access to. + # * Another would be to not use the terminating $(nl) but that would + # require updating all the using code so it does not simply + # prepend this variable to its own commands. + # * I guess the cleanest solution would be to update Boost Jam to + # allow explicitly specifying \n & \r characters in its scripts + # instead of always relying only on the 'current OS native newline + # sequence'. + # + # Some code found to depend on this behaviour: + # * This Boost Build module. + # * __test__ rule. + # * path-variable-setting-command rule. + # * python.jam toolset. + # * xsltproc.jam toolset. + # * fop.jam toolset. + # (todo) (07.07.2008.) (Jurko) + # + # I think that this works correctly in python -- Steven Watanabe + return variable + "=" + value + os.linesep + "export " + variable + os.linesep + +def path_variable_setting_command(variable, paths): + """ + Returns a command to sets a named shell path variable to the given NATIVE + paths on the current platform. + """ + assert(isinstance(variable, str)) + assert(isinstance(paths, list)) + sep = os.path.pathsep + return variable_setting_command(variable, sep.join(paths)) + +def prepend_path_variable_command(variable, paths): + """ + Returns a command that prepends the given paths to the named path variable on + the current platform. + """ + return path_variable_setting_command(variable, + paths + os.environ(variable).split(os.pathsep)) + +def file_creation_command(): + """ + Return a command which can create a file. If 'r' is result of invocation, then + 'r foobar' will create foobar with unspecified content. What happens if file + already exists is unspecified. + """ + if os_name() == 'NT': + return "echo. > " + else: + return "touch " + +#FIXME: global variable +__mkdir_set = set() +__re_windows_drive = re.compile(r'^.*:\$') + +def mkdir(engine, target): + # If dir exists, do not update it. Do this even for $(DOT). + bjam.call('NOUPDATE', target) + + global __mkdir_set + + # FIXME: Where is DOT defined? + #if $(<) != $(DOT) && ! $($(<)-mkdir): + if target != '.' and target not in __mkdir_set: + # Cheesy gate to prevent multiple invocations on same dir. + __mkdir_set.add(target) + + # Schedule the mkdir build action. + if os_name() == 'NT': + engine.set_update_action("common.MkDir1-quick-fix-for-windows", target, [], None) + else: + engine.set_update_action("common.MkDir1-quick-fix-for-unix", target, [], None) + + # Prepare a Jam 'dirs' target that can be used to make the build only + # construct all the target directories. + engine.add_dependency('dirs', target) + + # Recursively create parent directories. $(<:P) = $(<)'s parent & we + # recurse until root. + + s = os.path.dirname(target) + if os_name() == 'NT': + if(__re_windows_drive.match(s)): + s = '' + + if s: + if s != target: + engine.add_dependency(target, s) + mkdir(engine, s) + else: + bjam.call('NOTFILE', s) + +__re_version = re.compile(r'^([^.]+)[.]([^.]+)[.]?([^.]*)') + +def format_name(format, name, target_type, prop_set): + """ Given a target, as given to a custom tag rule, returns a string formatted + according to the passed format. Format is a list of properties that is + represented in the result. For each element of format the corresponding target + information is obtained and added to the result string. For all, but the + literal, the format value is taken as the as string to prepend to the output + to join the item to the rest of the result. If not given "-" is used as a + joiner. + + The format options can be: + + [joiner] + :: The basename of the target name. + [joiner] + :: The abbreviated toolset tag being used to build the target. + [joiner] + :: Indication of a multi-threaded build. + [joiner] + :: Collective tag of the build runtime. + [joiner] + :: Short version tag taken from the given "version-feature" + in the build properties. Or if not present, the literal + value as the version number. + [joiner] + :: Direct lookup of the given property-name value in the + build properties. /property-name/ is a regular expression. + e.g. will match every toolset. + /otherwise/ + :: The literal value of the format argument. + + For example this format: + + boost_ + + Might return: + + boost_thread-vc80-mt-gd-1_33.dll, or + boost_regex-vc80-gd-1_33.dll + + The returned name also has the target type specific prefix and suffix which + puts it in a ready form to use as the value from a custom tag rule. + """ + assert(isinstance(format, list)) + assert(isinstance(name, str)) + assert(isinstance(target_type, str) or not type) + # assert(isinstance(prop_set, property_set.PropertySet)) + if type.is_derived(target_type, 'LIB'): + result = "" ; + for f in format: + grist = get_grist(f) + if grist == '': + result += os.path.basename(name) + elif grist == '': + result += join_tag(ungrist(f), + toolset_tag(name, target_type, prop_set)) + elif grist == '': + result += join_tag(ungrist(f), + threading_tag(name, target_type, prop_set)) + elif grist == '': + result += join_tag(ungrist(f), + runtime_tag(name, target_type, prop_set)) + elif grist.startswith('') + if not version: + version = key + version = __re_version.match(version) + result += join_tag(ungrist(f), version[1] + '_' + version[2]) + elif grist.startswith('') + p0 = None + for prop in prop_set.raw(): + match = property_re.match(prop) + if match: + p0 = match[1] + break + if p0: + p = prop_set.get('<' + p0 + '>') + if p: + assert(len(p) == 1) + result += join_tag(ungrist(f), p) + else: + result += ungrist(f) + + result = virtual_target.add_prefix_and_suffix( + ''.join(result), target_type, prop_set) + return result + +def join_tag(joiner, tag): + if not joiner: joiner = '-' + return joiner + tag + +__re_toolset_version = re.compile(r"(\d+)[.](\d*)") + +def toolset_tag(name, target_type, prop_set): + tag = '' + + properties = prop_set.raw() + tools = prop_set.get('') + assert(len(tools) == 0) + tools = tools[0] + if tools.startswith('borland'): tag += 'bcb' + elif tools.startswith('como'): tag += 'como' + elif tools.startswith('cw'): tag += 'cw' + elif tools.startswith('darwin'): tag += 'xgcc' + elif tools.startswith('edg'): tag += edg + elif tools.startswith('gcc'): + flavor = prop_set.get('') + ''.find + if flavor.find('mingw') != -1: + tag += 'mgw' + else: + tag += 'gcc' + elif tools == 'intel': + if prop_set.get('') == ['win']: + tag += 'iw' + else: + tag += 'il' + elif tools.startswith('kcc'): tag += 'kcc' + elif tools.startswith('kylix'): tag += 'bck' + #case metrowerks* : tag += cw ; + #case mingw* : tag += mgw ; + elif tools.startswith('mipspro'): tag += 'mp' + elif tools.startswith('msvc'): tag += 'vc' + elif tools.startswith('sun'): tag += 'sw' + elif tools.startswith('tru64cxx'): tag += 'tru' + elif tools.startswith('vacpp'): tag += 'xlc' + + for prop in properties: + match = __re_toolset_version.match(prop) + if(match): + version = match + break + version_string = None + # For historical reasons, vc6.0 and vc7.0 use different naming. + if tag == 'vc': + if version.group(1) == '6': + # Cancel minor version. + version_string = '6' + elif version.group(1) == '7' and version.group(2) == '0': + version_string = '7' + + # On intel, version is not added, because it does not matter and it's the + # version of vc used as backend that matters. Ideally, we'd encode the + # backend version but that would break compatibility with V1. + elif tag == 'iw': + version_string = '' + + # On borland, version is not added for compatibility with V1. + elif tag == 'bcb': + version_string = '' + + if version_string is None: + version = version.group(1) + version.group(2) + + tag += version + + return tag + + +def threading_tag(name, target_type, prop_set): + tag = '' + properties = prop_set.raw() + if 'multi' in properties: tag = 'mt' + + return tag + + +def runtime_tag(name, target_type, prop_set ): + tag = '' + + properties = prop_set.raw() + if 'static' in properties: tag += 's' + + # This is an ugly thing. In V1, there's a code to automatically detect which + # properties affect a target. So, if does not affect gcc + # toolset, the tag rules won't even see . Similar + # functionality in V2 is not implemented yet, so we just check for toolsets + # which are known to care about runtime debug. + if 'msvc' in properties \ + or 'stlport' in properties \ + or 'win' in properties: + if 'on' in properties: tag += 'g' + + if 'on' in properties: tag += 'y' + if 'debug' in properties: tag += 'd' + if 'stlport' in properties: tag += 'p' + if 'hostios' in properties: tag += 'n' + + return tag + + +## TODO: +##rule __test__ ( ) +##{ +## import assert ; +## +## local nl = " +##" ; +## +## local save-os = [ modules.peek os : .name ] ; +## +## modules.poke os : .name : LINUX ; +## +## assert.result "PATH=foo:bar:baz$(nl)export PATH$(nl)" +## : path-variable-setting-command PATH : foo bar baz ; +## +## assert.result "PATH=foo:bar:$PATH$(nl)export PATH$(nl)" +## : prepend-path-variable-command PATH : foo bar ; +## +## modules.poke os : .name : NT ; +## +## assert.result "set PATH=foo;bar;baz$(nl)" +## : path-variable-setting-command PATH : foo bar baz ; +## +## assert.result "set PATH=foo;bar;%PATH%$(nl)" +## : prepend-path-variable-command PATH : foo bar ; +## +## modules.poke os : .name : $(save-os) ; +##} + +def init(manager): + engine = manager.engine() + + engine.register_action("common.MkDir1-quick-fix-for-unix", 'mkdir -p "$(<)"') + engine.register_action("common.MkDir1-quick-fix-for-windows", 'if not exist "$(<)\\" mkdir "$(<)"') + + import b2.tools.make + import b2.build.alias + + global __RM, __CP, __IGNORE, __LN + # ported from trunk@47281 + if os_name() == 'NT': + __RM = 'del /f /q' + __CP = 'copy' + __IGNORE = '2>nul >nul & setlocal' + __LN = __CP + #if not __LN: + # __LN = CP + else: + __RM = 'rm -f' + __CP = 'cp' + __IGNORE = '' + __LN = 'ln' + + engine.register_action("common.Clean", __RM + ' "$(>)"', + flags=['piecemeal', 'together', 'existing']) + engine.register_action("common.copy", __CP + ' "$(>)" "$(<)"') + engine.register_action("common.RmTemps", __RM + ' "$(>)" ' + __IGNORE, + flags=['quietly', 'updated', 'piecemeal', 'together']) + + engine.register_action("common.hard-link", + __RM + ' "$(<)" 2$(NULL_OUT) $(NULL_OUT)' + os.linesep + + __LN + ' "$(>)" "$(<)" $(NULL_OUT)') diff --git a/v2/tools/darwin.py b/v2/tools/darwin.py new file mode 100644 index 000000000..c29196060 --- /dev/null +++ b/v2/tools/darwin.py @@ -0,0 +1,57 @@ +# Copyright (C) Christopher Currie 2003. Permission to copy, use, +# modify, sell and distribute this software is granted provided this +# copyright notice appears in all copies. This software is provided +# "as is" without express or implied warranty, and with no claim as to +# its suitability for any purpose. + +# Please see http://article.gmane.org/gmane.comp.lib.boost.build/3389/ +# for explanation why it's a separate toolset. + +import common, gcc, builtin +from b2.build import feature, toolset, type, action, generators +from b2.util.utility import * + +toolset.register ('darwin') + +toolset.inherit_generators ('darwin', [], 'gcc') +toolset.inherit_flags ('darwin', 'gcc') +toolset.inherit_rules ('darwin', 'gcc') + +def init (version = None, command = None, options = None): + options = to_seq (options) + + condition = common.check_init_parameters ('darwin', None, ('version', version)) + + command = common.get_invocation_command ('darwin', 'g++', command) + + common.handle_options ('darwin', condition, command, options) + + gcc.init_link_flags ('darwin', 'darwin', condition) + +# Darwin has a different shared library suffix +type.set_generated_target_suffix ('SHARED_LIB', ['darwin'], 'dylib') + +# we need to be able to tell the type of .dylib files +type.register_suffixes ('dylib', 'SHARED_LIB') + +feature.feature ('framework', [], ['free']) + +toolset.flags ('darwin.compile', 'OPTIONS', 'shared', ['-dynamic']) +toolset.flags ('darwin.compile', 'OPTIONS', None, ['-Wno-long-double', '-no-cpp-precomp']) +toolset.flags ('darwin.compile.c++', 'OPTIONS', None, ['-fcoalesce-templates']) + +toolset.flags ('darwin.link', 'FRAMEWORK', '') + +# This is flag is useful for debugging the link step +# uncomment to see what libtool is doing under the hood +# toolset.flags ('darwin.link.dll', 'OPTIONS', None, '[-Wl,-v']) + +action.register ('darwin.compile.cpp', None, ['$(CONFIG_COMMAND) $(ST_OPTIONS) -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) -framework$(_)$(FRAMEWORK) $(OPTIONS)']) + +# TODO: how to set 'bind LIBRARIES'? +action.register ('darwin.link.dll', None, ['$(CONFIG_COMMAND) -dynamiclib -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) -framework$(_)$(FRAMEWORK) $(OPTIONS)']) + +def darwin_archive (manager, targets, sources, properties): + pass + +action.register ('darwin.archive', darwin_archive, ['ar -c -r -s $(ARFLAGS) "$(<:T)" "$(>:T)"']) diff --git a/v2/tools/gcc.py b/v2/tools/gcc.py new file mode 100644 index 000000000..2ec33075d --- /dev/null +++ b/v2/tools/gcc.py @@ -0,0 +1,796 @@ +# Status: being ported by Steven Watanabe +# Base revision: 47077 +# TODO: common.jam needs to be ported +# TODO: generators.jam needs to have register_c_compiler. +# +# Copyright 2001 David Abrahams. +# Copyright 2002-2006 Rene Rivera. +# Copyright 2002-2003 Vladimir Prus. +# Copyright (c) 2005 Reece H. Dunn. +# Copyright 2006 Ilya Sokolov. +# Copyright 2007 Roland Schwarz +# Copyright 2007 Boris Gubenko. +# Copyright 2008 Steven Watanabe +# +# 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) + +import os +import subprocess +import re + +import bjam + +from b2.tools import unix, common, rc, pch, builtin +from b2.build import feature, type, toolset, generators +from b2.util.utility import os_name, on_windows +from b2.manager import get_manager +from b2.build.generators import Generator +from b2.build.toolset import flags +from b2.util.utility import to_seq + +__debug = None + +def debug(): + global __debug + if __debug is None: + __debug = "--debug-configuration" in bjam.variable("ARGV") + return __debug + +feature.extend('toolset', ['gcc']) + + +toolset.inherit_generators('gcc', [], 'unix', ['unix.link', 'unix.link.dll']) +toolset.inherit_flags('gcc', 'unix') +toolset.inherit_rules('gcc', 'unix') + +generators.override('gcc.prebuilt', 'builtin.prebuilt') +generators.override('gcc.searched-lib-generator', 'searched-lib-generator') + +# Target naming is determined by types/lib.jam and the settings below this +# comment. +# +# On *nix: +# libxxx.a static library +# libxxx.so shared library +# +# On windows (mingw): +# libxxx.lib static library +# xxx.dll DLL +# xxx.lib import library +# +# On windows (cygwin) i.e. cygwin +# libxxx.a static library +# xxx.dll DLL +# libxxx.dll.a import library +# +# Note: user can always override by using the @rule +# This settings have been choosen, so that mingw +# is in line with msvc naming conventions. For +# cygwin the cygwin naming convention has been choosen. + +# Make the "o" suffix used for gcc toolset on all +# platforms +type.set_generated_target_suffix('OBJ', ['gcc'], 'o') +type.set_generated_target_suffix('STATIC_LIB', ['gcc', 'cygwin'], 'a') + +type.set_generated_target_suffix('IMPORT_LIB', ['gcc', 'cygwin'], 'dll.a') +type.set_generated_target_prefix('IMPORT_LIB', ['gcc', 'cygwin'], 'lib') + +__machine_match = re.compile('^([^ ]+)') +__version_match = re.compile('^([0-9.]+)') + +def init(version = None, command = None, options = None): + """ + Initializes the gcc toolset for the given version. If necessary, command may + be used to specify where the compiler is located. The parameter 'options' is a + space-delimited list of options, each one specified as + option-value. Valid option names are: cxxflags, linkflags and + linker-type. Accepted linker-type values are gnu, darwin, osf, hpux or sun + and the default value will be selected based on the current OS. + Example: + using gcc : 3.4 : : foo bar sun ; + """ + + options = to_seq(options) + command = to_seq(command) + + # Information about the gcc command... + # The command. + command = to_seq(common.get_invocation_command('gcc', 'g++', command)) + # The root directory of the tool install. + root = feature.get_values('', options) ; + # The bin directory where to find the command to execute. + bin = None + # The flavor of compiler. + flavor = feature.get_values('', options) + # Autodetect the root and bin dir if not given. + if command: + if not bin: + bin = common.get_absolute_tool_path(command[-1]) + if not root: + root = os.path.dirname(bin) + # Autodetect the version and flavor if not given. + if command: + machine_info = subprocess.Popen(command + ['-dumpmachine'], stdout=subprocess.PIPE).communicate()[0] + machine = __machine_match.search(machine_info).group(1) + + version_info = subprocess.Popen(command + ['-dumpversion'], stdout=subprocess.PIPE).communicate()[0] + version = __version_match.search(version_info).group(1) + if not flavor and machine.find('mingw') != -1: + flavor = 'mingw' + + condition = None + if flavor: + condition = common.check_init_parameters('gcc', None, + ('version', version), + ('flavor', flavor)) + else: + condition = common.check_init_parameters('gcc', None, + ('version', version)) + + if command: + command = command[0] + + common.handle_options('gcc', condition, command, options) + + linker = feature.get_values('', options) + if not linker: + if os_name() == 'OSF': + linker = 'osf' + elif os_name() == 'HPUX': + linker = 'hpux' ; + else: + linker = 'gnu' + + init_link_flags('gcc', linker, condition) + + # If gcc is installed in non-standard location, we'd need to add + # LD_LIBRARY_PATH when running programs created with it (for unit-test/run + # rules). + if command: + # On multilib 64-bit boxes, there are both 32-bit and 64-bit libraries + # and all must be added to LD_LIBRARY_PATH. The linker will pick the + # right onces. Note that we don't provide a clean way to build 32-bit + # binary with 64-bit compiler, but user can always pass -m32 manually. + lib_path = [os.path.join(root, 'bin'), + os.path.join(root, 'lib'), + os.path.join(root, 'lib32'), + os.path.join(root, 'lib64')] + if debug(): + print 'notice: using gcc libraries ::', condition, '::', lib_path + toolset.flags('gcc.link', 'RUN_PATH', condition, lib_path) + + # If it's not a system gcc install we should adjust the various programs as + # needed to prefer using the install specific versions. This is essential + # for correct use of MinGW and for cross-compiling. + + # - The archive builder. + archiver = common.get_invocation_command('gcc', + 'ar', feature.get_values('', options), [bin], path_last=True) + toolset.flags('gcc.archive', '.AR', condition, [archiver]) + if debug(): + print 'notice: using gcc archiver ::', condition, '::', archiver + + # - The resource compiler. + rc_command = common.get_invocation_command_nodefault('gcc', + 'windres', feature.get_values('', options), [bin], path_last=True) + rc_type = feature.get_values('', options) + + if not rc_type: + rc_type = 'windres' + + if not rc_command: + # If we can't find an RC compiler we fallback to a null RC compiler that + # creates empty object files. This allows the same Jamfiles to work + # across the board. The null RC uses the assembler to create the empty + # objects, so configure that. + rc_command = common.get_invocation_command('gcc', 'as', [], [bin], path_last=True) + rc_type = 'null' + rc.configure(rc_command, condition, '' + rc_type) + +###if [ os.name ] = NT +###{ +### # This causes single-line command invocation to not go through .bat files, +### # thus avoiding command-line length limitations. +### JAMSHELL = % ; +###} + +#FIXME: when register_c_compiler is moved to +# generators, these should be updated +builtin.register_c_compiler('gcc.compile.c++', ['CPP'], ['OBJ'], ['gcc']) +builtin.register_c_compiler('gcc.compile.c', ['C'], ['OBJ'], ['gcc']) +builtin.register_c_compiler('gcc.compile.asm', ['ASM'], ['OBJ'], ['gcc']) + +# pch support + +# The compiler looks for a precompiled header in each directory just before it +# looks for the include file in that directory. The name searched for is the +# name specified in the #include directive with ".gch" suffix appended. The +# logic in gcc-pch-generator will make sure that BASE_PCH suffix is appended to +# full name of the header. + +type.set_generated_target_suffix('PCH', ['gcc'], 'gch') + +# GCC-specific pch generator. +class GccPchGenerator(pch.PchGenerator): + + # Inherit the __init__ method + + def run_pch(self, project, name, prop_set, sources): + # Find the header in sources. Ignore any CPP sources. + header = None + for s in sources: + if type.is_derived(s.type, 'H'): + header = s + + # Error handling: Base header file name should be the same as the base + # precompiled header name. + header_name = header.name + header_basename = os.path.basename(header_name).rsplit('.', 1)[0] + if header_basename != name: + location = project.project_module + ###FIXME: + raise Exception() + ### errors.user-error "in" $(location)": pch target name `"$(name)"' should be the same as the base name of header file `"$(header-name)"'" ; + + pch_file = Generator.run(self, project, name, prop_set, [header]) + + # return result of base class and pch-file property as usage-requirements + # FIXME: what about multiple results from generator.run? + return (property_set.create('' + pch_file[0], '-Winvalid-pch'), + pch_file) + + # Calls the base version specifying source's name as the name of the created + # target. As result, the PCH will be named whatever.hpp.gch, and not + # whatever.gch. + def generated_targets(self, sources, prop_set, project, name = None): + name = sources[0].name + return Generator.generated_targets(self, sources, + prop_set, project, name) + +# Note: the 'H' source type will catch both '.h' header and '.hpp' header. The +# latter have HPP type, but HPP type is derived from H. The type of compilation +# is determined entirely by the destination type. +generators.register(GccPchGenerator('gcc.compile.c.pch', False, ['H'], ['C_PCH'], ['on', 'gcc' ])) +generators.register(GccPchGenerator('gcc.compile.c++.pch', False, ['H'], ['CPP_PCH'], ['on', 'gcc' ])) + +# Override default do-nothing generators. +generators.override('gcc.compile.c.pch', 'pch.default-c-pch-generator') +generators.override('gcc.compile.c++.pch', 'pch.default-cpp-pch-generator') + +flags('gcc.compile', 'PCH_FILE', ['on'], ['']) + +# Declare flags and action for compilation +flags('gcc.compile', 'OPTIONS', ['off'], ['-O0']) +flags('gcc.compile', 'OPTIONS', ['speed'], ['-O3']) +flags('gcc.compile', 'OPTIONS', ['space'], ['-Os']) + +flags('gcc.compile', 'OPTIONS', ['off'], ['-fno-inline']) +flags('gcc.compile', 'OPTIONS', ['on'], ['-Wno-inline']) +flags('gcc.compile', 'OPTIONS', ['full'], ['-finline-functions', '-Wno-inline']) + +flags('gcc.compile', 'OPTIONS', ['off'], ['-w']) +flags('gcc.compile', 'OPTIONS', ['on'], ['-Wall']) +flags('gcc.compile', 'OPTIONS', ['all'], ['-Wall', '-pedantic']) +flags('gcc.compile', 'OPTIONS', ['on'], ['-Werror']) + +flags('gcc.compile', 'OPTIONS', ['on'], ['-g']) +flags('gcc.compile', 'OPTIONS', ['on'], ['-pg']) +flags('gcc.compile', 'OPTIONS', ['off'], ['-fno-rtti']) + +# On cygwin and mingw, gcc generates position independent code by default, and +# warns if -fPIC is specified. This might not be the right way of checking if +# we're using cygwin. For example, it's possible to run cygwin gcc from NT +# shell, or using crosscompiling. But we'll solve that problem when it's time. +# In that case we'll just add another parameter to 'init' and move this login +# inside 'init'. +if not os_name () in ['CYGWIN', 'NT']: + print "osname:", os_name() + # This logic will add -fPIC for all compilations: + # + # lib a : a.cpp b ; + # obj b : b.cpp ; + # exe c : c.cpp a d ; + # obj d : d.cpp ; + # + # This all is fine, except that 'd' will be compiled with -fPIC even though + # it's not needed, as 'd' is used only in exe. However, it's hard to detect + # where a target is going to be used. Alternative, we can set -fPIC only + # when main target type is LIB but than 'b' will be compiled without -fPIC. + # In x86-64 that will lead to link errors. So, compile everything with + # -fPIC. + # + # Yet another alternative would be to create propagated + # feature, and set it when building shared libraries, but that's hard to + # implement and will increase target path length even more. + flags('gcc.compile', 'OPTIONS', ['shared'], ['-fPIC']) + +if os_name() != 'NT' and os_name() != 'OSF' and os_name() != 'HPUX': + # OSF does have an option called -soname but it doesn't seem to work as + # expected, therefore it has been disabled. + HAVE_SONAME = '' + SONAME_OPTION = '-h' + + +flags('gcc.compile', 'USER_OPTIONS', [], ['']) +flags('gcc.compile.c++', 'USER_OPTIONS',[], ['']) +flags('gcc.compile', 'DEFINES', [], ['']) +flags('gcc.compile', 'INCLUDES', [], ['']) + +engine = get_manager().engine() + +engine.register_action('gcc.compile.c++.pch', + '"$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"') + +engine.register_action('gcc.compile.c.pch', + '"$(CONFIG_COMMAND)" -x c-header $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"') + + +def gcc_compile_cpp(targets, sources, properties): + # Some extensions are compiled as C++ by default. For others, we need to + # pass -x c++. We could always pass -x c++ but distcc does not work with it. + extension = os.path.splitext (sources [0]) [1] + lang = '' + if not extension in ['.cc', '.cp', '.cxx', '.cpp', '.c++', '.C']: + lang = '-x c++' + get_manager().engine().set_target_variable (targets, 'LANG', lang) + engine.add_dependency(targets, bjam.call('get-target-variable', targets, 'PCH_FILE')) + +def gcc_compile_c(targets, sources, properties): + engine = get_manager().engine() + # If we use the name g++ then default file suffix -> language mapping does + # not work. So have to pass -x option. Maybe, we can work around this by + # allowing the user to specify both C and C++ compiler names. + #if $(>:S) != .c + #{ + engine.set_target_variable (targets, 'LANG', '-x c') + #} + engine.add_dependency(targets, bjam.call('get-target-variable', targets, 'PCH_FILE')) + +engine.register_action( + 'gcc.compile.c++', + '"$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-128 $(OPTIONS) ' + + '$(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" ' + + '-c -o "$(<:W)" "$(>:W)"', + function=gcc_compile_cpp, + bound_list=['PCH_FILE']) + +engine.register_action( + 'gcc.compile.c', + '"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) ' + + '-I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)"', + function=gcc_compile_c, + bound_list=['PCH_FILE']) + +def gcc_compile_asm(targets, sources, properties): + get_manager().engine().set_target_variable(targets, 'LANG', '-x assembler-with-cpp') + +engine.register_action( + 'gcc.compile.asm', + '"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"', + function=gcc_compile_asm) + + +class GccLinkingGenerator(unix.UnixLinkingGenerator): + """ + The class which check that we don't try to use the static + property while creating or using shared library, since it's not supported by + gcc/libc. + """ + def run(self, project, name, prop_set, sources): + # TODO: Replace this with the use of a target-os property. + + no_static_link = False + if bjam.variable('UNIX'): + no_static_link = True; + ##FIXME: what does this mean? +## { +## switch [ modules.peek : JAMUNAME ] +## { +## case * : no-static-link = true ; +## } +## } + + properties = prop_set.raw() + reason = None + if no_static_link and 'static' in properties: + if 'shared' in properties: + reason = "On gcc, DLL can't be build with 'static'." + elif type.is_derived(self.target_types[0], 'EXE'): + for s in sources: + source_type = s.type() + if source_type and type.is_derived(source_type, 'SHARED_LIB'): + reason = "On gcc, using DLLS together with the " +\ + "static options is not possible " + if reason: + print 'warning:', reason + print 'warning:',\ + "It is suggested to use 'static' together",\ + "with 'static'." ; + return + else: + generated_targets = unix.UnixLinkingGenerator.run(self, project, + name, prop_set, sources) + return generated_targets + +if on_windows(): + flags('gcc.link.dll', '.IMPLIB-COMMAND', [], ['-Wl,--out-implib,']) + generators.register( + GccLinkingGenerator('gcc.link', True, + ['OBJ', 'SEARCHED_LIB', 'STATIC_LIB', 'IMPORT_LIB'], + [ 'EXE' ], + [ 'gcc' ])) + generators.register( + GccLinkingGenerator('gcc.link.dll', True, + ['OBJ', 'SEARCHED_LIB', 'STATIC_LIB', 'IMPORT_LIB'], + ['IMPORT_LIB', 'SHARED_LIB'], + ['gcc'])) +else: + generators.register( + GccLinkingGenerator('gcc.link', True, + ['LIB', 'OBJ'], + ['EXE'], + ['gcc'])) + generators.register( + GccLinkingGenerator('gcc.link.dll', True, + ['LIB', 'OBJ'], + ['SHARED_LIB'], + ['gcc'])) + +# Declare flags for linking. +# First, the common flags. +flags('gcc.link', 'OPTIONS', ['on'], ['-g']) +flags('gcc.link', 'OPTIONS', ['on'], ['-pg']) +flags('gcc.link', 'USER_OPTIONS', [], ['']) +flags('gcc.link', 'LINKPATH', [], ['']) +flags('gcc.link', 'FINDLIBS-ST', [], ['']) +flags('gcc.link', 'FINDLIBS-SA', [], ['']) +flags('gcc.link', 'LIBRARIES', [], ['']) + +# For static we made sure there are no dynamic libraries in the +# link. On HP-UX not all system libraries exist as archived libraries (for +# example, there is no libunwind.a), so, on this platform, the -static option +# cannot be specified. +if os_name() != 'HPUX': + flags('gcc.link', 'OPTIONS', ['static'], ['-static']) + +# Now, the vendor specific flags. +# The parameter linker can be either gnu, darwin, osf, hpux or sun. +def init_link_flags(toolset, linker, condition): + """ + Now, the vendor specific flags. + The parameter linker can be either gnu, darwin, osf, hpux or sun. + """ + toolset_link = toolset + '.link' + if linker == 'gnu': + # Strip the binary when no debugging is needed. We use --strip-all flag + # as opposed to -s since icc (intel's compiler) is generally + # option-compatible with and inherits from the gcc toolset, but does not + # support -s. + + # FIXME: what does unchecked translate to? + flags(toolset_link, 'OPTIONS', map(lambda x: x + '/off', condition), ['-Wl,--strip-all']) # : unchecked ; + flags(toolset_link, 'RPATH', condition, ['']) # : unchecked ; + flags(toolset_link, 'RPATH_LINK', condition, ['']) # : unchecked ; + flags(toolset_link, 'START-GROUP', condition, ['-Wl,--start-group'])# : unchecked ; + flags(toolset_link, 'END-GROUP', condition, ['-Wl,--end-group']) # : unchecked ; + + # gnu ld has the ability to change the search behaviour for libraries + # referenced by -l switch. These modifiers are -Bstatic and -Bdynamic + # and change search for -l switches that follow them. The following list + # shows the tried variants. + # The search stops at the first variant that has a match. + # *nix: -Bstatic -lxxx + # libxxx.a + # + # *nix: -Bdynamic -lxxx + # libxxx.so + # libxxx.a + # + # windows (mingw,cygwin) -Bstatic -lxxx + # libxxx.a + # xxx.lib + # + # windows (mingw,cygwin) -Bdynamic -lxxx + # libxxx.dll.a + # xxx.dll.a + # libxxx.a + # xxx.lib + # cygxxx.dll (*) + # libxxx.dll + # xxx.dll + # libxxx.a + # + # (*) This is for cygwin + # Please note that -Bstatic and -Bdynamic are not a guarantee that a + # static or dynamic lib indeed gets linked in. The switches only change + # search patterns! + + # On *nix mixing shared libs with static runtime is not a good idea. + flags(toolset_link, 'FINDLIBS-ST-PFX', + map(lambda x: x + '/shared', condition), + ['-Wl,-Bstatic']) # : unchecked ; + flags(toolset_link, 'FINDLIBS-SA-PFX', + map(lambda x: x + '/shared', condition), + ['-Wl,-Bdynamic']) # : unchecked ; + + # On windows allow mixing of static and dynamic libs with static + # runtime. + flags(toolset_link, 'FINDLIBS-ST-PFX', + map(lambda x: x + '/static/windows', condition), + ['-Wl,-Bstatic']) # : unchecked ; + flags(toolset_link, 'FINDLIBS-SA-PFX', + map(lambda x: x + '/static/windows', condition), + ['-Wl,-Bdynamic']) # : unchecked ; + flags(toolset_link, 'OPTIONS', + map(lambda x: x + '/static/windows', condition), + ['-Wl,-Bstatic']) # : unchecked ; + + elif linker == 'darwin': + # On Darwin, the -s option to ld does not work unless we pass -static, + # and passing -static unconditionally is a bad idea. So, don't pass -s. + # at all, darwin.jam will use separate 'strip' invocation. + flags(toolset_link, 'RPATH', condition, ['']) # : unchecked ; + flags(toolset_link, 'RPATH_LINK', condition, ['']) # : unchecked ; + + elif linker == 'osf': + # No --strip-all, just -s. + flags(toolset_link, 'OPTIONS', map(lambda x: x + '/off', condition), ['-Wl,-s']) + # : unchecked ; + flags(toolset_link, 'RPATH', condition, ['']) # : unchecked ; + # This does not supports -R. + flags(toolset_link, 'RPATH_OPTION', condition, ['-rpath']) # : unchecked ; + # -rpath-link is not supported at all. + + elif linker == 'sun': + flags(toolset_link, 'OPTIONS', map(lambda x: x + '/off', condition), ['-Wl,-s']) + # : unchecked ; + flags(toolset_link, 'RPATH', condition, ['']) # : unchecked ; + # Solaris linker does not have a separate -rpath-link, but allows to use + # -L for the same purpose. + flags(toolset_link, 'LINKPATH', condition, ['']) # : unchecked ; + + # This permits shared libraries with non-PIC code on Solaris. + # VP, 2004/09/07: Now that we have -fPIC hardcode in link.dll, the + # following is not needed. Whether -fPIC should be hardcoded, is a + # separate question. + # AH, 2004/10/16: it is still necessary because some tests link against + # static libraries that were compiled without PIC. + flags(toolset_link, 'OPTIONS', map(lambda x: x + '/shared', condition), ['-mimpure-text']) + # : unchecked ; + + elif linker == 'hpux': + flags(toolset_link, 'OPTIONS', map(lambda x: x + '/off', condition), + ['-Wl,-s']) # : unchecked ; + flags(toolset_link, 'OPTIONS', map(lambda x: x + '/shared', condition), + ['-fPIC']) # : unchecked ; + + else: + # FIXME: + errors.user_error( + "$(toolset) initialization: invalid linker '$(linker)' " + + "The value '$(linker)' specified for is not recognized. " + + "Possible values are 'gnu', 'darwin', 'osf', 'hpux' or 'sun'") + +# Declare actions for linking. +def gcc_link(targets, sources, properties): + engine = get_manager().engine() + engine.set_target_variable(targets, 'SPACE', ' ') + # Serialize execution of the 'link' action, since running N links in + # parallel is just slower. For now, serialize only gcc links, it might be a + # good idea to serialize all links. + engine.set_target_variable(targets, 'JAM_SEMAPHORE', 'gcc-link-semaphore') + +engine.register_action( + 'gcc.link', + '"$(CONFIG_COMMAND)" -L"$(LINKPATH)" ' + + '-Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,"$(RPATH)" ' + + '-Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" ' + + '$(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) ' + + '-l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) ' + + '$(OPTIONS) $(USER_OPTIONS)', + function=gcc_link, + bound_list=['LIBRARIES']) + +# Default value. Mostly for the sake of intel-linux that inherits from gcc, but +# does not have the same logic to set the .AR variable. We can put the same +# logic in intel-linux, but that's hardly worth the trouble as on Linux, 'ar' is +# always available. +__AR = 'ar' + +flags('gcc.archive', 'AROPTIONS', [], ['']) + +def gcc_archive(targets, sources, properties): + # Always remove archive and start again. Here's rationale from + # + # Andre Hentz: + # + # I had a file, say a1.c, that was included into liba.a. I moved a1.c to + # a2.c, updated my Jamfiles and rebuilt. My program was crashing with absurd + # errors. After some debugging I traced it back to the fact that a1.o was + # *still* in liba.a + # + # Rene Rivera: + # + # Originally removing the archive was done by splicing an RM onto the + # archive action. That makes archives fail to build on NT when they have + # many files because it will no longer execute the action directly and blow + # the line length limit. Instead we remove the file in a different action, + # just before building the archive. + clean = targets[0] + '(clean)' + bjam.call('TEMPORARY', clean) + bjam.call('NOCARE', clean) + engine = get_manager().engine() + engine.set_target_variable('LOCATE', clean, bjam.call('get-target-variable', targets, 'LOCATE')) + engine.add_dependency(clean, sources) + engine.add_dependency(targets, clean) + engine.set_update_action('common.RmTemps', clean, targets, None) + +# Declare action for creating static libraries. +# The letter 'r' means to add files to the archive with replacement. Since we +# remove archive, we don't care about replacement, but there's no option "add +# without replacement". +# The letter 'c' suppresses the warning in case the archive does not exists yet. +# That warning is produced only on some platforms, for whatever reasons. +engine.register_action('gcc.archive', + '"$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)"', + function=gcc_archive, + flags=['piecemeal']) + +def gcc_link_dll(targets, sources, properties): + engine = get_manager().engine() + engine.set_target_variable(targets, 'SPACE', ' ') + engine.set_target_variable(targets, 'JAM_SEMAPHORE', 'gcc-link-semaphore') + +engine.register_action( + 'gcc.link.dll', + # Differ from 'link' above only by -shared. + '"$(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)" $(FINDLIBS-ST-PFX) ' + + '-l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) ' + + '$(OPTIONS) $(USER_OPTIONS)', + function = gcc_link_dll, + bound_list=['LIBRARIES']) + +# Set up threading support. It's somewhat contrived, so perform it at the end, +# to avoid cluttering other code. + +if on_windows(): + flags('gcc', 'OPTIONS', ['multi'], ['-mthreads']) +elif bjam.variable('UNIX'): + jamuname = bjam.variable('JAMUNAME') + host_os_name = jamuname[0] + if host_os_name.startswith('SunOS'): + flags('gcc', 'OPTIONS', ['multi'], ['-pthreads']) + flags('gcc', 'FINDLIBS-SA', [], ['rt']) + elif host_os_name == 'BeOS': + # BeOS has no threading options, don't set anything here. + pass + elif host_os_name.endswith('BSD'): + flags('gcc', 'OPTIONS', ['multi'], ['-pthread']) + # there is no -lrt on BSD + elif host_os_name == 'DragonFly': + flags('gcc', 'OPTIONS', ['multi'], ['-pthread']) + # there is no -lrt on BSD - DragonFly is a FreeBSD variant, + # which anoyingly doesn't say it's a *BSD. + elif host_os_name == 'IRIX': + # gcc on IRIX does not support multi-threading, don't set anything here. + pass + elif host_os_name == 'Darwin': + # Darwin has no threading options, don't set anything here. + pass + else: + flags('gcc', 'OPTIONS', ['multi'], ['-pthread']) + flags('gcc', 'FINDLIBS-SA', [], ['rt']) + +def cpu_flags(toolset, variable, architecture, instruction_set, values, default=None): + #FIXME: for some reason this fails. Probably out of date feature code +## if default: +## flags(toolset, variable, +## ['' + architecture + '/'], +## values) + flags(toolset, variable, + #FIXME: same as above + [##'/' + instruction_set, + '' + architecture + '/' + instruction_set], + values) + +# Set architecture/instruction-set options. +# +# x86 and compatible +flags('gcc', 'OPTIONS', ['x86/32'], ['-m32']) +flags('gcc', 'OPTIONS', ['x86/64'], ['-m64']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'i386', ['-march=i386'], default=True) +cpu_flags('gcc', 'OPTIONS', 'x86', 'i486', ['-march=i486']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'i586', ['-march=i586']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'i686', ['-march=i686']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium', ['-march=pentium']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium-mmx', ['-march=pentium-mmx']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentiumpro', ['-march=pentiumpro']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium2', ['-march=pentium2']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium3', ['-march=pentium3']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium3m', ['-march=pentium3m']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium-m', ['-march=pentium-m']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium4', ['-march=pentium4']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium4m', ['-march=pentium4m']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'prescott', ['-march=prescott']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'nocona', ['-march=nocona']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'k6', ['-march=k6']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'k6-2', ['-march=k6-2']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'k6-3', ['-march=k6-3']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon', ['-march=athlon']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-tbird', ['-march=athlon-tbird']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-4', ['-march=athlon-4']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-xp', ['-march=athlon-xp']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-mp', ['-march=athlon-mp']) +## +cpu_flags('gcc', 'OPTIONS', 'x86', 'k8', ['-march=k8']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'opteron', ['-march=opteron']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon64', ['-march=athlon64']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-fx', ['-march=athlon-fx']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'winchip-c6', ['-march=winchip-c6']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'winchip2', ['-march=winchip2']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'c3', ['-march=c3']) +cpu_flags('gcc', 'OPTIONS', 'x86', 'c3-2', ['-march=c3-2']) +# Sparc +flags('gcc', 'OPTIONS', ['sparc/32'], ['-m32']) +flags('gcc', 'OPTIONS', ['sparc/64'], ['-m64']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'c3', ['-mcpu=c3'], default=True) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'v7', ['-mcpu=v7']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'cypress', ['-mcpu=cypress']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'v8', ['-mcpu=v8']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'supersparc', ['-mcpu=supersparc']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'sparclite', ['-mcpu=sparclite']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'hypersparc', ['-mcpu=hypersparc']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'sparclite86x', ['-mcpu=sparclite86x']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'f930', ['-mcpu=f930']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'f934', ['-mcpu=f934']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'sparclet', ['-mcpu=sparclet']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'tsc701', ['-mcpu=tsc701']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'v9', ['-mcpu=v9']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'ultrasparc', ['-mcpu=ultrasparc']) +cpu_flags('gcc', 'OPTIONS', 'sparc', 'ultrasparc3', ['-mcpu=ultrasparc3']) +# RS/6000 & PowerPC +flags('gcc', 'OPTIONS', ['power/32'], ['-m32']) +flags('gcc', 'OPTIONS', ['power/64'], ['-m64']) +cpu_flags('gcc', 'OPTIONS', 'power', '403', ['-mcpu=403']) +cpu_flags('gcc', 'OPTIONS', 'power', '505', ['-mcpu=505']) +cpu_flags('gcc', 'OPTIONS', 'power', '601', ['-mcpu=601']) +cpu_flags('gcc', 'OPTIONS', 'power', '602', ['-mcpu=602']) +cpu_flags('gcc', 'OPTIONS', 'power', '603', ['-mcpu=603']) +cpu_flags('gcc', 'OPTIONS', 'power', '603e', ['-mcpu=603e']) +cpu_flags('gcc', 'OPTIONS', 'power', '604', ['-mcpu=604']) +cpu_flags('gcc', 'OPTIONS', 'power', '604e', ['-mcpu=604e']) +cpu_flags('gcc', 'OPTIONS', 'power', '620', ['-mcpu=620']) +cpu_flags('gcc', 'OPTIONS', 'power', '630', ['-mcpu=630']) +cpu_flags('gcc', 'OPTIONS', 'power', '740', ['-mcpu=740']) +cpu_flags('gcc', 'OPTIONS', 'power', '7400', ['-mcpu=7400']) +cpu_flags('gcc', 'OPTIONS', 'power', '7450', ['-mcpu=7450']) +cpu_flags('gcc', 'OPTIONS', 'power', '750', ['-mcpu=750']) +cpu_flags('gcc', 'OPTIONS', 'power', '801', ['-mcpu=801']) +cpu_flags('gcc', 'OPTIONS', 'power', '821', ['-mcpu=821']) +cpu_flags('gcc', 'OPTIONS', 'power', '823', ['-mcpu=823']) +cpu_flags('gcc', 'OPTIONS', 'power', '860', ['-mcpu=860']) +cpu_flags('gcc', 'OPTIONS', 'power', '970', ['-mcpu=970']) +cpu_flags('gcc', 'OPTIONS', 'power', '8540', ['-mcpu=8540']) +cpu_flags('gcc', 'OPTIONS', 'power', 'power', ['-mcpu=power']) +cpu_flags('gcc', 'OPTIONS', 'power', 'power2', ['-mcpu=power2']) +cpu_flags('gcc', 'OPTIONS', 'power', 'power3', ['-mcpu=power3']) +cpu_flags('gcc', 'OPTIONS', 'power', 'power4', ['-mcpu=power4']) +cpu_flags('gcc', 'OPTIONS', 'power', 'power5', ['-mcpu=power5']) +cpu_flags('gcc', 'OPTIONS', 'power', 'powerpc', ['-mcpu=powerpc']) +cpu_flags('gcc', 'OPTIONS', 'power', 'powerpc64', ['-mcpu=powerpc64']) +cpu_flags('gcc', 'OPTIONS', 'power', 'rios', ['-mcpu=rios']) +cpu_flags('gcc', 'OPTIONS', 'power', 'rios1', ['-mcpu=rios1']) +cpu_flags('gcc', 'OPTIONS', 'power', 'rios2', ['-mcpu=rios2']) +cpu_flags('gcc', 'OPTIONS', 'power', 'rsc', ['-mcpu=rsc']) +cpu_flags('gcc', 'OPTIONS', 'power', 'rs64a', ['-mcpu=rs64']) +# AIX variant of RS/6000 & PowerPC +flags('gcc', 'OPTIONS', ['power/32/aix'], ['-maix32']) +flags('gcc', 'OPTIONS', ['power/64/aix'], ['-maix64']) +flags('gcc', 'AROPTIONS', ['power/64/aix'], ['-X 64']) diff --git a/v2/tools/make.py b/v2/tools/make.py new file mode 100644 index 000000000..18dae4894 --- /dev/null +++ b/v2/tools/make.py @@ -0,0 +1,55 @@ +# Status: being ported by Vladimir Prus + +# Copyright 2003 Dave Abrahams +# Copyright 2003 Douglas Gregor +# Copyright 2006 Rene Rivera +# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# This module defines the 'make' main target rule. + +from b2.build.targets import BasicTarget +from b2.build.virtual_target import Action, FileTarget +from b2.build import type +from b2.manager import get_manager +import b2.build.property_set + +class MakeTarget(BasicTarget): + + def construct(self, name, source_targets, property_set): + + action_name = property_set.get("")[0] + + action = Action(get_manager(), source_targets, action_name, property_set) + # FIXME: type.type uses global data. + target = FileTarget(self.name(), 1, type.type(self.name()), + self.project(), action) + return [ b2.build.property_set.empty(), + [self.project().manager().virtual_targets().register(target)]] + +def make (target_name, sources, generating_rule, + requirements=None, usage_requirements=None): + + target_name = target_name[0] + generating_rule = generating_rule[0] + + if not requirements: + requirements = [] + + requirements.append("%s" % generating_rule) + m = get_manager() + targets = m.targets() + project = m.projects().current() + engine = m.engine() + engine.register_bjam_action(generating_rule) + + targets.main_target_alternative(MakeTarget( + target_name, project, + targets.main_target_sources(sources, target_name), + targets.main_target_requirements(requirements, project), + targets.main_target_default_build([], project), + targets.main_target_usage_requirements(usage_requirements or [], project))) + +get_manager().projects().add_rule("make", make) + diff --git a/v2/tools/pch.py b/v2/tools/pch.py new file mode 100644 index 000000000..21d3db09d --- /dev/null +++ b/v2/tools/pch.py @@ -0,0 +1,83 @@ +# Status: Being ported by Steven Watanabe +# Base revision: 47077 +# +# Copyright (c) 2005 Reece H. Dunn. +# Copyright 2006 Ilya Sokolov +# Copyright (c) 2008 Steven Watanabe +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +##### Using Precompiled Headers (Quick Guide) ##### +# +# Make precompiled mypch.hpp: +# +# import pch ; +# +# cpp-pch mypch +# : # sources +# mypch.hpp +# : # requiremnts +# msvc:mypch.cpp +# ; +# +# Add cpp-pch to sources: +# +# exe hello +# : main.cpp hello.cpp mypch +# ; + +from b2.build import type, feature, generators + +type.register('PCH', ['pch']) +type.register('C_PCH', [], 'PCH') +type.register('CPP_PCH', [], 'PCH') + +# Control precompiled header (PCH) generation. +feature.feature('pch', + ['on', 'off'], + ['propagated']) + +feature.feature('pch-header', [], ['free', 'dependency']) +feature.feature('pch-file', [], ['free', 'dependency']) + +class PchGenerator(generators.Generator): + """ + Base PCH generator. The 'run' method has the logic to prevent this generator + from being run unless it's being used for a top-level PCH target. + """ + def action_class(self): + return 'compile-action' + + def run(self, project, name, prop_set, sources): + if not name: + # Unless this generator is invoked as the top-most generator for a + # main target, fail. This allows using 'H' type as input type for + # this generator, while preventing Boost.Build to try this generator + # when not explicitly asked for. + # + # One bad example is msvc, where pch generator produces both PCH + # target and OBJ target, so if there's any header generated (like by + # bison, or by msidl), we'd try to use pch generator to get OBJ from + # that H, which is completely wrong. By restricting this generator + # only to pch main target, such problem is solved. + pass + else: + r = self.run_pch(project, name, + prop_set.add_raw('BOOST_BUILD_PCH_ENABLED'), + sources) + return generators.add_usage_requirements( + r, ['BOOST_BUILD_PCH_ENABLED']) + + # This rule must be overridden by the derived classes. + def run_pch(self, project, name, prop_set, sources): + pass + +#FIXME: dummy-generator in builtins.jam needs to be ported. +# NOTE: requirements are empty, default pch generator can be applied when +# pch=off. +###generators.register( +### [ new dummy-generator pch.default-c-pch-generator : : C_PCH ] ; +###generators.register +### [ new dummy-generator pch.default-cpp-pch-generator : : CPP_PCH ] ; diff --git a/v2/tools/rc.py b/v2/tools/rc.py new file mode 100644 index 000000000..0b82d231d --- /dev/null +++ b/v2/tools/rc.py @@ -0,0 +1,189 @@ +# Status: being ported by Steven Watanabe +# Base revision: 47077 +# +# Copyright (C) Andre Hentz 2003. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. +# +# Copyright (c) 2006 Rene Rivera. +# +# Copyright (c) 2008 Steven Watanabe +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +##import type ; +##import generators ; +##import feature ; +##import errors ; +##import scanner ; +##import toolset : flags ; + +from b2.build import type, toolset, generators, scanner, feature +from b2.tools import builtin +from b2.util import regex +from b2.build.toolset import flags +from b2.manager import get_manager + +__debug = None + +def debug(): + global __debug + if __debug is None: + __debug = "--debug-configuration" in bjam.variable("ARGV") + return __debug + +type.register('RC', ['rc']) + +def init(): + pass + +def configure (command = None, condition = None, options = None): + """ + Configures a new resource compilation command specific to a condition, + usually a toolset selection condition. The possible options are: + + * (rc|windres) - Indicates the type of options the command + accepts. + + Even though the arguments are all optional, only when a command, condition, + and at minimum the rc-type option are given will the command be configured. + This is so that callers don't have to check auto-configuration values + before calling this. And still get the functionality of build failures when + the resource compiler can't be found. + """ + rc_type = feature.get_values('', options) + if rc_type: + assert(len(rc_type) == 1) + rc_type = rc_type[0] + + if command and condition and rc_type: + flags('rc.compile.resource', '.RC', condition, command) + flags('rc.compile.resource', '.RC_TYPE', condition, rc_type.lower()) + flags('rc.compile.resource', 'DEFINES', [], ['']) + flags('rc.compile.resource', 'INCLUDES', [], ['']) + if debug(): + print 'notice: using rc compiler ::', condition, '::', command + +engine = get_manager().engine() + +class RCAction: + """Class representing bjam action defined from Python. + The function must register the action to execute.""" + + def __init__(self, action_name, function): + self.action_name = action_name + self.function = function + + def __call__(self, targets, sources, property_set): + if self.function: + self.function(targets, sources, property_set) + +# FIXME: What is the proper way to dispatch actions? +def rc_register_action(action_name, function = None): + global engine + if engine.actions.has_key(action_name): + raise "Bjam action %s is already defined" % action_name + engine.actions[action_name] = RCAction(action_name, function) + +def rc_compile_resource(targets, sources, properties): + rc_type = bjam.call('get-target-variable', targets, '.RC_TYPE') + global engine + engine.set_update_action('rc.compile.resource.' + rc_type, targets, sources, properties) + +rc_register_action('rc.compile.resource', rc_compile_resource) + + +engine.register_action( + 'rc.compile.resource.rc', + '"$(.RC)" -l 0x409 "-U$(UNDEFS)" "-D$(DEFINES)" -I"$(>:D)" -I"$(<:D)" -I"$(INCLUDES)" -fo "$(<)" "$(>)"') + +engine.register_action( + 'rc.compile.resource.windres', + '"$(.RC)" "-U$(UNDEFS)" "-D$(DEFINES)" -I"$(>:D)" -I"$(<:D)" -I"$(INCLUDES)" -o "$(<)" -i "$(>)"') + +# FIXME: this was originally declared quietly +engine.register_action( + 'compile.resource.null', + 'as /dev/null -o "$(<)"') + +# Since it's a common practice to write +# exe hello : hello.cpp hello.rc +# we change the name of object created from RC file, to +# avoid conflict with hello.cpp. +# The reason we generate OBJ and not RES, is that gcc does not +# seem to like RES files, but works OK with OBJ. +# See http://article.gmane.org/gmane.comp.lib.boost.build/5643/ +# +# Using 'register-c-compiler' adds the build directory to INCLUDES +# FIXME: switch to generators +builtin.register_c_compiler('rc.compile.resource', ['RC'], ['OBJ(%_res)'], []) + +__angle_include_re = "#include[ ]*<([^<]+)>" + +# Register scanner for resources +class ResScanner(scanner.Scanner): + + def __init__(self, includes): + scanner.__init__ ; + self.includes = includes + + def pattern(self): + return "(([^ ]+[ ]+(BITMAP|CURSOR|FONT|ICON|MESSAGETABLE|RT_MANIFEST)" +\ + "[ ]+([^ \"]+|\"[^\"]+\"))|(#include[ ]*(<[^<]+>|\"[^\"]+\")))" ; + + def process(self, target, matches, binding): + + angle = regex.transform(matches, "#include[ ]*<([^<]+)>") + quoted = regex.transform(matches, "#include[ ]*\"([^\"]+)\"") + res = regex.transform(matches, + "[^ ]+[ ]+(BITMAP|CURSOR|FONT|ICON|MESSAGETABLE|RT_MANIFEST)" +\ + "[ ]+(([^ \"]+)|\"([^\"]+)\")", [3, 4]) + + # Icons and other includes may referenced as + # + # IDR_MAINFRAME ICON "res\\icon.ico" + # + # so we have to replace double backslashes to single ones. + res = [ re.sub(r'\\\\', '/', match) for match in res ] + + # CONSIDER: the new scoping rule seem to defeat "on target" variables. + g = bjam.call('get-target-variable', target, 'HDRGRIST') + b = os.path.normalize_path(os.path.dirname(binding)) + + # Attach binding of including file to included targets. + # When target is directly created from virtual target + # this extra information is unnecessary. But in other + # cases, it allows to distinguish between two headers of the + # same name included from different places. + # We don't need this extra information for angle includes, + # since they should not depend on including file (we can't + # get literal "." in include path). + g2 = g + "#" + b + + g = "<" + g + ">" + g2 = "<" + g2 + ">" + angle = [g + x for x in angle] + quoted = [g2 + x for x in quoted] + res = [g2 + x for x in res] + + all = angle + quoted + + bjam.call('mark-included', target, all) + + engine = get_manager().engine() + + engine.add_dependency(target, res) + bjam.call('NOCARE', all + res) + engine.set_target_variable(angle, 'SEARCH', ungrist(self.includes)) + engine.set_target_variable(quoted, 'SEARCH', b + ungrist(self.includes)) + engine.set_target_variable(res, 'SEARCH', b + ungrist(self.includes)) ; + + # Just propagate current scanner to includes, in a hope + # that includes do not change scanners. + get_manager().scanners().propagate(self, angle + quoted) + +scanner.register(ResScanner, 'include') +type.set_scanner('RC', ResScanner) diff --git a/v2/tools/types/__init__.py b/v2/tools/types/__init__.py new file mode 100644 index 000000000..f972b7149 --- /dev/null +++ b/v2/tools/types/__init__.py @@ -0,0 +1,18 @@ +__all__ = [ + 'asm', + 'cpp', + 'exe', + 'html', + 'lib', + 'obj', + 'rsp', +] + +def register_all (): + for i in __all__: + m = __import__ (__name__ + '.' + i) + reg = i + '.register ()' + #exec (reg) + +# TODO: (PF) I thought these would be imported automatically. Anyone knows why they aren't? +register_all () diff --git a/v2/tools/types/asm.py b/v2/tools/types/asm.py new file mode 100644 index 000000000..b4e1c30e7 --- /dev/null +++ b/v2/tools/types/asm.py @@ -0,0 +1,13 @@ +# Copyright Craig Rodrigues 2005. +# Copyright (c) 2008 Steven Watanabe +# +# 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) + +from b2.build import type + +def register(): + type.register_type('ASM', ['s', 'S', 'asm']) + +register() diff --git a/v2/tools/types/cpp.py b/v2/tools/types/cpp.py new file mode 100644 index 000000000..7b56111c8 --- /dev/null +++ b/v2/tools/types/cpp.py @@ -0,0 +1,10 @@ +# Copyright David Abrahams 2004. 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) + +from b2.build import type + +def register (): + type.register_type ('CPP', ['cpp', 'cxx', 'cc']) + +register () diff --git a/v2/tools/types/exe.py b/v2/tools/types/exe.py new file mode 100644 index 000000000..a4935e24e --- /dev/null +++ b/v2/tools/types/exe.py @@ -0,0 +1,11 @@ +# Copyright David Abrahams 2004. 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) + +from b2.build import type + +def register (): + type.register_type ('EXE', ['exe'], None, ['NT', 'CYGWIN']) + type.register_type ('EXE', [], None, []) + +register () diff --git a/v2/tools/types/html.py b/v2/tools/types/html.py new file mode 100644 index 000000000..63af4d907 --- /dev/null +++ b/v2/tools/types/html.py @@ -0,0 +1,10 @@ +# Copyright David Abrahams 2004. 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) + +from b2.build import type + +def register (): + type.register_type ('HTML', ['html']) + +register () diff --git a/v2/tools/types/lib.py b/v2/tools/types/lib.py new file mode 100644 index 000000000..0bd3f5547 --- /dev/null +++ b/v2/tools/types/lib.py @@ -0,0 +1,23 @@ +# Copyright David Abrahams 2004. 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) + +from b2.build import type + +def register (): + + if not type.registered ('LIB'): + type.register ('LIB') + + type.register_type ('STATIC_LIB', ['lib', 'a'], 'LIB', ['NT', 'CYGWIN']) + type.register_type ('STATIC_LIB', ['a'], 'LIB') + + type.register_type ('IMPORT_LIB', [], 'STATIC_LIB') + type.set_generated_target_suffix ('IMPORT_LIB', [], 'lib') + + type.register_type ('SHARED_LIB', ['dll'], 'LIB', ['NT', 'CYGWIN']) + type.register_type ('SHARED_LIB', ['so'], 'LIB') + + type.register_type ('SEARCHED_LIB', [], 'LIB') + +register () diff --git a/v2/tools/types/obj.py b/v2/tools/types/obj.py new file mode 100644 index 000000000..e61e99a81 --- /dev/null +++ b/v2/tools/types/obj.py @@ -0,0 +1,11 @@ +# Copyright David Abrahams 2004. 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) + +from b2.build import type + +def register (): + type.register_type ('OBJ', ['obj'], None, ['NT', 'CYGWIN']) + type.register_type ('OBJ', ['o']) + +register () diff --git a/v2/tools/types/rsp.py b/v2/tools/types/rsp.py new file mode 100644 index 000000000..ccb379e95 --- /dev/null +++ b/v2/tools/types/rsp.py @@ -0,0 +1,10 @@ +# Copyright David Abrahams 2004. 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) + +from b2.build import type + +def register (): + type.register_type ('RSP', ['rsp']) + +register () diff --git a/v2/tools/unix.py b/v2/tools/unix.py new file mode 100644 index 000000000..025391d2d --- /dev/null +++ b/v2/tools/unix.py @@ -0,0 +1,150 @@ +# Copyright (c) 2004 Vladimir Prus. +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +""" This file implements linking semantics common to all unixes. On unix, static + libraries must be specified in a fixed order on the linker command line. Generators + declared there store information about the order and use it properly. +""" + +import builtin +from b2.build import generators, type +from b2.util.utility import * +from b2.util import set, sequence + +class UnixLinkingGenerator (builtin.LinkingGenerator): + + def __init__ (self, id, composing, source_types, target_types, requirements): + builtin.LinkingGenerator.__init__ (self, id, composing, source_types, target_types, requirements) + + def run (self, project, name, prop_set, sources): + result = builtin.LinkingGenerator.run (self, project, name, prop_set, sources) + if result: + set_library_order (project.manager (), sources, prop_set, result [1]) + + return result + + def generated_targets (self, sources, prop_set, project, name): + sources2 = [] + libraries = [] + for l in sources: + if type.is_derived (l.type (), 'LIB'): + libraries.append (l) + + else: + sources2.append (l) + + sources = sources2 + order_libraries (libraries) + + return builtin.LinkingGenerator.generated_targets (self, sources, prop_set, project, name) + + +class UnixArchiveGenerator (builtin.ArchiveGenerator): + def __init__ (self, id, composing, source_types, target_types_and_names, requirements): + builtin.ArchiveGenerator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) + + def run (self, project, name, prop_set, sources): + result = builtin.ArchiveGenerator.run(self, project, name, prop_set, sources) + set_library_order(project.manager(), sources, prop_set, result) + return result + +class UnixSearchedLibGenerator (builtin.SearchedLibGenerator): + + def __init__ (self): + builtin.SearchedLibGenerator.__init__ (self) + + def optional_properties (self): + return self.requirements () + + def run (self, project, name, prop_set, sources, multiple): + result = SearchedLibGenerator.run (project, name, prop_set, sources, multiple) + + set_library_order (sources, prop_set, result) + + return result + +class UnixPrebuiltLibGenerator (generators.Generator): + def __init__ (self, id, composing, source_types, target_types_and_names, requirements): + generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) + + def run (self, project, name, prop_set, sources, multiple): + f = prop_set.get ('') + set_library_order_aux (f, sources) + return (f, sources) + +### # The derived toolset must specify their own rules and actions. +# FIXME: restore? +# action.register ('unix.prebuilt', None, None) + + +generators.register (UnixPrebuiltLibGenerator ('unix.prebuilt', False, [], ['LIB'], ['', 'unix'])) + + + + + +### # Declare generators +### generators.register [ new UnixLinkingGenerator unix.link : LIB OBJ : EXE +### : unix ] ; +generators.register (UnixArchiveGenerator ('unix.archive', True, ['OBJ'], ['STATIC_LIB'], ['unix'])) + +### generators.register [ new UnixLinkingGenerator unix.link.dll : LIB OBJ : SHARED_LIB +### : unix ] ; +### +### generators.register [ new UnixSearchedLibGenerator +### unix.SearchedLibGenerator : : SEARCHED_LIB : unix ] ; +### +### +### # The derived toolset must specify their own actions. +### actions link { +### } +### +### actions link.dll { +### } + +def unix_archive (manager, targets, sources, properties): + pass + +# FIXME: restore? +#action.register ('unix.archive', unix_archive, ['']) + +### actions searched-lib-generator { +### } +### +### actions prebuilt { +### } + + +from b2.util.order import Order +__order = Order () + +def set_library_order_aux (from_libs, to_libs): + for f in from_libs: + for t in to_libs: + if f != t: + __order.add_pair (f, t) + +def set_library_order (manager, sources, prop_set, result): + used_libraries = [] + deps = prop_set.dependency () + + [ sources.append (manager.get_object (get_value (x))) for x in deps ] + sources = sequence.unique (sources) + + for l in sources: + if l.type () and type.is_derived (l.type (), 'LIB'): + used_libraries.append (l) + + created_libraries = [] + for l in result: + if l.type () and type.is_derived (l.type (), 'LIB'): + created_libraries.append (l) + + created_libraries = set.difference (created_libraries, used_libraries) + set_library_order_aux (created_libraries, used_libraries) + +def order_libraries (libraries): + return __order.order (libraries) + diff --git a/v2/util/__init__.py b/v2/util/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/v2/util/logger.py b/v2/util/logger.py new file mode 100644 index 000000000..a0d7a6e37 --- /dev/null +++ b/v2/util/logger.py @@ -0,0 +1,46 @@ +# Copyright Pedro Ferreira 2005. 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) + +import sys + +class NullLogger: + def __init__ (self): + self.indent_ = '' + + def log (self, source_name, *args): + if self.on () and self.interesting (source_name): + self.do_log (self.indent_) + for i in args: + self.do_log (i) + self.do_log ('\n') + + def increase_indent (self): + if self.on (): + self.indent_ += ' ' + + def decrease_indent (self): + if self.on () and len (self.indent_) > 4: + self.indent_ = self.indent_ [-4:] + + def do_log (self, *args): + pass + + def interesting (self, source_name): + return False + + def on (self): + return False + +class TextLogger (NullLogger): + def __init__ (self): + NullLogger.__init__ (self) + + def do_log (self, arg): + sys.stdout.write (str (arg)) + + def interesting (self, source_name): + return True + + def on (self): + return True diff --git a/v2/util/order.py b/v2/util/order.py new file mode 100644 index 000000000..4e67b3f1a --- /dev/null +++ b/v2/util/order.py @@ -0,0 +1,121 @@ +# Copyright (C) 2003 Vladimir Prus +# Use, modification, and distribution is subject to 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) + +class Order: + """Allows ordering arbitrary objects with regard to arbitrary binary relation. + + The primary use case is the gcc toolset, which is sensitive to + library order: if library 'a' uses symbols from library 'b', + then 'a' must be present before 'b' on the linker's command line. + + This requirement can be lifted for gcc with GNU ld, but for gcc with + Solaris LD (and for Solaris toolset as well), the order always matters. + + So, we need to store order requirements and then order libraries + according to them. It it not possible to use dependency graph as + order requirements. What we need is "use symbols" relationship + while dependency graph provides "needs to be updated" relationship. + + For example:: + lib a : a.cpp b; + lib b ; + + For static linking, the 'a' library need not depend on 'b'. However, it + still should come before 'b' on the command line. + """ + + def __init__ (self): + self.constraints_ = [] + + def add_pair (self, first, second): + """ Adds the constraint that 'first' should precede 'second'. + """ + self.constraints_.append ((first, second)) + + def order (self, objects): + """ Given a list of objects, reorder them so that the constains specified + by 'add_pair' are satisfied. + + The algorithm was adopted from an awk script by Nikita Youshchenko + (yoush at cs dot msu dot su) + """ + # The algorithm used is the same is standard transitive closure, + # except that we're not keeping in-degree for all vertices, but + # rather removing edges. + result = [] + + if not objects: + return result + + constraints = self.__eliminate_unused_constraits (objects) + + # Find some library that nobody depends upon and add it to + # the 'result' array. + obj = None + while objects: + new_objects = [] + while objects: + obj = objects [0] + + if self.__has_no_dependents (obj, constraints): + # Emulate break ; + new_objects.extend (objects [1:]) + objects = [] + + else: + new_objects.append (obj) + obj = None + objects = objects [1:] + + if not obj: + raise BaseException ("Circular order dependencies") + + # No problem with placing first. + result.append (obj) + + # Remove all containts where 'obj' comes first, + # since they are already satisfied. + constraints = self.__remove_satisfied (constraints, obj) + + # Add the remaining objects for further processing + # on the next iteration + objects = new_objects + + return result + + def __eliminate_unused_constraits (self, objects): + """ Eliminate constraints which mention objects not in 'objects'. + In graph-theory terms, this is finding subgraph induced by + ordered vertices. + """ + result = [] + for c in self.constraints_: + if c [0] in objects and c [1] in objects: + result.append (c) + + return result + + def __has_no_dependents (self, obj, constraints): + """ Returns true if there's no constraint in 'constraints' where + 'obj' comes second. + """ + failed = False + while constraints and not failed: + c = constraints [0] + + if c [1] == obj: + failed = True + + constraints = constraints [1:] + + return not failed + + def __remove_satisfied (self, constraints, obj): + result = [] + for c in constraints: + if c [0] != obj: + result.append (c) + + return result diff --git a/v2/util/path.py b/v2/util/path.py new file mode 100644 index 000000000..f5cbc63f3 --- /dev/null +++ b/v2/util/path.py @@ -0,0 +1,922 @@ +# Status: this module is ported on demand by however needs something +# from it. Functionality that is not needed by Python port will +# be dropped. + +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +# Performs various path manipulations. Path are always in a 'normilized' +# representation. In it, a path may be either: +# +# - '.', or +# +# - ['/'] [ ( '..' '/' )* (token '/')* token ] +# +# In plain english, path can be rooted, '..' elements are allowed only +# at the beginning, and it never ends in slash, except for path consisting +# of slash only. + +import os.path +from utility import to_seq +from glob import glob as builtin_glob + +def root (path, root): + """ If 'path' is relative, it is rooted at 'root'. Otherwise, it's unchanged. + """ + if os.path.isabs (path): + return path + else: + return os.path.join (root, path) + +def make (native): + """ Converts the native path into normalized form. + """ + # TODO: make os selection here. + return make_UNIX (native) + +def make_UNIX (native): + + # VP: I have no idea now 'native' can be empty here! But it can! + assert (native) + + return os.path.normpath (native) + +def native (path): + """ Builds a native representation of the path. + """ + # TODO: make os selection here. + return native_UNIX (path) + +def native_UNIX (path): + return path + + +def pwd (): + """ Returns the current working directory. + # TODO: is it a good idea to use the current dir? Some use-cases + may not allow us to depend on the current dir. + """ + return make (os.getcwd ()) + +def is_rooted (path): + """ Tests if a path is rooted. + """ + return path and path [0] == '/' + + +################################################################### +# Still to port. +# Original lines are prefixed with "# " +# +# # Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# # distribute this software is granted provided this copyright notice appears in +# # all copies. This software is provided "as is" without express or implied +# # warranty, and with no claim as to its suitability for any purpose. +# +# # Performs various path manipulations. Path are always in a 'normilized' +# # representation. In it, a path may be either: +# # +# # - '.', or +# # +# # - ['/'] [ ( '..' '/' )* (token '/')* token ] +# # +# # In plain english, path can be rooted, '..' elements are allowed only +# # at the beginning, and it never ends in slash, except for path consisting +# # of slash only. +# +# import modules ; +# import sequence ; +# import regex ; +# import errors : error ; +# +# +# os = [ modules.peek : OS ] ; +# if [ modules.peek : UNIX ] +# { +# local uname = [ modules.peek : JAMUNAME ] ; +# switch $(uname) +# { +# case CYGWIN* : +# os = CYGWIN ; +# +# case * : +# os = UNIX ; +# } +# } +# +# # +# # Tests if a path is rooted. +# # +# rule is-rooted ( path ) +# { +# return [ MATCH "^(/)" : $(path) ] ; +# } +# +# # +# # Tests if a path has a parent. +# # +# rule has-parent ( path ) +# { +# if $(path) != / { +# return 1 ; +# } else { +# return ; +# } +# } +# +# # +# # Returns the path without any directory components. +# # +# rule basename ( path ) +# { +# return [ MATCH "([^/]+)$" : $(path) ] ; +# } +# +# # +# # Returns parent directory of the path. If no parent exists, error is issued. +# # +# rule parent ( path ) +# { +# if [ has-parent $(path) ] { +# +# if $(path) = . { +# return .. ; +# } else { +# +# # Strip everything at the end of path up to and including +# # the last slash +# local result = [ regex.match "((.*)/)?([^/]+)" : $(path) : 2 3 ] ; +# +# # Did we strip what we shouldn't? +# if $(result[2]) = ".." { +# return $(path)/.. ; +# } else { +# if ! $(result[1]) { +# if [ is-rooted $(path) ] { +# result = / ; +# } else { +# result = . ; +# } +# } +# return $(result[1]) ; +# } +# } +# } else { +# error "Path '$(path)' has no parent" ; +# } +# } +# +# # +# # Returns path2 such that "[ join path path2 ] = .". +# # The path may not contain ".." element or be rooted. +# # +# rule reverse ( path ) +# { +# if $(path) = . +# { +# return $(path) ; +# } +# else +# { +# local tokens = [ regex.split $(path) "/" ] ; +# local tokens2 ; +# for local i in $(tokens) { +# tokens2 += .. ; +# } +# return [ sequence.join $(tokens2) : "/" ] ; +# } +# } +# +# # +# # Auxillary rule: does all the semantic of 'join', except for error cheching. +# # The error checking is separated because this rule is recursive, and I don't +# # like the idea of checking the same input over and over. +# # +# local rule join-imp ( elements + ) +# { +# return [ NORMALIZE_PATH $(elements:J="/") ] ; +# } +# +# # +# # Contanenates the passed path elements. Generates an error if +# # any element other than the first one is rooted. +# # +# rule join ( elements + ) +# { +# if ! $(elements[2]) +# { +# return $(elements[1]) ; +# } +# else +# { +# for local e in $(elements[2-]) +# { +# if [ is-rooted $(e) ] +# { +# error only first element may be rooted ; +# } +# } +# return [ join-imp $(elements) ] ; +# } +# } + + +def glob (dirs, patterns): + """ Returns the list of files matching the given pattern in the + specified directory. Both directories and patterns are + supplied as portable paths. Each pattern should be non-absolute + path, and can't contain "." or ".." elements. Each slash separated + element of pattern can contain the following special characters: + - '?', which match any character + - '*', which matches arbitrary number of characters. + A file $(d)/e1/e2/e3 (where 'd' is in $(dirs)) matches pattern p1/p2/p3 + if and only if e1 matches p1, e2 matches p2 and so on. + + For example: + [ glob . : *.cpp ] + [ glob . : */build/Jamfile ] + """ +# { +# local result ; +# if $(patterns:D) +# { +# # When a pattern has a directory element, we first glob for +# # directory, and then glob for file name is the found directories. +# for local p in $(patterns) +# { +# # First glob for directory part. +# local globbed-dirs = [ glob $(dirs) : $(p:D) ] ; +# result += [ glob $(globbed-dirs) : $(p:D="") ] ; +# } +# } +# else +# { +# # When a pattern has not directory, we glob directly. +# # Take care of special ".." value. The "GLOB" rule simply ignores +# # the ".." element (and ".") element in directory listings. This is +# # needed so that +# # +# # [ glob libs/*/Jamfile ] +# # +# # don't return +# # +# # libs/../Jamfile (which is the same as ./Jamfile) +# # +# # On the other hand, when ".." is explicitly present in the pattern +# # we need to return it. +# # +# for local dir in $(dirs) +# { +# for local p in $(patterns) +# { +# if $(p) != ".." +# { +# result += [ sequence.transform make +# : [ GLOB [ native $(dir) ] : $(p) ] ] ; +# } +# else +# { +# result += [ path.join $(dir) .. ] ; +# } +# } +# } +# } +# return $(result) ; +# } +# + +# TODO: (PF) I replaced the code above by this. I think it should work but needs to be tested. + result = [] + dirs = to_seq (dirs) + patterns = to_seq (patterns) + + splitdirs = [] + for dir in dirs: + splitdirs += dir.split (os.pathsep) + + for dir in splitdirs: + for pattern in patterns: + p = os.path.join (dir, pattern) + import glob + result.extend (glob.glob (p)) + return result + +# # +# # Returns true is the specified file exists. +# # +# rule exists ( file ) +# { +# return [ path.glob $(file:D) : $(file:D=) ] ; +# } +# NATIVE_RULE path : exists ; +# +# +# +# # +# # Find out the absolute name of path and returns the list of all the parents, +# # starting with the immediate one. Parents are returned as relative names. +# # If 'upper_limit' is specified, directories above it will be pruned. +# # +# rule all-parents ( path : upper_limit ? : cwd ? ) +# { +# cwd ?= [ pwd ] ; +# local path_ele = [ regex.split [ root $(path) $(cwd) ] "/" ] ; +# +# if ! $(upper_limit) { +# upper_limit = / ; +# } +# local upper_ele = [ regex.split [ root $(upper_limit) $(cwd) ] "/" ] ; +# +# # Leave only elements in 'path_ele' below 'upper_ele' +# while $(path_ele) && $(upper_ele[1]) = $(path_ele[1]) { +# upper_ele = $(upper_ele[2-]) ; +# path_ele = $(path_ele[2-]) ; +# } +# +# # All upper elements removed ? +# if ! $(upper_ele) { +# # Create the relative paths to parents, number of elements in 'path_ele' +# local result ; +# for local i in $(path_ele) { +# path = [ parent $(path) ] ; +# result += $(path) ; +# } +# return $(result) ; +# } +# else { +# error "$(upper_limit) is not prefix of $(path)" ; +# } +# } +# +# +# # +# # Search for 'pattern' in parent directories of 'dir', up till and including +# # 'upper_limit', if it is specified, or till the filesystem root otherwise. +# # +# rule glob-in-parents ( dir : patterns + : upper-limit ? ) +# { +# local result ; +# local parent-dirs = [ all-parents $(dir) : $(upper-limit) ] ; +# +# while $(parent-dirs) && ! $(result) +# { +# result = [ glob $(parent-dirs[1]) : $(patterns) ] ; +# parent-dirs = $(parent-dirs[2-]) ; +# } +# return $(result) ; +# } +# +# # +# # Assuming 'child' is a subdirectory of 'parent', return the relative +# # path from 'parent' to 'child' +# # +# rule relative ( child parent ) +# { +# if $(parent) = "." +# { +# return $(child) ; +# } +# else +# { +# local split1 = [ regex.split $(parent) / ] ; +# local split2 = [ regex.split $(child) / ] ; +# +# while $(split1) +# { +# if $(split1[1]) = $(split2[1]) +# { +# split1 = $(split1[2-]) ; +# split2 = $(split2[2-]) ; +# } +# else +# { +# errors.error $(child) is not a subdir of $(parent) ; +# } +# } +# return [ join $(split2) ] ; +# } +# } +# +# # Returns the minimal path to path2 that is relative path1. +# # +# rule relative-to ( path1 path2 ) +# { +# local root_1 = [ regex.split [ reverse $(path1) ] / ] ; +# local split1 = [ regex.split $(path1) / ] ; +# local split2 = [ regex.split $(path2) / ] ; +# +# while $(split1) && $(root_1) +# { +# if $(split1[1]) = $(split2[1]) +# { +# root_1 = $(root_1[2-]) ; +# split1 = $(split1[2-]) ; +# split2 = $(split2[2-]) ; +# } +# else +# { +# split1 = ; +# } +# } +# return [ join . $(root_1) $(split2) ] ; +# } + +# Returns the list of paths which are used by the operating system +# for looking up programs +def programs_path (): + raw = [] + names = ['PATH', 'Path', 'path'] + + for name in names: + raw.append(os.environ.get (name, '')) + + result = [] + for elem in raw: + if elem: + for p in elem.split(os.path.pathsep): + result.append(make(p)) + + return result + +# rule make-NT ( native ) +# { +# local tokens = [ regex.split $(native) "[/\\]" ] ; +# local result ; +# +# # Handle paths ending with slashes +# if $(tokens[-1]) = "" +# { +# tokens = $(tokens[1--2]) ; # discard the empty element +# } +# +# result = [ path.join $(tokens) ] ; +# +# if [ regex.match "(^.:)" : $(native) ] +# { +# result = /$(result) ; +# } +# +# if $(native) = "" +# { +# result = "." ; +# } +# +# return $(result) ; +# } +# +# rule native-NT ( path ) +# { +# local result = [ MATCH "^/?(.*)" : $(path) ] ; +# result = [ sequence.join [ regex.split $(result) "/" ] : "\\" ] ; +# return $(result) ; +# } +# +# rule make-CYGWIN ( path ) +# { +# return [ make-NT $(path) ] ; +# } +# +# rule native-CYGWIN ( path ) +# { +# local result = $(path) ; +# if [ regex.match "(^/.:)" : $(path) ] # win absolute +# { +# result = [ MATCH "^/?(.*)" : $(path) ] ; # remove leading '/' +# } +# return [ native-UNIX $(result) ] ; +# } +# +# # +# # split-VMS: splits input native path into +# # device dir file (each part is optional), +# # example: +# # +# # dev:[dir]file.c => dev: [dir] file.c +# # +# rule split-path-VMS ( native ) +# { +# local matches = [ MATCH ([a-zA-Z0-9_-]+:)?(\\[[^\]]*\\])?(.*)?$ : $(native) ] ; +# local device = $(matches[1]) ; +# local dir = $(matches[2]) ; +# local file = $(matches[3]) ; +# +# return $(device) $(dir) $(file) ; +# } +# +# # +# # Converts a native VMS path into a portable path spec. +# # +# # Does not handle current-device absolute paths such +# # as "[dir]File.c" as it is not clear how to represent +# # them in the portable path notation. +# # +# # Adds a trailing dot (".") to the file part if no extension +# # is present (helps when converting it back into native path). +# # +# rule make-VMS ( native ) +# { +# if [ MATCH ^(\\[[a-zA-Z0-9]) : $(native) ] +# { +# errors.error "Can't handle default-device absolute paths: " $(native) ; +# } +# +# local parts = [ split-path-VMS $(native) ] ; +# local device = $(parts[1]) ; +# local dir = $(parts[2]) ; +# local file = $(parts[3]) ; +# local elems ; +# +# if $(device) +# { +# # +# # rooted +# # +# elems = /$(device) ; +# } +# +# if $(dir) = "[]" +# { +# # +# # Special case: current directory +# # +# elems = $(elems) "." ; +# } +# else if $(dir) +# { +# dir = [ regex.replace $(dir) "\\[|\\]" "" ] ; +# local dir_parts = [ regex.split $(dir) \\. ] ; +# +# if $(dir_parts[1]) = "" +# { +# # +# # Relative path +# # +# dir_parts = $(dir_parts[2--1]) ; +# } +# +# # +# # replace "parent-directory" parts (- => ..) +# # +# dir_parts = [ regex.replace-list $(dir_parts) : - : .. ] ; +# +# elems = $(elems) $(dir_parts) ; +# } +# +# if $(file) +# { +# if ! [ MATCH (\\.) : $(file) ] +# { +# # +# # Always add "." to end of non-extension file +# # +# file = $(file). ; +# } +# elems = $(elems) $(file) ; +# } +# +# local portable = [ path.join $(elems) ] ; +# +# return $(portable) ; +# } +# +# # +# # Converts a portable path spec into a native VMS path. +# # +# # Relies on having at least one dot (".") included in the file +# # name to be able to differentiate it ftom the directory part. +# # +# rule native-VMS ( path ) +# { +# local device = "" ; +# local dir = $(path) ; +# local file = "" ; +# local native ; +# local split ; +# +# # +# # Has device ? +# # +# if [ is-rooted $(dir) ] +# { +# split = [ MATCH ^/([^:]+:)/?(.*) : $(dir) ] ; +# device = $(split[1]) ; +# dir = $(split[2]) ; +# } +# +# # +# # Has file ? +# # +# # This is no exact science, just guess work: +# # +# # If the last part of the current path spec +# # includes some chars, followed by a dot, +# # optionally followed by more chars - +# # then it is a file (keep your fingers crossed). +# # +# split = [ regex.split $(dir) / ] ; +# local maybe_file = $(split[-1]) ; +# +# if [ MATCH ^([^.]+\\..*) : $(maybe_file) ] +# { +# file = $(maybe_file) ; +# dir = [ sequence.join $(split[1--2]) : / ] ; +# } +# +# # +# # Has dir spec ? +# # +# if $(dir) = "." +# { +# dir = "[]" ; +# } +# else if $(dir) +# { +# dir = [ regex.replace $(dir) \\.\\. - ] ; +# dir = [ regex.replace $(dir) / . ] ; +# +# if $(device) = "" +# { +# # +# # Relative directory +# # +# dir = "."$(dir) ; +# } +# dir = "["$(dir)"]" ; +# } +# +# native = [ sequence.join $(device) $(dir) $(file) ] ; +# +# return $(native) ; +# } +# +# +# rule __test__ ( ) { +# +# import assert ; +# import errors : try catch ; +# +# assert.true is-rooted "/" ; +# assert.true is-rooted "/foo" ; +# assert.true is-rooted "/foo/bar" ; +# assert.result : is-rooted "." ; +# assert.result : is-rooted "foo" ; +# assert.result : is-rooted "foo/bar" ; +# +# assert.true has-parent "foo" ; +# assert.true has-parent "foo/bar" ; +# assert.true has-parent "." ; +# assert.result : has-parent "/" ; +# +# assert.result "." : basename "." ; +# assert.result ".." : basename ".." ; +# assert.result "foo" : basename "foo" ; +# assert.result "foo" : basename "bar/foo" ; +# assert.result "foo" : basename "gaz/bar/foo" ; +# assert.result "foo" : basename "/gaz/bar/foo" ; +# +# assert.result "." : parent "foo" ; +# assert.result "/" : parent "/foo" ; +# assert.result "foo/bar" : parent "foo/bar/giz" ; +# assert.result ".." : parent "." ; +# assert.result ".." : parent "../foo" ; +# assert.result "../../foo" : parent "../../foo/bar" ; +# +# +# assert.result "." : reverse "." ; +# assert.result ".." : reverse "foo" ; +# assert.result "../../.." : reverse "foo/bar/giz" ; +# +# assert.result "foo" : join "foo" ; +# assert.result "/foo" : join "/" "foo" ; +# assert.result "foo/bar" : join "foo" "bar" ; +# assert.result "foo/bar" : join "foo/giz" "../bar" ; +# assert.result "foo/giz" : join "foo/bar/baz" "../../giz" ; +# assert.result ".." : join "." ".." ; +# assert.result ".." : join "foo" "../.." ; +# assert.result "../.." : join "../foo" "../.." ; +# assert.result "/foo" : join "/bar" "../foo" ; +# assert.result "foo/giz" : join "foo/giz" "." ; +# assert.result "." : join lib2 ".." ; +# assert.result "/" : join "/a" ".." ; +# +# assert.result /a/b : join /a/b/c .. ; +# +# assert.result "foo/bar/giz" : join "foo" "bar" "giz" ; +# assert.result "giz" : join "foo" ".." "giz" ; +# assert.result "foo/giz" : join "foo" "." "giz" ; +# +# try ; +# { +# join "a" "/b" ; +# } +# catch only first element may be rooted ; +# +# local CWD = "/home/ghost/build" ; +# assert.result : all-parents . : . : $(CWD) ; +# assert.result . .. ../.. ../../.. : all-parents "Jamfile" : "" : $(CWD) ; +# assert.result foo . .. ../.. ../../.. : all-parents "foo/Jamfile" : "" : $(CWD) ; +# assert.result ../Work .. ../.. ../../.. : all-parents "../Work/Jamfile" : "" : $(CWD) ; +# +# local CWD = "/home/ghost" ; +# assert.result . .. : all-parents "Jamfile" : "/home" : $(CWD) ; +# assert.result . : all-parents "Jamfile" : "/home/ghost" : $(CWD) ; +# +# assert.result "c/d" : relative "a/b/c/d" "a/b" ; +# assert.result "foo" : relative "foo" "." ; +# +# local save-os = [ modules.peek path : os ] ; +# modules.poke path : os : NT ; +# +# assert.result "foo/bar/giz" : make "foo/bar/giz" ; +# assert.result "foo/bar/giz" : make "foo\\bar\\giz" ; +# assert.result "foo" : make "foo/." ; +# assert.result "foo" : make "foo/bar/.." ; +# assert.result "/D:/My Documents" : make "D:\\My Documents" ; +# assert.result "/c:/boost/tools/build/new/project.jam" : make "c:\\boost\\tools\\build\\test\\..\\new\\project.jam" ; +# +# assert.result "foo\\bar\\giz" : native "foo/bar/giz" ; +# assert.result "foo" : native "foo" ; +# assert.result "D:\\My Documents\\Work" : native "/D:/My Documents/Work" ; +# +# modules.poke path : os : UNIX ; +# +# assert.result "foo/bar/giz" : make "foo/bar/giz" ; +# assert.result "/sub1" : make "/sub1/." ; +# assert.result "/sub1" : make "/sub1/sub2/.." ; +# assert.result "sub1" : make "sub1/." ; +# assert.result "sub1" : make "sub1/sub2/.." ; +# assert.result "/foo/bar" : native "/foo/bar" ; +# +# modules.poke path : os : VMS ; +# +# # +# # Don't really need to poke os before these +# # +# assert.result "disk:" "[dir]" "file" : split-path-VMS "disk:[dir]file" ; +# assert.result "disk:" "[dir]" "" : split-path-VMS "disk:[dir]" ; +# assert.result "disk:" "" "" : split-path-VMS "disk:" ; +# assert.result "disk:" "" "file" : split-path-VMS "disk:file" ; +# assert.result "" "[dir]" "file" : split-path-VMS "[dir]file" ; +# assert.result "" "[dir]" "" : split-path-VMS "[dir]" ; +# assert.result "" "" "file" : split-path-VMS "file" ; +# assert.result "" "" "" : split-path-VMS "" ; +# +# # +# # Special case: current directory +# # +# assert.result "" "[]" "" : split-path-VMS "[]" ; +# assert.result "disk:" "[]" "" : split-path-VMS "disk:[]" ; +# assert.result "" "[]" "file" : split-path-VMS "[]file" ; +# assert.result "disk:" "[]" "file" : split-path-VMS "disk:[]file" ; +# +# # +# # Make portable paths +# # +# assert.result "/disk:" : make "disk:" ; +# assert.result "foo/bar/giz" : make "[.foo.bar.giz]" ; +# assert.result "foo" : make "[.foo]" ; +# assert.result "foo" : make "[.foo.bar.-]" ; +# assert.result ".." : make "[.-]" ; +# assert.result ".." : make "[-]" ; +# assert.result "." : make "[]" ; +# assert.result "giz.h" : make "giz.h" ; +# assert.result "foo/bar/giz.h" : make "[.foo.bar]giz.h" ; +# assert.result "/disk:/my_docs" : make "disk:[my_docs]" ; +# assert.result "/disk:/boost/tools/build/new/project.jam" : make "disk:[boost.tools.build.test.-.new]project.jam" ; +# +# # +# # Special case (adds '.' to end of file w/o extension to +# # disambiguate from directory in portable path spec). +# # +# assert.result "Jamfile." : make "Jamfile" ; +# assert.result "dir/Jamfile." : make "[.dir]Jamfile" ; +# assert.result "/disk:/dir/Jamfile." : make "disk:[dir]Jamfile" ; +# +# # +# # Make native paths +# # +# assert.result "disk:" : native "/disk:" ; +# assert.result "[.foo.bar.giz]" : native "foo/bar/giz" ; +# assert.result "[.foo]" : native "foo" ; +# assert.result "[.-]" : native ".." ; +# assert.result "[.foo.-]" : native "foo/.." ; +# assert.result "[]" : native "." ; +# assert.result "disk:[my_docs.work]" : native "/disk:/my_docs/work" ; +# assert.result "giz.h" : native "giz.h" ; +# assert.result "disk:Jamfile." : native "/disk:Jamfile." ; +# assert.result "disk:[my_docs.work]Jamfile." : native "/disk:/my_docs/work/Jamfile." ; +# +# modules.poke path : os : $(save-os) ; +# +# } + +# + + +#def glob(dir, patterns): +# result = [] +# for pattern in patterns: +# result.extend(builtin_glob(os.path.join(dir, pattern))) +# return result + +def glob(dirs, patterns, exclude_patterns=None): + """Returns the list of files matching the given pattern in the + specified directory. Both directories and patterns are + supplied as portable paths. Each pattern should be non-absolute + path, and can't contain '.' or '..' elements. Each slash separated + element of pattern can contain the following special characters: + - '?', which match any character + - '*', which matches arbitrary number of characters. + A file $(d)/e1/e2/e3 (where 'd' is in $(dirs)) matches pattern p1/p2/p3 + if and only if e1 matches p1, e2 matches p2 and so on. + For example: + [ glob . : *.cpp ] + [ glob . : */build/Jamfile ] + """ + + assert(isinstance(patterns, list)) + assert(isinstance(dirs, list)) + + if not exclude_patterns: + exclude_patterns = [] + else: + assert(isinstance(exclude_patterns, list)) + + real_patterns = [os.path.join(d, p) for p in patterns for d in dirs] + real_exclude_patterns = [os.path.join(d, p) for p in exclude_patterns + for d in dirs] + + inc = [os.path.normpath(name) for p in real_patterns + for name in builtin_glob(p)] + exc = [os.path.normpath(name) for p in real_exclude_patterns + for name in builtin_glob(p)] + return [x for x in inc if x not in exc] + +def glob_tree(roots, patterns, exclude_patterns=None): + """Recursive version of GLOB. Builds the glob of files while + also searching in the subdirectories of the given roots. An + optional set of exclusion patterns will filter out the + matching entries from the result. The exclusions also apply + to the subdirectory scanning, such that directories that + match the exclusion patterns will not be searched.""" + + if not exclude_patterns: + exclude_patterns = [] + + result = glob(roots, patterns, exclude_patterns) + subdirs = [s for s in result if s != "." and s != ".." and os.path.isdir(s)] + if subdirs: + result.extend(glob_tree(subdirs, patterns, exclude_patterns)) + + return result + +def glob_in_parents(dir, patterns, upper_limit=None): + """Recursive version of GLOB which glob sall parent directories + of dir until the first match is found. Returns an empty result if no match + is found""" + + assert(isinstance(dir, str)) + assert(isinstance(patterns, list)) + + result = [] + + absolute_dir = os.path.join(os.getcwd(), dir) + absolute_dir = os.path.normpath(absolute_dir) + while absolute_dir: + new_dir = os.path.split(absolute_dir)[0] + if new_dir == absolute_dir: + break + result = glob([new_dir], patterns) + if result: + break + absolute_dir = new_dir + + return result + + +# The relpath functionality is written by +# Cimarron Taylor +def split(p, rest=[]): + (h,t) = os.path.split(p) + if len(h) < 1: return [t]+rest + if len(t) < 1: return [h]+rest + return split(h,[t]+rest) + +def commonpath(l1, l2, common=[]): + if len(l1) < 1: return (common, l1, l2) + if len(l2) < 1: return (common, l1, l2) + if l1[0] != l2[0]: return (common, l1, l2) + return commonpath(l1[1:], l2[1:], common+[l1[0]]) + +def relpath(p1, p2): + (common,l1,l2) = commonpath(split(p1), split(p2)) + p = [] + if len(l1) > 0: + p = [ '../' * len(l1) ] + p = p + l2 + if p: + return os.path.join( *p ) + else: + return "." diff --git a/v2/util/regex.py b/v2/util/regex.py new file mode 100644 index 000000000..29e26ecf4 --- /dev/null +++ b/v2/util/regex.py @@ -0,0 +1,25 @@ +# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +import re + +def transform (list, pattern, indices = [1]): + """ Matches all elements of 'list' agains the 'pattern' + and returns a list of the elements indicated by indices of + all successfull matches. If 'indices' is omitted returns + a list of first paranthethised groups of all successfull + matches. + """ + result = [] + + for e in list: + m = re.match (pattern, e) + + if m: + for i in indices: + result.append (m.group (i)) + + return result + diff --git a/v2/util/sequence.py b/v2/util/sequence.py new file mode 100644 index 000000000..a0160107d --- /dev/null +++ b/v2/util/sequence.py @@ -0,0 +1,52 @@ +# (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +import operator + +def unique (values): + # TODO: is this the most efficient way? + # consider using a set from Python 2.4. + return list(set(values)) +# cache = {} +# result = [] +# for v in values: +# if not cache.has_key(v): +# cache[v] = None +# result.append(v) +# return result + + + +def max_element (elements, ordered = None): + """ Returns the maximum number in 'elements'. Uses 'ordered' for comparisons, + or '<' is none is provided. + """ + if not ordered: ordered = operator.lt + + max = elements [0] + for e in elements [1:]: + if ordered (max, e): + max = e + + return max + +def select_highest_ranked (elements, ranks): + """ Returns all of 'elements' for which corresponding element in parallel + list 'rank' is equal to the maximum value in 'rank'. + """ + if not elements: + return [] + + max_rank = max_element (ranks) + + result = [] + while elements: + if ranks [0] == max_rank: + result.append (elements [0]) + + elements = elements [1:] + ranks = ranks [1:] + + return result diff --git a/v2/util/set.py b/v2/util/set.py new file mode 100644 index 000000000..dc7cf3282 --- /dev/null +++ b/v2/util/set.py @@ -0,0 +1,42 @@ +# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +from utility import to_seq + +def difference (b, a): + """ Returns the elements of B that are not in A. + """ + result = [] + for element in b: + if not element in a: + result.append (element) + + return result + +def intersection (set1, set2): + """ Removes from set1 any items which don't appear in set2 and returns the result. + """ + result = [] + for v in set1: + if v in set2: + result.append (v) + return result + +def contains (small, large): + """ Returns true iff all elements of 'small' exist in 'large'. + """ + small = to_seq (small) + large = to_seq (large) + + for s in small: + if not s in large: + return False + return True + +def equal (a, b): + """ Returns True iff 'a' contains the same elements as 'b', irrespective of their order. + # TODO: Python 2.4 has a proper set class. + """ + return contains (a, b) and contains (b, a) diff --git a/v2/util/utility.py b/v2/util/utility.py new file mode 100644 index 000000000..afea765b9 --- /dev/null +++ b/v2/util/utility.py @@ -0,0 +1,155 @@ +# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +""" Utility functions to add/remove/get grists. + Grists are string enclosed in angle brackets (<>) that are used as prefixes. See Jam for more information. +""" + +import re +import os +import bjam +from b2.exceptions import * + +__re_grist_and_value = re.compile (r'(<[^>]*>)(.*)') +__re_grist_content = re.compile ('^<(.*)>$') +__re_backslash = re.compile (r'\\') + +def to_seq (value): + """ If value is a sequence, returns it. + If it is a string, returns a sequence with value as its sole element. + """ + if not value: + return [] + + if isinstance (value, str): + return [value] + + else: + return value + +def replace_references_by_objects (manager, refs): + objs = [] + for r in refs: + objs.append (manager.get_object (r)) + return objs + +def add_grist (features): + """ Transform a string by bracketing it with "<>". If already bracketed, does nothing. + features: one string or a sequence of strings + return: the gristed string, if features is a string, or a sequence of gristed strings, if features is a sequence + """ + + def grist_one (feature): + if feature [0] != '<' and feature [len (feature) - 1] != '>': + return '<' + feature + '>' + else: + return feature + + if isinstance (features, str): + return grist_one (features) + else: + return [ grist_one (feature) for feature in features ] + +def replace_grist (features, new_grist): + """ Replaces the grist of a string by a new one. + Returns the string with the new grist. + """ + def replace_grist_one (name, new_grist): + split = __re_grist_and_value.match (name) + if not split: + return new_grist + name + else: + return new_grist + split.group (2) + + if isinstance (features, str): + return replace_grist_one (features, new_grist) + else: + return [ replace_grist_one (feature, new_grist) for feature in features ] + +def get_value (property): + """ Gets the value of a property, that is, the part following the grist, if any. + """ + return replace_grist (property, '') + +def get_grist (value): + """ Returns the grist of a string. + If value is a sequence, does it for every value and returns the result as a sequence. + """ + def get_grist_one (name): + split = __re_grist_and_value.match (name) + if not split: + return '' + else: + return split.group (1) + + if isinstance (value, str): + return get_grist_one (value) + else: + return [ get_grist_one (v) for v in value ] + +def ungrist (value): + """ Returns the value without grist. + If value is a sequence, does it for every value and returns the result as a sequence. + """ + def ungrist_one (value): + stripped = __re_grist_content.match (value) + if not stripped: + raise BaseException ("in ungrist: '%s' is not of the form <.*>" % value) + + return stripped.group (1) + + if isinstance (value, str): + return ungrist_one (value) + else: + return [ ungrist_one (v) for v in value ] + +def replace_suffix (name, new_suffix): + """ Replaces the suffix of name by new_suffix. + If no suffix exists, the new one is added. + """ + split = os.path.splitext (name) + return split [0] + new_suffix + +def forward_slashes (s): + """ Converts all backslashes to forward slashes. + """ + return __re_backslash.sub ('/', s) + + +def split_action_id (id): + """ Splits an id in the toolset and specific rule parts. E.g. + 'gcc.compile.c++' returns ('gcc', 'compile.c++') + """ + split = id.split ('.', 1) + toolset = split [0] + name = '' + if len (split) > 1: + name = split [1] + return (toolset, name) + +def os_name (): + result = bjam.variable("OS") + assert(len(result) == 1) + return result[0] + +def platform (): + return bjam.variable("OSPLAT") + +def os_version (): + return bjam.variable("OSVER") + +def on_windows (): + """ Returns true if running on windows, whether in cygwin or not. + """ + if bjam.variable("NT"): + return True + + elif bjam.variable("UNIX"): + + uname = bjam.variable("JAMUNAME") + if uname and uname[0].startswith("CYGWIN"): + return True + + return False From 6a166a80e92a60a8263f752ce1cf65b91a25a1dc Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 27 Jul 2009 09:22:07 +0000 Subject: [PATCH 055/198] Add Python bootstrap code [SVN r55202] --- v2/kernel/bootstrap.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 v2/kernel/bootstrap.py diff --git a/v2/kernel/bootstrap.py b/v2/kernel/bootstrap.py new file mode 100644 index 000000000..cc12add11 --- /dev/null +++ b/v2/kernel/bootstrap.py @@ -0,0 +1,25 @@ +# Copyright 2009 Vladimir Prus +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +import imp +import sys + +def bootstrap(root_path): + """Performs python-side bootstrapping of Boost.Build/Python. + + This function arranges for 'b2.whatever' package names to work, while also + allowing to put python files alongside corresponding jam modules. + """ + + m = imp.new_module("b2") + # Note that: + # 1. If __path__ is not list of strings, nothing will work + # 2. root_path is already list of strings. + m.__path__ = root_path + sys.modules["b2"] = m + + import b2.build_system + b2.build_system.main() + From 7f11155d0616ac1942c45f3eb96ed61d753f0b37 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 28 Jul 2009 07:11:09 +0000 Subject: [PATCH 056/198] Improve reporting of failed test due to unexpected changes. [SVN r55220] --- v2/test/BoostBuild.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/v2/test/BoostBuild.py b/v2/test/BoostBuild.py index 1324b154c..35a4f22cf 100644 --- a/v2/test/BoostBuild.py +++ b/v2/test/BoostBuild.py @@ -667,9 +667,10 @@ class Tester(TestCmd.TestCmd): self.ignore('*/gmon.out') if not self.unexpected_difference.empty(): - print 'FAILED' - print '------- The following changes were unexpected -------' - self.unexpected_difference.pprint() + annotation('failure', 'Unexpected changes found') + output = StringIO.StringIO() + self.unexpected_difference.pprint(output) + annotation("unexpected changes", output.getvalue()) self.fail_test(1) def __expect_line(self, content, expected, expected_to_exist): From 7d1d907ec5175b5694d6e438062c8820dc99f93d Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 1 Aug 2009 10:31:05 +0000 Subject: [PATCH 057/198] Testsuite fixes for windows. [SVN r55332] --- v2/test/generators-test/extra.jam | 13 +++++++++---- v2/test/generators-test/nm.jam | 6 +++++- v2/test/notfile.py | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/v2/test/generators-test/extra.jam b/v2/test/generators-test/extra.jam index 88e142dd5..d3500c73c 100644 --- a/v2/test/generators-test/extra.jam +++ b/v2/test/generators-test/extra.jam @@ -65,7 +65,10 @@ TOUCH = [ common.file-touch-command ] ; actions whale { echo "Whale consuming " $(>) - $(TOUCH) $(<) + $(TOUCH) $(<[1]) + $(TOUCH) $(<[2]) + $(TOUCH) $(<[3]) + $(TOUCH) $(<[4]) } rule dolphin ( targets * : source * : properties * ) @@ -85,7 +88,8 @@ rule wd ( targets * : source * : properties * ) actions wd { echo "WD consuming" $(>) - $(TOUCH) $(<) + $(TOUCH) $(<[1]) + $(TOUCH) $(<[2]) } rule x ( target * : source * : properties * ) @@ -96,7 +100,7 @@ rule x ( target * : source * : properties * ) actions x { echo "X: source is " $(>) - $(TOUCH) $(<) + $(TOUCH) $(<[1]) } rule x_pro ( target * : source * : properties * ) @@ -107,7 +111,8 @@ rule x_pro ( target * : source * : properties * ) actions x_pro { echo "X_PRO: source is " $(>) - $(TOUCH) $(<) + $(TOUCH) $(<[1]) + $(TOUCH) $(<[2]) } diff --git a/v2/test/generators-test/nm.jam b/v2/test/generators-test/nm.jam index ae5cda8a5..0b1ad984a 100644 --- a/v2/test/generators-test/nm.jam +++ b/v2/test/generators-test/nm.jam @@ -4,6 +4,9 @@ # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) import modules ; +import common ; + +TOUCH = [ common.file-touch-command ] ; rule target-source ( targets * : sources * : properties * ) { @@ -30,7 +33,8 @@ rule cpp-mark ( targets * : sources * : properties * ) actions cpp-mark { echo "CPP-MARK consuming " $(>) - touch $(<) + $(TOUCH) $(<[1]) + $(TOUCH) $(<[2]) } diff --git a/v2/test/notfile.py b/v2/test/notfile.py index ba6509392..c061326a9 100644 --- a/v2/test/notfile.py +++ b/v2/test/notfile.py @@ -22,7 +22,7 @@ notfile hello_valgrind : @valgrind : hello ; actions valgrind { - valgrind $(>) + valgrind $(>[1]) } """) From dc1d368b4215862d4e1c6bdf338d5f8971167d3f Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 1 Aug 2009 10:32:10 +0000 Subject: [PATCH 058/198] Supress warning message when target does not exist. [SVN r55333] --- v2/tools/symlink.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/symlink.jam b/v2/tools/symlink.jam index bdfcb546a..b33e8260c 100644 --- a/v2/tools/symlink.jam +++ b/v2/tools/symlink.jam @@ -133,7 +133,7 @@ actions ln-UNIX actions ln-NT { echo "NT symlinks not supported yet, making copy" - del /f /q "$(<)" 2$(NULL_OUT) $(NULL_OUT) + del /f /q "$(<)" 2>nul >nul copy "$(>)" "$(<)" $(NULL_OUT) } From 3f897d99456b235fb71faf08401d0d1d6c758249 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 1 Aug 2009 10:33:24 +0000 Subject: [PATCH 059/198] Fix improper name of PDB and manifest targets. [SVN r55334] --- v2/tools/msvc.jam | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index c1fcd8eca..cda37f382 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -1031,11 +1031,13 @@ class msvc-linking-generator : linking-generator if $(result) { - local name = [ $(result[0]).name ] ; + local name-main = [ $(result[0]).name ] ; local action = [ $(result[0]).action ] ; if [ $(property-set).get ] = "on" - { + { + # PDB is 'good' target -- the basename is the same of for main target, with + # .pdb as extension local target = [ class.new file-target $(name) : PDB : $(project) : $(action) ] ; local registered-target = [ virtual-target.register $(target) ] ; if $(target) != $(registered-target) @@ -1047,7 +1049,10 @@ class msvc-linking-generator : linking-generator if [ $(property-set).get ] = "off" { - local target = [ class.new file-target $(name) : MANIFEST : $(project) : $(action) ] ; + # Manifest is evil target. It has .manifest appened to the name of + # main target, including extension. E.g. a.exe.manifest. We use 'exact' + # name because to achieve this effect. + local target = [ class.new file-target $(name-main).manifest exact : MANIFEST : $(project) : $(action) ] ; local registered-target = [ virtual-target.register $(target) ] ; if $(target) != $(registered-target) { From 2203adba3e710846f8d7682c3a6eee2db1c10da0 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 11 Aug 2009 16:53:35 +0000 Subject: [PATCH 060/198] Properly register targets created by notfile generator. [SVN r55529] --- v2/tools/notfile.jam | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/v2/tools/notfile.jam b/v2/tools/notfile.jam index 48d00f2de..97a5b0e87 100644 --- a/v2/tools/notfile.jam +++ b/v2/tools/notfile.jam @@ -39,7 +39,8 @@ class notfile-generator : generator action = [ new action $(sources) : notfile.run : $(property-set) ] ; } - return [ new notfile-target $(name) : $(project) : $(action) ] ; + return [ virtual-target.register + [ new notfile-target $(name) : $(project) : $(action) ] ] ; } } From ccd3103a80d73ad7b00d8d248e32bb5dec8c86d0 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 22 Aug 2009 08:04:31 +0000 Subject: [PATCH 061/198] New --dump-generators option to list all generators. [SVN r55711] --- v2/build-system.jam | 10 +++++++--- v2/build/generators.jam | 11 +++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/v2/build-system.jam b/v2/build-system.jam index ef2e83068..434d2195e 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -25,7 +25,7 @@ import toolset ; import utility ; import version ; import virtual-target ; - +import generators ; ################################################################################ # @@ -686,8 +686,12 @@ local rule should-clean-project ( project ) { targets += [ project.target [ project.module-name "." ] ] ; } - - + + if [ option.get dump-generators : : true ] + { + generators.dump ; + } + # Now that we have a set of targets to build and a set of property sets to # build the targets with, we can start the main build process by using each # property set to generate virtual targets from all of our listed targets diff --git a/v2/build/generators.jam b/v2/build/generators.jam index d086bb006..1b7a269bb 100644 --- a/v2/build/generators.jam +++ b/v2/build/generators.jam @@ -722,6 +722,8 @@ class generator # rule register ( g ) { + .all-generators += $(g) ; + # A generator can produce several targets of the same type. We want unique # occurrence of that generator in .generators.$(t) in that case, otherwise, # it will be tried twice and we will get a false ambiguity. @@ -1393,3 +1395,12 @@ rule add-usage-requirements ( result * : raw-properties * ) } } } + +rule dump ( ) +{ + for local g in $(.all-generators) + { + ECHO [ $(g).id ] ":" [ $(g).source-types ] -> [ $(g).target-types ] ; + } +} + From 12f3e6fb225218fc5824d1013af3527028cd8c75 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 22 Aug 2009 08:21:07 +0000 Subject: [PATCH 062/198] Fix viable-source-types-for-generator-real looping thinko. The loop over 'source-types' was meant to terminate if we ever get '*'. However, it was for loop, and assinging empty list to source-types had no effect. As result, the function would sometimes return a list containing '*' and other types, which other code did not expect and mishandled. [SVN r55712] --- v2/build/generators.jam | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/build/generators.jam b/v2/build/generators.jam index 1b7a269bb..1515525f2 100644 --- a/v2/build/generators.jam +++ b/v2/build/generators.jam @@ -952,8 +952,10 @@ rule viable-source-types-for-generator-real ( generator ) else { local result ; - for local s in $(source-types) + while $(source-types) { + local s = $(source-types[1]) ; + source-types = $(source-types[2-]) ; local viable-sources = [ generators.viable-source-types $(s) ] ; if $(viable-sources) = * { From 9f84f695dd3ec1eb4613d61bdb9c35c073df6cd4 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 22 Aug 2009 08:38:00 +0000 Subject: [PATCH 063/198] Minir doc tweaks [SVN r55714] --- v2/doc/src/advanced.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/doc/src/advanced.xml b/v2/doc/src/advanced.xml index 3350c273d..8d194742a 100644 --- a/v2/doc/src/advanced.xml +++ b/v2/doc/src/advanced.xml @@ -768,7 +768,8 @@ bjam toolset=gcc variant=debug optimization=space (Arbitrary string) - Additional macro definitions for C and C++ compilers. + Additional macro definitions for C and C++ compilers. The string should be either + SYMBOL or SYMBOL=VALUE @@ -1022,6 +1023,7 @@ exe c : c.cpp /boost/program_options//program_options ;
Requirements + requirements Requirements are the properties that should always be present when building a target. Typically, they are includes and defines: From 0f454eb2aa3504911abbd97e776a8eb4af6b830e Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 22 Aug 2009 08:42:34 +0000 Subject: [PATCH 064/198] Rename advanced.xml to overview.xml, to reduce confusion. [SVN r55715] --- v2/doc/src/faq.xml | 2 +- v2/doc/src/howto.xml | 6 +-- v2/doc/src/{advanced.xml => overview.xml} | 52 +++++++++++------------ v2/doc/src/reference.xml | 12 +++--- v2/doc/src/standalone.xml | 2 +- v2/doc/src/tasks.xml | 6 +-- v2/doc/src/tutorial.xml | 2 +- v2/doc/src/userman.xml | 2 +- v2/doc/src/v1_vs_v2.xml | 14 +++--- 9 files changed, 49 insertions(+), 49 deletions(-) rename v2/doc/src/{advanced.xml => overview.xml} (97%) diff --git a/v2/doc/src/faq.xml b/v2/doc/src/faq.xml index fe1bd5a81..551e22e25 100644 --- a/v2/doc/src/faq.xml +++ b/v2/doc/src/faq.xml @@ -30,7 +30,7 @@ Use conditional requirements or indirect conditional requirements. See - . + . diff --git a/v2/doc/src/howto.xml b/v2/doc/src/howto.xml index 4a8b892b3..bd128a321 100644 --- a/v2/doc/src/howto.xml +++ b/v2/doc/src/howto.xml @@ -9,20 +9,20 @@ If you've just found out about Boost.Build V2 and want to know if it will work for you, start with . You can continue with . When you're ready to try Boost.Build + linkend="bbv2.overview" />. When you're ready to try Boost.Build in practice, go to . If you are about to use Boost.Build on your project, or already using it and have a problem, look at . + "bbv2.overview"/>. If you're trying to build a project which uses Boost.Build, see and then read about - . + . diff --git a/v2/doc/src/advanced.xml b/v2/doc/src/overview.xml similarity index 97% rename from v2/doc/src/advanced.xml rename to v2/doc/src/overview.xml index 8d194742a..20977b0a3 100644 --- a/v2/doc/src/advanced.xml +++ b/v2/doc/src/overview.xml @@ -2,7 +2,7 @@ - + Overview @@ -57,32 +57,32 @@ - How to configure + How to configure Boost.Build - How to declare targets in + How to declare targets in Jamfiles - How the build process + How the build process works Some Basics about the Boost.Jam language. See . + "bbv2.overview.jam_language"/>. -
+
Boost.Jam Language @@ -245,7 +245,7 @@ import module : rule ; unqualified names. - + Sometimes, you'd need to specify the actual command lines to be used when creating targets. In jam language, you use named actions to do this. For example: @@ -293,7 +293,7 @@ actions create-file-from-another
-
+
Configuration @@ -491,7 +491,7 @@ using gcc : 3.4 : g++-3.4 ;
-
+
Invocation To invoke Boost.Build, type bjam on the command line. Three kinds @@ -523,7 +523,7 @@ using gcc : 3.4 : g++-3.4 ; -
+
Examples To build all targets defined in Jamfile in the current directory with default properties, run: @@ -546,7 +546,7 @@ bjam toolset=gcc variant=debug optimization=space
-
+
Options Boost.Build recognizes the following command line options. @@ -688,13 +688,13 @@ bjam toolset=gcc variant=debug optimization=space
-
+
Properties In the simplest case, the build is performed with a single set of properties, that you specify on the command line with elements in the form feature=value. - The complete list of features can be found in . + The complete list of features can be found in . The most common features are summarized below. @@ -842,7 +842,7 @@ bjam include=static,shared -
+
Targets All command line elements that are neither options nor properties are the names of the @@ -852,10 +852,10 @@ bjam include=static,shared
-
+
Declaring Targets - + A Main target is a user-defined named entity that can be built, for example an executable file. Declaring a main target is usually done using one of the main @@ -989,7 +989,7 @@ exe b : [ glob *.cpp ] ; # all .cpp files in this directory are sources considered relative to the source directory — which is typically the directory where the Jamfile is located, but can be changed as described in . + "bbv2.overview.projects.attributes.projectrule"/>. @@ -1021,7 +1021,7 @@ exe c : c.cpp /boost/program_options//program_options ;
-
+
Requirements requirements Requirements are the properties that should always be present when @@ -1030,7 +1030,7 @@ exe c : c.cpp /boost/program_options//program_options ; exe hello : hello.cpp : <include>/opt/boost <define>MY_DEBUG ; There is a number of other features, listed in - . For example if + . For example if a library can only be built statically, or a file can't be compiled with optimization due to a compiler bug, one can use @@ -1039,7 +1039,7 @@ obj main : main.cpp : <optimization>off ; - Sometimes, particular relationships need to be maintained + Sometimes, particular relationships need to be maintained among a target's build properties. This can be achieved with conditional requirements. For example, you might want to set @@ -1067,7 +1067,7 @@ lib network : network.cpp - + A more powerful variant of conditional requirements is indirect conditional requirements. You can provide a rule that will be called with the current build properties and can compute additional properties @@ -1184,7 +1184,7 @@ explicit install_programs ;
-
+
Projects As mentioned before, targets are grouped into projects, @@ -1244,7 +1244,7 @@ project tennis documentation; you almost *had* to get it wrong once. --> - + The default values for those attributes are given in the table below. @@ -1372,7 +1372,7 @@ project tennis of material. -->
-
+
The Build Process When you've described your targets, you want Boost.Build to run the @@ -1405,7 +1405,7 @@ project tennis -
+
Build Request @@ -1423,7 +1423,7 @@ bjam app1 lib1//lib1 toolset=gcc variant=debug optimization=full bjam app1 lib1//lib1 gcc debug optimization=full The complete syntax, which has some additional shortcuts, is - described in . + described in .
diff --git a/v2/doc/src/reference.xml b/v2/doc/src/reference.xml index f0009fea2..4141ec16d 100644 --- a/v2/doc/src/reference.xml +++ b/v2/doc/src/reference.xml @@ -158,7 +158,7 @@ ECHO [ glob-tree *.cpp : .svn ] ; project Declares project id and attributes, including - project requirements. See . + project requirements. See . @@ -235,7 +235,7 @@ path-constant DATA : data/a.txt ;
-
+
Builtin features This section documents the features that are built-in into @@ -299,7 +299,7 @@ path-constant DATA : data/a.txt ; - + link @@ -314,7 +314,7 @@ path-constant DATA : data/a.txt ; - + runtime linking runtime-link @@ -1399,7 +1399,7 @@ using stlport : version : Build process The general overview of the build process was given in the - user documentation. + user documentation. This section provides additional details, and some specific rules. @@ -1631,7 +1631,7 @@ exe a : a.cpp Features of this kind are propagated to dependencies. That is, if a main target is built using a + "bbv2.overview.targets.main">main target is built using a propagated property, the build systems attempts to use the same property when building any of its dependencies as part of that main diff --git a/v2/doc/src/standalone.xml b/v2/doc/src/standalone.xml index be5743dfd..dde41b0db 100644 --- a/v2/doc/src/standalone.xml +++ b/v2/doc/src/standalone.xml @@ -11,7 +11,7 @@ - + diff --git a/v2/doc/src/tasks.xml b/v2/doc/src/tasks.xml index 0d7549988..2b3e68bd1 100644 --- a/v2/doc/src/tasks.xml +++ b/v2/doc/src/tasks.xml @@ -12,7 +12,7 @@ This section describes main targets types that Boost.Build supports out-of-the-box. Unless otherwise noted, all mentioned main target rules have - the common signature, described in . + the common signature, described in .
@@ -537,7 +537,7 @@ actions in2out If you run bjam and file.out does not exist, Boost.Build will run the in2out command to create that file. For more details on specifying actions, see . + linkend="bbv2.overview.jam_language.actions"/>. @@ -725,7 +725,7 @@ exe app : app.cpp : <implicit-dependency>parser ; When using gcc, you first need to specify your cross compiler - in user-config.jam (see ), + in user-config.jam (see ), for example: using gcc : arm : arm-none-linux-gnueabi-g++ ; diff --git a/v2/doc/src/tutorial.xml b/v2/doc/src/tutorial.xml index b63fac3a3..19c387efa 100644 --- a/v2/doc/src/tutorial.xml +++ b/v2/doc/src/tutorial.xml @@ -285,7 +285,7 @@ top/ information More details can be found in - . + . diff --git a/v2/doc/src/userman.xml b/v2/doc/src/userman.xml index 437852136..a431800ba 100644 --- a/v2/doc/src/userman.xml +++ b/v2/doc/src/userman.xml @@ -11,7 +11,7 @@ - + diff --git a/v2/doc/src/v1_vs_v2.xml b/v2/doc/src/v1_vs_v2.xml index 482b61cf8..91b639cf5 100644 --- a/v2/doc/src/v1_vs_v2.xml +++ b/v2/doc/src/v1_vs_v2.xml @@ -16,7 +16,7 @@ some of the syntax was changed, and some new important features were added. This chapter describes most of the changes. -
+
Configuration In V1, toolsets were configured by environment variables. If you @@ -25,12 +25,12 @@ toolset. In V2, toolsets are configured by the using, and you can easily configure several versions of a toolset. See for details. + linkend="bbv2.overview.configuration"/> for details.
-
+
Writing Jamfiles Probably one of the most important differences in V2 Jamfiles is @@ -38,7 +38,7 @@ requirements (for example, a common #include path), it was necessary to manually write the requirements or use a helper rule or template target. In V2, the common properties can be specified with the requirements project - attribute, as documented in . + attribute, as documented in . Usage requirements @@ -54,7 +54,7 @@ The difference between lib and dll targets in V1 is completely eliminated in V2. There's only one library target type, lib, which can create either static or shared libraries depending on the value of the - <link> + <link> feature. If your target should be only built in one way, you can add <link>shared or <link>static to its requirements. @@ -77,7 +77,7 @@ exe a : a.cpp ../foo//bar ;
-
+
Build process The command line syntax in V2 is completely different. For example @@ -92,7 +92,7 @@ bjam toolset=msvc variant=release some_target bjam msvc release some_target - See the reference for a + See the reference for a complete description of the syntax. From 439c0b1d377f4489944031ca9a5c2afb3fb5179f Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 22 Aug 2009 12:23:39 +0000 Subject: [PATCH 065/198] Expand overview. Improve extending documentation. [SVN r55716] --- v2/doc/src/extending.xml | 433 ++++++++++++++++++++++++++++++++++----- v2/doc/src/overview.xml | 87 ++++++++ v2/doc/src/reference.xml | 174 ---------------- 3 files changed, 473 insertions(+), 221 deletions(-) diff --git a/v2/doc/src/extending.xml b/v2/doc/src/extending.xml index a702d3ca5..3d0fda569 100644 --- a/v2/doc/src/extending.xml +++ b/v2/doc/src/extending.xml @@ -8,64 +8,403 @@
Introduction - This document explains how to extend Boost.Build to accomodate - your local requirements. Let's start with a simple but - realistic example. + + This section explains how to extend Boost.Build to accomodate your + local requirements—primarily to add support for non-standard + tools you have. Before we start, be sure you have read and understoon + the concept of metatarget, , + which is critical to understanding the remaining material. + - Say you're writing an application that generates C++ code. If - you ever did this, you know that it's not nice. Embedding large - portions of C++ code in string literals is very awkward. A much - better solution is: + + The current version of Boost.Build has three levels of targets, listed + below. + - - - - Write the template of the code to be generated, leaving - placeholders at the points that will change - - + + + + metatarget + + + Object that is created from declarations in Jamfiles. May + be called with a set of properties to produce concrete + targets. + + + + + + concrete target + + + Object that corresponds to a file or an action. + + + + + + jam target + + + Low-level concrete target that is specific to Boost.Jam build + engine. Essentially a string—most often a name of file. + + + + + - - - Access the template in your application and replace - placeholders with appropriate text. - - + + In most cases, you will only have to deal with concrete targets and + the process that creates concrete targets from + metatargets. Extending metatarget level is rarely required. The jam + targets are typically only used inside the command line patterns. + - - Write the result. - - + + All of the Boost.Jam target-related builtin functions, like + DEPENDS or ALWAYS operate on jam + targets. Applying them to metatargets or concrete targets has no + effect. + + +
+ Metatargets + + Metatarget is an object that records information specified + in Jamfile, such as metatarget kind, name, sources and properties, + and can be called with specific properties to generate concrete + targets. At the code level it is represented by an instance of + class derived from abstract-target. + This name is historic, and will be eventuall changed to + metatarget + + + The generate method takes the build properties + (as an instance of the property-set class) and returns + a list containing: + + As front element—Usage-requirements from this invocation + (an instance of property-set) + As subsequent elements—created concrete targets ( + instances of the virtual-target class.) + + + It's possible to lookup a metataget by target-id using the + targets.resolve-reference function, and the + targets.generate-from-reference function can both + lookup and generate a metatarget. + + The abstract-target class has three immediate + derived classes: + + + project-target that + corresponds to a project and is not intended for further + subclassing. The generate method of this + class builds all targets in the project that are not marked as + explicit. + + main-target corresponds to a target in a project + and contains one or more target alternatives. This class also should not be + subclassed. The generate method of this class selects + an alternative to build, and calls the generate method of that + alternative. + + basic-target corresponds to a + specific target alternative. This is base class, with a number of + derived classes. The generate method + processes the target requirements and requested build properties to + determine final properties for the target, builds all sources, and + finally calls the abstract construct method with the list + of source virtual targets, and the final properties. + + + + + The instances of the project-target and + main-target classes are created + implicitly—when loading a new Jamfiles, or when a new target + alternative with as-yet unknown name is created. The instances of the + classes derived from basic-target are typically + created when Jamfile calls a metatarget rule, + such as such as exe. + + + It it permissible to create a custom class derived from + basic-target and create new metatarget rule + that creates instance of such target. However, in the majority + of cases, a specific subclass of basic-target— + typed-target is used. That class is associated + with a type and relays to generators + to construct concrete targets of that type. This process will be explained below. + When a new type is declared, a new metatarget rule is automatically defined. + That rule creates new instance of type-target, associated with that type. + + +
- It's quite easy to achieve. You write special verbatim files that are - just C++, except that the very first line of the file contains the name of a - variable that should be generated. A simple tool is created that takes a - verbatim file and creates a cpp file with a single char* variable - whose name is taken from the first line of the verbatim file and whose value - is the file's properly quoted content. +
+ Concrete targets - Let's see what Boost.Build can do. + Concrete targets are represented by instance of classes derived + from virtual-target. The most commonly used + subclass is file-target. A file target is associated + with an action that creates it— an instance of the action + class. The action, in turn, hold a list of source targets. It also holds the + property-set instance with the build properties that + should be used for the action. - First off, Boost.Build has no idea about "verbatim files". So, you must - register a new target type. The following code does it: + Here's an example of creating a target from another target, source + +local a = [ new action $(source) : common.copy : $(property-set) ] ; +local t = [ new file-target $(name) : CPP : $(project) : $(a) ] ; + + The first line creates an instance of the action> class. + The first parameter is the list of sources. The second parameter is the name + a jam-level action. + The third parameter is the property-set applying to this action. The second line + creates a target. We specifie a name, a type and a project. We also pass the + action object created earlier. If the action creates several targets, we can repeat + the second line several times. + In some cases, code that creates concrete targets may be invoked more than + once with the same properties. Returning to different instance of file-target + that correspond to the same file clearly will result in problems. Therefore, whenever + returning targets you should pass them via the virtual-target.register + function, that will replace targets with previously created identical ones, as + necessary.This create-then-register pattern is caused by limitations + of the Boost.Jam language. Python port is likely to never create duplicate targets. + Here are a couple of examples: + +return [ virtual-target.register $(t) ] ; +return [ sequence.transform virtual-target.register : $(targets) ] ; + + + +
+ +
+ Generators + + In theory, every kind of metatarget in Boost.Build (like exe, + lib or obj) could be implemented + by writing a new metatarget class that, independently of the other code, figures + what files to produce and what commands to use. However, that would be rather inflexible. + For example, adding support for a new compiler would require editing several metatargets. + + + In practice, most files have specific types, and most tools + consume and produce files of specific type. To take advantage of this + fact, Boost.Build defines concept of target type and + generators + generators, and has special metatarget class + typed-target. Target type is merely an + identifier. It is associated with a set of file extensions that + correspond to that type. Generator is an abstraction of a tool. It advertises + the types it produces and, if called with a set of input target, tries to construct + output targets of the advertised types. Finally, typed-target + is associated with specific target type, and relays the generator (or generators) + for that type. + + + A generator is an instance of a class derived from generator. + The generator class itself is suitable for common cases. + You can define derived classes for custom scenarios. + + + + + +
+ +
+ +
+ Example: 1-to-1 generator + + Say you're writing an application that generates C++ code. If + you ever did this, you know that it's not nice. Embedding large + portions of C++ code in string literals is very awkward. A much + better solution is: + + + + + Write the template of the code to be generated, leaving + placeholders at the points that will change + + + + + + Access the template in your application and replace + placeholders with appropriate text. + + + + + Write the result. + + + + It's quite easy to achieve. You write special verbatim files that are + just C++, except that the very first line of the file contains the name of a + variable that should be generated. A simple tool is created that takes a + verbatim file and creates a cpp file with a single char* variable + whose name is taken from the first line of the verbatim file and whose value + is the file's properly quoted content. + + Let's see what Boost.Build can do. + + First off, Boost.Build has no idea about "verbatim files". So, you must + register a new target type. The following code does it: + import type ; type.register VERBATIM : verbatim ; - The first parameter to type.register gives - the name of the declared type. By convention, it's uppercase. The second - parameter is the suffix for files of this type. So, if Boost.Build sees - code.verbatim in a list of sources, it knows that it's of - type VERBATIM. + The first parameter to type.register gives + the name of the declared type. By convention, it's uppercase. The second + parameter is the suffix for files of this type. So, if Boost.Build sees + code.verbatim in a list of sources, it knows that it's of + type VERBATIM. - Next, you tell Boost.Build that the verbatim files can be - transformed into C++ files in one build step. A - generator is a template for a build step that - transforms targets of one type (or set of types) into another. Our - generator will be called verbatim.inline-file; it - transforms VERBATIM files into CPP files: + Next, you tell Boost.Build that the verbatim files can be + transformed into C++ files in one build step. A + generator is a template for a build step that + transforms targets of one type (or set of types) into another. Our + generator will be called verbatim.inline-file; it + transforms VERBATIM files into CPP files: import generators ; @@ -73,9 +412,9 @@ generators.register-standard verbatim.inline-file : VERBATIM : CPP ; - Lastly, you have to inform Boost.Build about the shell - commands used to make that transformation. That's done with an - actions declaration. + Lastly, you have to inform Boost.Build about the shell + commands used to make that transformation. That's done with an + actions declaration. actions inline-file diff --git a/v2/doc/src/overview.xml b/v2/doc/src/overview.xml index 20977b0a3..e6d72e53e 100644 --- a/v2/doc/src/overview.xml +++ b/v2/doc/src/overview.xml @@ -82,6 +82,93 @@ +
+ Concepts + + Boost.Build has a few unique concepts that are introduced in this section. The best + way to explain the concepts is by comparison with more classical build tools. + + + When using any flavour of make, you directly specify targets + and commands that are used to create them from other target. The below example + creates a.o from a.c using a hardcoded + compiler invocation command. + +a.o: a.c + g++ -o a.o -g a.c + + This is rather low-level description mechanism and it's hard to adjust commands, options, + and sets of created targets depending on the used compiler and operating system. + + + + To improve portability, most modern build system provide a set of higher-level + functions that can be used in build description files. Consider this example: + +add_program ("a", "a.c") + + This is a function call that creates targets necessary to create executable file + from source file a.c. Depending on configured properties, + different commands line may be used. However, add_program is higher-level, + but rather thin level. All targets are created immediately when build description + is parsed, which makes it impossible to perform multi-variant builds. Often, change + in any build property requires complete reconfiguration of the build tree. + + + + In order to support true multivariant builds, Boost.Build introduces the concept of + metatarget definition + main target metataget + metatarget—object that is created when build description + is parsed and can be later called with specific build properties to generate + actual targets. + + + + Consider an example: + +exe a : a.cpp ; + + When this declaration is parsed, Boost.Build creates a metatarget, but does not + yet decides what files must be created, or what commands must be used. After + all build files are parsed, Boost.Build considers properties requested on the + command line. Supposed you have invoked Boost.Build with: + +bjam toolset=gcc toolset=msvc + + In that case, the metatarget will be called twice, once with toolset=gcc + and once with toolset=msvc. Both invocations will produce concrete + targets, that will have different extensions and use different command lines. + + + + Another key concept is + propertydefinition + build property. Build property is a variable + that affects the build process. It can be specified on the command line, and is + passed when calling a metatarget. While all build tools have a similar mechanism, + Boost.Build differs by requiring that all build properties are declared in advance, + and providing a large set of properties with portable semantics. + + + + The final concept is propertypropagation + property propagation. Boost.Build does not require that every + metatarget is called with the same properties. Instead, the + "top-level" metatargets are called with the properties specified on the command line. + Each metatarget can elect to augment or override some properties (in particular, + using the requirements mechanism, see ). + Then, the dependency metatargets are called with modified properties and produce + concrete targets that are then used in build process. Of course, dependency metatargets + maybe in turn modify build properties and have dependencies of their own. + + + For more in-depth treatment of the requirements and concepts, you may refer + to SYRCoSE 2009 Boost.Build article. + + +
+
Boost.Jam Language diff --git a/v2/doc/src/reference.xml b/v2/doc/src/reference.xml index 4141ec16d..51720a998 100644 --- a/v2/doc/src/reference.xml +++ b/v2/doc/src/reference.xml @@ -1947,180 +1947,6 @@ requested-properties -> property-path
-
- Generators - - The information is this section is likely to be outdated - and misleading. - - - To construct a main target with given properties from sources, - it is required to create a dependency graph for that main target, - which will also include actions to be run. The algorithm for - creating the dependency graph is described here. - - The fundamental concept is generator. If encapsulates - the notion of build tool and is capable to converting a set of - input targets into a set of output targets, with some properties. - Generator matches a build tool as closely as possible: it works - only when the tool can work with requested properties (for - example, msvc compiler can't work when requested toolset is gcc), - and should produce exactly the same targets as the tool (for - example, if Borland's linker produces additional files with debug - information, generator should also). - - Given a set of generators, the fundamental operation is to - construct a target of a given type, with given properties, from a - set of targets. That operation is performed by rule - generators.construct and the used algorithm is described - below. - -
- Selecting and ranking viable generators - - Each generator, in addition to target types that it can - produce, have attribute that affects its applicability in - particular sitiation. Those attributes are: - - - - - Required properties, which are properties absolutely - necessary for the generator to work. For example, generator - encapsulating the gcc compiler would have <toolset>gcc as - required property. - - - - - - Optional properties, which increase the generators - suitability for a particual build. - - - - - - Generator's required and optional properties may not include - either free or incidental properties. (Allowing this would - greatly complicate caching targets). - - - When trying to construct a target, the first step is to select - all possible generators for the requested target type, which - required properties are a subset of requested properties. - Generators that were already selected up the call stack are - excluded. In addition, if any composing generators were selected - up the call stack, all other composing generators are ignored - (TODO: define composing generators). The found generators - are assigned a rank, which is the number of optional properties - present in requested properties. Finally, generators with highest - rank are selected for futher processing. - -
-
- Running generators - - When generators are selected, each is run to produce a list of - created targets. This list might include targets that are not of - requested types, because generators create the same targets as - some tool, and tool's behaviour is fixed. (Note: should specify - that in some cases we actually want extra targets). If generator - fails, it returns an empty list. Generator is free to call - 'construct' again, to convert sources to the types it can handle. - It also can pass modified properties to 'construct'. However, a - generator is not allowed to modify any propagated properties, - otherwise when actually consuming properties we might discover - that the set of propagated properties is different from what was - used for building sources. - - For all targets that are not of requested types, we try to - convert them to requested type, using a second call to - construct. This is done in order to support - transformation sequences where single source file expands to - several later. See this - message for details. - -
- -
- Selecting dependency graph - - - After all generators are run, - it is necessary to decide which of successfull invocation will be - taken as final result. At the moment, this is not done. Instead, - it is checked whether all successfull generator invocation - returned the same target list. Error is issued otherwise. - - -
- -
- Property adjustment - - Because target location is determined by the build system, it - is sometimes necessary to adjust properties, in order to not - break actions. For example, if there's an action that generates - a header, say "a_parser.h", and a source file "a.cpp" which - includes that file, we must make everything work as if a_parser.h - is generated in the same directory where it would be generated - without any subvariants. - - Correct property adjustment can be done only after all targets - are created, so the approach taken is: - - - - - When dependency graph is constructed, each action can be - assigned a rule for property adjustment. - - - - - - When virtual target is actualized, that rule is run and - return the final set of properties. At this stage it can use - information of all created virtual targets. - - - - - In case of quoted includes, no adjustment can give 100% correct - results. If target dirs are not changed by build system, quoted - includes are searched in "." and then in include path, while angle - includes are searched only in include path. When target dirs are - changed, we'd want to make quoted includes to be search in "." then in - additional dirs and then in the include path and make angle includes - be searched in include path, probably with additional paths added at - some position. Unless, include path already has "." as the first - element, this is not possible. So, either generated headers should not - be included with quotes, or first element of include path should be - ".", which essentially erases the difference between quoted and angle - includes. Note: the only way to get - "." as include path into compiler command line is via verbatim - compiler option. In all other case, Boost.Build will convert "." into - directory where it occurs. - -
- -
- Transformations cache - - - Under certain conditions, an - attempt is made to cache results of transformation search. First, - the sources are replaced with targets with special name and the - found target list is stored. Later, when properties, requested - type, and source type are the same, the store target list is - retrieved and cloned, with appropriate change in names. - - -
-
- -
  • Bug tracker +
  • Bug tracker

    @@ -127,9 +127,9 @@ div.sidebar p.rubric {

    Milestone 13 is planned as bugfix release. Milestone 14 will focus on improving user documentation. Milestone 15 will see most of Boost.Build reimplemented in Python, to make extending - Boost.Build even easier for end users (see PythonPort). + Boost.Build even easier for end users (see PythonPort). The specific issues planned for each release can be found on the - roadmap. + roadmap. @@ -139,7 +139,7 @@ div.sidebar p.rubric { Post everything to the mailing list.

    Bugs and feature requests can be entered at our - bug tracker. + bug tracker.

    If you'd like to help with development, just pick a bug in the tracker that you'd like to fix, or feel free to implement From 40b04382bef443f6b1dfc7b7dbe2b68c2815d9d1 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 22 Oct 2009 08:23:41 +0000 Subject: [PATCH 098/198] Don't pass -m64 if address-model is 32_64 (or anything != 64). [SVN r57061] --- v2/tools/gcc.jam | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 8350e592d..21b265d8a 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -373,10 +373,12 @@ rule setup-address-model ( targets * : sources * : properties * ) { option = -m32 ; } - else + else if $(model) = 64 { option = -m64 ; } + # For darwin, the model can be 32_64. darwin.jam will handle that + # on its own. } OPTIONS on $(targets) += $(option) ; } From 0206ac15a359a854d803d0a0c2e6f712daeec693 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 24 Oct 2009 18:03:02 +0000 Subject: [PATCH 099/198] Make cflags and cxxflags work for intel linux. This was originally in [50708] but appears to have been accidentally undone in [51570]. [SVN r57140] --- v2/tools/intel-linux.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/tools/intel-linux.jam b/v2/tools/intel-linux.jam index 82c498c3a..97effd848 100644 --- a/v2/tools/intel-linux.jam +++ b/v2/tools/intel-linux.jam @@ -118,7 +118,7 @@ rule compile.c++ ( targets * : sources * : properties * ) actions compile.c++ bind PCH_FILE { - "$(CONFIG_COMMAND)" -c -xc++ $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -use-pch"$(PCH_FILE)" -c -o "$(<)" "$(>)" + "$(CONFIG_COMMAND)" -c -xc++ $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -use-pch"$(PCH_FILE)" -c -o "$(<)" "$(>)" } rule compile.c ( targets * : sources * : properties * ) @@ -131,7 +131,7 @@ rule compile.c ( targets * : sources * : properties * ) actions compile.c bind PCH_FILE { - "$(CONFIG_COMMAND)" -c -xc $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -use-pch"$(PCH_FILE)" -c -o "$(<)" "$(>)" + "$(CONFIG_COMMAND)" -c -xc $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -use-pch"$(PCH_FILE)" -c -o "$(<)" "$(>)" } rule compile.c++.pch ( targets * : sources * : properties * ) From 3479c209b2bc3ffeb253ceeaaccfde82e3949ea2 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 4 Nov 2009 16:14:47 +0000 Subject: [PATCH 100/198] Remove debug prints [SVN r57359] --- v2/tools/python.jam | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/v2/tools/python.jam b/v2/tools/python.jam index ae954d638..66f2aabec 100644 --- a/v2/tools/python.jam +++ b/v2/tools/python.jam @@ -1099,15 +1099,6 @@ class python-test-generator : generator local python ; local other-pythons ; - #XXX(bhy) DEBUG CODE BEGIN - ECHO beforerun= ; - for local s in $(sources) - { - ECHO [ $(s).name ] ; - } - ECHO ; - #XXX(bhy) DEBUG CODE END - # Make new target that converting Python source by 2to3 when running with Python 3. local rule make-2to3-source ( source ) { @@ -1268,14 +1259,6 @@ rule bpl-test ( name : sources * : requirements * ) { local s ; sources ?= $(name).py $(name).cpp ; - #XXX(bhy) DEBUG CODE BEGIN - ECHO bpl-test= ; - for local s in $(sources) - { - ECHO $(s) ; - } - ECHO ; - #XXX(bhy) DEBUG CODE END return [ testing.make-test run-pyd : $(sources) /boost/python//boost_python : $(requirements) : $(name) ] ; } From 5f42091cdf53d13b96990169f81d46821552235b Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 5 Nov 2009 08:05:20 +0000 Subject: [PATCH 101/198] Suppress warning. [SVN r57403] --- historic/jam/src/build.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/historic/jam/src/build.bat b/historic/jam/src/build.bat index fa14fc453..f927b7697 100644 --- a/historic/jam/src/build.bat +++ b/historic/jam/src/build.bat @@ -470,7 +470,7 @@ if "_%BJAM_UPDATE%_" == "_update_" ( @echo ON @if "_%BJAM_UPDATE%_" == "_update_" goto Skip_Bootstrap -rd /S /Q bootstrap +if exist bootstrap rd /S /Q bootstrap md bootstrap @if not exist jamgram.y goto Bootstrap_GrammarPrep @if not exist jamgramtab.h goto Bootstrap_GrammarPrep From e15e8d56b802d01c8ec5ec01e8a04ff783e28d33 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 5 Nov 2009 09:39:06 +0000 Subject: [PATCH 102/198] Make cflags affect both C and C++ compilers, per docs. [SVN r57405] --- v2/tools/msvc.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index c66a8e25d..fbc89d670 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -1173,7 +1173,7 @@ local rule register-toolset-really ( ) toolset.flags msvc.compile CFLAGS off/static/multi : /MT ; toolset.flags msvc.compile CFLAGS on/static/multi : /MTd ; - toolset.flags msvc.compile.c OPTIONS : ; + toolset.flags msvc.compile OPTIONS : ; toolset.flags msvc.compile.c++ OPTIONS : ; toolset.flags msvc.compile PDB_CFLAG on/database : /Fd ; From 655d63b7ea2697b570723565300f7eddccf82fb2 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Thu, 5 Nov 2009 23:54:24 +0000 Subject: [PATCH 103/198] Add latest iPhone OS versions. [SVN r57431] --- v2/tools/darwin.jam | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 925db1b91..7a7abd289 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -23,7 +23,9 @@ feature framework : : free ; ## The MacOSX versions we can target. .macosx-versions = 10.6 10.5 10.4 10.3 10.2 10.1 - iphone-2.3 iphonesim-2.3 + iphone-3.2 iphonesim-3.2 + iphone-3.1 iphonesim-3.1 + iphone-3.0 iphonesim-3.0 iphone-2.2 iphonesim-2.2 iphone-2.1 iphonesim-2.1 iphone-2.0 iphonesim-2.0 From 8bdd56f4391d534b24509a7c0937ba400f4c6e2b Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Fri, 6 Nov 2009 02:16:50 +0000 Subject: [PATCH 104/198] Need the point iPhone OS version also, for the SDK detection to work. [SVN r57432] --- v2/tools/darwin.jam | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 7a7abd289..1ceb996c7 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -24,8 +24,10 @@ feature framework : : free ; .macosx-versions = 10.6 10.5 10.4 10.3 10.2 10.1 iphone-3.2 iphonesim-3.2 + iphone-3.1.2 iphonesim-3.1.2 iphone-3.1 iphonesim-3.1 iphone-3.0 iphonesim-3.0 + iphone-2.2.1 iphonesim-2.2.1 iphone-2.2 iphonesim-2.2 iphone-2.1 iphonesim-2.1 iphone-2.0 iphonesim-2.0 From 8934f9f9176605ab8df578e798daf0a719ac1d0c Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Sat, 7 Nov 2009 01:05:17 +0000 Subject: [PATCH 105/198] Make it possible to detect and use patch release MacOSX/iPhoneOS SDKs. [SVN r57442] --- v2/tools/darwin.jam | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 1ceb996c7..e46aa3555 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -215,6 +215,10 @@ local rule init-available-sdk-versions ( condition * : root ? ) local sdk-match = [ MATCH ([^0-9]+)([0-9]+)[.]([0-9x]+)[.]?([0-9x]+)? : $(sdk:D=) ] ; local sdk-platform = $(sdk-match[1]:L) ; local sdk-version = $(sdk-match[2]).$(sdk-match[3]) ; + if $(sdk-match[4]) + { + sdk-version = $(sdk-version).$(sdk-match[4]) ; + } if $(sdk-version) { switch $(sdk-platform) From 30810450f5ab10454ea7a0c8d287dd0b5f45a087 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Sat, 7 Nov 2009 14:53:24 +0000 Subject: [PATCH 106/198] Pass the correct minos options to Xcode for iPhoneOS. [SVN r57449] --- v2/tools/darwin.jam | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index e46aa3555..f3abdd4f3 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -261,10 +261,25 @@ flags darwin.compile OPTIONS ; # option to not be included to let the compiler default take hold. for local macosx-version in $(.macosx-versions) { - flags darwin.compile OPTIONS $(macosx-version) - : -mmacosx-version-min=$(macosx-version) ; - flags darwin.link OPTIONS $(macosx-version) - : -mmacosx-version-min=$(macosx-version) ; + switch $(macosx-version) + { + case iphone* : + { + local version-match = [ MATCH ([^0-9]+)([0-9.]+) : $(macosx-version) ] ; + flags darwin.compile OPTIONS $(macosx-version) + : -miphoneos-version-min=$(version-match[2]) ; + flags darwin.link OPTIONS $(macosx-version) + : -miphoneos-version-min=$(version-match[2]) ; + } + + case * : + { + flags darwin.compile OPTIONS $(macosx-version) + : -mmacosx-version-min=$(macosx-version) ; + flags darwin.link OPTIONS $(macosx-version) + : -mmacosx-version-min=$(macosx-version) ; + } + } } # The following adds objective-c support to darwin. From 1157253f34d1c3ed37cb3b500c1ac016121cf413 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 8 Nov 2009 13:54:58 +0000 Subject: [PATCH 107/198] Detect, and produce proper diagnostics for '10.6 and ppc64 does not work'. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to Boris DuÅ¡ek for testing this patch. [SVN r57484] --- v2/tools/darwin.jam | 154 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 119 insertions(+), 35 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index f3abdd4f3..d8437c7b9 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -16,6 +16,10 @@ import type ; import common ; import generators ; import path : basename ; +import version ; +import property-set ; +import regex ; +import errors ; ## Use a framework. feature framework : : free ; @@ -64,7 +68,7 @@ generators.override darwin.compile.c++.pch : pch.default-cpp-pch-generator ; type.set-generated-target-suffix PCH : darwin : gch ; -toolset.inherit-rules darwin : gcc ; +toolset.inherit-rules darwin : gcc : localize ; toolset.inherit-flags darwin : gcc : static arm/32 @@ -86,6 +90,17 @@ toolset.inherit-flags darwin : gcc # rule init ( version ? : command * : options * : requirement * ) { + # First time around, figure what is host OSX version + if ! $(.host-osx-version) + { + .host-osx-version = [ MATCH "^([0-9.]+)" + : [ SHELL "/usr/bin/sw_vers -productVersion" ] ] ; + if $(.debug-configuration) + { + ECHO notice: OSX version on this machine is $(.host-osx-version) ; + } + } + # - The root directory of the tool install. local root = [ feature.get-values : $(options) ] ; @@ -125,6 +140,8 @@ rule init ( version ? : command * : options * : requirement * ) version ?= $(real-version) ; } + .real-version.$(version) = $(real-version) ; + # - Define the condition for this toolset instance. local condition = [ common.check-init-parameters darwin $(requirement) : version $(version) ] ; @@ -288,10 +305,105 @@ for local macosx-version in $(.macosx-versions) generators.register-c-compiler darwin.compile.m : OBJECTIVE_C : OBJ : darwin ; generators.register-c-compiler darwin.compile.mm : OBJECTIVE_CPP : OBJ : darwin ; -rule compile.m +rule setup-address-model ( targets * : sources * : properties * ) +{ + local ps = [ property-set.create $(properties) ] ; + local arch = [ $(ps).get ] ; + local address-model = [ $(ps).get ] ; + local osx-version = [ $(ps).get ] ; + local gcc-version = [ $(ps).get ] ; + gcc-version = $(.real-version.$(gcc-version)) ; + local options ; + + local support-ppc64 = 1 ; + + osx-version ?= $(.host-osx-version) ; + + if $(osx-version) && ! [ version.version-less [ regex.split $(osx-version) \\. ] : 10 6 ] + { + # When targeting 10.6: + # - gcc 4.2 will give a compiler errir if ppc64 compilation is requested + # - gcc 4.0 will compile fine, somehow, but then fail at link time + support-ppc64 = ; + } + + switch $(arch) + { + case combined : + { + if $(address-model) = 32_64 { + if $(support-ppc64) { + options = -arch i386 -arch ppc -arch x86_64 -arch ppc64 ; + } else { + # Build 3-way binary + options = -arch i386 -arch ppc -arch x86_64 ; + } + } else if $(address-model) = 64 { + if $(support-ppc64) { + options = -arch x86_64 -arch ppc64 ; + } else { + errors.user-error "64-bit PPC compilation is not supported when targeting OSX 10.6 or later" ; + } + } else { + options = -arch i386 -arch ppc ; + } + } + + case x86 : + { + if $(address-model) = 32_64 { + options = -arch i386 -arch x86_64 ; + } else if $(address-model) = 64 { + options = -arch x86_64 ; + } else { + options = -arch i386 ; + } + } + + case power : + { + if ! $(support-ppc64) + && ( $(address-model) = 32_64 || $(address-model) = 64 ) + { + errors.user-error "64-bit PPC compilation is not supported when targeting OSX 10.6 or later" ; + } + + if $(address-model) = 32_64 { + options = -arch ppc -arch ppc64 ; + } else if $(address-model) = 64 { + options = -arch ppc64 ; + } else { + options = -arch ppc ; + } + } + + case arm : + { + options = -arch armv6 ; + } + } + + if $(options) + { + OPTIONS on $(targets) += $(options) ; + } +} + +rule setup-threading ( targets * : sources * : properties * ) +{ + gcc.setup-threading $(targets) : $(sources) : $(properties) ; +} + +rule setup-fpic ( targets * : sources * : properties * ) +{ + gcc.setup-fpic $(targets) : $(sources) : $(properties) ; +} + +rule compile.m ( targets * : sources * : properties * ) { LANG on $(<) = "-x objective-c" ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + setup-arch-addr-flags $(targets) : $(sources) : $(properties) ; } actions compile.m @@ -303,6 +415,7 @@ rule compile.mm ( targets * : sources * : properties * ) { LANG on $(<) = "-x objective-c++" ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + setup-arch-addr-flags $(targets) : $(sources) : $(properties) ; } actions compile.mm @@ -310,37 +423,6 @@ actions compile.mm "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } -# Add option selection for combined and specific architecture combinations. - -local rule arch-addr-flags ( toolset variable - : architecture : address-model + : values + : default ? ) -{ - if $(default) - { - flags $(toolset) $(variable) - $(architecture)/ - : $(values) ; - } - flags $(toolset) $(variable) - /$(address-model) - $(architecture)/$(address-model) - : $(values) ; -} - -arch-addr-flags darwin OPTIONS : combined : 32 : -arch i386 -arch ppc : default ; -arch-addr-flags darwin OPTIONS : combined : 64 : -arch x86_64 -arch ppc64 ; -arch-addr-flags darwin OPTIONS : combined : 32_64 : -arch i386 -arch ppc -arch x86_64 -arch ppc64 ; - -arch-addr-flags darwin OPTIONS : x86 : 32 : -arch i386 : default ; -arch-addr-flags darwin OPTIONS : x86 : 64 : -arch x86_64 ; -arch-addr-flags darwin OPTIONS : x86 : 32_64 : -arch i386 -arch x86_64 ; - -arch-addr-flags darwin OPTIONS : power : 32 : -arch ppc : default ; -arch-addr-flags darwin OPTIONS : power : 64 : -arch ppc64 ; -arch-addr-flags darwin OPTIONS : power : 32_64 : -arch ppc -arch ppc64 ; - -arch-addr-flags darwin OPTIONS : arm : 32 : -arch armv6 : default ; - # Set the max header padding to allow renaming of libs for installation. flags darwin.link.dll OPTIONS : -headerpad_max_install_names ; @@ -383,8 +465,9 @@ local rule prepare-framework-path ( target + ) } } -rule link +rule link ( targets * : sources * : properties * ) { + setup-address-model $(targets) : $(sources) : $(properties) ; prepare-framework-path $(<) ; } @@ -398,8 +481,9 @@ actions link bind LIBRARIES $(NEED_STRIP)"$(.STRIP)" $(NEED_STRIP)-S $(NEED_STRIP)-x $(NEED_STRIP)"$(<)" } -rule link.dll +rule link.dll ( targets * : sources * : properties * ) { + setup-address-model $(targets) : $(sources) : $(properties) ; prepare-framework-path $(<) ; } From 1c984dd917a0fa06f95685746b7151136eda3c85 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 8 Nov 2009 17:32:53 +0000 Subject: [PATCH 108/198] Allow to localize rules when inheriting toolset. [SVN r57490] --- v2/build/toolset.jam | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/build/toolset.jam b/v2/build/toolset.jam index 1ffe08fe7..f2036d999 100644 --- a/v2/build/toolset.jam +++ b/v2/build/toolset.jam @@ -449,7 +449,7 @@ rule inherit-flags ( toolset : base : prohibited-properties * : prohibited-vars } -rule inherit-rules ( toolset : base ) +rule inherit-rules ( toolset : base : localize ? ) { # It appears that "action" creates a local rule. local base-generators = [ generators.generators-for-toolset $(base) ] ; @@ -459,8 +459,8 @@ rule inherit-rules ( toolset : base ) rules += [ MATCH "[^.]*\.(.*)" : [ $(g).rule-name ] ] ; } rules = [ sequence.unique $(rules) ] ; - IMPORT $(base) : $(rules) : $(toolset) : $(rules) ; - IMPORT $(base) : $(rules) : : $(toolset).$(rules) ; + IMPORT $(base) : $(rules) : $(toolset) : $(rules) : $(localize) ; + IMPORT $(toolset) : $(rules) : : $(toolset).$(rules) ; } From 6ae006207e8ff2948ea56264190638b0494fefb9 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 9 Nov 2009 02:13:02 +0000 Subject: [PATCH 109/198] Fix references to non-existent rules that caused building to fail. And fix building with iPhone as it uses a symbolic version number, not jsut numeric. [SVN r57500] --- v2/tools/darwin.jam | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index d8437c7b9..9569f73b3 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -3,7 +3,7 @@ # Copyright 2003, 2004, 2005, 2006 Vladimir Prus # Copyright 2005-2007 Mat Marcus # Copyright 2005-2007 Adobe Systems Incorporated -# Copyright 2007-2008 Rene Rivera +# Copyright 2007-2009 Rene Rivera # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) @@ -319,14 +319,22 @@ rule setup-address-model ( targets * : sources * : properties * ) osx-version ?= $(.host-osx-version) ; - if $(osx-version) && ! [ version.version-less [ regex.split $(osx-version) \\. ] : 10 6 ] + switch $(osx-version) { - # When targeting 10.6: - # - gcc 4.2 will give a compiler errir if ppc64 compilation is requested - # - gcc 4.0 will compile fine, somehow, but then fail at link time - support-ppc64 = ; + case iphone* : + { + support-ppc64 = ; + } + + case * : + if $(osx-version) && ! [ version.version-less [ regex.split $(osx-version) \\. ] : 10 6 ] + { + # When targeting 10.6: + # - gcc 4.2 will give a compiler errir if ppc64 compilation is requested + # - gcc 4.0 will compile fine, somehow, but then fail at link time + support-ppc64 = ; + } } - switch $(arch) { case combined : @@ -403,7 +411,7 @@ rule compile.m ( targets * : sources * : properties * ) { LANG on $(<) = "-x objective-c" ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; - setup-arch-addr-flags $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; } actions compile.m @@ -415,7 +423,7 @@ rule compile.mm ( targets * : sources * : properties * ) { LANG on $(<) = "-x objective-c++" ; gcc.setup-fpic $(targets) : $(sources) : $(properties) ; - setup-arch-addr-flags $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; } actions compile.mm From 62df71534fa1ab7e68420ed99603291e7c44b5d7 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 10 Nov 2009 07:21:03 +0000 Subject: [PATCH 110/198] Support more parameters to rules. Fixes #2552. Patch from Jonathan Biggar. [SVN r57536] --- historic/jam/src/expand.c | 21 +++++++++++ historic/jam/src/lists.h | 2 +- historic/jam/test/rule_param.jam | 60 ++++++++++++++++++++++++++++++++ historic/jam/test/test.jam | 1 + 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 historic/jam/test/rule_param.jam diff --git a/historic/jam/src/expand.c b/historic/jam/src/expand.c index 890610626..d8e58827c 100644 --- a/historic/jam/src/expand.c +++ b/historic/jam/src/expand.c @@ -110,6 +110,24 @@ LIST * var_expand( LIST * l, char * in, char * end, LOL * lol, int cancopyin ) return list_copy( l, lol_get( lol, in[ 2 ] - '1' ) ); } } + else if ( in[0] == '$' && in[1] == '(' && in[2] == '1' && in[4] == ')' && + in[5] == '\0') { + + switch( in[3] ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return list_copy( l, lol_get( lol, in[3]-'0'+10-1 ) ); + } + } /* Expand @() files, to single item plus accompanying file. */ if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) && ( *( end - 1 ) == ')' ) ) @@ -354,6 +372,9 @@ expand: value = lol_get( lol, 1 ); else if ( ( varname[0] >= '1' ) && ( varname[0] <= '9' ) ) value = lol_get( lol, varname[0] - '1' ); + else if( varname[0] == '1' && varname[1] >= '0' && + varname[1] <= '9' && !varname[2] ) + value = lol_get( lol, varname[1] - '0' + 10 - 1 ); } if ( !value ) diff --git a/historic/jam/src/lists.h b/historic/jam/src/lists.h index 50362a66f..ef5292397 100644 --- a/historic/jam/src/lists.h +++ b/historic/jam/src/lists.h @@ -63,7 +63,7 @@ struct _list { typedef struct _lol LOL; -# define LOL_MAX 9 +# define LOL_MAX 19 struct _lol { int count; diff --git a/historic/jam/test/rule_param.jam b/historic/jam/test/rule_param.jam new file mode 100644 index 000000000..8bc51becf --- /dev/null +++ b/historic/jam/test/rule_param.jam @@ -0,0 +1,60 @@ +#~ Copyright 2008 Jonathan Biggar +#~ Distributed under the Boost Software License, Version 1.0. +#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +ECHO -- Testing parameter passing... ; + +rule test_numeric_params +{ + assert $(1) : (==) : 1 ; + assert $(2) : (==) : 2 ; + assert $(3) : (==) : 3 ; + assert $(4) : (==) : 4 ; + assert $(5) : (==) : 5 ; + assert $(6) : (==) : 6 ; + assert $(7) : (==) : 7 ; + assert $(8) : (==) : 8 ; + assert $(9) : (==) : 9 ; + assert $(10) : (==) : 10 ; + assert $(11) : (==) : 11 ; + assert $(12) : (==) : 12 ; + assert $(13) : (==) : 13 ; + assert $(14) : (==) : 14 ; + assert $(15) : (==) : 15 ; + assert $(16) : (==) : 16 ; + assert $(17) : (==) : 17 ; + assert $(18) : (==) : 18 ; + assert $(19) : (==) : 19 ; +} + +rule test_named_params ( p1 : p2 : p3 : p4 : p5 : p6 : p7 : p8 : p9 : + p10 : p11 : p12 : p13 : p14 : p15 : p16 : p17 : p18 : p19 ) + + +{ + assert $(p1) : (==) : 1 ; + assert $(p2) : (==) : 2 ; + assert $(p3) : (==) : 3 ; + assert $(p4) : (==) : 4 ; + assert $(p5) : (==) : 5 ; + assert $(p6) : (==) : 6 ; + assert $(p7) : (==) : 7 ; + assert $(p8) : (==) : 8 ; + assert $(p9) : (==) : 9 ; + assert $(p10) : (==) : 10 ; + assert $(p11) : (==) : 11 ; + assert $(p12) : (==) : 12 ; + assert $(p13) : (==) : 13 ; + assert $(p14) : (==) : 14 ; + assert $(p15) : (==) : 15 ; + assert $(p16) : (==) : 16 ; + assert $(p17) : (==) : 17 ; + assert $(p18) : (==) : 18 ; + assert $(p19) : (==) : 19 ; +} + +test_numeric_params 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : + 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 ; + +test_named_params 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : + 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 ; diff --git a/historic/jam/test/test.jam b/historic/jam/test/test.jam index 1595f2e2c..f6f537766 100644 --- a/historic/jam/test/test.jam +++ b/historic/jam/test/test.jam @@ -14,6 +14,7 @@ TESTS = parallel_actions parallel_multifile_actions_1 parallel_multifile_actions_2 + rule_param stress_var_expand target_var var_expand From e4f56ac3597dada64bf6bf73cd40e90fd60a5815 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Tue, 10 Nov 2009 17:20:51 +0000 Subject: [PATCH 111/198] Fix generating invalid feature values for OSX version to target. [SVN r57545] --- v2/tools/darwin.jam | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 9569f73b3..37bfbf88c 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -232,10 +232,6 @@ local rule init-available-sdk-versions ( condition * : root ? ) local sdk-match = [ MATCH ([^0-9]+)([0-9]+)[.]([0-9x]+)[.]?([0-9x]+)? : $(sdk:D=) ] ; local sdk-platform = $(sdk-match[1]:L) ; local sdk-version = $(sdk-match[2]).$(sdk-match[3]) ; - if $(sdk-match[4]) - { - sdk-version = $(sdk-version).$(sdk-match[4]) ; - } if $(sdk-version) { switch $(sdk-platform) @@ -246,10 +242,18 @@ local rule init-available-sdk-versions ( condition * : root ? ) } case iphoneos : { + if $(sdk-match[4]) + { + sdk-version = $(sdk-version).$(sdk-match[4]) ; + } sdk-version = iphone-$(sdk-version) ; } case iphonesimulator : { + if $(sdk-match[4]) + { + sdk-version = $(sdk-version).$(sdk-match[4]) ; + } sdk-version = iphonesim-$(sdk-version) ; } case * : From 1878e3b7778eb66c2bfdbcd552416d4ebf147900 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Wed, 11 Nov 2009 01:09:00 +0000 Subject: [PATCH 112/198] Generalize the detection and addition of OSX SDKs so that it doesn't rely on a fixed feature list. [SVN r57558] --- v2/tools/darwin.jam | 165 ++++++++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 68 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 37bfbf88c..507201e79 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -24,29 +24,11 @@ import errors ; ## Use a framework. feature framework : : free ; -## The MacOSX versions we can target. -.macosx-versions = - 10.6 10.5 10.4 10.3 10.2 10.1 - iphone-3.2 iphonesim-3.2 - iphone-3.1.2 iphonesim-3.1.2 - iphone-3.1 iphonesim-3.1 - iphone-3.0 iphonesim-3.0 - iphone-2.2.1 iphonesim-2.2.1 - iphone-2.2 iphonesim-2.2 - iphone-2.1 iphonesim-2.1 - iphone-2.0 iphonesim-2.0 - iphone-1.x - ; - ## The MacOSX version to compile for, which maps to the SDK to use (sysroot). -feature macosx-version - : $(.macosx-versions) - : propagated link-incompatible symmetric optional ; +feature macosx-version : : propagated link-incompatible symmetric optional ; ## The minimal MacOSX version to target. -feature macosx-version-min - : $(.macosx-versions) - : propagated optional ; +feature macosx-version-min : : propagated optional ; ############################################################################# @@ -190,7 +172,7 @@ rule init ( version ? : command * : options * : requirement * ) flags darwin.link .STRIP $(condition) : $(strip[1]) ; if $(.debug-configuration) { - ECHO notice: using strip :: $(condition) :: $(strip[1]) ; + ECHO notice: using strip for $(condition) at $(strip[1]) ; } } @@ -202,7 +184,7 @@ rule init ( version ? : command * : options * : requirement * ) flags darwin.archive .LIBTOOL $(condition) : $(archiver[1]) ; if $(.debug-configuration) { - ECHO notice: using archiver :: $(condition) :: $(archiver[1]) ; + ECHO notice: using archiver for $(condition) at $(archiver[1]) ; } # - Initialize the SDKs available in the root for this tool. @@ -220,6 +202,94 @@ rule init ( version ? : command * : options * : requirement * ) #~ EXIT ; } +# Add and set options for a discovered SDK version. +local rule init-sdk ( condition * : root ? : version + : version-feature ? ) +{ + local rule version-to-feature ( version + ) + { + switch $(version[1]) + { + case iphone* : + { + return $(version[1])-$(version[2-]:J=.) ; + } + case mac* : + { + return $(version[2-]:J=.) ; + } + case * : + { + return $(version:J=.) ; + } + } + } + + if $(version-feature) + { + if $(.debug-configuration) + { + ECHO notice: available sdk for $(condition)/$(version-feature) at $(sdk) ; + } + + # Add the version to the features for specifying them. + if ! $(version-feature) in [ feature.values macosx-version ] + { + feature.extend macosx-version : $(version-feature) ; + } + if ! $(version-feature) in [ feature.values macosx-version-min ] + { + feature.extend macosx-version-min : $(version-feature) ; + } + + # Set the flags the version needs to compile with, first + # generic options. + flags darwin.compile OPTIONS $(condition)/$(version-feature) + : -isysroot $(sdk) ; + flags darwin.link OPTIONS $(condition)/$(version-feature) + : -isysroot $(sdk) ; + + # Then device variation options. + switch $(version[1]) + { + case iphone* : + { + flags darwin.compile OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + flags darwin.link OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + } + + case mac* : + { + flags darwin.compile OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + flags darwin.link OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + } + } + + return $(version-feature) ; + } + else if $(version[4]) + { + # We have a patch version of an SDK. We want to set up + # both the specific patch version, and the minor version. + # So we recurse to set up the minor version. Plus the minor version. + return + [ init-sdk $(condition) : $(root) + : $(version[1-3]) : [ version-to-feature $(version[1-3]) ] ] + [ init-sdk $(condition) : $(root) + : $(version) : [ version-to-feature $(version) ] ] ; + } + else + { + # Yes, this is intentionally recursive. + return + [ init-sdk $(condition) : $(root) + : $(version) : [ version-to-feature $(version) ] ] ; + } +} + # Determine the MacOSX SDK versions installed and their locations. local rule init-available-sdk-versions ( condition * : root ? ) { @@ -231,45 +301,29 @@ local rule init-available-sdk-versions ( condition * : root ? ) { local sdk-match = [ MATCH ([^0-9]+)([0-9]+)[.]([0-9x]+)[.]?([0-9x]+)? : $(sdk:D=) ] ; local sdk-platform = $(sdk-match[1]:L) ; - local sdk-version = $(sdk-match[2]).$(sdk-match[3]) ; + local sdk-version = $(sdk-match[2-]) ; if $(sdk-version) { switch $(sdk-platform) { case macosx : { - sdk-version = $(sdk-version) ; + sdk-version = mac $(sdk-version) ; } case iphoneos : { - if $(sdk-match[4]) - { - sdk-version = $(sdk-version).$(sdk-match[4]) ; - } - sdk-version = iphone-$(sdk-version) ; + sdk-version = iphone $(sdk-version) ; } case iphonesimulator : { - if $(sdk-match[4]) - { - sdk-version = $(sdk-version).$(sdk-match[4]) ; - } - sdk-version = iphonesim-$(sdk-version) ; + sdk-version = iphonesim $(sdk-version) ; } case * : { sdk-version = $(sdk-version:J=-) ; } } - result += $(sdk-version) ; - flags darwin.compile OPTIONS $(condition)/$(sdk-version) - : -isysroot $(sdk) ; - flags darwin.link OPTIONS $(condition)/$(sdk-version) - : -isysroot $(sdk) ; - if $(.debug-configuration) - { - ECHO notice: available sdk :: $(condition)/$(sdk-version) :: $(sdk) ; - } + result += [ init-sdk $(condition) : $(sdk) : $(sdk-version) ] ; } } return $(result) ; @@ -278,31 +332,6 @@ local rule init-available-sdk-versions ( condition * : root ? ) # Generic options. flags darwin.compile OPTIONS ; -# Minimal OSX target option. Note that the default is for the min-version -# option to not be included to let the compiler default take hold. -for local macosx-version in $(.macosx-versions) -{ - switch $(macosx-version) - { - case iphone* : - { - local version-match = [ MATCH ([^0-9]+)([0-9.]+) : $(macosx-version) ] ; - flags darwin.compile OPTIONS $(macosx-version) - : -miphoneos-version-min=$(version-match[2]) ; - flags darwin.link OPTIONS $(macosx-version) - : -miphoneos-version-min=$(version-match[2]) ; - } - - case * : - { - flags darwin.compile OPTIONS $(macosx-version) - : -mmacosx-version-min=$(macosx-version) ; - flags darwin.link OPTIONS $(macosx-version) - : -mmacosx-version-min=$(macosx-version) ; - } - } -} - # The following adds objective-c support to darwin. # Thanks to http://thread.gmane.org/gmane.comp.lib.boost.build/13759 From 49ba9f990df6693ff25746934c5e7295545892c3 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 18 Nov 2009 20:31:33 +0000 Subject: [PATCH 113/198] Use qpic=large. On Linux, set soname. Fixes #2595. [SVN r57756] --- v2/tools/vacpp.jam | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/v2/tools/vacpp.jam b/v2/tools/vacpp.jam index a3ab61c6c..cb8c028f8 100644 --- a/v2/tools/vacpp.jam +++ b/v2/tools/vacpp.jam @@ -92,7 +92,7 @@ if [ os.name ] = AIX else { # Linux PPC - flags vacpp.compile CFLAGS shared : -qpic ; + flags vacpp.compile CFLAGS shared : -qpic=large ; flags vacpp FINDLIBS : rt ; } @@ -120,6 +120,9 @@ flags vacpp VA_C_COMPILER multi : xlc_r ; flags vacpp VA_CXX_COMPILER single : xlC ; flags vacpp VA_CXX_COMPILER multi : xlC_r ; +SPACE = " " ; + +flags vacpp.link.dll HAVE_SONAME linux : "" ; actions vacpp.link bind NEEDLIBS { @@ -128,7 +131,7 @@ actions vacpp.link bind NEEDLIBS actions vacpp.link.dll bind NEEDLIBS { - xlC_r -G $(LINKFLAGS) -o "$(<[1])" -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) + xlC_r -G $(LINKFLAGS) -o "$(<[1])" $(HAVE_SONAME)-Wl,-soname$(SPACE)-Wl,$(<[-1]:D=) -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) } actions vacpp.compile.c From 10c496d79cd78126272041e6bed03be394767217 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 19 Nov 2009 20:05:06 +0000 Subject: [PATCH 114/198] Improve detection of MT requirement of MPI. Some MPI implementation report -lpthread in the compilation flags. Previously, we'd add -lpthread, but did not set threading=multi, as we really should. One manifestation of this issue is that when building with such an MPI, and requesting "threading=single threading=multi", we'd actually try to build two variants of everything, including "mpi.so" Python extension. And since Python extensions, at present, do not have their name adjusted for different variants, we'd get duplicate target name error. [SVN r57791] --- v2/tools/mpi.jam | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/v2/tools/mpi.jam b/v2/tools/mpi.jam index bad0615b8..12c2993e9 100644 --- a/v2/tools/mpi.jam +++ b/v2/tools/mpi.jam @@ -163,7 +163,20 @@ rule cmdline_to_features ( cmdline : unknown-features ? ) local add = [ add_feature $(prefix) $(name) $(cmdline) ] ; if $(add) { - result += $(add[1]) ; + + if $(add[1]) = pthread + { + # Uhm. It's not really nice that this MPI implementation + # uses -lpthread as opposed to -pthread. We do want to + # set multi, instead of -lpthread. + result += "multi" ; + MPI_EXTRA_REQUIREMENTS += "multi" ; + } + else + { + result += $(add[1]) ; + } + cmdline = $(add[2]) ; matched = yes ; } From a52d4903a90a3049d387d5d38594eae00bad48ee Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 20 Nov 2009 13:25:25 +0000 Subject: [PATCH 115/198] Use libwhatever.a naming for static libraries on mingw, as that matches how all libraries shipped with mingw are named. Further, we previously used libwhatever.lib, and the linker never searched for this spelling. [SVN r57816] --- v2/tools/types/lib.jam | 1 + 1 file changed, 1 insertion(+) diff --git a/v2/tools/types/lib.jam b/v2/tools/types/lib.jam index 345385f85..2810f9436 100644 --- a/v2/tools/types/lib.jam +++ b/v2/tools/types/lib.jam @@ -16,6 +16,7 @@ type.set-generated-target-prefix LIB : cygwin : "cyg" ; type.register STATIC_LIB : a lib : LIB ; type.set-generated-target-suffix STATIC_LIB : windows : lib ; +type.set-generated-target-suffix STATIC_LIB : gcc windows : a ; type.set-generated-target-suffix STATIC_LIB : cygwin : lib ; type.set-generated-target-prefix STATIC_LIB : : lib ; From 53d1b607d5620008dcfc3a294d221072ae7251c9 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 21 Nov 2009 11:49:13 +0000 Subject: [PATCH 116/198] Permit path-constant with relative path in config files. Thanks to Johan Nilsson for the bug report. [SVN r57836] --- v2/build-system.jam | 6 ++++++ v2/build/targets.jam | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/v2/build-system.jam b/v2/build-system.jam index 434d2195e..2399d8957 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -238,6 +238,12 @@ local rule load-config ( module-name : filename : path + : must-find ? ) "$(filename)" "from" $(where) "." ; } + # Set source location so that path-constant in config files + # with relative paths work. This is of most importance + # for project-config.jam, but may be used in other + # config files as well. + local attributes = [ project.attributes $(module-name) ] ; + $(attributes).set source-location : $(where:D) : exact ; modules.load $(module-name) : $(filename) : $(path) ; project.load-used-projects $(module-name) ; } diff --git a/v2/build/targets.jam b/v2/build/targets.jam index b6ccc0131..38736769a 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -485,7 +485,17 @@ class project-target : abstract-target local r ; for local v in $(value) { - v = [ path.root [ path.make $(v) ] $(self.location) ] ; + local l = $(self.location) ; + if ! $(l) + { + # Project corresponding to config files do not have + # 'location' attribute, but do have source location. + # It might be more reasonable to make every project have + # a location and use some other approach to prevent buildable + # targets in config files, but that's for later. + l = [ get source-location ] ; + } + v = [ path.root [ path.make $(v) ] $(l) ] ; # Now make the value absolute path. v = [ path.root $(v) [ path.pwd ] ] ; # Constants should be in platform-native form. From f37422c6c17a2b85c8643b9ac516757764e82c35 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 22 Nov 2009 08:05:36 +0000 Subject: [PATCH 117/198] Implement the 'always' function. [SVN r57842] --- v2/build/project.jam | 10 ++++++++++ v2/build/targets.jam | 29 +++++++++++++++++++++++++++-- v2/doc/src/reference.xml | 20 ++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/v2/build/project.jam b/v2/build/project.jam index e55521901..8b6a75c05 100644 --- a/v2/build/project.jam +++ b/v2/build/project.jam @@ -1035,6 +1035,16 @@ module project-rules } } + rule always ( target-names * ) + { + import project ; + local t = [ project.current ] ; + for local n in $(target-names) + { + $(t).mark-target-as-always $(n) ; + } + } + rule glob ( wildcards + : excludes * ) { import project ; diff --git a/v2/build/targets.jam b/v2/build/targets.jam index 38736769a..3d38002d3 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -303,6 +303,13 @@ class project-target : abstract-target # before main target instances are created. self.explicit-targets += $(target-name) ; } + + rule mark-target-as-always ( target-name ) + { + # Record the name of the target, not instance, since this rule is called + # before main target instances are created. + self.always-targets += $(target-name) ; + } # Add new target alternative # @@ -466,6 +473,11 @@ class project-target : abstract-target self.main-targets += $(t) ; target = $(self.main-target.$(name)) ; } + + if $(name) in $(self.always-targets) + { + $(a).always ; + } $(target).add-alternative $(a) ; } @@ -1100,7 +1112,12 @@ class basic-target : abstract-target [ full-name ] ; } } - + + rule always ( ) + { + self.always = 1 ; + } + # Returns the list of abstract-targets which are used as sources. The extra # properties specified for sources are not represented. The only user for # this rule at the moment is the "--dump-tests" feature of the test system. @@ -1267,7 +1284,15 @@ class basic-target : abstract-target { local gur = $(result[1]) ; result = $(result[2-]) ; - + + if $(self.always) + { + for local t in $(result) + { + $(t).always ; + } + } + local s = [ create-subvariant $(result) : [ virtual-target.recent-targets ] : $(property-set) : $(source-targets) diff --git a/v2/doc/src/reference.xml b/v2/doc/src/reference.xml index 51720a998..45c7c9d60 100644 --- a/v2/doc/src/reference.xml +++ b/v2/doc/src/reference.xml @@ -181,6 +181,26 @@ ECHO [ glob-tree *.cpp : .svn ] ; their containing project is built. + + always + always building a metatarget + + The always funciton takes a single + parameter—a list of metatarget names. The top-level targets produced + by the named metatargets will be always considered out of date. Consider this example: + + +exe hello : hello.cpp ; +exe bye : bye.cpp ; +always hello ; + + If a build of hello is requested, then the binary will + always be relinked. The object files will not be recompiled, though. Note that if + a build of hello is not requested, for example you specify just + bye on the command line, hello will not + be relinked. + + constant From e8a2c87cd38e3c2730bc89e44dc4aa3eb2cc3ccd Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 24 Nov 2009 11:32:13 +0000 Subject: [PATCH 118/198] Make vacpp respect the standard options to 'using'. [SVN r57887] --- v2/tools/vacpp.jam | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/v2/tools/vacpp.jam b/v2/tools/vacpp.jam index cb8c028f8..40404c0ca 100644 --- a/v2/tools/vacpp.jam +++ b/v2/tools/vacpp.jam @@ -27,7 +27,8 @@ rule init ( version ? : command * : options * ) command = [ common.get-invocation-command vacpp : xlC : $(command) : "/usr/vacpp/bin/xlC" ] ; - + + ECHO "XXXXXXXX" $(options) ; common.handle-options vacpp : $(condition) : $(command) : $(options) ; } @@ -100,13 +101,13 @@ else flags vacpp CFLAGS on : -pg ; flags vacpp LINKFLAGS on : -pg ; -flags vacpp CFLAGS ; -flags vacpp C++FLAGS ; +flags vacpp.compile OPTIONS ; +flags vacpp.compile.c++ OPTIONS ; flags vacpp DEFINES ; flags vacpp UNDEFS ; flags vacpp HDRS ; flags vacpp STDHDRS ; -flags vacpp LINKFLAGS ; +flags vacpp.link OPTIONS ; flags vacpp ARFLAGS ; flags vacpp LIBPATH ; @@ -126,22 +127,22 @@ flags vacpp.link.dll HAVE_SONAME linux : "" ; actions vacpp.link bind NEEDLIBS { - $(VA_CXX_COMPILER) $(EXE-LINKFLAGS) $(LINKFLAGS) -o "$(<[1])" -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) + $(VA_CXX_COMPILER) $(EXE-LINKFLAGS) $(LINKFLAGS) -o "$(<[1])" -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) $(OPTIONS) $(USER_OPTIONS) } actions vacpp.link.dll bind NEEDLIBS { - xlC_r -G $(LINKFLAGS) -o "$(<[1])" $(HAVE_SONAME)-Wl,-soname$(SPACE)-Wl,$(<[-1]:D=) -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) + xlC_r -G $(LINKFLAGS) -o "$(<[1])" $(HAVE_SONAME)-Wl,-soname$(SPACE)-Wl,$(<[-1]:D=) -L$(LIBPATH) -L$(STDLIBPATH) "$(>)" "$(NEEDLIBS)" "$(NEEDLIBS)" -l$(FINDLIBS) $(OPTIONS) $(USER_OPTIONS) } actions vacpp.compile.c { - $(VA_C_COMPILER) -c -I$(BOOST_ROOT) -U$(UNDEFS) -D$(DEFINES) $(CFLAGS) -I"$(HDRS)" -I"$(STDHDRS)" -o "$(<)" "$(>)" + $(VA_C_COMPILER) -c $(OPTIONS) $(USER_OPTIONS) -I$(BOOST_ROOT) -U$(UNDEFS) -D$(DEFINES) $(CFLAGS) -I"$(HDRS)" -I"$(STDHDRS)" -o "$(<)" "$(>)" } actions vacpp.compile.c++ { - $(VA_CXX_COMPILER) -c -I$(BOOST_ROOT) -U$(UNDEFS) -D$(DEFINES) $(CFLAGS) $(C++FLAGS) -I"$(HDRS)" -I"$(STDHDRS)" -o "$(<)" "$(>)" + $(VA_CXX_COMPILER) -c $(OPTIONS) $(USER_OPTIONS) -I$(BOOST_ROOT) -U$(UNDEFS) -D$(DEFINES) $(CFLAGS) $(C++FLAGS) -I"$(HDRS)" -I"$(STDHDRS)" -o "$(<)" "$(>)" } actions updated together piecemeal vacpp.archive From f3b40af375d1ee5c82297ae100acd5391a18c9e6 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 24 Nov 2009 11:48:06 +0000 Subject: [PATCH 119/198] Remove debug print [SVN r57888] --- v2/tools/vacpp.jam | 1 - 1 file changed, 1 deletion(-) diff --git a/v2/tools/vacpp.jam b/v2/tools/vacpp.jam index 40404c0ca..f4080fc04 100644 --- a/v2/tools/vacpp.jam +++ b/v2/tools/vacpp.jam @@ -28,7 +28,6 @@ rule init ( version ? : command * : options * ) command = [ common.get-invocation-command vacpp : xlC : $(command) : "/usr/vacpp/bin/xlC" ] ; - ECHO "XXXXXXXX" $(options) ; common.handle-options vacpp : $(condition) : $(command) : $(options) ; } From 49539d3a81a4d6d96a79c41fb5d31c0f60b744be Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 24 Nov 2009 16:56:01 +0000 Subject: [PATCH 120/198] Fix link [SVN r57893] --- historic/jam/doc/bjam.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index 52f974a9c..2c070456a 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -410,7 +410,7 @@ The arguments starting with the "=--option=" forms are passed to the =build.jam= [warning Most probably, you are looking for [@http://boost.org/boost-build2/doc/html/index.html Boost.Build -manual] or [@http://www.boost.org/boost-build2/doc/html/bbv2/advanced/invocation.html Boost.Build +manual] or [@http://www.boost.org/boost-build2/doc/html/bbv2/overview/invocation.html Boost.Build command-line syntax]. This section documents only low-level options used by the Boost.Jam build engine, and does not mention any high-level syntax of Boost.Build] From c3cad9230991780407515d9ece0eb1c66eee045c Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 27 Nov 2009 06:43:04 +0000 Subject: [PATCH 121/198] When generating PCH-compiling cpp, end it with newline. Fixes #3602. [SVN r57965] --- v2/tools/msvc.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index fbc89d670..12084ffe2 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -378,7 +378,7 @@ rule compile-c-c++ ( targets + : sources * ) # syntax highlighting in the messy N-quoted code below. actions compile-c-c++-pch { - $(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" "@($(<[1]:W).cpp:E=#include $(.escaped-double-quote)$(>[1]:D=)$(.escaped-double-quote))" $(.CC.FILTER) + $(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" "@($(<[1]:W).cpp:E=#include $(.escaped-double-quote)$(>[1]:D=)$(.escaped-double-quote)$(.nl))" $(.CC.FILTER) } From d565fceb83d79dbd4b144957936ed261ec037d8b Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 27 Nov 2009 19:53:52 +0000 Subject: [PATCH 122/198] Pack everything 'mpicxx -show' in single 'linkflags' feature, to avoid undesired reordering. [SVN r57977] --- v2/tools/mpi.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/mpi.jam b/v2/tools/mpi.jam index 12c2993e9..38012474e 100644 --- a/v2/tools/mpi.jam +++ b/v2/tools/mpi.jam @@ -218,7 +218,7 @@ rule cmdline_to_features ( cmdline : unknown-features ? ) if $(otherflags) { for unknown in $(unknown-features) { - result += "$(unknown)$(otherflags)" ; + result += "$(unknown)$(otherflags:J= )" ; } } From c143e54ae060183a4cd3ad9ceee9af2146defa12 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 27 Nov 2009 19:57:44 +0000 Subject: [PATCH 123/198] Allow Boost.Build to control keep-going behaviour [SVN r57978] --- historic/jam/src/jam.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/historic/jam/src/jam.c b/historic/jam/src/jam.c index cbffb4765..fc2f6bb29 100644 --- a/historic/jam/src/jam.c +++ b/historic/jam/src/jam.c @@ -495,6 +495,20 @@ int main( int argc, char * * argv, char * * arg_environ ) } } + /* KEEP_GOING overrides -q option. */ + { + LIST *p = L0; + p = var_get ("KEEP_GOING"); + if (p) + { + int v = atoi (p->string); + if (v == 0) + globs.quitquick = 1; + else + globs.quitquick = 0; + } + } + /* Now make target. */ { PROFILE_ENTER( MAIN_MAKE ); From 5e202da8b614cd4839ccc368cac1a53e99e442ba Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 27 Nov 2009 20:14:52 +0000 Subject: [PATCH 124/198] Implement --keep-going option. [SVN r57979] --- v2/build-system.jam | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/v2/build-system.jam b/v2/build-system.jam index 2399d8957..71a9b4931 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -904,6 +904,21 @@ local rule should-clean-project ( project ) modules.poke : PARALLELISM : $(j) ; } + local k = [ option.get keep-going : false : true ] ; + if $(k) in "on" "yes" "true" + { + modules.poke : KEEP_GOING : 1 ; + } + else if $(k) in "off" "no" "false" + { + modules.poke : KEEP_GOING : 0 ; + } + else + { + ECHO "error: Invalid value for the --keep-going option" ; + EXIT ; + } + # The 'all' pseudo target is not strictly needed expect in the case when we # use it below but people often assume they always have this target # available and do not declare it themselves before use which may cause From 8ce7f291a5639c20892101e5c10212e48a27c83c Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 28 Nov 2009 17:46:46 +0000 Subject: [PATCH 125/198] Fix warnings. Closes #3656. Patch from Mateusz Loskot. [SVN r58011] --- historic/jam/src/hash.c | 4 ++-- historic/jam/src/hcache.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/historic/jam/src/hash.c b/historic/jam/src/hash.c index 8ca25cc5e..fbd1a8993 100644 --- a/historic/jam/src/hash.c +++ b/historic/jam/src/hash.c @@ -448,12 +448,12 @@ static void hashstat( struct hash * hp ) run = here; } - printf( "%s table: %d+%d+%d (%dK+%ldK) items+table+hash, %f density\n", + printf( "%s table: %d+%d+%d (%dK+%luK) items+table+hash, %f density\n", hp->name, count, hp->items.nel, hp->tab.nel, hp->items.nel * hp->items.size / 1024, - hp->tab.nel * sizeof( ITEM ** ) / 1024, + (long unsigned)hp->tab.nel * sizeof( ITEM ** ) / 1024, (float)count / (float)sets ); } diff --git a/historic/jam/src/hcache.c b/historic/jam/src/hcache.c index e34aa9e94..70bb798cc 100644 --- a/historic/jam/src/hcache.c +++ b/historic/jam/src/hcache.c @@ -166,7 +166,7 @@ void write_netstring( FILE * f, char const * s ) { if ( !s ) s = ""; - fprintf( f, "%lu\t%s\n", strlen( s ), s ); + fprintf( f, "%lu\t%s\n", (long unsigned)strlen( s ), s ); } From 46bb584ae9adaf0376b00137023c6858d886d039 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Sat, 28 Nov 2009 20:01:31 +0000 Subject: [PATCH 126/198] Fix quickbook scanner for filenames that start with 't'. (bjam doesn't support '\t') [SVN r58015] --- v2/tools/quickbook.jam | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/tools/quickbook.jam b/v2/tools/quickbook.jam index 38d93d9c5..f4874b292 100644 --- a/v2/tools/quickbook.jam +++ b/v2/tools/quickbook.jam @@ -256,9 +256,9 @@ class qbk-scanner : common-scanner { rule pattern ( ) { - return "\\[[ \t]*include[ \t]+([^]]+)\\]" - "\\[[ \t]*include:[a-zA-Z0-9_]+[ \t]+([^]]+)\\]" - "\\[[ \t]*import[ \t]+([^]]+)\\]" ; + return "\\[[ ]*include[ ]+([^]]+)\\]" + "\\[[ ]*include:[a-zA-Z0-9_]+[ ]+([^]]+)\\]" + "\\[[ ]*import[ ]+([^]]+)\\]" ; } } From 5e8560efba5c351a844a51e5e34397bf6c411665 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 2 Dec 2009 16:47:48 +0000 Subject: [PATCH 127/198] Make 'keep-going' option default to 'true' if not specified. [SVN r58101] --- v2/build-system.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/build-system.jam b/v2/build-system.jam index 71a9b4931..c948c64b6 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -904,7 +904,7 @@ local rule should-clean-project ( project ) modules.poke : PARALLELISM : $(j) ; } - local k = [ option.get keep-going : false : true ] ; + local k = [ option.get keep-going : true : true ] ; if $(k) in "on" "yes" "true" { modules.poke : KEEP_GOING : 1 ; From 543b57aa127ec31084795e10945e82ee51869330 Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Tue, 8 Dec 2009 18:22:04 +0000 Subject: [PATCH 128/198] Add msvc-10.0express support [SVN r58238] --- v2/tools/msvc.jam | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index 12084ffe2..6bb0d6ff8 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -1322,7 +1322,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] # Known toolset versions, in order of preference. -.known-versions = 10.0 9.0 9.0express 8.0 8.0express 7.1 7.1toolkit 7.0 6.0 ; +.known-versions = 10.0 10.0express 9.0 9.0express 8.0 8.0express 7.1 7.1toolkit 7.0 6.0 ; # Version aliases. .version-alias-6 = 6.0 ; @@ -1342,6 +1342,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] .version-9.0-reg = "VisualStudio\\9.0\\Setup\\VC" ; .version-9.0express-reg = "VCExpress\\9.0\\Setup\\VC" ; .version-10.0-reg = "VisualStudio\\10.0\\Setup\\VC" ; +.version-10.0express-reg = "VCExpress\\10.0\\Setup\\VC" ; # Visual C++ Toolkit 2003 does not store its installation path in the registry. # The environment variable 'VCToolkitInstallDir' and the default installation From d6f5f0bf72b678b4801761fa219a40b8b038fdac Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 12 Dec 2009 23:08:29 +0000 Subject: [PATCH 129/198] Do not strip binaries in release variant. [SVN r58331] --- v2/tools/builtin.jam | 6 ++++++ v2/tools/gcc.jam | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index 881ef732c..1565213ec 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -121,6 +121,12 @@ feature.feature asynch-exceptions : off on : propagated ; feature.feature extern-c-nothrow : off on : propagated ; feature.feature debug-symbols : on off : propagated ; +# Controls whether the binary should be stripped -- that is have +# everything not necessary to running removed. This option should +# not be very often needed. Also, this feature will show up in +# target paths of everything, not just binaries. Should fix that +# when impelementing feature relevance. +feature.feature strip : off on : propagated ; feature.feature define : : free ; feature.feature undef : : free ; feature.feature "include" : : free path ; #order-sensitive ; diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 21b265d8a..51d39a2ee 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -676,7 +676,7 @@ rule init-link-flags ( toolset linker condition ) # as opposed to -s since icc (intel's compiler) is generally # option-compatible with and inherits from the gcc toolset, but does not # support -s. - toolset.flags $(toolset).link OPTIONS $(condition)/off : -Wl,--strip-all : unchecked ; + toolset.flags $(toolset).link OPTIONS $(condition)/on : -Wl,--strip-all : unchecked ; toolset.flags $(toolset).link RPATH $(condition) : : unchecked ; toolset.flags $(toolset).link RPATH_LINK $(condition) : : unchecked ; toolset.flags $(toolset).link START-GROUP $(condition) : -Wl,--start-group : unchecked ; @@ -731,7 +731,7 @@ rule init-link-flags ( toolset linker condition ) case hpux : { - toolset.flags $(toolset).link OPTIONS $(condition)/off + toolset.flags $(toolset).link OPTIONS $(condition)/on : -Wl,-s : unchecked ; toolset.flags $(toolset).link OPTIONS $(condition)/shared : -fPIC : unchecked ; @@ -740,7 +740,7 @@ rule init-link-flags ( toolset linker condition ) case osf : { # No --strip-all, just -s. - toolset.flags $(toolset).link OPTIONS $(condition)/off + toolset.flags $(toolset).link OPTIONS $(condition)/on : -Wl,-s : unchecked ; toolset.flags $(toolset).link RPATH $(condition) : : unchecked ; @@ -752,7 +752,7 @@ rule init-link-flags ( toolset linker condition ) case sun : { - toolset.flags $(toolset).link OPTIONS $(condition)/off + toolset.flags $(toolset).link OPTIONS $(condition)/on : -Wl,-s : unchecked ; toolset.flags $(toolset).link RPATH $(condition) : : unchecked ; From ef5f69c03212afc724edad422e6d5bd6b6f7fa46 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 14 Dec 2009 06:50:05 +0000 Subject: [PATCH 130/198] Don't generate empty -F if framework is specified without path. Patch from Sohail Somani. [SVN r58371] --- v2/tools/darwin.jam | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 507201e79..a040d9c8a 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -497,12 +497,15 @@ local rule prepare-framework-path ( target + ) # The -F option specifies the directories where a framework # is searched for. So, if we find feature # with some path, we need to generate property -F option. - local framework-path = [ on $(target) return $(FRAMEWORK:D) ] ; + local framework-paths = [ on $(target) return $(FRAMEWORK:D) ] ; # Be sure to generate no -F if there's no path. - if $(framework-path) != "" + for local framework-path in $(framework-paths) { - FRAMEWORK_PATH on $(target) += -F$(framework-path) ; + if $(framework-path) != "" + { + FRAMEWORK_PATH on $(target) += -F$(framework-path) ; + } } } From a735b092e0a8230b55884052bc05e8fdc869284c Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 14 Dec 2009 07:54:58 +0000 Subject: [PATCH 131/198] Fix typo. Closes #3756. [SVN r58372] --- v2/doc/src/reference.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/doc/src/reference.xml b/v2/doc/src/reference.xml index 45c7c9d60..bb838d403 100644 --- a/v2/doc/src/reference.xml +++ b/v2/doc/src/reference.xml @@ -1743,7 +1743,7 @@ exe a : a.cpp dependency - The value of dependency feature if a target reference. + The value of a dependency feature is a target reference. When used for building of a main target, the value of dependency feature is treated as additional dependency. From 65c06998164e4c439aebdb9ed8cecef31bca38c1 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 14 Dec 2009 18:33:34 +0000 Subject: [PATCH 132/198] Adjust for off vs. off. [SVN r58382] --- v2/tools/darwin.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index a040d9c8a..94180f947 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -165,7 +165,7 @@ rule init ( version ? : command * : options * : requirement * ) # Otherwise we need to find a strip program to use. And hence # also tell the link action that we need to use a strip # post-process. - flags darwin.link NEED_STRIP $(condition)/off : "" ; + flags darwin.link NEED_STRIP $(condition)/off : "" ; strip = [ common.get-invocation-command darwin : strip : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; From ebb6a6942d356375ffba792181f79479ae005bff Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Wed, 16 Dec 2009 03:56:33 +0000 Subject: [PATCH 133/198] Really fix on vs. off change for Xcode gcc. [SVN r58409] --- v2/tools/darwin.jam | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 94180f947..a14987c33 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -152,20 +152,20 @@ rule init ( version ? : command * : options * : requirement * ) # We can turn off strip by specifying it as empty. In which # case we switch to using the linker to do the strip. flags darwin.link.dll OPTIONS - $(condition)/LIB/shared/32/off : -Wl,-x ; + $(condition)/LIB/shared/32/on : -Wl,-x ; flags darwin.link.dll OPTIONS - $(condition)/LIB/shared//off : -Wl,-x ; + $(condition)/LIB/shared//on : -Wl,-x ; flags darwin.link OPTIONS - $(condition)/EXE/32/off : -s ; + $(condition)/EXE/32/on : -s ; flags darwin.link OPTIONS - $(condition)/EXE//off : -s ; + $(condition)/EXE//on : -s ; } else { # Otherwise we need to find a strip program to use. And hence # also tell the link action that we need to use a strip # post-process. - flags darwin.link NEED_STRIP $(condition)/off : "" ; + flags darwin.link NEED_STRIP $(condition)/on : "" ; strip = [ common.get-invocation-command darwin : strip : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; From 5d761fa5e46ef4565b756b6b44d84c37d54eebfc Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 18 Dec 2009 10:07:57 +0000 Subject: [PATCH 134/198] Do not treat 'sun-stlport' feature value as two subfeatures. Fixes #3736. Patch from K. Noel Belcourt. [SVN r58451] --- v2/build/feature.jam | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/build/feature.jam b/v2/build/feature.jam index 0005405eb..6f54adefb 100644 --- a/v2/build/feature.jam +++ b/v2/build/feature.jam @@ -469,7 +469,9 @@ rule validate-value-string ( feature value-string ) if $($(feature).subfeatures) { - if ! ( $(value-string) in $($(feature).subfeatures) ) { + if ! ( $(value-string) in $($(feature).values) ) + && ! ( $(value-string) in $($(feature).subfeatures) ) + { values = [ regex.split $(value-string) - ] ; } } From 0907cc652c27a64905d86d05c4b5c44e5e0a87a6 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 4 Jan 2010 10:36:08 +0000 Subject: [PATCH 135/198] Only use Python port of Boost.Build when exactly '--python' is passed. Fixes #3814. [SVN r58668] --- v2/kernel/bootstrap.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/kernel/bootstrap.jam b/v2/kernel/bootstrap.jam index fb918e7a8..a629d214f 100644 --- a/v2/kernel/bootstrap.jam +++ b/v2/kernel/bootstrap.jam @@ -131,7 +131,7 @@ local dont-build = [ option.process ] ; # if ! $(dont-build) { - if ! [ MATCH (--python) : $(ARGV) ] + if ! --python in $(ARGV) { # Allow users to override the build system file from the # command-line (mostly for testing) From 128e18fafbc515f35218c3b948ce7083ad5c2a72 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Mon, 4 Jan 2010 17:51:29 +0000 Subject: [PATCH 136/198] Add latex support to doxygen toolset [SVN r58682] --- v2/tools/doxygen.jam | 167 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/v2/tools/doxygen.jam b/v2/tools/doxygen.jam index a3e3a2561..0c6682958 100644 --- a/v2/tools/doxygen.jam +++ b/v2/tools/doxygen.jam @@ -36,6 +36,7 @@ import toolset : flags ; import alias ; import common ; import modules ; +import project ; # Use to specify extra configuration paramters. These get translated @@ -60,6 +61,9 @@ feature.feature doxygen.doxproc.id : : free ; # The title for the resulting BoostBook reference section. feature.feature doxygen.doxproc.title : : free ; +# Location for images when generating XML +feature.feature doxygen:xml-imagedir : : free ; + # Doxygen configuration input file. type.register DOXYFILE : doxyfile ; @@ -75,6 +79,7 @@ type.register DOXYGEN_HTML_MULTIFILE : html-dir : HTML ; # Redirection HTML file to HTML multifile directory. type.register DOXYGEN_HTML : : HTML ; +type.register DOXYGEN_XML_IMAGES : doxygen-xml-images ; # Initialize the Doxygen module. Parameters are: # name: the name of the 'doxygen' executable. If not specified, the name @@ -152,6 +157,8 @@ rule init ( name ? ) : DOXYFILE : DOXYGEN_HTML_MULTIFILE ; generators.register-standard doxygen.html-redirect : DOXYGEN_HTML_MULTIFILE : DOXYGEN_HTML ; + generators.register-standard doxygen.copy-latex-pngs + : DOXYGEN_HTML : DOXYGEN_XML_IMAGES ; IMPORT $(__name__) : doxygen : : doxygen ; } @@ -343,6 +350,129 @@ rule html-redirect ( target : source : properties * ) : true ; } +rule copy-latex-pngs ( target : source : requirements * ) +{ + local directory = [ path.native + [ feature.get-values : + $(requirements) ] ] ; + if [ os.name ] = NT + { + CP on $(target) = copy /y ; + MKDIR on $(target) = mkdir ; + FROM on $(target) = \\*.png ; + TOHTML on $(target) = .\\html\\$(directory) ; + TOPDF on $(target) = \\$(directory) ; + } + else + { + CP on $(target) = cp ; + MKDIR on $(target) = mkdir -p ; + FROM on $(target) = /*.png ; + TOHTML on $(target) = ./html/$(directory) ; + TOPDF on $(target) = $(target:D)/$(directory) ; + } +} + +actions copy-latex-pngs +{ + $(MKDIR) $(TOHTML) + $(MKDIR) $(<:D)$(TOPDF) + $(CP) $(>:S=)$(FROM) $(TOHTML) + $(CP) $(>:S=)$(FROM) $(<:D)$(TOPDF) + echo "Stamped" > "$(<)" +} + +# building latex images for doxygen XML depends +# on latex, dvips, and ps being in your PATH. +# This is true for most Unix installs, but +# not on Win32, where you will need to install +# MkTex and Ghostscript and add these tools +# to your path. + +actions check-latex +{ + latex -version >$(<) +} + +actions check-dvips +{ + dvips -version >$(<) +} + +if [ os.name ] = "NT" +{ + actions check-gs + { + gswin32c -version >$(<) + } +} +else +{ + actions check-gs + { + gs -version >$(<) + } +} + +rule check-tools ( ) +{ + if ! $(.check-tools-targets) + { + # Find the root project. + local root-project = [ project.current ] ; + root-project = [ $(root-project).project-module ] ; + while + [ project.attribute $(root-project) parent-module ] && + [ project.attribute $(root-project) parent-module ] != user-config + { + root-project = + [ project.attribute $(root-project) parent-module ] ; + } + + .latex.check = [ new file-target latex.check + : + : [ project.target $(root-project) ] + : [ new action : doxygen.check-latex ] + : + ] ; + .dvips.check = [ new file-target dvips.check + : + : [ project.target $(root-project) ] + : [ new action : doxygen.check-dvips ] + : + ] ; + .gs.check = [ new file-target gs.check + : + : [ project.target $(root-project) ] + : [ new action : doxygen.check-gs ] + : + ] ; + .check-tools-targets = $(.latex.check) $(.dvips.check) $(.gs.check) ; + } + return $(.check-tools-targets) ; +} + +project.initialize $(__name__) ; +project doxygen ; + +class doxygen-check-tools-target-class : basic-target +{ + import doxygen ; + rule construct ( name : sources * : property-set ) + { + return [ property-set.empty ] [ doxygen.check-tools ] ; + } +} + +local project = [ project.current ] ; + +targets.main-target-alternative + [ new doxygen-check-tools-target-class check-tools : $(project) + : [ targets.main-target-sources : check-tools : no-renaming ] + : [ targets.main-target-requirements : $(project) ] + : [ targets.main-target-default-build : $(project) ] + : [ targets.main-target-usage-requirements : $(project) ] + ] ; # User-level rule to generate BoostBook XML from a set of headers via Doxygen. # @@ -412,6 +542,43 @@ rule doxygen ( target : sources * : requirements * : default-build * : usage-req requirements = [ property.change $(requirements) : ] ; local target-xml = $(target:B=$(target:B)-xml) ; + # Check whether we need to build images + local images-location = + [ feature.get-values : $(requirements) ] ; + if $(images-location) + { + doxygen $(target).doxygen-xml-images.html : $(sources) + : $(requirements) + QUIET=YES + WARNINGS=NO + WARN_IF_UNDOCUMENTED=NO + /doxygen//check-tools ; + $(project).mark-target-as-explicit + $(target).doxygen-xml-images.html ; + + targets.main-target-alternative + [ new typed-target $(target).doxygen-xml-images + : $(project) : DOXYGEN_XML_IMAGES + : $(target).doxygen-xml-images.html + : [ targets.main-target-requirements $(requirements) + : $(project) ] + : [ targets.main-target-default-build $(default-build) + : $(project) ] + ] ; + + $(project).mark-target-as-explicit + $(target).doxygen-xml-images ; + + if ! [ regex.match "^(.*/)$" : $(images-location) ] + { + images-location = $(images-location)/ ; + } + + requirements += + $(target).doxygen-xml-images + boost.doxygen.formuladir=$(images-location) ; + } + ## The doxygen configuration file. targets.main-target-alternative [ new typed-target $(target-xml:S=.tag) : $(project) : DOXYFILE From e8853b49472ce7a5aa2dada67a99d7eac30baccf Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 6 Jan 2010 19:00:48 +0000 Subject: [PATCH 137/198] Do not relink on installation if dll paths did not change. [SVN r58775] --- v2/tools/stage.jam | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/v2/tools/stage.jam b/v2/tools/stage.jam index fcbcd5acc..769b5f574 100644 --- a/v2/tools/stage.jam +++ b/v2/tools/stage.jam @@ -354,16 +354,38 @@ class installed-exe-generator : generator rule run ( project name ? : property-set : source : multiple ? ) { + local need-relink ; + if [ $(property-set).get ] in NT CYGWIN || [ $(property-set).get ] in windows cygwin { - # Relinking is never needed on NT. - return [ stage.copy-file $(project) + } + else + { + # See if the dll-path properties are not changed during + # install. If so, copy, don't relink. + local a = [ $(source).action ] ; + local p = [ $(a).properties ] ; + local original = [ $(p).get ] ; + local current = [ $(property-set).get ] ; + + ECHO "XXX" $(current) "--" $(original) ; + + if $(current) != $(original) + { + need-relink = true ; + } + } + + + if $(need-relink) + { + return [ stage.relink-file $(project) : $(source) : $(property-set) ] ; } else { - return [ stage.relink-file $(project) + return [ stage.copy-file $(project) : $(source) : $(property-set) ] ; } } From b1106b4ca090b6ddd59fb72f4d6ca1410f40ea1a Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 6 Jan 2010 19:07:49 +0000 Subject: [PATCH 138/198] Remove debug print [SVN r58776] --- v2/tools/stage.jam | 2 -- 1 file changed, 2 deletions(-) diff --git a/v2/tools/stage.jam b/v2/tools/stage.jam index 769b5f574..4c998ff93 100644 --- a/v2/tools/stage.jam +++ b/v2/tools/stage.jam @@ -369,8 +369,6 @@ class installed-exe-generator : generator local original = [ $(p).get ] ; local current = [ $(property-set).get ] ; - ECHO "XXX" $(current) "--" $(original) ; - if $(current) != $(original) { need-relink = true ; From 78149308c4a2e327eedad104f37a9bf280d334d8 Mon Sep 17 00:00:00 2001 From: "K. Noel Belcourt" Date: Thu, 7 Jan 2010 05:57:33 +0000 Subject: [PATCH 139/198] This patch ensures we close both file descriptors following the dup2 calls. This is mostly for completeness but also because I've occasionally noticed output isn't flushed from a terminated process (when it is killed for exceeding the time limit). I'm sure this won't break anything and am slightly hopeful that it might ensure output is flushed before the terminating process is killed. [SVN r58785] --- historic/jam/src/execunix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/historic/jam/src/execunix.c b/historic/jam/src/execunix.c index 87da9b816..90736baf4 100644 --- a/historic/jam/src/execunix.c +++ b/historic/jam/src/execunix.c @@ -226,13 +226,13 @@ void exec_cmd dup2( out[1], STDOUT_FILENO ); if ( globs.pipe_action == 0 ) - { dup2( out[1], STDERR_FILENO ); - close( err[1] ); - } else dup2( err[1], STDERR_FILENO ); + close( out[1] ); + close( err[1] ); + /* Make this process a process group leader so that when we kill it, all * child processes of this process are terminated as well. We use * killpg(pid, SIGKILL) to kill the process group leader and all its From 5d01f61653c82c9e9b393c1bce2bd7301380b7d7 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 27 Jan 2010 20:48:52 +0000 Subject: [PATCH 140/198] Remove debug prints. [SVN r59283] --- v2/tools/docutils.jam | 2 -- 1 file changed, 2 deletions(-) diff --git a/v2/tools/docutils.jam b/v2/tools/docutils.jam index 9055f8d53..bf0616174 100644 --- a/v2/tools/docutils.jam +++ b/v2/tools/docutils.jam @@ -44,9 +44,7 @@ generators.register-standard docutils.html : ReST : HTML ; rule init ( docutils-dir ? : tools-dir ? ) { - ECHO docutils-dir= $(docutils-dir) ; docutils-dir ?= [ modules.peek : DOCUTILS_DIR ] ; - ECHO tools-dir= $(tools-dir) ; tools-dir ?= $(docutils-dir)/tools ; if ! $(.initialized) From c159193c727aa7db1d28527bf252001f19e4ff25 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 27 Jan 2010 20:49:56 +0000 Subject: [PATCH 141/198] Improve fortran support. Patch from Spencer Olson. [SVN r59284] --- v2/tools/gcc.jam | 12 ++++++++++++ v2/tools/gfortran.jam | 2 +- v2/tools/intel-linux.jam | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 51d39a2ee..1b5181855 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -25,6 +25,7 @@ import rc ; import regex ; import set ; import unix ; +import fortran ; if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] @@ -224,6 +225,7 @@ if [ os.name ] = NT generators.register-c-compiler gcc.compile.c++ : CPP : OBJ : gcc ; generators.register-c-compiler gcc.compile.c : C : OBJ : gcc ; generators.register-c-compiler gcc.compile.asm : ASM : OBJ : gcc ; +generators.register-fortran-compiler gcc.compile.fortran : FORTRAN FORTRAN90 : OBJ : gcc ; # pch support @@ -400,6 +402,7 @@ toolset.flags gcc.compile.c++ USER_OPTIONS ; toolset.flags gcc.compile DEFINES ; toolset.flags gcc.compile INCLUDES ; toolset.flags gcc.compile.c++ TEMPLATE_DEPTH ; +toolset.flags gcc.compile.fortran USER_OPTIONS ; rule compile.c++.pch ( targets * : sources * : properties * ) { @@ -471,6 +474,10 @@ rule compile.c ( targets * : sources * : properties * ) DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; } +rule compile.fortran +{ +} + actions compile.c++ bind PCH_FILE { "$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<:W)" "$(>:W)" @@ -481,6 +488,11 @@ actions compile.c bind PCH_FILE "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } +actions compile.fortran +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + rule compile.asm { LANG on $(<) = "-x assembler-with-cpp" ; diff --git a/v2/tools/gfortran.jam b/v2/tools/gfortran.jam index bd7fe70a2..0aa69b85c 100644 --- a/v2/tools/gfortran.jam +++ b/v2/tools/gfortran.jam @@ -36,4 +36,4 @@ actions compile.fortran gcc -Wall $(OPTIONS) -D$(DEFINES) -I$(INCLUDES) -c -o "$(<)" "$(>)" } -generators.register-fortran-compiler gfortran.compile.fortran : FORTRAN : OBJ ; +generators.register-fortran-compiler gfortran.compile.fortran : FORTRAN FORTRAN90 : OBJ ; diff --git a/v2/tools/intel-linux.jam b/v2/tools/intel-linux.jam index 97effd848..9a4f11ae0 100644 --- a/v2/tools/intel-linux.jam +++ b/v2/tools/intel-linux.jam @@ -150,6 +150,11 @@ actions compile.c++.pch rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -pch-create "$(<)" "$(>)" } +actions compile.fortran +{ + "ifort" -c $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + rule compile.c.pch ( targets * : sources * : properties * ) { gcc.setup-threading $(targets) : $(sources) : $(properties) ; From 7da4b7f27e6392416afc5811e8201281df6f47a8 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 27 Jan 2010 20:52:05 +0000 Subject: [PATCH 142/198] Intel improvements: 1. Honors USER_OPTION (that is cflags/cxxflags features) 2. Adds an speed option Patch from Spencer Olson. [SVN r59285] --- v2/tools/intel-linux.jam | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/v2/tools/intel-linux.jam b/v2/tools/intel-linux.jam index 9a4f11ae0..06e11fe03 100644 --- a/v2/tools/intel-linux.jam +++ b/v2/tools/intel-linux.jam @@ -32,7 +32,8 @@ type.set-generated-target-suffix PCH : intel li toolset.inherit-rules intel-linux : gcc ; toolset.inherit-flags intel-linux : gcc - : off on full space + : off on full + space speed off all on ; @@ -103,6 +104,7 @@ rule init ( version ? : command * : options * ) SPACE = " " ; flags intel-linux.compile OPTIONS space : "-O1" ; # no specific space optimization flag in icc +flags intel-linux.compile OPTIONS speed : "-O3 -ip" ; # no specific space optimization flag in icc flags intel-linux.compile OPTIONS off : -w0 ; flags intel-linux.compile OPTIONS on : -w1 ; @@ -147,7 +149,7 @@ rule compile.c++.pch ( targets * : sources * : properties * ) # actions compile.c++.pch { - rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -pch-create "$(<)" "$(>)" + rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -pch-create "$(<)" "$(>)" } actions compile.fortran @@ -164,7 +166,7 @@ rule compile.c.pch ( targets * : sources * : properties * ) actions compile.c.pch { - rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c-header $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -pch-create "$(<)" "$(>)" + rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -pch-create "$(<)" "$(>)" } rule link ( targets * : sources * : properties * ) @@ -191,7 +193,7 @@ rule link.dll ( targets * : sources * : properties * ) # Differ from 'link' above only by -shared. actions link.dll bind LIBRARIES { - "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,-R$(SPACE)-Wl,"$(RPATH)" -o "$(<)" -Wl,-soname$(SPACE)-Wl,$(<[1]:D=) -shared "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(OPTIONS) + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,-R$(SPACE)-Wl,"$(RPATH)" -o "$(<)" -Wl,-soname$(SPACE)-Wl,$(<[1]:D=) -shared "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(OPTIONS) $(USER_OPTIONS) } From af2b583d802d59045b1b516b671d67c0bbdf605e Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 5 Feb 2010 11:47:14 +0000 Subject: [PATCH 143/198] Use right default command for C compilations. Patch from Spencer E. Olson [SVN r59507] --- v2/tools/pathscale.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/pathscale.jam b/v2/tools/pathscale.jam index 1a81444e1..4a12479e3 100644 --- a/v2/tools/pathscale.jam +++ b/v2/tools/pathscale.jam @@ -30,7 +30,7 @@ rule init ( version ? : command * : options * ) toolset.flags pathscale.compile.fortran90 OPTIONS $(condition) : [ feature.get-values : $(options) ] : unchecked ; - command_c = $(command_c[1--2]) $(command[-1]:B=pathCC) ; + command_c = $(command_c[1--2]) $(command[-1]:B=pathcc) ; toolset.flags pathscale CONFIG_C_COMMAND $(condition) : $(command_c) ; From 71e87fc94bad313105889a1a350947c75874b42f Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 5 Feb 2010 11:51:46 +0000 Subject: [PATCH 144/198] Use the pathf90 compiler for all compilation. Patch from Spencer E. Olson. Here's the explanation from email: The version of pathscale that I have access to is 3.2 and there doesn't seem to be a pathf77 executable. The man pages do not reference it, and there is also not a -f77 option that specifies that the code is fortran 77. Rather, the man pages indicate that pathscale only recognizes the difference between free form and fixed form fortran instead of F77 vs F90 vs F95. Does anyone have an available installation that differs from this? If not, I'll submit another small patch for changing this to using pathf90 to compile f77 code--I've tested it and it appears to work well enough. [SVN r59508] --- v2/tools/pathscale.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/tools/pathscale.jam b/v2/tools/pathscale.jam index 4a12479e3..f1dd149a7 100644 --- a/v2/tools/pathscale.jam +++ b/v2/tools/pathscale.jam @@ -36,7 +36,7 @@ rule init ( version ? : command * : options * ) # fortran support local f-command = [ common.get-invocation-command pathscale : pathf90 : $(command) ] ; - local command_f = $(command_f[1--2]) $(f-command[-1]:B=pathf77) ; + local command_f = $(command_f[1--2]) $(f-command[-1]:B=pathf90) ; local command_f90 = $(command_f[1--2]) $(f-command[-1]:B=pathf90) ; toolset.flags pathscale CONFIG_F_COMMAND $(condition) : $(command_f) ; @@ -87,7 +87,7 @@ actions compile.c++ actions compile.fortran { - "$(CONFIG_F_COMMAND)" -f77 $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" + "$(CONFIG_F_COMMAND)" $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } rule compile.fortran90 ( targets * : sources * : properties * ) From ab541ea5442bad5842279ded9137588ea2f1284b Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 5 Feb 2010 12:42:09 +0000 Subject: [PATCH 145/198] Enable soname for HPUX. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch from João Luís Pinto. [SVN r59509] --- v2/tools/gcc.jam | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 1b5181855..2e7c44414 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -396,6 +396,12 @@ if [ os.name ] != NT && [ os.name ] != OSF && [ os.name ] != HPUX && [ os.name ] SONAME_OPTION = -h ; } +# HPUX, for some reason, seem to use '+h', not '-h'. +if [ os.name ] = HPUX +{ + HAVE_SONAME = "" ; + SONAME_OPTION = +h ; +} toolset.flags gcc.compile USER_OPTIONS ; toolset.flags gcc.compile.c++ USER_OPTIONS ; From da1422075e37b90ef3fc2d45571864d53ab5eab4 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 8 Feb 2010 15:18:57 +0000 Subject: [PATCH 146/198] Make sure we use the same name of project module everywhere. In particular, this fixes problems on windows where project.load may be passed a path from Jamfile, and then load-jamfile may use a path using the exact case as stored in the file system -- different from the path passed to project.load. [SVN r59581] --- v2/build/project.jam | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/v2/build/project.jam b/v2/build/project.jam index 8b6a75c05..b4d2227e6 100644 --- a/v2/build/project.jam +++ b/v2/build/project.jam @@ -59,7 +59,7 @@ rule load ( jamfile-location ) # If Jamfile is already loaded, don't try again. if ! $(module-name) in $(.jamfile-modules) { - load-jamfile $(jamfile-location) ; + load-jamfile $(jamfile-location) : $(module-name) ; # We want to make sure that child project are loaded only after parent # projects. In particular, because parent projects define attributes @@ -277,6 +277,7 @@ rule find-jamfile ( # local rule load-jamfile ( dir # The directory of the project Jamfile. + : jamfile-module ) { # See if the Jamfile is where it should be. @@ -293,10 +294,6 @@ local rule load-jamfile ( : "Filenames are: " $(jamfile-to-load:D=) ; } - # The module of the Jamfile. - # - local jamfile-module = [ module-name [ path.parent $(jamfile-to-load) ] ] ; - # Initialize the Jamfile module before loading. # initialize $(jamfile-module) : [ path.parent $(jamfile-to-load) ] From ace132ca90a96332f8ab1409db3ee77cbd12f76a Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Thu, 11 Feb 2010 20:02:10 +0000 Subject: [PATCH 147/198] Need to remove inherited xsl:param we get from the top-level Jamroot. [SVN r59644] --- historic/jam/doc/build.jam | 1 + 1 file changed, 1 insertion(+) diff --git a/historic/jam/doc/build.jam b/historic/jam/doc/build.jam index 5f23814e2..5523007ae 100644 --- a/historic/jam/doc/build.jam +++ b/historic/jam/doc/build.jam @@ -30,6 +30,7 @@ boostbook standalone html html/images boost.root=. + -nav.layout=horizontal nav.layout=none html:admon.graphics.path=images/ html:navig.graphics.path=images/ From 45613ec05e1d95e7991005de62d9cc73cc1d4d49 Mon Sep 17 00:00:00 2001 From: "K. Noel Belcourt" Date: Fri, 12 Feb 2010 17:09:32 +0000 Subject: [PATCH 148/198] Replace -g with -gopt to reduce quantity of debug info. [SVN r59661] --- v2/tools/pgi.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/tools/pgi.jam b/v2/tools/pgi.jam index ca220235e..752dc5539 100644 --- a/v2/tools/pgi.jam +++ b/v2/tools/pgi.jam @@ -58,7 +58,7 @@ generators.register-fortran-compiler pgi.compile.fortran : FORTRAN : OBJ : shared : -fpic ; -flags pgi.compile OPTIONS on : -g ; +flags pgi.compile OPTIONS on : -gopt ; flags pgi.compile OPTIONS on : -xprofile=tcov ; flags pgi.compile OPTIONS speed : -fast -Mx,8,0x10000000 ; flags pgi.compile OPTIONS space : -xO2 -xspace ; @@ -92,7 +92,7 @@ actions compile.fortran } # Declare flags and actions for linking -flags pgi.link OPTIONS on : -g ; +flags pgi.link OPTIONS on : -gopt ; # Strip the binary when no debugging is needed flags pgi.link OPTIONS off : -s ; flags pgi.link OPTIONS on : -xprofile=tcov ; From b300e8a92127dd0062f1898d0680b2eaf5cc6053 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 20 Feb 2010 11:03:58 +0000 Subject: [PATCH 149/198] Special case processing of RPATH, so that '$ORIGIN' can be used Fixes #3479 [SVN r59765] --- v2/tools/gcc.jam | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 2e7c44414..c8e786fd9 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -799,6 +799,23 @@ rule init-link-flags ( toolset linker condition ) } } +# Enclose the RPATH variable on 'targets' in (double) quotes, +# unless it's already enclosed in single quotes. +# This special casing is done because it's common to pass +# '$ORIGIN' to linker -- and it has to have single quotes +# to prevent expansion by shell -- and if we add double +# quotes then preventing properties of single quotes disappear. +rule quote-rpath ( targets * ) +{ + local r = [ on $(targets[1]) return $(RPATH) ] ; + if ! [ MATCH "('.*')" : $(r) ] + { + r = "\"$(r)\"" ; + ECHO "Yeah" $(r) ; + } + RPATH on $(targets) = $(r) ; +} + # Declare actions for linking. rule link ( targets * : sources * : properties * ) { @@ -809,11 +826,12 @@ rule link ( targets * : sources * : properties * ) # parallel is just slower. For now, serialize only gcc links, it might be a # good idea to serialize all links. JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; + quote-rpath $(targets) ; } actions link bind LIBRARIES { - "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,"$(RPATH)" -Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" $(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,$(RPATH) -Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" $(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) } @@ -871,12 +889,13 @@ rule link.dll ( targets * : sources * : properties * ) setup-address-model $(targets) : $(sources) : $(properties) ; SPACE on $(targets) = " " ; JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; + quote-rpath $(targets) ; } # Differs from 'link' above only by -shared. actions link.dll bind LIBRARIES { - "$(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)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -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)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) } rule setup-threading ( targets * : sources * : properties * ) From b5e096cc6693f94d5e9875356a438821e6647e2c Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 21 Feb 2010 23:33:13 +0000 Subject: [PATCH 150/198] Various new builtins. [SVN r59829] --- historic/jam/src/builtins.c | 123 ++++++++++++++++++++++++++++++++++++ historic/jam/src/builtins.h | 3 + historic/jam/src/jam.c | 12 ++-- 3 files changed, 132 insertions(+), 6 deletions(-) diff --git a/historic/jam/src/builtins.c b/historic/jam/src/builtins.c index cbe42fea6..bd84bc2c0 100644 --- a/historic/jam/src/builtins.c +++ b/historic/jam/src/builtins.c @@ -187,6 +187,12 @@ void load_builtins() builtin_update, 0, args ); } + { + char * args[] = { "targets", "*", ":", "log", "?", 0 }; + bind_builtin( "UPDATE_NOW", + builtin_update_now, 0, args ); + } + { char * args[] = { "string", "pattern", "replacements", "+", 0 }; duplicate_rule( "subst", @@ -355,6 +361,18 @@ void load_builtins() builtin_md5, 0, args ) ; } + { + char * args[] = { "name", ":", "mode", 0 }; + bind_builtin( "FILE_OPEN", + builtin_file_open, 0, args ); + } + + { + char * args[] = { "string", ":", "width", 0 }; + bind_builtin( "PAD", + builtin_pad, 0, args ); + } + /* Initialize builtin modules. */ init_set(); init_path(); @@ -1266,6 +1284,60 @@ LIST * builtin_update( PARSE * parse, FRAME * frame ) return result; } +extern int anyhow; + +/* Takes a list of target names as first argument, and immediately + updates them. + Second parameter, if specified, if the descriptor (converted to a string) + of a log file where all build output is redirected. +*/ +LIST * builtin_update_now( PARSE * parse, FRAME * frame ) +{ + LIST * targets = lol_get( frame->args, 0 ); + LIST * log = lol_get( frame->args, 1 ); + int status = 0; + int original_stdout; + int original_stderr; + int n; + int targets_count; + const char** targets2; + int i; + + + if (log) + { + int fd = atoi(log->string); + /* Redirect stdout and stderr, temporary, to the log file. */ + original_stdout = dup (0); + original_stderr = dup (1); + dup2 (fd, 0); + dup2 (fd, 1); + } + + targets_count = list_length( targets ); + targets2 = (const char * *)BJAM_MALLOC( targets_count * sizeof( char * ) ); + for (i = 0 ; targets; targets = list_next( targets ) ) + targets2[ i++ ] = targets->string; + status |= make( targets_count, targets2, anyhow); + free( targets ); + + if (log) + { + /* Flush whatever stdio might have buffered, while descriptions + 0 and 1 still refer to the log file. */ + fflush (stdout); + fflush (stderr); + dup2 (original_stdout, 0); + dup2 (original_stderr, 1); + close (original_stdout); + close (original_stderr); + } + + if (status == 0) + return list_new (L0, newstr ("ok")); + else + return L0; +} LIST * builtin_search_for_target( PARSE * parse, FRAME * frame ) { @@ -1544,6 +1616,57 @@ LIST * builtin_md5( PARSE * parse, FRAME * frame ) return list_new (0, newstr(hex_output)); } +LIST *builtin_file_open( PARSE *parse, FRAME *frame ) +{ + char* name = lol_get(frame->args, 0)->string; + char* mode = lol_get(frame->args, 1)->string; + int fd; + char buffer[sizeof("4294967295")]; + + if (strcmp(mode, "w") == 0) + { + fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } + else + { + fd = open(name, O_RDONLY); + } + + if (fd != -1) + { + sprintf(buffer, "%d", fd); + return list_new(L0, newstr(buffer)); + } + else + { + return L0; + } +} + +LIST *builtin_pad( PARSE *parse, FRAME *frame ) +{ + char *string = lol_get(frame->args, 0)->string; + char *width_s = lol_get(frame->args, 1)->string; + + int current = strlen (string); + int desired = atoi(width_s); + if (current >= desired) + return list_new (L0, string); + else + { + char *buffer = malloc (desired + 1); + int i; + LIST *result; + + strcpy (buffer, string); + for (i = current; i < desired; ++i) + buffer[i] = ' '; + buffer[desired] = '\0'; + result = list_new (L0, newstr (buffer)); + free (buffer); + return result; + } +} #ifdef HAVE_PYTHON diff --git a/historic/jam/src/builtins.h b/historic/jam/src/builtins.h index c996528a3..ba37b87ea 100644 --- a/historic/jam/src/builtins.h +++ b/historic/jam/src/builtins.h @@ -42,6 +42,7 @@ LIST *builtin_caller_module( PARSE *parse, FRAME *args ); LIST *builtin_backtrace( PARSE *parse, FRAME *args ); LIST *builtin_pwd( PARSE *parse, FRAME *args ); LIST *builtin_update( PARSE *parse, FRAME *args ); +LIST *builtin_update_now( PARSE *parse, FRAME *args ); LIST *builtin_search_for_target( PARSE *parse, FRAME *args ); LIST *builtin_import_module( PARSE *parse, FRAME *args ); LIST *builtin_imported_modules( PARSE *parse, FRAME *frame ); @@ -56,6 +57,8 @@ LIST *builtin_check_if_file( PARSE *parse, FRAME *frame ); LIST *builtin_python_import_rule( PARSE *parse, FRAME *frame ); LIST *builtin_shell( PARSE *parse, FRAME *frame ); LIST *builtin_md5( PARSE *parse, FRAME *frame ); +LIST *builtin_file_open( PARSE *parse, FRAME *frame ); +LIST *builtin_pad( PARSE *parse, FRAME *frame ); void backtrace( FRAME *frame ); diff --git a/historic/jam/src/jam.c b/historic/jam/src/jam.c index fc2f6bb29..b10974e08 100644 --- a/historic/jam/src/jam.c +++ b/historic/jam/src/jam.c @@ -200,6 +200,8 @@ static void run_unit_tests() } #endif +int anyhow = 0; + #ifdef HAVE_PYTHON extern PyObject * bjam_call ( PyObject * self, PyObject * args ); extern PyObject * bjam_import_rule ( PyObject * self, PyObject * args ); @@ -214,7 +216,6 @@ int main( int argc, char * * argv, char * * arg_environ ) char * s; struct option optv[N_OPTS]; char const * all = "all"; - int anyhow = 0; int status; int arg_c = argc; char * * arg_v = argv; @@ -448,6 +449,9 @@ int main( int argc, char * * argv, char * * arg_environ ) } } + if (!targets_to_update()) + mark_target_for_updating("all"); + /* Parse ruleset. */ { FRAME frame[ 1 ]; @@ -514,11 +518,7 @@ int main( int argc, char * * argv, char * * arg_environ ) PROFILE_ENTER( MAIN_MAKE ); LIST * targets = targets_to_update(); - if ( !targets ) - { - status |= make( 1, &all, anyhow ); - } - else + if (targets) { int targets_count = list_length( targets ); const char * * targets2 = (const char * *) From 29840f6a482ce7a37ea66c6f708d0e60f46f41f5 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 21 Feb 2010 23:42:19 +0000 Subject: [PATCH 151/198] Remove debug print [SVN r59830] --- v2/tools/gcc.jam | 1 - 1 file changed, 1 deletion(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index c8e786fd9..1448541c1 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -811,7 +811,6 @@ rule quote-rpath ( targets * ) if ! [ MATCH "('.*')" : $(r) ] { r = "\"$(r)\"" ; - ECHO "Yeah" $(r) ; } RPATH on $(targets) = $(r) ; } From 0406045d19ac7e909a8b52ee9661e5313e587af7 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 21 Feb 2010 23:46:42 +0000 Subject: [PATCH 152/198] Fix scalability issues in subvariant.all-referenced-targets. [SVN r59831] --- v2/build/virtual-target.jam | 11 +++++++---- v2/tools/stage.jam | 11 +++++++---- v2/util/set.jam | 30 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/v2/build/virtual-target.jam b/v2/build/virtual-target.jam index d1cd33548..87661adac 100644 --- a/v2/build/virtual-target.jam +++ b/v2/build/virtual-target.jam @@ -1218,7 +1218,7 @@ class subvariant # referred to using the dependency property are returned as properties, not # targets. # - rule all-referenced-targets ( ) + rule all-referenced-targets ( theset ) { # Find directly referenced targets. local deps = [ $(self.build-properties).dependency ] ; @@ -1228,17 +1228,20 @@ class subvariant local r ; for local t in $(all-targets) { - r += [ $(t:G=).creating-subvariant ] ; + if ! [ $(theset).contains $(t) ] + { + $(theset).add $(t) ; + r += [ $(t:G=).creating-subvariant ] ; + } } r = [ sequence.unique $(r) ] ; for local s in $(r) { if $(s) != $(__name__) { - all-targets += [ $(s).all-referenced-targets ] ; + $(s).all-referenced-targets $(theset) ; } } - return $(all-targets) ; } # Returns the properties specifying implicit include paths to generated diff --git a/v2/tools/stage.jam b/v2/tools/stage.jam index 4c998ff93..296e7558e 100644 --- a/v2/tools/stage.jam +++ b/v2/tools/stage.jam @@ -246,20 +246,23 @@ class install-target-class : basic-target s += [ $(t).creating-subvariant ] ; } s = [ sequence.unique $(s) ] ; - - local result = $(targets) ; + + local result = [ new set ] ; + $(result).add $(targets) ; + for local i in $(s) { - result += [ $(i).all-referenced-targets ] ; + $(i).all-referenced-targets $(result) ; } local result2 ; - for local r in $(result) + for local r in [ $(result).list ] { if $(r:G) != { result2 += $(r:G=) ; } } + DELETE_MODULE $(result) ; result = [ sequence.unique $(result2) ] ; } diff --git a/v2/util/set.jam b/v2/util/set.jam index 9562a164f..fc179134f 100644 --- a/v2/util/set.jam +++ b/v2/util/set.jam @@ -3,6 +3,36 @@ # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +class set +{ + rule __init__ ( ) + { + } + + rule add ( elements * ) + { + for local e in $(elements) + { + if ! $($(e)) + { + $(e) = 1 ; + self.result += $(e) ; + } + } + } + + rule contains ( element ) + { + return $($(element)) ; + } + + rule list ( ) + { + return $(self.result) ; + } +} + + # Returns the elements of set1 that are not in set2. # From 94dc4b82e91f75670f049d3cf7dc774f38f82f31 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 21 Feb 2010 23:59:08 +0000 Subject: [PATCH 153/198] Report where the headers and libraries were built to at the end of the build. [SVN r59833] --- v2/build-system.jam | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/v2/build-system.jam b/v2/build-system.jam index c948c64b6..535bd6b1a 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -113,6 +113,10 @@ rule set-default-toolset ( toolset : version ? ) .default-toolset-version = $(version) ; } +rule set-post-build-hook ( function ) +{ + .post-build-hook = $(function) ; +} ################################################################################ # @@ -947,8 +951,23 @@ local rule should-clean-project ( project ) UPDATE clean ; } else - { + { DEPENDS all : $(actual-targets) ; - UPDATE all $(.out-xml) ; + if UPDATE_NOW in [ RULENAMES ] + { + local ok = [ UPDATE_NOW all $(.out-xml) ] ; + if $(.post-build-hook) + { + $(.post-build-hook) $(ok) ; + } + # Prevent automatic update of the 'all' target, now that + # we have explicitly updated what we wanted. + UPDATE ; + } + else + { + UPDATE all $(.out-xml) ; + } } } + From a8f11a4efb541aa86b8938717feb5cfba93f3f2e Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 22 Feb 2010 10:41:26 +0000 Subject: [PATCH 154/198] Don't produce any messages upon seeing no. [SVN r59840] --- v2/build/targets.jam | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/v2/build/targets.jam b/v2/build/targets.jam index 3d38002d3..d86aa04f6 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -1320,8 +1320,10 @@ class basic-target : abstract-target } else if [ $(rproperties).get ] = no { - ECHO [ targets.indent ] "Skipping build of: " [ full-name ] - " no in common properties" ; + # If we just see no, we cannot produce any reasonable + # diagnostics. The code that adds this property is expected + # to explain why a target is not built, for example using + # the configure.log-component-configuration function. } else { From 88c0c9562242d0b0d7c94a3a38bbe04752ea3ad4 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Feb 2010 10:27:50 +0000 Subject: [PATCH 155/198] Robustify against dots in Jamfile pathnames. Without this patch, if a Jamfile uses $(__name__).some-rule for example as generator id, the rule would not be actually called if Jamfile's path included a dot. [SVN r59862] --- v2/util/indirect.jam | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/v2/util/indirect.jam b/v2/util/indirect.jam index c116506fd..ec63f1920 100644 --- a/v2/util/indirect.jam +++ b/v2/util/indirect.jam @@ -51,9 +51,15 @@ rule make-qualified ( rulename bound-args * : frames ? ) else { frames ?= 1 ; - # Take the first dot-separated element as module name. This disallows - # module names with dots, but allows rule names with dots. - local module-context = [ MATCH ^([^.]*)\\..* : $(rulename) ] ; + # If the rule name includes a Jamfile module, grab it. + local module-context = [ MATCH ^(Jamfile<[^>]*>)\\..* : $(rulename) ] ; + + if ! $(module-context) + { + # Take the first dot-separated element as module name. This disallows + # module names with dots, but allows rule names with dots. + module-context = [ MATCH ^([^.]*)\\..* : $(rulename) ] ; + } module-context ?= [ CALLER_MODULE $(frames) ] ; return [ make $(rulename) $(bound-args) : $(module-context) ] ; } From f59ca8b641c3b8b72e9d0b54549beef2ad88a2fa Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Feb 2010 12:08:58 +0000 Subject: [PATCH 156/198] Add 'ignore -n' option to BUILTIN_UPDATE_NOW. [SVN r59865] --- historic/jam/src/builtins.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/historic/jam/src/builtins.c b/historic/jam/src/builtins.c index bd84bc2c0..1e28191b4 100644 --- a/historic/jam/src/builtins.c +++ b/historic/jam/src/builtins.c @@ -1290,18 +1290,22 @@ extern int anyhow; updates them. Second parameter, if specified, if the descriptor (converted to a string) of a log file where all build output is redirected. + Third parameter, if non-empty, specifies that the -n option should have + no effect -- that is, all out-of-date targets should be rebuild. */ LIST * builtin_update_now( PARSE * parse, FRAME * frame ) { LIST * targets = lol_get( frame->args, 0 ); LIST * log = lol_get( frame->args, 1 ); + LIST * force = lol_get (frame->args, 2); int status = 0; int original_stdout; int original_stderr; int n; - int targets_count; - const char** targets2; - int i; + int targets_count; + const char** targets2; + int i; + int original_noexec; if (log) @@ -1314,6 +1318,12 @@ LIST * builtin_update_now( PARSE * parse, FRAME * frame ) dup2 (fd, 1); } + if (force) + { + original_noexec = globs.noexec; + globs.noexec = 0; + } + targets_count = list_length( targets ); targets2 = (const char * *)BJAM_MALLOC( targets_count * sizeof( char * ) ); for (i = 0 ; targets; targets = list_next( targets ) ) @@ -1321,6 +1331,11 @@ LIST * builtin_update_now( PARSE * parse, FRAME * frame ) status |= make( targets_count, targets2, anyhow); free( targets ); + if (force) + { + globs.noexec = original_noexec; + } + if (log) { /* Flush whatever stdio might have buffered, while descriptions From b50d69df139d1f5562295b9ca7565d902f4d09ed Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Feb 2010 12:18:49 +0000 Subject: [PATCH 157/198] Fix UPDATE_NOW declaration [SVN r59866] --- historic/jam/src/builtins.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/historic/jam/src/builtins.c b/historic/jam/src/builtins.c index 1e28191b4..66d178af7 100644 --- a/historic/jam/src/builtins.c +++ b/historic/jam/src/builtins.c @@ -188,7 +188,9 @@ void load_builtins() } { - char * args[] = { "targets", "*", ":", "log", "?", 0 }; + char * args[] = { "targets", "*", + ":", "log", "?", + ":", "ignore-minus-n", "?", 0 }; bind_builtin( "UPDATE_NOW", builtin_update_now, 0, args ); } From 1fb10d066cea609b7bf66909730f8b45494e4b34 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 24 Feb 2010 12:26:26 +0000 Subject: [PATCH 158/198] Configuration framework and boost.math long long configuration. [SVN r59867] --- v2/build-system.jam | 24 +++++++ v2/build/configure.jam | 157 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 v2/build/configure.jam diff --git a/v2/build-system.jam b/v2/build-system.jam index 535bd6b1a..df48ec11f 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -26,6 +26,7 @@ import utility ; import version ; import virtual-target ; import generators ; +import configure ; ################################################################################ # @@ -113,6 +114,11 @@ rule set-default-toolset ( toolset : version ? ) .default-toolset-version = $(version) ; } +rule set-pre-build-hook ( function ) +{ + .pre-build-hook = $(function) ; +} + rule set-post-build-hook ( function ) { .post-build-hook = $(function) ; @@ -702,6 +708,17 @@ local rule should-clean-project ( project ) generators.dump ; } + # We wish to put config.log in the build directory corresponding + # to Jamroot, so that the location does not differ depending on + # directory where we do build. The amount of indirection necessary + # here is scary. + local first-project = [ $(targets[0]).project ] ; + local first-project-root-location = [ $(first-project).get project-root ] ; + local first-project-root-module = [ project.load $(first-project-root-location) ] ; + local first-project-root = [ project.target $(first-project-root-module) ] ; + local first-build-build-dir = [ $(first-project-root).build-dir ] ; + configure.set-log-file $(first-build-build-dir)/config.log ; + # Now that we have a set of targets to build and a set of property sets to # build the targets with, we can start the main build process by using each # property set to generate virtual targets from all of our listed targets @@ -952,6 +969,13 @@ local rule should-clean-project ( project ) } else { + configure.print-configure-checks-summary ; + + if $(.pre-build-hook) + { + $(.pre-build-hook) ; + } + DEPENDS all : $(actual-targets) ; if UPDATE_NOW in [ RULENAMES ] { diff --git a/v2/build/configure.jam b/v2/build/configure.jam new file mode 100644 index 000000000..b1cd60402 --- /dev/null +++ b/v2/build/configure.jam @@ -0,0 +1,157 @@ +# Copyright (c) 2010 Vladimir Prus. +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +# This module defines function to help with two main tasks: +# +# - Discovering build-time configuration for the purposes of adjusting +# build process. +# - Reporting what is built, and how it is configured. + +import targets ; +import errors ; +import targets ; +import sequence ; + +rule log-summary ( ) +{ + +} + +.width = 30 ; + +rule set-width ( width ) +{ + .width = $(width) ; +} + +# Declare that the components specified by the parameter exist. +rule register-components ( components * ) +{ + .components += $(components) ; +} + +# Declare that the components specified by the parameters will +# be build. +rule components-building ( components * ) +{ + .built-components += $(components) ; +} + +# Report something about component configuration that the +# user should better know. +rule log-component-configuration ( component : message ) +{ + # FIXME: implement per-property-set logs + .component-logs.$(component) += $(message) ; +} + +rule log-check-result ( result ) +{ + .check-results += $(result) ; +} + +rule print-component-configuration ( ) +{ + local c = [ sequence.unique $(.components) ] ; + + ECHO "Component configuration:\n" ; + for c in $(.components) + { + local s ; + if $(c) in $(.built-components) + { + s = "building" ; + } + else + { + s = "not building" ; + } + ECHO [ PAD " - $(c)" : $(.width) ] ": $(s)" ; + for local m in $(.component-logs.$(c)) + { + ECHO " -" $(m) ; + } + } + ECHO ; +} + +rule print-configure-checks-summary ( ) +{ + # FIXME: the problem with that approach is tha + # the user sees checks summary when all checks are + # done, and has no progress reporting while the + # checks are being executed. + if $(.check-results) + { + ECHO "Configuration checks summary\n" ; + + for local r in $(.check-results) + { + ECHO $(r) ; + } + ECHO ; + } +} + +# Attempt to build a metatarget named by 'metatarget-reference' +# in context of 'project' with properties 'ps'. +# Returns non-empty value if build is OK. +rule builds ( metatarget-reference : project : ps : what ) +{ + local result ; + + if ! $(.$(what)-tested.$(ps)) + { + .$(what)-tested.$(ps) = true ; + + local targets = [ targets.generate-from-reference + $(metatarget-reference) : $(project) : $(ps) ] ; + + local jam-targets ; + for local t in $(targets[2-]) + { + jam-targets += [ $(t).actualize ] ; + } + + if ! UPDATE_NOW in [ RULENAMES ] + { + # Cannot determine. Assume existance. + } + else + { + local x = [ PAD " - $(what)" : $(.width) ] ; + if [ UPDATE_NOW $(jam-targets) : $(.log-fd) : ignore-minus-n ] + { + .$(what)-supported.$(ps) = yes ; + result = true ; + log-check-result "$(x) : yes" ; + } + else + { + log-check-result "$(x) : no" ; + } + } + return $(result) ; + } + else + { + return $(.$(what)-supported.$(ps)) ; + } +} + +# Called by Boost.Build startup code to specify name of a file +# that will receive results of configure checks. This +# should never be called by users. +rule set-log-file ( log-file ) +{ + # FIXME: remove this check as soon as Boost regression tests + # start using trunk bjam + if FILE_OPEN in [ RULENAMES ] + { + .log-fd = [ FILE_OPEN $(log-file) : "w" ] ; + } +} + From f5a356823e3bb976e71f1a157093fcd37c92c10d Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 25 Feb 2010 11:41:41 +0000 Subject: [PATCH 159/198] Ran ranlib on static libraries. [SVN r59898] --- v2/tools/gcc.jam | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 1448541c1..51d4e7519 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -103,12 +103,12 @@ rule init ( version ? : command * : options * ) bin ?= [ common.get-absolute-tool-path $(command[-1]) ] ; root ?= $(bin:D) ; } + # The 'command' variable can have multiple elements. When calling + # the SHELL builtin we need a single string. + local command-string = $(command:J=" ") ; # Autodetect the version and flavor if not given. if $(command) - { - # The 'command' variable can have multiple elements. When calling - # the SHELL builtin we need a single string. - local command-string = $(command:J=" ") ; + { local machine = [ MATCH "^([^ ]+)" : [ SHELL "$(command-string) -dumpmachine" ] ] ; version ?= [ MATCH "^([0-9.]+)" @@ -185,17 +185,35 @@ rule init ( version ? : command * : options * ) # If it's not a system gcc install we should adjust the various programs as # needed to prefer using the install specific versions. This is essential # for correct use of MinGW and for cross-compiling. + + local nl = " +" ; # - The archive builder. - local archiver = - [ common.get-invocation-command gcc - : ar : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; + local archiver = [ common.get-invocation-command gcc + : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : [ SHELL "$(command-string) -print-prog-name=ar" ] ] ] + : [ feature.get-values : $(options) ] + : $(bin) + : search-path ] ; toolset.flags gcc.archive .AR $(condition) : $(archiver[1]) ; if $(.debug-configuration) { ECHO notice: using gcc archiver :: $(condition) :: $(archiver[1]) ; } + # - Ranlib + local ranlib = [ common.get-invocation-command gcc + : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : [ SHELL "$(command-string) -print-prog-name=ranlib" ] ] ] + : [ feature.get-values : $(options) ] + : $(bin) + : search-path ] ; + toolset.flags gcc.archive .RANLIB $(condition) : $(ranlib[1]) ; + if $(.debug-configuration) + { + ECHO notice: using gcc ranlib :: $(condition) :: $(ranlib[1]) ; + } + + # - The resource compiler. local rc = [ common.get-invocation-command-nodefault gcc @@ -839,6 +857,7 @@ actions link bind LIBRARIES # logic in intel-linux, but that's hardly worth the trouble as on Linux, 'ar' is # always available. .AR = ar ; +.RANLIB = ranlib ; toolset.flags gcc.archive AROPTIONS ; @@ -879,9 +898,9 @@ rule archive ( targets * : sources * : properties * ) actions piecemeal archive { "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" + "$(.RANLIB)" "$(<)" } - rule link.dll ( targets * : sources * : properties * ) { setup-threading $(targets) : $(sources) : $(properties) ; From 442fffbefbaac98edfc9250b14f33c86639fe4fc Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 28 Feb 2010 07:51:21 +0000 Subject: [PATCH 160/198] Use same flags for fortran compilation as for C and C++. Fixes #3916. [SVN r59986] --- v2/tools/gcc.jam | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 51d4e7519..0cdadbbb5 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -498,8 +498,11 @@ rule compile.c ( targets * : sources * : properties * ) DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; } -rule compile.fortran +rule compile.fortran ( targets * : sources * : properties * ) { + setup-threading $(targets) : $(sources) : $(properties) ; + setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; } actions compile.c++ bind PCH_FILE From 5f9ec5037816c6d6213686c1a0ecdf16eb6dc9ae Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 1 Mar 2010 22:32:00 +0000 Subject: [PATCH 161/198] Adjust library names on mingw/cygwin. Fixes #1907 [SVN r60062] --- v2/tools/gcc.jam | 28 -------------------- v2/tools/types/lib.jam | 58 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/v2/tools/gcc.jam b/v2/tools/gcc.jam index 0cdadbbb5..0584df477 100644 --- a/v2/tools/gcc.jam +++ b/v2/tools/gcc.jam @@ -44,39 +44,11 @@ toolset.inherit-rules gcc : unix ; generators.override gcc.prebuilt : builtin.prebuilt ; generators.override gcc.searched-lib-generator : searched-lib-generator ; -# Target naming is determined by types/lib.jam and the settings below this -# comment. -# -# On *nix: -# libxxx.a static library -# libxxx.so shared library -# -# On windows (mingw): -# libxxx.lib static library -# xxx.dll DLL -# xxx.lib import library -# -# On windows (cygwin) i.e. cygwin -# libxxx.a static library -# xxx.dll DLL -# libxxx.dll.a import library -# -# Implementation notes: -# * User can always override this by using the @rule. -# * These settings have been chosen so that mingw is in line with msvc naming -# conventions. -# * For cygwin the cygwin naming convention has been chosen. - # Make gcc toolset object files use the "o" suffix on all platforms. type.set-generated-target-suffix OBJ : gcc : o ; type.set-generated-target-suffix OBJ : gcc windows : o ; type.set-generated-target-suffix OBJ : gcc cygwin : o ; -type.set-generated-target-suffix STATIC_LIB : gcc cygwin : a ; -type.set-generated-target-suffix IMPORT_LIB : gcc cygwin : dll.a ; -type.set-generated-target-prefix IMPORT_LIB : gcc cygwin : lib ; - - # Initializes the gcc toolset for the given version. If necessary, command may # be used to specify where the compiler is located. The parameter 'options' is a # space-delimited list of options, each one specified as diff --git a/v2/tools/types/lib.jam b/v2/tools/types/lib.jam index 2810f9436..854ab8fd5 100644 --- a/v2/tools/types/lib.jam +++ b/v2/tools/types/lib.jam @@ -5,26 +5,64 @@ import type ; # for set-generated-target-suffix import os ; +# The following naming scheme is used for libraries. +# +# On *nix: +# libxxx.a static library +# libxxx.so shared library +# +# On windows (msvc) +# libxxx.lib static library +# xxx.dll DLL +# xxx.lib import library +# +# On windows (mingw): +# libxxx.a static library +# libxxx.dll DLL +# libxxx.dll.a import library +# +# On cygwin i.e. cygwin +# libxxx.a static library +# cygxxx.dll DLL +# libxxx.dll.a import library +# + type.register LIB ; -type.set-generated-target-prefix LIB : : "lib" ; -type.set-generated-target-prefix LIB : windows : "" ; -type.set-generated-target-prefix LIB : cygwin : "cyg" ; - -# FIXME: should not register both extensions on both -# platforms. +# FIXME: should not register both extensions on both platforms. type.register STATIC_LIB : a lib : LIB ; -type.set-generated-target-suffix STATIC_LIB : windows : lib ; -type.set-generated-target-suffix STATIC_LIB : gcc windows : a ; -type.set-generated-target-suffix STATIC_LIB : cygwin : lib ; +# The 'lib' prefix is used everywhere type.set-generated-target-prefix STATIC_LIB : : lib ; +# Use '.lib' suffix for windows +type.set-generated-target-suffix STATIC_LIB : windows : lib ; + +# Except with gcc. +type.set-generated-target-suffix STATIC_LIB : gcc windows : a ; + +# Use xxx.lib for import libs type IMPORT_LIB : : STATIC_LIB ; -type.set-generated-target-suffix IMPORT_LIB : : lib ; type.set-generated-target-prefix IMPORT_LIB : : "" ; +type.set-generated-target-suffix IMPORT_LIB : : lib ; + +# Except with gcc (mingw or cygwin), where use libxxx.dll.a +type.set-generated-target-prefix IMPORT_LIB : gcc : lib ; +type.set-generated-target-suffix IMPORT_LIB : gcc : dll.a ; type.register SHARED_LIB : so dll dylib : LIB ; + +# Both mingw and cygwin use libxxx.dll naming scheme. +# On Linux, use "lib" prefix +type.set-generated-target-prefix SHARED_LIB : : lib ; +# But don't use it on windows +type.set-generated-target-prefix SHARED_LIB : windows : "" ; +# But use it again on mingw +type.set-generated-target-prefix SHARED_LIB : gcc windows : lib ; +# And use 'cyg' on cygwin +type.set-generated-target-prefix SHARED_LIB : cygwin : cyg ; + + type.set-generated-target-suffix SHARED_LIB : windows : dll ; type.set-generated-target-suffix SHARED_LIB : cygwin : dll ; type.set-generated-target-suffix SHARED_LIB : darwin : dylib ; From 52c1c81918c23da3007db0ff8fa39c9e95cd69af Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 4 Mar 2010 23:02:57 +0000 Subject: [PATCH 162/198] Implement PRECIOUS builtin. Note that existing code already tried to assume that the 'together' flag means precious, but as described at: http://public.perforce.com:8080/@md=d&cd=//public/jam/src/&cdf=//public/jam/src/make1.c&c=klD@/1494?ac=10 this was not really wrong. Now, PRECIOUS makes the target never removed. I plan to extend it so that user can control what exit codes cause the target to be removed. [SVN r60157] --- historic/jam/src/builtins.c | 20 ++++++++++++++++++++ historic/jam/src/builtins.h | 1 + historic/jam/src/make1.c | 12 ++++++++++-- historic/jam/src/rules.h | 2 ++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/historic/jam/src/builtins.c b/historic/jam/src/builtins.c index 66d178af7..4f94edc3a 100644 --- a/historic/jam/src/builtins.c +++ b/historic/jam/src/builtins.c @@ -375,6 +375,12 @@ void load_builtins() builtin_pad, 0, args ); } + { + char * args[] = { "targets", "*", 0 }; + bind_builtin( "PRECIOUS", + builtin_precious, 0, args ); + } + /* Initialize builtin modules. */ init_set(); init_path(); @@ -1685,6 +1691,20 @@ LIST *builtin_pad( PARSE *parse, FRAME *frame ) } } +LIST *builtin_precious( PARSE *parse, FRAME *frame ) +{ + LIST* targets = lol_get(frame->args, 0); + + for ( ; targets; targets = list_next( targets ) ) + { + TARGET* t = bindtarget (targets->string); + t->flags |= T_FLAG_PRECIOUS; + } + + return L0; +} + + #ifdef HAVE_PYTHON LIST * builtin_python_import_rule( PARSE * parse, FRAME * frame ) diff --git a/historic/jam/src/builtins.h b/historic/jam/src/builtins.h index ba37b87ea..1edaab738 100644 --- a/historic/jam/src/builtins.h +++ b/historic/jam/src/builtins.h @@ -59,6 +59,7 @@ LIST *builtin_shell( PARSE *parse, FRAME *frame ); LIST *builtin_md5( PARSE *parse, FRAME *frame ); LIST *builtin_file_open( PARSE *parse, FRAME *frame ); LIST *builtin_pad( PARSE *parse, FRAME *frame ); +LIST *builtin_precious( PARSE *parse, FRAME *frame ); void backtrace( FRAME *frame ); diff --git a/historic/jam/src/make1.c b/historic/jam/src/make1.c index 26ee5c41e..8001f3339 100644 --- a/historic/jam/src/make1.c +++ b/historic/jam/src/make1.c @@ -839,12 +839,20 @@ static void make1d( state * pState ) /* If the command was interrupted or failed and the target is not * "precious", remove the targets. */ - if ( ( status != EXEC_CMD_OK ) && !( cmd->rule->actions->flags & RULE_TOGETHER ) ) + if (status != EXEC_CMD_OK) { LIST * targets = lol_get( &cmd->args, 0 ); for ( ; targets; targets = list_next( targets ) ) - if ( !unlink( targets->string ) ) + { + int need_unlink = 1; + TARGET* t = bindtarget ( targets->string ); + if (t->flags & T_FLAG_PRECIOUS) + { + need_unlink = 0; + } + if (need_unlink && !unlink( targets->string ) ) printf( "...removing %s\n", targets->string ); + } } /* Free this command and call make1c() to move onto the next one scheduled diff --git a/historic/jam/src/rules.h b/historic/jam/src/rules.h index 9ad4171b6..78e056202 100644 --- a/historic/jam/src/rules.h +++ b/historic/jam/src/rules.h @@ -166,6 +166,8 @@ struct _target */ #define T_FLAG_ISFILE 0x0400 +#define T_FLAG_PRECIOUS 0x0800 + char binding; /* how target relates to a real file or * folder */ From 29ea1c32e417a04be4199b3fc23eea60a6b710c0 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 6 Mar 2010 07:24:10 +0000 Subject: [PATCH 163/198] Default to darwin toolset on OSX. [SVN r60222] --- v2/build-system.jam | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v2/build-system.jam b/v2/build-system.jam index df48ec11f..6a313108a 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -589,6 +589,10 @@ local rule should-clean-project ( project ) { default-toolset = msvc ; } + else if [ os.name ] = MACOSX + { + default-toolset = darwin ; + } } ECHO "warning: No toolsets are configured." ; From 29d0eea7fa30b5a6d3b18db644ecf24564ea0bbd Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 6 Mar 2010 18:12:13 +0000 Subject: [PATCH 164/198] Document new builtins [SVN r60252] --- historic/jam/doc/bjam.qbk | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index 2c070456a..c3cc1a9d4 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -1040,6 +1040,77 @@ builtin rule, =COMMAND= can be used as an alias for =SHELL= in such a case. [endsect] +[section =MD5= ] + +[pre +rule MD5 ( /string/ ) +] + +=MD5= computes the MD5 hash of the string passed as paramater and returns it. + +[endsect] + +[section =SPLIT_BY_CHARACTERS= ] + +[pre +rule SPLIT_BY_CHARACTERS ( /string/ : /delimiters/ ) +] + +=SPLIT_BY_CHARACTERS= splits the specified /string/ on any delimiter character +present in /delimiters/ and returns the resulting list. + +[endsect] + +[section =PRECIOUS= ] + +[pre +rule PRECIOUS ( /targets/ * ) +] + +The =PRECIOUS= rule specifies that each of the targets passed as the arguments +should not be removed even if the command updating that target fails. + +[endsect] + +[section =PAD= ] + +[pre +rule PAD ( /string/ : /width/ ) +] + +If /string/ is shorter than /width/ characters, pads it with whitespace +characters on the right, and returns the result. Otherwise, returns +/string/ unmodified. + +[endsect] + +[section =FILE_OPEN= ] + +[pre +rule FILE_OPEN ( /filename/ : /mode/ ) +] + +The =FILE_OPEN= rule opens the specified file and returns a file +descriptor. The /mode/ parameter can be either "w" or "r". Note +that at present, only the =UPDATE_NOW= rule can use the resulting +file descriptor number. + +[endsect] + +[section =UPDATE_NOW= ] + +[pre +rule UPDATE_NOW ( /targets/ * : /log/ ? : /ignore-minus-n/ ? ) +] + +The =UPDATE_NOW= caused the specified targets to be updated immediately. +If update was successfully, non-empty string is returned. The /log/ parameter, +if present, specifies a descriptor of a file where all output from building +is redirected. If the /ignore-minus-n/ parameter is specified, the targets +are updated even if the =-n= parameter is specified on the command line. + +[endsect] + [endsect] [endsect] From 5f5e7aa253ea843074738a7ca6fd857d6d0fc9a3 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sat, 6 Mar 2010 18:49:05 +0000 Subject: [PATCH 165/198] Ignore bin/config.log when testing [SVN r60253] --- v2/test/BoostBuild.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/test/BoostBuild.py b/v2/test/BoostBuild.py index 35a4f22cf..49db78476 100644 --- a/v2/test/BoostBuild.py +++ b/v2/test/BoostBuild.py @@ -660,12 +660,14 @@ class Tester(TestCmd.TestCmd): self.ignore('*.pdb') # MSVC program database files. self.ignore('*.rsp') # Response files. self.ignore('*.tds') # Borland debug symbols. - self.ignore('*.manifest') # MSVC DLL manifests. + self.ignore('*.manifest') # MSVC DLL manifests. # Debug builds of bjam built with gcc produce this profiling data. self.ignore('gmon.out') self.ignore('*/gmon.out') + self.ignore("bin/config.log") + if not self.unexpected_difference.empty(): annotation('failure', 'Unexpected changes found') output = StringIO.StringIO() From 79f0d5d1e1195c9acc087abb03ba8641e9113d66 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 16 Mar 2010 18:02:55 +0000 Subject: [PATCH 166/198] Simplify 'alias' metatarget rule [SVN r60652] --- v2/build/alias.jam | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/v2/build/alias.jam b/v2/build/alias.jam index 48019cb98..1a34c20b1 100644 --- a/v2/build/alias.jam +++ b/v2/build/alias.jam @@ -58,15 +58,9 @@ rule alias ( name : sources * : requirements * : default-build * : { local project = [ project.current ] ; - targets.main-target-alternative - [ new alias-target-class $(name) : $(project) - : [ targets.main-target-sources $(sources) : $(name) : no-renaming ] - : [ targets.main-target-requirements $(requirements) : $(project) ] - : [ targets.main-target-default-build $(default-build) : $(project) - ] - : [ targets.main-target-usage-requirements $(usage-requirements) : - $(project) ] - ] ; + return [ targets.create-metatarget alias-target-class : $(project) + : $(name) : $(sources) : $(requirements) + : $(default-build) : $(usage-requirements) ] ; } From e847e519ef94122e3dbd821c75fcf1b2a5ed6ad0 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 16 Mar 2010 22:24:52 +0000 Subject: [PATCH 167/198] Somewhat simplify Boost.Math configuration checks [SVN r60658] --- v2/build/configure.jam | 66 ++++++++++++++++++++++++++++++++++++++---- v2/build/targets.jam | 22 ++++++++++++-- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/v2/build/configure.jam b/v2/build/configure.jam index b1cd60402..c50bf73ef 100644 --- a/v2/build/configure.jam +++ b/v2/build/configure.jam @@ -14,6 +14,7 @@ import targets ; import errors ; import targets ; import sequence ; +import "class" : new ; rule log-summary ( ) { @@ -48,16 +49,25 @@ rule log-component-configuration ( component : message ) .component-logs.$(component) += $(message) ; } + + rule log-check-result ( result ) { - .check-results += $(result) ; + if ! $(.announced-checks) + { + ECHO "Performing configuration checks\n" ; + .announced-checks = 1 ; + } + + ECHO $(result) ; + #.check-results += $(result) ; } rule print-component-configuration ( ) { local c = [ sequence.unique $(.components) ] ; - ECHO "Component configuration:\n" ; + ECHO "\nComponent configuration:\n" ; for c in $(.components) { local s ; @@ -102,11 +112,11 @@ rule print-configure-checks-summary ( ) rule builds ( metatarget-reference : project : ps : what ) { local result ; - + if ! $(.$(what)-tested.$(ps)) - { + { .$(what)-tested.$(ps) = true ; - + local targets = [ targets.generate-from-reference $(metatarget-reference) : $(project) : $(ps) ] ; @@ -155,3 +165,49 @@ rule set-log-file ( log-file ) } } +# Frontend rules + +class check-target-builds-worker +{ + import configure ; + import property-set ; + import targets ; + import property ; + + rule __init__ ( target : true-properties * : false-properties * ) + { + self.target = $(target) ; + self.true-properties = $(true-properties) ; + self.false-properties = $(false-properties) ; + } + + rule check ( properties * ) + { + # FIXME: this should not be hardcoded. Other checks might + # want to consider different set of features as relevant. + local toolset = [ property.select : $(properties) ] ; + local ps = [ property-set.create $(toolset) ] ; + local t = [ targets.current ] ; + local p = [ $(t).project ] ; + if [ configure.builds $(self.target) : $(p) : $(ps) : "$(self.target) builds" ] + { + return $(self.true-properties) ; + } + else + { + return $(self.false-properties) ; + } + } +} + + +rule check-target-builds ( target : true-properties * : false-properties * ) +{ + local instance = [ new check-target-builds-worker $(target) : $(true-properties) + : $(false-properties) ] ; + return @$(instance).check ; +} + +IMPORT $(__name__) : check-target-builds : : check-target-builds ; + + diff --git a/v2/build/targets.jam b/v2/build/targets.jam index d86aa04f6..2c9b64154 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -968,7 +968,7 @@ rule evaluate-requirements ( requirements : context : what ) # might come from project's requirements. local unconditional = [ feature.expand [ $(requirements).non-conditional ] ] ; - + local raw = [ $(context).raw ] ; raw = [ property.refine $(raw) : $(unconditional) ] ; @@ -997,7 +997,7 @@ rule evaluate-requirements ( requirements : context : what ) # properties. So the list of indirect conditionals does not # change. local indirect = [ $(requirements).get ] ; - indirect = [ MATCH @(.*) : $(indirect) ] ; + indirect = [ MATCH ^@(.*) : $(indirect) ] ; local ok ; while $(count) @@ -1066,6 +1066,22 @@ rule common-properties2 ( build-request requirements ) refined ] ; } +rule push-target ( target ) +{ + .targets = $(target) $(.targets) ; +} + +rule pop-target ( ) +{ + .targets = $(.targets[2-]) ; +} + +# Return the metatarget that is currently being generated. +rule current ( ) +{ + return $(.targets[1]) ; +} + # Implements the most standard way of constructing main target alternative from # sources. Allows sources to be either file or other main target and handles @@ -1221,6 +1237,7 @@ class basic-target : abstract-target ECHO [ targets.indent ] "Command line free features: " [ $(cf).raw ] ; ECHO [ targets.indent ] "Target requirements: " [ $(self.requirements).raw ] ; } + targets.push-target $(__name__) ; if ! $(self.generated.$(property-set)) { @@ -1361,6 +1378,7 @@ class basic-target : abstract-target } } + targets.pop-target ; targets.decrease-indent ; return $(self.generated.$(property-set)) ; } From 83c1703151b96645d7e5514a68d078ee8bae8829 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Thu, 18 Mar 2010 08:06:50 +0000 Subject: [PATCH 168/198] Revert 60652. The 'alias' rule passes 'no-rename' to main-target-sources, and targets.create-metatarget does not, presently, support that. As result: alias a : [ python-extension xxx ] [ bpl-test yyy : xxx ] ; Does not work since 'xxx' gets renamed to a..xxx. We obviously need to handle 'no-rename', but for now, just revert the patch. [SVN r60688] --- v2/build/alias.jam | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/v2/build/alias.jam b/v2/build/alias.jam index 1a34c20b1..48019cb98 100644 --- a/v2/build/alias.jam +++ b/v2/build/alias.jam @@ -58,9 +58,15 @@ rule alias ( name : sources * : requirements * : default-build * : { local project = [ project.current ] ; - return [ targets.create-metatarget alias-target-class : $(project) - : $(name) : $(sources) : $(requirements) - : $(default-build) : $(usage-requirements) ] ; + targets.main-target-alternative + [ new alias-target-class $(name) : $(project) + : [ targets.main-target-sources $(sources) : $(name) : no-renaming ] + : [ targets.main-target-requirements $(requirements) : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) + ] + : [ targets.main-target-usage-requirements $(usage-requirements) : + $(project) ] + ] ; } From 841a0e7a6baf8871aecc3ca892494de22cb82fbb Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Fri, 19 Mar 2010 14:05:43 +0000 Subject: [PATCH 169/198] Fix minor typo. [SVN r60708] --- historic/jam/doc/bjam.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index c3cc1a9d4..a4ff8615b 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -1104,7 +1104,7 @@ rule UPDATE_NOW ( /targets/ * : /log/ ? : /ignore-minus-n/ ? ) ] The =UPDATE_NOW= caused the specified targets to be updated immediately. -If update was successfully, non-empty string is returned. The /log/ parameter, +If update was successfull, non-empty string is returned. The /log/ parameter, if present, specifies a descriptor of a file where all output from building is redirected. If the /ignore-minus-n/ parameter is specified, the targets are updated even if the =-n= parameter is specified on the command line. From b6015ec58b54ec15977b21de911db9ac238c9b77 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Fri, 19 Mar 2010 14:37:40 +0000 Subject: [PATCH 170/198] Add change history for 3.1.18 release. [SVN r60709] --- historic/jam/doc/history.qbk | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/historic/jam/doc/history.qbk b/historic/jam/doc/history.qbk index bc403ab70..f5a89b0c1 100644 --- a/historic/jam/doc/history.qbk +++ b/historic/jam/doc/history.qbk @@ -1,5 +1,49 @@ [variablelist +[[3.1.18] [ + +After years of bjam developments.. This is going to be the last unbundled release of the +3.1.x series. From this point forward bjam will only be bundled as part of the larger +Boost Build system. And hence will likely change name at some point. As a side effect +of this move people will get more frequent release of bjam (or whatever it ends up being +called). + +[list + [li New built-ins, MD5, SPLIT_BY_CHARACTERS, PRECIOUS, PAD, FILE_OPEN, and UPDATE_NOW. + -- ['Vladimir P.] + ] + [li Ensure all file descriptors are closed when executing actions complete on *nix. + -- ['Noel B.] + ] + [li Fix warnings, patch from Mateusz Loskot. + -- ['Vladimir P.] + ] + [li Add KEEP_GOING var to programatically override the '-q' option. + -- ['Vladimir P.] + ] + [li Add more parameters, up to 19 from 9, to rule invocations. Patch from + Jonathan Biggar. + -- ['Vladimir P.] + ] + [li Print failed command output even if the normally quite '-d0' option. + -- ['Vladimir P.] + ] + [li Build of bjam with vc10, aka Visual Studio 2010. + -- ['Vladimir P.] + ] + [li More macros for detection of OSPLAT, patch from John W. Bito. + -- ['Vladimir P.] + ] + [li Add PARALLELISM var to programatically override the '-j' option. + -- ['Vladimir P.] + ] + [li Tweak doc building to allow for PDF generation of docs. + -- ['John M.] + ] +] + +]] + [[3.1.17] [ A year in the making this release has many stability improvements and various performance From 44c9b51abef5b99538304d4b358cf2bd5f6a9242 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 22 Mar 2010 09:57:29 +0000 Subject: [PATCH 171/198] Use Python 2.6, if available [SVN r60764] --- historic/jam/src/build.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/historic/jam/src/build.jam b/historic/jam/src/build.jam index 782419f8a..6f7d541e2 100644 --- a/historic/jam/src/build.jam +++ b/historic/jam/src/build.jam @@ -57,7 +57,7 @@ if $(with-python) { --python-include = [ .path $(python-location) include ] ; --python-lib = ; - for local v in 25 24 23 22 + for local v in 26 25 24 23 22 { --python-lib ?= [ GLOB [ .path $(python-location) libs ] : "python$(v).lib" ] @@ -80,7 +80,7 @@ if $(with-python) { --python-include = ; --python-lib = ; - for local v in 2.5 2.4 2.3 2.2 + for local v in 2.6 2.5 2.4 2.3 2.2 { local inc = [ GLOB [ .path $(python-location) include ] : python$(v) ] ; local lib = [ GLOB [ .path $(python-location) lib ] : libpython$(v)* ] ; From 92011153d9d8c595ae3622bcda540086ff2e87a5 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 22 Mar 2010 15:26:45 +0000 Subject: [PATCH 172/198] Fix possible problems when trailing or leading whitespace is added to the env vars used, for example CC and CFLAGS when using the cc toolset. Even though I can't get compilation to fail even with the extra whitespace during building it does seem to fail for some compilers that don't ignore the whitespace arguments. (Fixes #3630) [SVN r60768] --- historic/jam/src/build.jam | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/historic/jam/src/build.jam b/historic/jam/src/build.jam index 6f7d541e2..d27d1210d 100644 --- a/historic/jam/src/build.jam +++ b/historic/jam/src/build.jam @@ -2,6 +2,20 @@ #~ Distributed under the Boost Software License, Version 1.0. #~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +# Clean env vars of any "extra" empty values. +for local v in ARGV CC CFLAGS +{ + local values ; + for local x in $($(v)) + { + if $(x) != "" + { + values += $(x) ; + } + } + $(v) = $(values) ; +} + # Platform related specifics. if $(NT) { rule .path { return "$(<:J=\\)" ; } ./ = "/" ; } else if $(OS2) { rule .path { return "$(<:J=\\)" ; } ./ = "/" ; } From 31bf50d2c7f30c406b15d0e9e69dc5d1c7e5fa3a Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 22 Mar 2010 19:26:23 +0000 Subject: [PATCH 173/198] Change nbsp entities to use custom docbook tags inatead as the entities seems to no longer be available. [SVN r60771] --- historic/jam/doc/bjam.qbk | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index a4ff8615b..2ca9e81cc 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -37,7 +37,7 @@ [/ Templates ] -[template literal[text]''''''[text]''''''] +[template literal[text]''''''[text]''''''] [template list[items]''''''[items]''''''] [template orderedlist[items]''''''[items]''''''] [template li[text]''''''[text]''''''] @@ -378,29 +378,29 @@ The arguments immediately after the toolset are passed directly to the setup scr The arguments starting with the "=--option=" forms are passed to the =build.jam= script and are used to further customize what gets built. Options and targets supported by the =build.jam= script: [variablelist - [[[^---]] + [[[literal ---]] [Empty option when one wants to only specify a target.]] - [[[^--release]] + [[[literal --release]] [The default, builds the optimized executable.]] - [[[^--debug]] + [[[literal --debug]] [Builds debugging versions of the executable. When built they are placed in their own directory "=bin./platform/.debug=".]] - [[[^--grammar]] + [[[literal --grammar]] [Normally the Jam language grammar parsing files are not regenerated. This forces building of the grammar, although it may not force the regeneration of the grammar parser. If the parser is out of date it will be regenerated and subsequently built.]] - [[[^--with-python=/path/]] + [[[literal --with-python=/path/]] [Enables Python integration, given a path to the Python libraries.]] - [[[^--gc]] + [[[literal --gc]] [Enables use of the Boehm Garbage Collector. The build will look for the Boehm-GC source in a "boehm_gc" subdirectory from the =bjam= sources.]] - [[[^--duma]] + [[[literal --duma]] [Enables use of the DUMA (Detect Uintended Memory Access) debugging memory allocator. The build expects to find the DUMA source files in a "duma" subdirectory from the =bjam= sources.]] - [[[^--toolset-root=/path/]] + [[[literal --toolset-root=/path/]] [Indicates where the toolset used to build is located. This option is passed in by the bootstrap (=build.bat= or =build.sh=) script.]] - [[[^--show-locate-target]] + [[[literal --show-locate-target]] [For information, prints out where it will put the built executable.]] - [[[^--noassert]] + [[[literal --noassert]] [Disable debug assertions, even if building the debug version of the executable.]] - [[[^dist]] + [[[literal dist]] [Generate packages (compressed archives) as appropriate for distribution in the platform, if possible.]] - [[[^clean]] + [[[literal clean]] [Remove all the built executables and objects.]] ] @@ -427,10 +427,10 @@ Options are either singular or have an accompanying value. When a value is allow [variablelist - [ [[^-a]] + [ [[literal -a]] [Build all targets anyway, even if they are up-to-date.] ] - [ [[^-d''' '''/n/]] + [ [[literal -d /n/]] [ Enable cummulative debugging levels from 1 to n. Values are: @@ -451,43 +451,43 @@ Options are either singular or have an accompanying value. When a value is allow ] ] ] - [ [[^-d''' '''+/n/]] + [ [[literal -d +/n/]] [Enable debugging level /n/.] ] - [ [[^-d''' '''0]] + [ [[literal -d 0]] [Turn off all debugging levels. Only errors are reported.] ] - [ [[^-f''' '''/Jambase/]] [Read /Jambase/ instead of using the built-in + [ [[literal -f /Jambase/]] [Read /Jambase/ instead of using the built-in Jambase. Only one -f flag is permitted, but the /Jambase/ may explicitly include other files. A /Jambase/ name of "-" is allowed, in which case console input is read until it is closed, at which point the input is treated as the Jambase.] ] - [ [[^-j''' '''/n/]] + [ [[literal -j /n/]] [Run up to /n/ shell commands concurrently (UNIX and NT only). The default is 1.] ] - [ [[^-l''' '''/n/]] + [ [[literal -l /n/]] [Limit actions to running for /n/ number of seconds, after which they are stopped. Note: Windows only.] ] - [ [[^-n]] + [ [[literal -n]] [Don't actually execute the updating actions, but do everything else. This changes the debug level default to =-d 2=.] ] - [ [[^-o''' '''/file/]] + [ [[literal -o /file/]] [Write the updating actions to the specified file instead of running them.] ] - [ [[^-q]] + [ [[literal -q]] [Quit quickly (as if an interrupt was received) as soon as *any* target fails.] ] - [ [[^-s''' '''/var/=/value/]] + [ [[literal -s /var/=/value/]] [Set the variable /var/ to /value/, overriding both internal variables and variables imported from the environment.] ] - [ [[^-t''' '''/target/]] + [ [[literal -t /target/]] [Rebuild /target/ and everything that depends on it, even if it is up-to-date.] ] - [ [[^--''' '''/value/]] + [ [[literal -- /value/]] [The option and /value/ is ignored, but is available from the =$(ARGV)= variable. ]] - [ [[^-v]] + [ [[literal -v]] [Print the version of =bjam= and exit.] ] ] From e132e449d20afa208fce30e76191a5f97c112061 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 22 Mar 2010 19:27:23 +0000 Subject: [PATCH 174/198] Fix building of standalone bjam docs after Daniel's changes broke them. [SVN r60772] --- historic/jam/doc/build.jam | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/historic/jam/doc/build.jam b/historic/jam/doc/build.jam index 5523007ae..5d137c484 100644 --- a/historic/jam/doc/build.jam +++ b/historic/jam/doc/build.jam @@ -30,8 +30,9 @@ boostbook standalone html html/images boost.root=. - -nav.layout=horizontal + -boost.defaults=Boost nav.layout=none + boost.image=Boost html:admon.graphics.path=images/ html:navig.graphics.path=images/ ; From 661cf165f00697bb391159e6b412222d7759a512 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 22 Mar 2010 19:33:19 +0000 Subject: [PATCH 175/198] Add LIBS to the env vars list as it's also use in the cc toolset. [SVN r60773] --- historic/jam/src/build.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/historic/jam/src/build.jam b/historic/jam/src/build.jam index d27d1210d..89cf558b9 100644 --- a/historic/jam/src/build.jam +++ b/historic/jam/src/build.jam @@ -3,7 +3,7 @@ #~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) # Clean env vars of any "extra" empty values. -for local v in ARGV CC CFLAGS +for local v in ARGV CC CFLAGS LIBS { local values ; for local x in $($(v)) From 60dd40f958fa43d81d84ce77f844fdd99a38814e Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 22 Mar 2010 21:04:59 +0000 Subject: [PATCH 176/198] Bump bjam to 3.1.19. [SVN r60778] --- historic/jam/doc/bjam.qbk | 4 ++-- historic/jam/src/boost-jam.spec | 2 +- historic/jam/src/build.jam | 2 +- historic/jam/src/patchlevel.h | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index 2ca9e81cc..300a1f8c9 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -1,6 +1,6 @@ [article Boost.Jam [quickbook 1.3] - [version: 3.1.18] + [version: 3.1.19] [authors [Rivera, Rene], [Abrahams, David], [Prus, Vladimir]] [copyright 2003 2004 2005 2006 2007 Rene Rivera, David Abrahams, Vladimir Prus] [category tool-build] @@ -21,7 +21,7 @@ [/ Shortcuts ] -[def :version: 3.1.18] +[def :version: 3.1.19] [/ Images ] diff --git a/historic/jam/src/boost-jam.spec b/historic/jam/src/boost-jam.spec index b3e49886a..bc572fc96 100644 --- a/historic/jam/src/boost-jam.spec +++ b/historic/jam/src/boost-jam.spec @@ -1,5 +1,5 @@ Name: boost-jam -Version: 3.1.18 +Version: 3.1.19 Summary: Build tool Release: 1 Source: %{name}-%{version}.tgz diff --git a/historic/jam/src/build.jam b/historic/jam/src/build.jam index 89cf558b9..432197cc2 100644 --- a/historic/jam/src/build.jam +++ b/historic/jam/src/build.jam @@ -27,7 +27,7 @@ else { . = "." ; } ./ ?= "" ; # Info about what we are building. -_VERSION_ = 3 1 18 ; +_VERSION_ = 3 1 19 ; NAME = boost-jam ; VERSION = $(_VERSION_:J=$(.)) ; RELEASE = 1 ; diff --git a/historic/jam/src/patchlevel.h b/historic/jam/src/patchlevel.h index 2b2043a3c..5d5979057 100644 --- a/historic/jam/src/patchlevel.h +++ b/historic/jam/src/patchlevel.h @@ -9,9 +9,9 @@ #define VERSION_MAJOR 3 #define VERSION_MINOR 1 -#define VERSION_PATCH 18 +#define VERSION_PATCH 19 #define VERSION_MAJOR_SYM "03" #define VERSION_MINOR_SYM "1" -#define VERSION_PATCH_SYM "18" -#define VERSION "3.1.18" +#define VERSION_PATCH_SYM "19" +#define VERSION "3.1.19" #define JAMVERSYM "JAMVERSION=3.1" From 6a4b52d95b9842d4464fbad75a49dd9d6caa9c38 Mon Sep 17 00:00:00 2001 From: David Dean Date: Fri, 2 Apr 2010 18:35:00 +0000 Subject: [PATCH 177/198] make abi/borland_prefix and borland.jam defaults match [SVN r61007] --- v2/tools/borland.jam | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/v2/tools/borland.jam b/v2/tools/borland.jam index c9bf01acc..6330c7cdf 100644 --- a/v2/tools/borland.jam +++ b/v2/tools/borland.jam @@ -80,18 +80,27 @@ flags borland.compile OPTIONS on : -w! ; # Deal with various runtime configs... # This should be not for DLL -flags borland OPTIONS console : -tWC ; +local target = [ feature.get-values target-os : $(properties) ] ; -# -tWR sets -tW as well, so we turn it off here and then turn it -# on again later if we need it: -flags borland OPTIONS shared : -tWR -tWC ; -flags borland OPTIONS gui : -tW ; - -flags borland OPTIONS LIB/shared : -tWD ; -# Hmm.. not sure what's going on here. -flags borland OPTIONS : -WM- ; -flags borland OPTIONS multi : -tWM ; +flags borland OPTIONS console : -tC ; +switch $(target) +{ + case windows : + { + # -tWR sets -tW as well, so we turn it off here and then turn it + # on again later if we need it: + flags borland OPTIONS shared : -tWR -tWC ; + flags borland OPTIONS gui : -tW ; + flags borland OPTIONS LIB/shared : -tWD ; + # Hmm.. not sure what's going on here. + flags borland OPTIONS : -WM- ; + flags borland OPTIONS multi : -tWM ; + } + case darwin : + { + } +} flags borland.compile OPTIONS ; @@ -117,13 +126,13 @@ flags borland NEED_IMPLIB LIB/shared : "" ; actions compile.c++ { - "$(CONFIG_COMMAND)" -j5 -g255 -q -c -P -a8 -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" + "$(CONFIG_COMMAND)" -j5 -g255 -q -c -P -a8 -Vx- -Ve- -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" } # For C, we don't pass -P flag actions compile.c { - "$(CONFIG_COMMAND)" -j5 -g255 -q -c -a8 -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" + "$(CONFIG_COMMAND)" -j5 -g255 -q -c -a8 -Vx- -Ve- -b- $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -I"$(STDHDRS)" -o"$(<)" "$(>)" } From 7bc3446c3c18b07a1bfe3b708c72be4f71184286 Mon Sep 17 00:00:00 2001 From: David Dean Date: Fri, 2 Apr 2010 22:48:06 +0000 Subject: [PATCH 178/198] revert accidental commit [SVN r61008] --- v2/tools/borland.jam | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/v2/tools/borland.jam b/v2/tools/borland.jam index 6330c7cdf..6e43ca93a 100644 --- a/v2/tools/borland.jam +++ b/v2/tools/borland.jam @@ -80,27 +80,18 @@ flags borland.compile OPTIONS on : -w! ; # Deal with various runtime configs... # This should be not for DLL -local target = [ feature.get-values target-os : $(properties) ] ; +flags borland OPTIONS console : -tWC ; -flags borland OPTIONS console : -tC ; +# -tWR sets -tW as well, so we turn it off here and then turn it +# on again later if we need it: +flags borland OPTIONS shared : -tWR -tWC ; +flags borland OPTIONS gui : -tW ; + +flags borland OPTIONS LIB/shared : -tWD ; +# Hmm.. not sure what's going on here. +flags borland OPTIONS : -WM- ; +flags borland OPTIONS multi : -tWM ; -switch $(target) -{ - case windows : - { - # -tWR sets -tW as well, so we turn it off here and then turn it - # on again later if we need it: - flags borland OPTIONS shared : -tWR -tWC ; - flags borland OPTIONS gui : -tW ; - flags borland OPTIONS LIB/shared : -tWD ; - # Hmm.. not sure what's going on here. - flags borland OPTIONS : -WM- ; - flags borland OPTIONS multi : -tWM ; - } - case darwin : - { - } -} flags borland.compile OPTIONS ; From 75de05efec50accd547a95a169ad438ec1bd2593 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Sun, 4 Apr 2010 21:04:48 +0000 Subject: [PATCH 179/198] Disambiguiate targets and directories passed to 'build-project'. Previously, if a Jamfile had: exe a : a.cpp ; build-project a ; where a is also a subdirectory, we'd get error. [SVN r61054] --- v2/build/targets.jam | 2 +- v2/test/build_dir.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/v2/build/targets.jam b/v2/build/targets.jam index 2c9b64154..f988e691b 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -288,7 +288,7 @@ class project-target : abstract-target local self-location = [ get location ] ; for local pn in [ get projects-to-build ] { - result += [ find $(pn) ] ; + result += [ find $(pn)/ ] ; } return $(result) ; diff --git a/v2/test/build_dir.py b/v2/test/build_dir.py index efdd522a4..c5bcbc5b9 100644 --- a/v2/test/build_dir.py +++ b/v2/test/build_dir.py @@ -36,6 +36,7 @@ t.expect_addition(["build/$toolset/debug/a.exe", # Test that building from child projects work. t.run_build_system(subdir='src') +t.ignore("build/config.log") t.expect_nothing_more() # Test that project can override build dir. From 792d4e5bb486ead5410a56fce8ba7f3c8fde930c Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Sun, 18 Apr 2010 14:35:57 +0000 Subject: [PATCH 180/198] Add an objective-c header scanner so that #import dependencies can be found. To minimize code it reuses the c-scanner which is now moved into the cpp.jam type file. [SVN r61358] --- v2/tools/builtin.jam | 82 +------------------------------------- v2/tools/types/cpp.jam | 88 +++++++++++++++++++++++++++++++++++++++-- v2/tools/types/objc.jam | 23 ++++++++++- 3 files changed, 108 insertions(+), 85 deletions(-) diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index 1565213ec..5ad05b311 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -1,5 +1,5 @@ # Copyright 2002, 2003, 2004, 2005 Dave Abrahams -# Copyright 2002, 2005, 2006, 2007 Rene Rivera +# Copyright 2002, 2005, 2006, 2007, 2010 Rene Rivera # Copyright 2006 Juergen Hunold # Copyright 2005 Toon Knapen # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus @@ -429,86 +429,6 @@ class searched-lib-target : abstract-file-target } -class c-scanner : scanner -{ - import path ; - import regex ; - import scanner ; - import sequence ; - import virtual-target ; - - rule __init__ ( includes * ) - { - scanner.__init__ ; - - for local i in $(includes) - { - self.includes += [ sequence.transform path.native - : [ regex.split $(i:G=) "&&" ] ] ; - } - } - - rule pattern ( ) - { - return "#[ \t]*include[ ]*(<(.*)>|\"(.*)\")" ; - } - - rule process ( target : matches * : binding ) - { - local angle = [ regex.transform $(matches) : "<(.*)>" ] ; - angle = [ sequence.transform path.native : $(angle) ] ; - local quoted = [ regex.transform $(matches) : "\"(.*)\"" ] ; - quoted = [ sequence.transform path.native : $(quoted) ] ; - - # CONSIDER: the new scoping rule seem to defeat "on target" variables. - local g = [ on $(target) return $(HDRGRIST) ] ; - local b = [ NORMALIZE_PATH $(binding:D) ] ; - - # Attach binding of including file to included targets. When a target is - # directly created from virtual target this extra information is - # unnecessary. But in other cases, it allows us to distinguish between - # two headers of the same name included from different places. We do not - # need this extra information for angle includes, since they should not - # depend on including file (we can not get literal "." in include path). - local g2 = $(g)"#"$(b) ; - - angle = $(angle:G=$(g)) ; - quoted = $(quoted:G=$(g2)) ; - - local all = $(angle) $(quoted) ; - - INCLUDES $(target) : $(all) ; - NOCARE $(all) ; - SEARCH on $(angle) = $(self.includes:G=) ; - SEARCH on $(quoted) = $(b) $(self.includes:G=) ; - - # Just propagate the current scanner to includes in hope that includes - # do not change scanners. - scanner.propagate $(__name__) : $(angle) $(quoted) : $(target) ; - - ISFILE $(angle) $(quoted) ; - } -} - - -type.register H : h ; -type.register HPP : hpp : H ; -type.register C : c ; - -scanner.register c-scanner : include ; - -# It most cases where a CPP file or a H file is a source of some action, we -# should rebuild the result if any of files included by CPP/H are changed. One -# case when this is not needed is installation, which is handled specifically. -type.set-scanner CPP : c-scanner ; -type.set-scanner C : c-scanner ; -# One case where scanning of H/HPP files is necessary is PCH generation -- if -# any header included by HPP being precompiled changes, we need to recompile the -# header. -type.set-scanner H : c-scanner ; -type.set-scanner HPP : c-scanner ; - - # The generator class for libraries (target type LIB). Depending on properties # it will request building of the appropriate specific library type -- # -- SHARED_LIB, STATIC_LIB or SHARED_LIB. diff --git a/v2/tools/types/cpp.jam b/v2/tools/types/cpp.jam index 82638f84a..defa3e566 100644 --- a/v2/tools/types/cpp.jam +++ b/v2/tools/types/cpp.jam @@ -1,4 +1,86 @@ -# Copyright David Abrahams 2004. 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) +# Copyright David Abrahams 2004. +# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +# Copyright 2010 Rene Rivera +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +import type ; +import scanner ; + +class c-scanner : scanner +{ + import path ; + import regex ; + import scanner ; + import sequence ; + import virtual-target ; + + rule __init__ ( includes * ) + { + scanner.__init__ ; + + for local i in $(includes) + { + self.includes += [ sequence.transform path.native + : [ regex.split $(i:G=) "&&" ] ] ; + } + } + + rule pattern ( ) + { + return "#[ \t]*include[ ]*(<(.*)>|\"(.*)\")" ; + } + + rule process ( target : matches * : binding ) + { + local angle = [ regex.transform $(matches) : "<(.*)>" ] ; + angle = [ sequence.transform path.native : $(angle) ] ; + local quoted = [ regex.transform $(matches) : "\"(.*)\"" ] ; + quoted = [ sequence.transform path.native : $(quoted) ] ; + + # CONSIDER: the new scoping rule seem to defeat "on target" variables. + local g = [ on $(target) return $(HDRGRIST) ] ; + local b = [ NORMALIZE_PATH $(binding:D) ] ; + + # Attach binding of including file to included targets. When a target is + # directly created from virtual target this extra information is + # unnecessary. But in other cases, it allows us to distinguish between + # two headers of the same name included from different places. We do not + # need this extra information for angle includes, since they should not + # depend on including file (we can not get literal "." in include path). + local g2 = $(g)"#"$(b) ; + + angle = $(angle:G=$(g)) ; + quoted = $(quoted:G=$(g2)) ; + + local all = $(angle) $(quoted) ; + + INCLUDES $(target) : $(all) ; + NOCARE $(all) ; + SEARCH on $(angle) = $(self.includes:G=) ; + SEARCH on $(quoted) = $(b) $(self.includes:G=) ; + + # Just propagate the current scanner to includes in hope that includes + # do not change scanners. + scanner.propagate $(__name__) : $(angle) $(quoted) : $(target) ; + + ISFILE $(angle) $(quoted) ; + } +} + +scanner.register c-scanner : include ; + type CPP : cpp cxx cc ; +type H : h ; +type HPP : hpp : H ; +type C : c ; + +# It most cases where a CPP file or a H file is a source of some action, we +# should rebuild the result if any of files included by CPP/H are changed. One +# case when this is not needed is installation, which is handled specifically. +type.set-scanner CPP : c-scanner ; +type.set-scanner C : c-scanner ; +# One case where scanning of H/HPP files is necessary is PCH generation -- if +# any header included by HPP being precompiled changes, we need to recompile the +# header. +type.set-scanner H : c-scanner ; +type.set-scanner HPP : c-scanner ; diff --git a/v2/tools/types/objc.jam b/v2/tools/types/objc.jam index 79ab12acf..f8d31c072 100644 --- a/v2/tools/types/objc.jam +++ b/v2/tools/types/objc.jam @@ -1,5 +1,26 @@ -# Copyright Rene Rivera 2008. +# Copyright Rene Rivera 2008, 2010. # 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) +import type ; +import scanner ; +import types/cpp ; + +class objc-scanner : c-scanner +{ + rule __init__ ( includes * ) + { + c-scanner.__init__ $(includes) ; + } + + rule pattern ( ) + { + return "#[ \t]*include|import[ ]*(<(.*)>|\"(.*)\")" ; + } +} + +scanner.register objc-scanner : include ; + type OBJECTIVE_C : m ; type OBJECTIVE_CPP : mm ; +type.set-scanner OBJECTIVE_C : objc-scanner ; +type.set-scanner OBJECTIVE_CPP : objc-scanner ; From 8d6d34446e64afbbe334a6bb96d280ee0e17e3cb Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Sun, 18 Apr 2010 19:43:40 +0000 Subject: [PATCH 181/198] Experimental Boost.Build configuration for Clang. [SVN r61377] --- v2/tools/clang-darwin.jam | 536 +++++++++++++++++++++ v2/tools/clang-unix.jam | 990 ++++++++++++++++++++++++++++++++++++++ v2/tools/clang.jam | 27 ++ v2/tools/common.jam | 2 + 4 files changed, 1555 insertions(+) create mode 100644 v2/tools/clang-darwin.jam create mode 100644 v2/tools/clang-unix.jam create mode 100644 v2/tools/clang.jam diff --git a/v2/tools/clang-darwin.jam b/v2/tools/clang-darwin.jam new file mode 100644 index 000000000..8548718e4 --- /dev/null +++ b/v2/tools/clang-darwin.jam @@ -0,0 +1,536 @@ +# Copyright 2003 Christopher Currie +# Copyright 2006 Dave Abrahams +# Copyright 2003, 2004, 2005, 2006 Vladimir Prus +# Copyright 2005-2007 Mat Marcus +# Copyright 2005-2007 Adobe Systems Incorporated +# Copyright 2007-2009 Rene Rivera +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +import feature : feature ; +import toolset : flags ; +import type ; +import common ; +import generators ; +import path : basename ; +import version ; +import property-set ; +import regex ; +import errors ; + +## Use a framework. +feature framework : : free ; + +## The MacOSX version to compile for, which maps to the SDK to use (sysroot). +feature macosx-version : : propagated link-incompatible symmetric optional ; + +## The minimal MacOSX version to target. +feature macosx-version-min : : propagated optional ; + +############################################################################# + +if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] +{ + .debug-configuration = true ; +} + +feature.extend toolset : clang-darwin ; +import clang-unix ; +feature.extend-subfeature toolset clang : platform : darwin ; + +toolset.inherit-generators clang-darwin + clang darwin + : clang-unix + : clang-unix.mingw.link clang-unix.mingw.link.dll + ; + +generators.override clang-darwin.prebuilt : builtin.prebuilt ; +generators.override clang-darwin.searched-lib-generator : searched-lib-generator ; + +# Override default do-nothing generators. +generators.override clang-darwin.compile.c.pch : pch.default-c-pch-generator ; +generators.override clang-darwin.compile.c++.pch : pch.default-cpp-pch-generator ; + +type.set-generated-target-suffix PCH : clang-darwin : gch ; + +toolset.inherit-rules clang-darwin : clang-unix : localize ; +toolset.inherit-flags clang-darwin : clang-unix + : static + arm/32 + arm/64 + arm/ + x86/32 + x86/64 + x86/ + power/32 + power/64 + power/ ; + +# Options: +# +# PATH +# Platform root path. The common autodetection will set this to +# "/Developer". And when a command is given it will be set to +# the corresponding "*.platform/Developer" directory. +# +rule init ( version ? : command * : options * : requirement * ) +{ + # First time around, figure what is host OSX version + if ! $(.host-osx-version) + { + .host-osx-version = [ MATCH "^([0-9.]+)" + : [ SHELL "/usr/bin/sw_vers -productVersion" ] ] ; + if $(.debug-configuration) + { + ECHO notice: OSX version on this machine is $(.host-osx-version) ; + } + } + + # - The root directory of the tool install. + local root = [ feature.get-values : $(options) ] ; + + # - The bin directory where to find the commands to execute. + local bin ; + + # - The configured compile driver command. + local command = [ common.get-invocation-command clang-darwin : clang++ : $(command) ] ; + + # The version as reported by the compiler + local real-version ; + + # - Autodetect the root and bin dir if not given. + if $(command) + { + bin ?= [ common.get-absolute-tool-path $(command[1]) ] ; + if $(bin) = "/usr/bin" + { + root ?= /Developer ; + } + else + { + local r = $(bin:D) ; + r = $(r:D) ; + root ?= $(r) ; + } + } + + # - Autodetect the version if not given. + if $(command) + { + # - The 'command' variable can have multiple elements. When calling + # the SHELL builtin we need a single string. + local command-string = $(command:J=" ") ; + real-version = [ MATCH "^([0-9.]+)" + : [ SHELL "$(command-string) -dumpversion" ] ] ; + version ?= $(real-version) ; + } + + .real-version.$(version) = $(real-version) ; + + # - Define the condition for this toolset instance. + local condition = + [ common.check-init-parameters clang-darwin $(requirement) : version $(version) ] ; + + # - Set the toolset generic common options. + common.handle-options clang-darwin : $(condition) : $(command) : $(options) ; + + # - Set the link flags common with the clang-unix toolset. + clang-unix.init-link-flags clang-darwin darwin $(condition) ; + + # - The symbol strip program. + local strip ; + if in $(options) + { + # We can turn off strip by specifying it as empty. In which + # case we switch to using the linker to do the strip. + flags clang-darwin.link.dll OPTIONS + $(condition)/LIB/shared/32/on : -Wl,-x ; + flags clang-darwin.link.dll OPTIONS + $(condition)/LIB/shared//on : -Wl,-x ; + flags clang-darwin.link OPTIONS + $(condition)/EXE/32/on : -s ; + flags clang-darwin.link OPTIONS + $(condition)/EXE//on : -s ; + } + else + { + # Otherwise we need to find a strip program to use. And hence + # also tell the link action that we need to use a strip + # post-process. + flags clang-darwin.link NEED_STRIP $(condition)/on : "" ; + strip = + [ common.get-invocation-command clang-darwin + : strip : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; + flags clang-darwin.link .STRIP $(condition) : $(strip[1]) ; + if $(.debug-configuration) + { + ECHO notice: using strip for $(condition) at $(strip[1]) ; + } + } + + # - The archive builder (libtool is the default as creating + # archives in darwin is complicated. + local archiver = + [ common.get-invocation-command clang-darwin + : libtool : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; + flags clang-darwin.archive .LIBTOOL $(condition) : $(archiver[1]) ; + if $(.debug-configuration) + { + ECHO notice: using archiver for $(condition) at $(archiver[1]) ; + } + + # - Initialize the SDKs available in the root for this tool. + local sdks = [ init-available-sdk-versions $(condition) : $(root) ] ; + + #~ ECHO --- ; + #~ ECHO --- bin :: $(bin) ; + #~ ECHO --- root :: $(root) ; + #~ ECHO --- version :: $(version) ; + #~ ECHO --- condition :: $(condition) ; + #~ ECHO --- strip :: $(strip) ; + #~ ECHO --- archiver :: $(archiver) ; + #~ ECHO --- sdks :: $(sdks) ; + #~ ECHO --- ; + #~ EXIT ; +} + +# Add and set options for a discovered SDK version. +local rule init-sdk ( condition * : root ? : version + : version-feature ? ) +{ + local rule version-to-feature ( version + ) + { + switch $(version[1]) + { + case iphone* : + { + return $(version[1])-$(version[2-]:J=.) ; + } + case mac* : + { + return $(version[2-]:J=.) ; + } + case * : + { + return $(version:J=.) ; + } + } + } + + if $(version-feature) + { + if $(.debug-configuration) + { + ECHO notice: available sdk for $(condition)/$(version-feature) at $(sdk) ; + } + + # Add the version to the features for specifying them. + if ! $(version-feature) in [ feature.values macosx-version ] + { + feature.extend macosx-version : $(version-feature) ; + } + if ! $(version-feature) in [ feature.values macosx-version-min ] + { + feature.extend macosx-version-min : $(version-feature) ; + } + + # Set the flags the version needs to compile with, first + # generic options. + flags clang-darwin.compile OPTIONS $(condition)/$(version-feature) + : -isysroot $(sdk) ; + flags clang-darwin.link OPTIONS $(condition)/$(version-feature) + : -isysroot $(sdk) ; + + # Then device variation options. + switch $(version[1]) + { + case iphone* : + { + flags clang-darwin.compile OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + flags clang-darwin.link OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + } + + case mac* : + { + flags clang-darwin.compile OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + flags clang-darwin.link OPTIONS $(version-feature) + : -miphoneos-version-min=$(version[2-]:J=.) ; + } + } + + return $(version-feature) ; + } + else if $(version[4]) + { + # We have a patch version of an SDK. We want to set up + # both the specific patch version, and the minor version. + # So we recurse to set up the minor version. Plus the minor version. + return + [ init-sdk $(condition) : $(root) + : $(version[1-3]) : [ version-to-feature $(version[1-3]) ] ] + [ init-sdk $(condition) : $(root) + : $(version) : [ version-to-feature $(version) ] ] ; + } + else + { + # Yes, this is intentionally recursive. + return + [ init-sdk $(condition) : $(root) + : $(version) : [ version-to-feature $(version) ] ] ; + } +} + +# Determine the MacOSX SDK versions installed and their locations. +local rule init-available-sdk-versions ( condition * : root ? ) +{ + root ?= /Developer ; + local sdks-root = $(root)/SDKs ; + local sdks = [ GLOB $(sdks-root) : MacOSX*.sdk iPhoneOS*.sdk iPhoneSimulator*.sdk ] ; + local result ; + for local sdk in $(sdks) + { + local sdk-match = [ MATCH ([^0-9]+)([0-9]+)[.]([0-9x]+)[.]?([0-9x]+)? : $(sdk:D=) ] ; + local sdk-platform = $(sdk-match[1]:L) ; + local sdk-version = $(sdk-match[2-]) ; + if $(sdk-version) + { + switch $(sdk-platform) + { + case macosx : + { + sdk-version = mac $(sdk-version) ; + } + case iphoneos : + { + sdk-version = iphone $(sdk-version) ; + } + case iphonesimulator : + { + sdk-version = iphonesim $(sdk-version) ; + } + case * : + { + sdk-version = $(sdk-version:J=-) ; + } + } + result += [ init-sdk $(condition) : $(sdk) : $(sdk-version) ] ; + } + } + return $(result) ; +} + +# Generic options. +flags clang-darwin.compile OPTIONS ; + +# The following adds objective-c support to clang-darwin. +# Thanks to http://thread.gmane.org/gmane.comp.lib.boost.build/13759 + +generators.register-c-compiler clang-darwin.compile.m : OBJECTIVE_C : OBJ : clang-darwin ; +generators.register-c-compiler clang-darwin.compile.mm : OBJECTIVE_CPP : OBJ : clang-darwin ; + +rule setup-address-model ( targets * : sources * : properties * ) +{ + local ps = [ property-set.create $(properties) ] ; + local arch = [ $(ps).get ] ; + local address-model = [ $(ps).get ] ; + local osx-version = [ $(ps).get ] ; + local clang-unix-version = [ $(ps).get ] ; + clang-unix-version = $(.real-version.$(clang-unix-version)) ; + local options ; + + local support-ppc64 = 1 ; + + osx-version ?= $(.host-osx-version) ; + + switch $(osx-version) + { + case iphone* : + { + support-ppc64 = ; + } + + case * : + if $(osx-version) && ! [ version.version-less [ regex.split $(osx-version) \\. ] : 10 6 ] + { + # When targeting 10.6: + # - clang-unix 4.2 will give a compiler errir if ppc64 compilation is requested + # - clang-unix 4.0 will compile fine, somehow, but then fail at link time + support-ppc64 = ; + } + } + switch $(arch) + { + case combined : + { + if $(address-model) = 32_64 { + if $(support-ppc64) { + options = -arch i386 -arch ppc -arch x86_64 -arch ppc64 ; + } else { + # Build 3-way binary + options = -arch i386 -arch ppc -arch x86_64 ; + } + } else if $(address-model) = 64 { + if $(support-ppc64) { + options = -arch x86_64 -arch ppc64 ; + } else { + errors.user-error "64-bit PPC compilation is not supported when targeting OSX 10.6 or later" ; + } + } else { + options = -arch i386 -arch ppc ; + } + } + + case x86 : + { + if $(address-model) = 32_64 { + options = -arch i386 -arch x86_64 ; + } else if $(address-model) = 64 { + options = -arch x86_64 ; + } else { + options = -arch i386 ; + } + } + + case power : + { + if ! $(support-ppc64) + && ( $(address-model) = 32_64 || $(address-model) = 64 ) + { + errors.user-error "64-bit PPC compilation is not supported when targeting OSX 10.6 or later" ; + } + + if $(address-model) = 32_64 { + options = -arch ppc -arch ppc64 ; + } else if $(address-model) = 64 { + options = -arch ppc64 ; + } else { + options = -arch ppc ; + } + } + + case arm : + { + options = -arch armv6 ; + } + } + + if $(options) + { + OPTIONS on $(targets) += $(options) ; + } +} + +rule setup-threading ( targets * : sources * : properties * ) +{ + clang-unix.setup-threading $(targets) : $(sources) : $(properties) ; +} + +rule setup-fpic ( targets * : sources * : properties * ) +{ + clang-unix.setup-fpic $(targets) : $(sources) : $(properties) ; +} + +rule compile.m ( targets * : sources * : properties * ) +{ + LANG on $(<) = "-x objective-c" ; + clang-unix.setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; +} + +actions compile.m +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +rule compile.mm ( targets * : sources * : properties * ) +{ + LANG on $(<) = "-x objective-c++" ; + clang-unix.setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; +} + +actions compile.mm +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +# Set the max header padding to allow renaming of libs for installation. +flags clang-darwin.link.dll OPTIONS : -headerpad_max_install_names ; + +# To link the static runtime we need to link to all the core runtime libraries. +flags clang-darwin.link OPTIONS static + : -nodefaultlibs -shared-libgcc -lstdc++-static -lgcc_eh -lgcc -lSystem ; + +# Strip as much as possible when optimizing. +flags clang-darwin.link OPTIONS speed : -Wl,-dead_strip -no_dead_strip_inits_and_terms ; +flags clang-darwin.link OPTIONS space : -Wl,-dead_strip -no_dead_strip_inits_and_terms ; + +# Dynamic/shared linking. +flags clang-darwin.compile OPTIONS shared : -dynamic ; + +# Misc options. +flags clang-darwin.compile OPTIONS : -gdwarf-2 ; + +# Add the framework names to use. +flags clang-darwin.link FRAMEWORK ; + +# This is flag is useful for debugging the link step +# uncomment to see what libtool is doing under the hood +#~ flags clang-darwin.link.dll OPTIONS : -Wl,-v ; + +_ = " " ; + +# set up the -F option to include the paths to any frameworks used. +local rule prepare-framework-path ( target + ) +{ + # The -framework option only takes basename of the framework. + # The -F option specifies the directories where a framework + # is searched for. So, if we find feature + # with some path, we need to generate property -F option. + local framework-paths = [ on $(target) return $(FRAMEWORK:D) ] ; + + # Be sure to generate no -F if there's no path. + for local framework-path in $(framework-paths) + { + if $(framework-path) != "" + { + FRAMEWORK_PATH on $(target) += -F$(framework-path) ; + } + } +} + +rule link ( targets * : sources * : properties * ) +{ + setup-address-model $(targets) : $(sources) : $(properties) ; + prepare-framework-path $(<) ; +} + +# Note that using strip without any options was reported to result in broken +# binaries, at least on OS X 10.5.5, see: +# http://svn.boost.org/trac/boost/ticket/2347 +# So we pass -S -x. +actions link bind LIBRARIES +{ + "$(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)-S $(NEED_STRIP)-x $(NEED_STRIP)"$(<)" +} + +rule link.dll ( targets * : sources * : properties * ) +{ + setup-address-model $(targets) : $(sources) : $(properties) ; + prepare-framework-path $(<) ; +} + +actions link.dll bind LIBRARIES +{ + "$(CONFIG_COMMAND)" -dynamiclib -Wl,-single_module -install_name "$(<:B)$(<:S)" -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=:S=) $(OPTIONS) $(USER_OPTIONS) +} + +# We use libtool instead of ar to support universal binary linking +# TODO: Find a way to use the underlying tools, i.e. lipo, to do this. +actions piecemeal archive +{ + "$(.LIBTOOL)" -static -o "$(<:T)" $(ARFLAGS) "$(>:T)" +} diff --git a/v2/tools/clang-unix.jam b/v2/tools/clang-unix.jam new file mode 100644 index 000000000..5c55faf84 --- /dev/null +++ b/v2/tools/clang-unix.jam @@ -0,0 +1,990 @@ +# Copyright 2001 David Abrahams. +# Copyright 2002-2006 Rene Rivera. +# Copyright 2002-2003 Vladimir Prus. +# Copyright (c) 2005 Reece H. Dunn. +# Copyright 2006 Ilya Sokolov. +# Copyright 2007 Roland Schwarz +# Copyright 2007 Boris Gubenko. +# +# 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) + +import "class" : new ; +import common ; +import errors ; +import feature ; +import generators ; +import os ; +import pch ; +import property ; +import property-set ; +import toolset ; +import type ; +import rc ; +import regex ; +import set ; +import unix ; + + +if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] +{ + .debug-configuration = true ; +} + + +feature.extend toolset : clang-unix ; +# feature.subfeature toolset clang : flavor : : optional ; + +toolset.inherit-generators clang-unix : unix : unix.link unix.link.dll ; +toolset.inherit-flags clang-unix : unix ; +toolset.inherit-rules clang-unix : unix ; + +generators.override clang-unix.prebuilt : builtin.prebuilt ; +generators.override clang-unix.searched-lib-generator : searched-lib-generator ; + +# Make clang-unix toolset object files use the "o" suffix on all platforms. +type.set-generated-target-suffix OBJ : clang-unix : o ; +type.set-generated-target-suffix OBJ : clang-unix windows : o ; +type.set-generated-target-suffix OBJ : clang-unix cygwin : o ; + +# Initializes the clang-unix toolset for the given version. If necessary, command may +# be used to specify where the compiler is located. The parameter 'options' is a +# space-delimited list of options, each one specified as +# option-value. Valid option names are: cxxflags, linkflags and +# linker-type. Accepted linker-type values are aix, darwin, gnu, hpux, osf or +# sun and the default value will be selected based on the current OS. +# Example: +# using clang-unix : 1.5 : : foo bar sun ; +# +rule init ( version ? : command * : options * ) +{ + # Information about the clang command... + # The command. + local command = [ common.get-invocation-command clang-unix : clang++ : $(command) ] ; + # The root directory of the tool install. + local root = [ feature.get-values : $(options) ] ; + # The bin directory where to find the command to execute. + local bin ; + # The flavor of compiler. + local flavor = [ feature.get-values : $(options) ] ; + # Autodetect the root and bin dir if not given. + if $(command) + { + bin ?= [ common.get-absolute-tool-path $(command[-1]) ] ; + root ?= $(bin:D) ; + } + # The 'command' variable can have multiple elements. When calling + # the SHELL builtin we need a single string. + local command-string = $(command:J=" ") ; + # Autodetect the version and flavor if not given. + if $(command) + { + local machine = [ MATCH "^([^ ]+)" + : [ SHELL "$(command-string) -dumpmachine" ] ] ; + version ?= [ MATCH "^([0-9.]+)" + : [ SHELL "$(command-string) -dumpversion" ] ] ; + switch $(machine:L) + { + case *mingw* : flavor ?= mingw ; + } + } + + local condition ; + if $(flavor) + { + condition = [ common.check-init-parameters clang-unix + : version $(version) + : flavor $(flavor) + ] ; + } + else + { + condition = [ common.check-init-parameters clang-unix + : version $(version) + ] ; + condition = $(condition) ; #/ ; + } + + common.handle-options clang-unix : $(condition) : $(command) : $(options) ; + + local linker = [ feature.get-values : $(options) ] ; + # The logic below should actually be keyed on + if ! $(linker) + { + if [ os.name ] = OSF + { + linker = osf ; + } + else if [ os.name ] = HPUX + { + linker = hpux ; + } + else if [ os.name ] = AIX + { + linker = aix ; + } + else if [ os.name ] = SOLARIS + { + linker = sun ; + } + else + { + linker = gnu ; + } + } + init-link-flags clang-unix $(linker) $(condition) ; + + + # If clang is installed in non-standard location, we'd need to add + # LD_LIBRARY_PATH when running programs created with it (for unit-test/run + # rules). + if $(command) + { + # On multilib 64-bit boxes, there are both 32-bit and 64-bit libraries + # and all must be added to LD_LIBRARY_PATH. The linker will pick the + # right onces. Note that we don't provide a clean way to build 32-bit + # binary with 64-bit compiler, but user can always pass -m32 manually. + local lib_path = $(root)/bin $(root)/lib $(root)/lib32 $(root)/lib64 ; + if $(.debug-configuration) + { + ECHO notice: using clang libraries :: $(condition) :: $(lib_path) ; + } + toolset.flags clang-unix.link RUN_PATH $(condition) : $(lib_path) ; + } + + # If it's not a system clang install we should adjust the various programs as + # needed to prefer using the install specific versions. This is essential + # for correct use of MinGW and for cross-compiling. + + local nl = " +" ; + + # - The archive builder. + local archiver = [ common.get-invocation-command clang-unix + : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : [ SHELL "$(command-string) -print-prog-name=ar" ] ] ] + : [ feature.get-values : $(options) ] + : $(bin) + : search-path ] ; + toolset.flags clang-unix.archive .AR $(condition) : $(archiver[1]) ; + if $(.debug-configuration) + { + ECHO notice: using clang-unix archiver :: $(condition) :: $(archiver[1]) ; + } + + # - Ranlib + local ranlib = [ common.get-invocation-command clang-unix + : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : [ SHELL "$(command-string) -print-prog-name=ranlib" ] ] ] + : [ feature.get-values : $(options) ] + : $(bin) + : search-path ] ; + toolset.flags clang-unix.archive .RANLIB $(condition) : $(ranlib[1]) ; + if $(.debug-configuration) + { + ECHO notice: using clang-unix ranlib :: $(condition) :: $(ranlib[1]) ; + } + + + # - The resource compiler. + local rc = + [ common.get-invocation-command-nodefault clang-unix + : windres : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; + local rc-type = + [ feature.get-values : $(options) ] ; + rc-type ?= windres ; + if ! $(rc) + { + # If we can't find an RC compiler we fallback to a null RC compiler that + # creates empty object files. This allows the same Jamfiles to work + # across the board. The null RC uses the assembler to create the empty + # objects, so configure that. + rc = [ common.get-invocation-command clang-unix : as : : $(bin) : search-path ] ; + rc-type = null ; + } + rc.configure $(rc) : $(condition) : $(rc-type) ; +} + +if [ os.name ] = NT +{ + # This causes single-line command invocation to not go through .bat files, + # thus avoiding command-line length limitations. + JAMSHELL = % ; +} + +generators.register-c-compiler clang-unix.compile.c++ : CPP : OBJ : clang-unix ; +generators.register-c-compiler clang-unix.compile.c : C : OBJ : clang-unix ; +generators.register-c-compiler clang-unix.compile.asm : ASM : OBJ : clang-unix ; + +# pch support + +# The compiler looks for a precompiled header in each directory just before it +# looks for the include file in that directory. The name searched for is the +# name specified in the #include directive with ".gch" suffix appended. The +# logic in clang-unix-pch-generator will make sure that BASE_PCH suffix is appended to +# full name of the header. + +type.set-generated-target-suffix PCH : clang-unix : gch ; + +# clang-specific pch generator. +class clang-unix-pch-generator : pch-generator +{ + import project ; + import property-set ; + import type ; + + rule run-pch ( project name ? : property-set : sources + ) + { + # Find the header in sources. Ignore any CPP sources. + local header ; + for local s in $(sources) + { + if [ type.is-derived [ $(s).type ] H ] + { + header = $(s) ; + } + } + + # Error handling: Base header file name should be the same as the base + # precompiled header name. + local header-name = [ $(header).name ] ; + local header-basename = $(header-name:B) ; + if $(header-basename) != $(name) + { + local location = [ $(project).project-module ] ; + errors.user-error "in" $(location)": pch target name `"$(name)"' should be the same as the base name of header file `"$(header-name)"'" ; + } + + local pch-file = [ generator.run $(project) $(name) : $(property-set) + : $(header) ] ; + + # return result of base class and pch-file property as usage-requirements + return + [ property-set.create $(pch-file) -Winvalid-pch ] + $(pch-file) + ; + } + + # Calls the base version specifying source's name as the name of the created + # target. As result, the PCH will be named whatever.hpp.gch, and not + # whatever.gch. + rule generated-targets ( sources + : property-set : project name ? ) + { + name = [ $(sources[1]).name ] ; + return [ generator.generated-targets $(sources) + : $(property-set) : $(project) $(name) ] ; + } +} + +# Note: the 'H' source type will catch both '.h' header and '.hpp' header. The +# latter have HPP type, but HPP type is derived from H. The type of compilation +# is determined entirely by the destination type. +generators.register [ new clang-unix-pch-generator clang-unix.compile.c.pch : H : C_PCH : on clang-unix ] ; +generators.register [ new clang-unix-pch-generator clang-unix.compile.c++.pch : H : CPP_PCH : on clang-unix ] ; + +# Override default do-nothing generators. +generators.override clang-unix.compile.c.pch : pch.default-c-pch-generator ; +generators.override clang-unix.compile.c++.pch : pch.default-cpp-pch-generator ; + +toolset.flags clang-unix.compile PCH_FILE on : ; + +# Declare flags and action for compilation. +toolset.flags clang-unix.compile OPTIONS off : -O0 ; +toolset.flags clang-unix.compile OPTIONS speed : -O3 ; +toolset.flags clang-unix.compile OPTIONS space : -Os ; + +toolset.flags clang-unix.compile OPTIONS off : -fno-inline ; +toolset.flags clang-unix.compile OPTIONS on : -Wno-inline ; +toolset.flags clang-unix.compile OPTIONS full : -finline-functions -Wno-inline ; + +toolset.flags clang-unix.compile OPTIONS off : -w ; +toolset.flags clang-unix.compile OPTIONS on : -Wall ; +toolset.flags clang-unix.compile OPTIONS all : -Wall -pedantic ; +toolset.flags clang-unix.compile OPTIONS on : -Werror ; + +toolset.flags clang-unix.compile OPTIONS on : -g ; +toolset.flags clang-unix.compile OPTIONS on : -pg ; +toolset.flags clang-unix.compile OPTIONS off : -fno-rtti ; + +rule setup-fpic ( targets * : sources * : properties * ) +{ + local link = [ feature.get-values link : $(properties) ] ; + if $(link) = shared + { + local target = [ feature.get-values target-os : $(properties) ] ; + + # This logic will add -fPIC for all compilations: + # + # lib a : a.cpp b ; + # obj b : b.cpp ; + # exe c : c.cpp a d ; + # obj d : d.cpp ; + # + # This all is fine, except that 'd' will be compiled with -fPIC even though + # it is not needed, as 'd' is used only in exe. However, it is hard to + # detect where a target is going to be used. Alternatively, we can set -fPIC + # only when main target type is LIB but than 'b' would be compiled without + # -fPIC which would lead to link errors on x86-64. So, compile everything + # with -fPIC. + # + # Yet another alternative would be to create a propagated + # feature and set it when building shared libraries, but that would be hard + # to implement and would increase the target path length even more. + + # On Windows, fPIC is default, specifying -fPIC explicitly leads to + # a warning. + if $(target) != cygwin && $(target) != windows + { + OPTIONS on $(targets) += -fPIC ; + } + } +} + +rule setup-address-model ( targets * : sources * : properties * ) +{ + local model = [ feature.get-values address-model : $(properties) ] ; + if $(model) + { + local option ; + local os = [ feature.get-values target-os : $(properties) ] ; + if $(os) = aix + { + if $(model) = 32 + { + option = -maix32 ; + } + else + { + option = -maix64 ; + } + } + else + { + if $(model) = 32 + { + option = -m32 ; + } + else if $(model) = 64 + { + option = -m64 ; + } + # For darwin, the model can be 32_64. darwin.jam will handle that + # on its own. + } + OPTIONS on $(targets) += $(option) ; + } +} + + +# FIXME: this should not use os.name. +if [ os.name ] != NT && [ os.name ] != OSF && [ os.name ] != HPUX && [ os.name ] != AIX +{ + # OSF does have an option called -soname but it does not seem to work as + # expected, therefore it has been disabled. + HAVE_SONAME = "" ; + SONAME_OPTION = -h ; +} + +# HPUX, for some reason, seem to use '+h', not '-h'. +if [ os.name ] = HPUX +{ + HAVE_SONAME = "" ; + SONAME_OPTION = +h ; +} + +toolset.flags clang-unix.compile USER_OPTIONS ; +toolset.flags clang-unix.compile.c++ USER_OPTIONS ; +toolset.flags clang-unix.compile DEFINES ; +toolset.flags clang-unix.compile INCLUDES ; +toolset.flags clang-unix.compile.c++ TEMPLATE_DEPTH ; + +rule compile.c++.pch ( targets * : sources * : properties * ) +{ + setup-threading $(targets) : $(sources) : $(properties) ; + setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; +} + +actions compile.c++.pch +{ + "$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +rule compile.c.pch ( targets * : sources * : properties * ) +{ + setup-threading $(targets) : $(sources) : $(properties) ; + setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; +} + +actions compile.c.pch +{ + "$(CONFIG_COMMAND)" -x c-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +rule compile.c++ ( targets * : sources * : properties * ) +{ + setup-threading $(targets) : $(sources) : $(properties) ; + setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; + + # Some extensions are compiled as C++ by default. For others, we need to + # pass -x c++. We could always pass -x c++ but distcc does not work with it. + if ! $(>:S) in .cc .cp .cxx .cpp .c++ .C + { + LANG on $(<) = "-x c++" ; + } + DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; + + # Here we want to raise the template-depth parameter value to something + # higher than the default value of 17. Note that we could do this using the + # feature.set-default rule but we do not want to set the default value for + # all toolsets as well. + # + # TODO: This 'modified default' has been inherited from some 'older Boost + # Build implementation' and has most likely been added to make some Boost + # library parts compile correctly. We should see what exactly prompted this + # and whether we can get around the problem more locally. + local template-depth = [ on $(<) return $(TEMPLATE_DEPTH) ] ; + if ! $(template-depth) + { + TEMPLATE_DEPTH on $(<) = 128 ; + } +} + +rule compile.c ( targets * : sources * : properties * ) +{ + setup-threading $(targets) : $(sources) : $(properties) ; + setup-fpic $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; + + # If we use the name clang++ then default file suffix -> language mapping does + # not work. So have to pass -x option. Maybe, we can work around this by + # allowing the user to specify both C and C++ compiler names. + #if $(>:S) != .c + #{ + LANG on $(<) = "-x c" ; + #} + DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; +} + +actions compile.c++ bind PCH_FILE +{ + "$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<:W)" "$(>:W)" +} + +actions compile.c bind PCH_FILE +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +rule compile.asm +{ + LANG on $(<) = "-x assembler-with-cpp" ; +} + +actions compile.asm +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +# The class which check that we don't try to use the static +# property while creating or using shared library, since it's not supported by +# clang/libc. +class clang-unix-linking-generator : unix-linking-generator +{ + rule run ( project name ? : property-set : sources + ) + { + # TODO: Replace this with the use of a target-os property. + local no-static-link = ; + if [ modules.peek : UNIX ] + { + switch [ modules.peek : JAMUNAME ] + { + case * : no-static-link = true ; + } + } + + local properties = [ $(property-set).raw ] ; + local reason ; + if $(no-static-link) && static in $(properties) + { + if shared in $(properties) + { + reason = + "On clang, DLL can't be build with 'static'." ; + } + else if [ type.is-derived $(self.target-types[1]) EXE ] + { + for local s in $(sources) + { + local type = [ $(s).type ] ; + if $(type) && [ type.is-derived $(type) SHARED_LIB ] + { + reason = + "On clang, using DLLS together with the" + "static options is not possible " ; + } + } + } + } + if $(reason) + { + ECHO warning: + $(reason) ; + ECHO warning: + "It is suggested to use 'static' together" + "with 'static'." ; + return ; + } + else + { + local generated-targets = [ unix-linking-generator.run $(project) + $(name) : $(property-set) : $(sources) ] ; + return $(generated-targets) ; + } + } +} + +# The set of permissible input types is different on mingw. +# So, define two sets of generators, with mingw generators +# selected when target-os=windows. + +local g ; +g = [ new clang-unix-linking-generator clang-unix.mingw.link + : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB + : EXE + : clang-unix windows ] ; +$(g).set-rule-name clang-unix.link ; +generators.register $(g) ; + +g = [ new clang-unix-linking-generator clang-unix.mingw.link.dll + : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB + : IMPORT_LIB SHARED_LIB + : clang-unix windows ] ; +$(g).set-rule-name clang-unix.link.dll ; +generators.register $(g) ; + +generators.register + [ new clang-unix-linking-generator clang-unix.link + : LIB OBJ + : EXE + : clang-unix ] ; +generators.register + [ new clang-unix-linking-generator clang-unix.link.dll + : LIB OBJ + : SHARED_LIB + : clang-unix ] ; + +generators.override clang-unix.mingw.link : clang-unix.link ; +generators.override clang-unix.mingw.link.dll : clang-unix.link.dll ; + +# Cygwin is similar to msvc and mingw in that it uses import libraries. +# While in simple cases, it can directly link to a shared library, +# it is believed to be slower, and not always possible. Define cygwin-specific +# generators here. + +g = [ new clang-unix-linking-generator clang-unix.cygwin.link + : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB + : EXE + : clang-unix cygwin ] ; +$(g).set-rule-name clang-unix.link ; +generators.register $(g) ; + +g = [ new clang-unix-linking-generator clang-unix.cygwin.link.dll + : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB + : IMPORT_LIB SHARED_LIB + : clang-unix cygwin ] ; +$(g).set-rule-name clang-unix.link.dll ; +generators.register $(g) ; + +generators.override clang-unix.cygwin.link : clang-unix.link ; +generators.override clang-unix.cygwin.link.dll : clang-unix.link.dll ; + +# Declare flags for linking. +# First, the common flags. +toolset.flags clang-unix.link OPTIONS on : -g ; +toolset.flags clang-unix.link OPTIONS on : -pg ; +toolset.flags clang-unix.link USER_OPTIONS ; +toolset.flags clang-unix.link LINKPATH ; +toolset.flags clang-unix.link FINDLIBS-ST ; +toolset.flags clang-unix.link FINDLIBS-SA ; +toolset.flags clang-unix.link LIBRARIES ; + +toolset.flags clang-unix.link.dll .IMPLIB-COMMAND windows : "-Wl,--out-implib," ; +toolset.flags clang-unix.link.dll .IMPLIB-COMMAND cygwin : "-Wl,--out-implib," ; + +# For static we made sure there are no dynamic libraries in the +# link. On HP-UX not all system libraries exist as archived libraries (for +# example, there is no libunwind.a), so, on this platform, the -static option +# cannot be specified. +if [ os.name ] != HPUX +{ + toolset.flags clang-unix.link OPTIONS static : -static ; +} + +# Now, the vendor specific flags. +# The parameter linker can be either aix, darwin, gnu, hpux, osf or sun. +rule init-link-flags ( toolset linker condition ) +{ + switch $(linker) + { + case aix : + { + # + # On AIX we *have* to use the native linker. + # + # Using -brtl, the AIX linker will look for libraries with both the .a + # and .so extensions, such as libfoo.a and libfoo.so. Without -brtl, the + # AIX linker looks only for libfoo.a. Note that libfoo.a is an archived + # file that may contain shared objects and is different from static libs + # as on Linux. + # + # The -bnoipath strips the prepending (relative) path of libraries from + # the loader section in the target library or executable. Hence, during + # load-time LIBPATH (identical to LD_LIBRARY_PATH) or a hard-coded + # -blibpath (*similar* to -lrpath/-lrpath-link) is searched. Without + # this option, the prepending (relative) path + library name is + # hard-coded in the loader section, causing *only* this path to be + # searched during load-time. Note that the AIX linker does not have an + # -soname equivalent, this is as close as it gets. + # + # The above options are definately for AIX 5.x, and most likely also for + # AIX 4.x and AIX 6.x. For details about the AIX linker see: + # http://download.boulder.ibm.com/ibmdl/pub/software/dw/aix/es-aix_ll.pdf + # + + toolset.flags $(toolset).link OPTIONS : -Wl,-brtl -Wl,-bnoipath + : unchecked ; + } + + case darwin : + { + # On Darwin, the -s option to ld does not work unless we pass -static, + # and passing -static unconditionally is a bad idea. So, don't pass -s. + # at all, darwin.jam will use separate 'strip' invocation. + toolset.flags $(toolset).link RPATH $(condition) : : unchecked ; + toolset.flags $(toolset).link RPATH_LINK $(condition) : : unchecked ; + } + + case gnu : + { + # Strip the binary when no debugging is needed. We use --strip-all flag + # as opposed to -s since icc (intel's compiler) is generally + # option-compatible with and inherits from the clang toolset, but does not + # support -s. + toolset.flags $(toolset).link OPTIONS $(condition)/on : -Wl,--strip-all : unchecked ; + toolset.flags $(toolset).link RPATH $(condition) : : unchecked ; + toolset.flags $(toolset).link RPATH_LINK $(condition) : : unchecked ; + toolset.flags $(toolset).link START-GROUP $(condition) : -Wl,--start-group : unchecked ; + toolset.flags $(toolset).link END-GROUP $(condition) : -Wl,--end-group : unchecked ; + + # gnu ld has the ability to change the search behaviour for libraries + # referenced by -l switch. These modifiers are -Bstatic and -Bdynamic + # and change search for -l switches that follow them. The following list + # shows the tried variants. + # The search stops at the first variant that has a match. + # *nix: -Bstatic -lxxx + # libxxx.a + # + # *nix: -Bdynamic -lxxx + # libxxx.so + # libxxx.a + # + # windows (mingw,cygwin) -Bstatic -lxxx + # libxxx.a + # xxx.lib + # + # windows (mingw,cygwin) -Bdynamic -lxxx + # libxxx.dll.a + # xxx.dll.a + # libxxx.a + # xxx.lib + # cygxxx.dll (*) + # libxxx.dll + # xxx.dll + # libxxx.a + # + # (*) This is for cygwin + # Please note that -Bstatic and -Bdynamic are not a guarantee that a + # static or dynamic lib indeed gets linked in. The switches only change + # search patterns! + + # On *nix mixing shared libs with static runtime is not a good idea. + toolset.flags $(toolset).link FINDLIBS-ST-PFX $(condition)/shared + : -Wl,-Bstatic : unchecked ; + toolset.flags $(toolset).link FINDLIBS-SA-PFX $(condition)/shared + : -Wl,-Bdynamic : unchecked ; + + # On windows allow mixing of static and dynamic libs with static + # runtime. + toolset.flags $(toolset).link FINDLIBS-ST-PFX $(condition)/static/windows + : -Wl,-Bstatic : unchecked ; + toolset.flags $(toolset).link FINDLIBS-SA-PFX $(condition)/static/windows + : -Wl,-Bdynamic : unchecked ; + toolset.flags $(toolset).link OPTIONS $(condition)/static/windows + : -Wl,-Bstatic : unchecked ; + } + + case hpux : + { + toolset.flags $(toolset).link OPTIONS $(condition)/on + : -Wl,-s : unchecked ; + toolset.flags $(toolset).link OPTIONS $(condition)/shared + : -fPIC : unchecked ; + } + + case osf : + { + # No --strip-all, just -s. + toolset.flags $(toolset).link OPTIONS $(condition)/on + : -Wl,-s : unchecked ; + toolset.flags $(toolset).link RPATH $(condition) : + : unchecked ; + # This does not supports -R. + toolset.flags $(toolset).link RPATH_OPTION $(condition) : -rpath + : unchecked ; + # -rpath-link is not supported at all. + } + + case sun : + { + toolset.flags $(toolset).link OPTIONS $(condition)/on + : -Wl,-s : unchecked ; + toolset.flags $(toolset).link RPATH $(condition) : + : unchecked ; + # Solaris linker does not have a separate -rpath-link, but allows to use + # -L for the same purpose. + toolset.flags $(toolset).link LINKPATH $(condition) : + : unchecked ; + + # This permits shared libraries with non-PIC code on Solaris. + # VP, 2004/09/07: Now that we have -fPIC hardcode in link.dll, the + # following is not needed. Whether -fPIC should be hardcoded, is a + # separate question. + # AH, 2004/10/16: it is still necessary because some tests link against + # static libraries that were compiled without PIC. + toolset.flags $(toolset).link OPTIONS $(condition)/shared + : -mimpure-text : unchecked ; + } + + case * : + { + errors.user-error + "$(toolset) initialization: invalid linker '$(linker)'" : + "The value '$(linker)' specified for is not recognized." : + "Possible values are 'aix', 'darwin', 'gnu', 'hpux', 'osf' or 'sun'" ; + } + } +} + +# Enclose the RPATH variable on 'targets' in (double) quotes, +# unless it's already enclosed in single quotes. +# This special casing is done because it's common to pass +# '$ORIGIN' to linker -- and it has to have single quotes +# to prevent expansion by shell -- and if we add double +# quotes then preventing properties of single quotes disappear. +rule quote-rpath ( targets * ) +{ + local r = [ on $(targets[1]) return $(RPATH) ] ; + if ! [ MATCH "('.*')" : $(r) ] + { + r = "\"$(r)\"" ; + } + RPATH on $(targets) = $(r) ; +} + +# Declare actions for linking. +rule link ( targets * : sources * : properties * ) +{ + setup-threading $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; + SPACE on $(targets) = " " ; + # Serialize execution of the 'link' action, since running N links in + # parallel is just slower. For now, serialize only clang links, it might be a + # good idea to serialize all links. + JAM_SEMAPHORE on $(targets) = clang-unix-link-semaphore ; + quote-rpath $(targets) ; +} + +actions link bind LIBRARIES +{ + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,$(RPATH) -Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" $(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) + +} + +# Default value. Mostly for the sake of intel-linux that inherits from clang, but +# does not have the same logic to set the .AR variable. We can put the same +# logic in intel-linux, but that's hardly worth the trouble as on Linux, 'ar' is +# always available. +.AR = ar ; +.RANLIB = ranlib ; + +toolset.flags clang-unix.archive AROPTIONS ; + +rule archive ( targets * : sources * : properties * ) +{ + # Always remove archive and start again. Here is the rationale from + # + # Andre Hentz: + # + # I had a file, say a1.c, that was included into liba.a. I moved a1.c to + # a2.c, updated my Jamfiles and rebuilt. My program was crashing with absurd + # errors. After some debugging I traced it back to the fact that a1.o was + # *still* in liba.a + # + # Rene Rivera: + # + # Originally removing the archive was done by splicing an RM onto the + # archive action. That makes archives fail to build on NT when they have + # many files because it will no longer execute the action directly and blow + # the line length limit. Instead we remove the file in a different action, + # just before building the archive. + # + local clean.a = $(targets[1])(clean) ; + TEMPORARY $(clean.a) ; + NOCARE $(clean.a) ; + LOCATE on $(clean.a) = [ on $(targets[1]) return $(LOCATE) ] ; + DEPENDS $(clean.a) : $(sources) ; + DEPENDS $(targets) : $(clean.a) ; + common.RmTemps $(clean.a) : $(targets) ; +} + +# Declare action for creating static libraries. +# The letter 'r' means to add files to the archive with replacement. Since we +# remove archive, we don't care about replacement, but there's no option "add +# without replacement". +# The letter 'c' suppresses the warning in case the archive does not exists yet. +# That warning is produced only on some platforms, for whatever reasons. +actions piecemeal archive +{ + "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" + "$(.RANLIB)" "$(<)" +} + +rule link.dll ( targets * : sources * : properties * ) +{ + setup-threading $(targets) : $(sources) : $(properties) ; + setup-address-model $(targets) : $(sources) : $(properties) ; + SPACE on $(targets) = " " ; + JAM_SEMAPHORE on $(targets) = clang-unix-link-semaphore ; + quote-rpath $(targets) ; +} + +# Differs from 'link' above only by -shared. +actions link.dll bind LIBRARIES +{ + "$(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)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) +} + +rule setup-threading ( targets * : sources * : properties * ) +{ + local threading = [ feature.get-values threading : $(properties) ] ; + if $(threading) = multi + { + local target = [ feature.get-values target-os : $(properties) ] ; + local option ; + local libs ; + + switch $(target) + { + case windows : + { + option = -mthreads ; + } + case cygwin : + { + option = -mthreads ; + } + case solaris : + { + option = -pthreads ; + libs = rt ; + } + case beos : + { + # BeOS has no threading options, so do not set anything here. + } + case *bsd : + { + option = -pthread ; + # There is no -lrt on BSD. + } + case sgi : + { + # clang on IRIX does not support multi-threading so do not set anything + # here. + } + case darwin : + { + # Darwin has no threading options so do not set anything here. + } + case * : + { + option = -pthread ; + libs = rt ; + } + } + + if $(option) + { + OPTIONS on $(targets) += $(option) ; + } + if $(libs) + { + FINDLIBS-SA on $(targets) += $(libs) ; + } + } +} + +local rule cpu-flags ( toolset variable : architecture : instruction-set + : values + : default ? ) +{ + if $(default) + { + toolset.flags $(toolset) $(variable) + $(architecture)/ + : $(values) ; + } + toolset.flags $(toolset) $(variable) + /$(instruction-set) + $(architecture)/$(instruction-set) + : $(values) ; +} + +# Set architecture/instruction-set options. +# +# x86 and compatible +cpu-flags clang-unix OPTIONS : x86 : native : -march=native : default ; +cpu-flags clang-unix OPTIONS : x86 : i386 : -march=i386 ; +cpu-flags clang-unix OPTIONS : x86 : i486 : -march=i486 ; +cpu-flags clang-unix OPTIONS : x86 : i586 : -march=i586 ; +cpu-flags clang-unix OPTIONS : x86 : i686 : -march=i686 ; +cpu-flags clang-unix OPTIONS : x86 : pentium : -march=pentium ; +cpu-flags clang-unix OPTIONS : x86 : pentium-mmx : -march=pentium-mmx ; +cpu-flags clang-unix OPTIONS : x86 : pentiumpro : -march=pentiumpro ; +cpu-flags clang-unix OPTIONS : x86 : pentium2 : -march=pentium2 ; +cpu-flags clang-unix OPTIONS : x86 : pentium3 : -march=pentium3 ; +cpu-flags clang-unix OPTIONS : x86 : pentium3m : -march=pentium3m ; +cpu-flags clang-unix OPTIONS : x86 : pentium-m : -march=pentium-m ; +cpu-flags clang-unix OPTIONS : x86 : pentium4 : -march=pentium4 ; +cpu-flags clang-unix OPTIONS : x86 : pentium4m : -march=pentium4m ; +cpu-flags clang-unix OPTIONS : x86 : prescott : -march=prescott ; +cpu-flags clang-unix OPTIONS : x86 : nocona : -march=nocona ; +cpu-flags clang-unix OPTIONS : x86 : core2 : -march=core2 ; +cpu-flags clang-unix OPTIONS : x86 : k6 : -march=k6 ; +cpu-flags clang-unix OPTIONS : x86 : k6-2 : -march=k6-2 ; +cpu-flags clang-unix OPTIONS : x86 : k6-3 : -march=k6-3 ; +cpu-flags clang-unix OPTIONS : x86 : athlon : -march=athlon ; +cpu-flags clang-unix OPTIONS : x86 : athlon-tbird : -march=athlon-tbird ; +cpu-flags clang-unix OPTIONS : x86 : athlon-4 : -march=athlon-4 ; +cpu-flags clang-unix OPTIONS : x86 : athlon-xp : -march=athlon-xp ; +cpu-flags clang-unix OPTIONS : x86 : athlon-mp : -march=athlon-mp ; +## +cpu-flags clang-unix OPTIONS : x86 : k8 : -march=k8 ; +cpu-flags clang-unix OPTIONS : x86 : opteron : -march=opteron ; +cpu-flags clang-unix OPTIONS : x86 : athlon64 : -march=athlon64 ; +cpu-flags clang-unix OPTIONS : x86 : athlon-fx : -march=athlon-fx ; +cpu-flags clang-unix OPTIONS : x86 : winchip-c6 : -march=winchip-c6 ; +cpu-flags clang-unix OPTIONS : x86 : winchip2 : -march=winchip2 ; +cpu-flags clang-unix OPTIONS : x86 : c3 : -march=c3 ; +cpu-flags clang-unix OPTIONS : x86 : c3-2 : -march=c3-2 ; + diff --git a/v2/tools/clang.jam b/v2/tools/clang.jam new file mode 100644 index 000000000..818ba8778 --- /dev/null +++ b/v2/tools/clang.jam @@ -0,0 +1,27 @@ +# 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) + +# This is a generic 'clang' toolset. Depending on the current system, it +# forwards either to 'clang-unix' or 'clang-darwin' modules. + +import feature ; +import os ; +import toolset ; + +feature.extend toolset : clang ; +feature.subfeature toolset clang : platform : : propagated link-incompatible ; + +rule init ( * : * ) +{ + if [ os.name ] = MACOSX + { + toolset.using clang-darwin : + $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; + } + else + { + toolset.using clang-unix : + $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; + } +} diff --git a/v2/tools/common.jam b/v2/tools/common.jam index 05cd392d2..f56b9d00e 100644 --- a/v2/tools/common.jam +++ b/v2/tools/common.jam @@ -825,6 +825,8 @@ local rule toolset-tag ( name : type ? : property-set ) switch [ $(property-set).get ] { case borland* : tag += bcb ; + case clang-unix* : tag += gcc ; + case clang-darwin* : tag += xgcc ; case como* : tag += como ; case cw : tag += cw ; case darwin* : tag += xgcc ; From 618cc10481e348d6729746973097a80319a34003 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 19 Apr 2010 03:43:14 +0000 Subject: [PATCH 182/198] Call explicit type register rule to avoid errors when the type file is imported from another type file. (Fixes #4118). [SVN r61393] --- v2/tools/types/cpp.jam | 8 ++++---- v2/tools/types/objc.jam | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/v2/tools/types/cpp.jam b/v2/tools/types/cpp.jam index defa3e566..3159cdd77 100644 --- a/v2/tools/types/cpp.jam +++ b/v2/tools/types/cpp.jam @@ -69,10 +69,10 @@ class c-scanner : scanner scanner.register c-scanner : include ; -type CPP : cpp cxx cc ; -type H : h ; -type HPP : hpp : H ; -type C : c ; +type.register CPP : cpp cxx cc ; +type.register H : h ; +type.register HPP : hpp : H ; +type.register C : c ; # It most cases where a CPP file or a H file is a source of some action, we # should rebuild the result if any of files included by CPP/H are changed. One diff --git a/v2/tools/types/objc.jam b/v2/tools/types/objc.jam index f8d31c072..709cbd0c7 100644 --- a/v2/tools/types/objc.jam +++ b/v2/tools/types/objc.jam @@ -20,7 +20,7 @@ class objc-scanner : c-scanner scanner.register objc-scanner : include ; -type OBJECTIVE_C : m ; -type OBJECTIVE_CPP : mm ; +type.register OBJECTIVE_C : m ; +type.register OBJECTIVE_CPP : mm ; type.set-scanner OBJECTIVE_C : objc-scanner ; type.set-scanner OBJECTIVE_CPP : objc-scanner ; From 656169f5f39e4e429faf666d303017d05a42303d Mon Sep 17 00:00:00 2001 From: Jeremiah Willcock Date: Tue, 20 Apr 2010 18:49:18 +0000 Subject: [PATCH 183/198] Fixed various issues in docs (mostly duplicate bookmarks and broken links) found by inspect tool [SVN r61437] --- v2/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/index.html b/v2/index.html index 965022c98..63df93fec 100644 --- a/v2/index.html +++ b/v2/index.html @@ -129,7 +129,7 @@ div.sidebar p.rubric { of Boost.Build reimplemented in Python, to make extending Boost.Build even easier for end users (see PythonPort). The specific issues planned for each release can be found on the - roadmap. + roadmap. From 99a99424d80f0b1d64145d115be029e0cf5c2ff0 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Thu, 29 Apr 2010 20:21:14 +0000 Subject: [PATCH 184/198] Add a force-load dependency feature to allow for cases where the linker removes 'unused' symbols as is common in the linking model of Apple's Objective C [SVN r61684] --- v2/tools/darwin.jam | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index a14987c33..6fada2c2e 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -3,7 +3,7 @@ # Copyright 2003, 2004, 2005, 2006 Vladimir Prus # Copyright 2005-2007 Mat Marcus # Copyright 2005-2007 Adobe Systems Incorporated -# Copyright 2007-2009 Rene Rivera +# Copyright 2007-2010 Rene Rivera # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) @@ -30,6 +30,9 @@ feature macosx-version : : propagated link-incompatible symmetric optional ; ## The minimal MacOSX version to target. feature macosx-version-min : : propagated optional ; +## A dependency, that is forced to be included in the link. +feature force-load : : free dependency incidental ; + ############################################################################# if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] @@ -484,6 +487,9 @@ flags darwin.compile OPTIONS : -no-cpp-precomp -gdwarf-2 ; # Add the framework names to use. flags darwin.link FRAMEWORK ; +# +flags darwin.link FORCE_LOAD ; + # This is flag is useful for debugging the link step # uncomment to see what libtool is doing under the hood #~ flags darwin.link.dll OPTIONS : -Wl,-v ; @@ -519,9 +525,9 @@ rule link ( targets * : sources * : properties * ) # binaries, at least on OS X 10.5.5, see: # http://svn.boost.org/trac/boost/ticket/2347 # So we pass -S -x. -actions link bind LIBRARIES +actions link bind LIBRARIES FORCE_LOAD { - "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=:S=) $(OPTIONS) $(USER_OPTIONS) + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -o "$(<)" "$(>)" -Wl,-force_load$(_)"$(FORCE_LOAD)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=:S=) $(OPTIONS) $(USER_OPTIONS) $(NEED_STRIP)"$(.STRIP)" $(NEED_STRIP)-S $(NEED_STRIP)-x $(NEED_STRIP)"$(<)" } From fbebafefd7055ec8ca44ffafa87ebda7a60aaaba Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Thu, 29 Apr 2010 22:02:27 +0000 Subject: [PATCH 185/198] Manually add dependency to force_load declared source. [SVN r61689] --- v2/tools/darwin.jam | 1 + 1 file changed, 1 insertion(+) diff --git a/v2/tools/darwin.jam b/v2/tools/darwin.jam index 6fada2c2e..4b506afdd 100644 --- a/v2/tools/darwin.jam +++ b/v2/tools/darwin.jam @@ -517,6 +517,7 @@ local rule prepare-framework-path ( target + ) rule link ( targets * : sources * : properties * ) { + DEPENDS $(targets) : [ on $(targets) return $(FORCE_LOAD) ] ; setup-address-model $(targets) : $(sources) : $(properties) ; prepare-framework-path $(<) ; } From d4ba79eba56937a1ecd063c2d2568b9ce8f77fd7 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Sun, 9 May 2010 00:14:41 +0000 Subject: [PATCH 186/198] Try to fix Boost.Build support for Clang on Linux [SVN r61860] --- v2/tools/clang-darwin.jam | 608 +++++------------------ v2/tools/clang-linux.jam | 177 +++++++ v2/tools/clang-unix.jam | 990 -------------------------------------- v2/tools/clang.jam | 2 +- 4 files changed, 299 insertions(+), 1478 deletions(-) create mode 100644 v2/tools/clang-linux.jam delete mode 100644 v2/tools/clang-unix.jam diff --git a/v2/tools/clang-darwin.jam b/v2/tools/clang-darwin.jam index 8548718e4..a8abc7d6a 100644 --- a/v2/tools/clang-darwin.jam +++ b/v2/tools/clang-darwin.jam @@ -1,536 +1,170 @@ -# Copyright 2003 Christopher Currie -# Copyright 2006 Dave Abrahams -# Copyright 2003, 2004, 2005, 2006 Vladimir Prus -# Copyright 2005-2007 Mat Marcus -# Copyright 2005-2007 Adobe Systems Incorporated -# Copyright 2007-2009 Rene Rivera -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +# Copyright Vladimir Prus 2004. +# Copyright Noel Belcourt 2007. +# 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) +import clang ; import feature : feature ; +import os ; +import toolset ; import toolset : flags ; -import type ; +import gcc ; import common ; -import generators ; -import path : basename ; -import version ; -import property-set ; -import regex ; import errors ; +import generators ; -## Use a framework. -feature framework : : free ; +feature.extend-subfeature toolset clang : platform : darwin ; -## The MacOSX version to compile for, which maps to the SDK to use (sysroot). -feature macosx-version : : propagated link-incompatible symmetric optional ; +toolset.inherit-generators clang-darwin + clang darwin + : gcc + # Don't inherit PCH generators. They were not tested, and probably + # don't work for this compiler. + : gcc.mingw.link gcc.mingw.link.dll gcc.compile.c.pch gcc.compile.c++.pch + ; -## The minimal MacOSX version to target. -feature macosx-version-min : : propagated optional ; - -############################################################################# +generators.override clang-darwin.prebuilt : builtin.lib-generator ; +generators.override clang-darwin.prebuilt : builtin.prebuilt ; +generators.override clang-darwin.searched-lib-generator : searched-lib-generator ; +toolset.inherit-rules clang-darwin : gcc ; +toolset.inherit-flags clang-darwin : gcc + : off on full space + off all on + x86/32 + x86/64 + ; + if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] { .debug-configuration = true ; } + +# vectorization diagnostics +feature vectorize : off on full ; -feature.extend toolset : clang-darwin ; -import clang-unix ; -feature.extend-subfeature toolset clang : platform : darwin ; - -toolset.inherit-generators clang-darwin - clang darwin - : clang-unix - : clang-unix.mingw.link clang-unix.mingw.link.dll - ; - -generators.override clang-darwin.prebuilt : builtin.prebuilt ; -generators.override clang-darwin.searched-lib-generator : searched-lib-generator ; - -# Override default do-nothing generators. -generators.override clang-darwin.compile.c.pch : pch.default-c-pch-generator ; -generators.override clang-darwin.compile.c++.pch : pch.default-cpp-pch-generator ; - -type.set-generated-target-suffix PCH : clang-darwin : gch ; - -toolset.inherit-rules clang-darwin : clang-unix : localize ; -toolset.inherit-flags clang-darwin : clang-unix - : static - arm/32 - arm/64 - arm/ - x86/32 - x86/64 - x86/ - power/32 - power/64 - power/ ; - -# Options: -# -# PATH -# Platform root path. The common autodetection will set this to -# "/Developer". And when a command is given it will be set to -# the corresponding "*.platform/Developer" directory. -# -rule init ( version ? : command * : options * : requirement * ) +# Initializes the clang-darwin toolset +# version in optional +# name (default clang++) is used to invoke the specified clang complier +# compile and link options allow you to specify addition command line options for each version +rule init ( version ? : command * : options * ) { - # First time around, figure what is host OSX version - if ! $(.host-osx-version) - { - .host-osx-version = [ MATCH "^([0-9.]+)" - : [ SHELL "/usr/bin/sw_vers -productVersion" ] ] ; - if $(.debug-configuration) - { - ECHO notice: OSX version on this machine is $(.host-osx-version) ; - } - } - - # - The root directory of the tool install. - local root = [ feature.get-values : $(options) ] ; - - # - The bin directory where to find the commands to execute. - local bin ; - - # - The configured compile driver command. - local command = [ common.get-invocation-command clang-darwin : clang++ : $(command) ] ; - - # The version as reported by the compiler - local real-version ; - - # - Autodetect the root and bin dir if not given. + command = [ common.get-invocation-command clang-darwin : clang++ + : $(command) ] ; + + # Determine the version + local command-string = $(command:J=" ") ; if $(command) - { - bin ?= [ common.get-absolute-tool-path $(command[1]) ] ; - if $(bin) = "/usr/bin" - { - root ?= /Developer ; - } - else - { - local r = $(bin:D) ; - r = $(r:D) ; - root ?= $(r) ; - } - } - - # - Autodetect the version if not given. - if $(command) - { - # - The 'command' variable can have multiple elements. When calling - # the SHELL builtin we need a single string. - local command-string = $(command:J=" ") ; - real-version = [ MATCH "^([0-9.]+)" + { + version ?= [ MATCH "^([0-9.]+)" : [ SHELL "$(command-string) -dumpversion" ] ] ; - version ?= $(real-version) ; } - - .real-version.$(version) = $(real-version) ; - - # - Define the condition for this toolset instance. - local condition = - [ common.check-init-parameters clang-darwin $(requirement) : version $(version) ] ; - - # - Set the toolset generic common options. + + local condition = [ common.check-init-parameters clang-darwin + : version $(version) ] ; + common.handle-options clang-darwin : $(condition) : $(command) : $(options) ; - - # - Set the link flags common with the clang-unix toolset. - clang-unix.init-link-flags clang-darwin darwin $(condition) ; - # - The symbol strip program. - local strip ; - if in $(options) - { - # We can turn off strip by specifying it as empty. In which - # case we switch to using the linker to do the strip. - flags clang-darwin.link.dll OPTIONS - $(condition)/LIB/shared/32/on : -Wl,-x ; - flags clang-darwin.link.dll OPTIONS - $(condition)/LIB/shared//on : -Wl,-x ; - flags clang-darwin.link OPTIONS - $(condition)/EXE/32/on : -s ; - flags clang-darwin.link OPTIONS - $(condition)/EXE//on : -s ; - } - else - { - # Otherwise we need to find a strip program to use. And hence - # also tell the link action that we need to use a strip - # post-process. - flags clang-darwin.link NEED_STRIP $(condition)/on : "" ; - strip = - [ common.get-invocation-command clang-darwin - : strip : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; - flags clang-darwin.link .STRIP $(condition) : $(strip[1]) ; - if $(.debug-configuration) - { - ECHO notice: using strip for $(condition) at $(strip[1]) ; - } - } + gcc.init-link-flags clang-darwin darwin $(condition) ; - # - The archive builder (libtool is the default as creating - # archives in darwin is complicated. - local archiver = - [ common.get-invocation-command clang-darwin - : libtool : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; - flags clang-darwin.archive .LIBTOOL $(condition) : $(archiver[1]) ; - if $(.debug-configuration) - { - ECHO notice: using archiver for $(condition) at $(archiver[1]) ; - } - - # - Initialize the SDKs available in the root for this tool. - local sdks = [ init-available-sdk-versions $(condition) : $(root) ] ; - - #~ ECHO --- ; - #~ ECHO --- bin :: $(bin) ; - #~ ECHO --- root :: $(root) ; - #~ ECHO --- version :: $(version) ; - #~ ECHO --- condition :: $(condition) ; - #~ ECHO --- strip :: $(strip) ; - #~ ECHO --- archiver :: $(archiver) ; - #~ ECHO --- sdks :: $(sdks) ; - #~ ECHO --- ; - #~ EXIT ; } -# Add and set options for a discovered SDK version. -local rule init-sdk ( condition * : root ? : version + : version-feature ? ) +SPACE = " " ; + +flags clang-darwin.compile OPTIONS ; +flags clang-darwin.compile OPTIONS ; +# flags clang-darwin.compile INCLUDES ; + +# Declare flags and action for compilation. +toolset.flags clang-darwin.compile OPTIONS off : -O0 ; +toolset.flags clang-darwin.compile OPTIONS speed : -O3 ; +toolset.flags clang-darwin.compile OPTIONS space : -Os ; + +toolset.flags clang-darwin.compile OPTIONS off : -fno-inline ; +toolset.flags clang-darwin.compile OPTIONS on : -Wno-inline ; +toolset.flags clang-darwin.compile OPTIONS full : -finline-functions -Wno-inline ; + +toolset.flags clang-darwin.compile OPTIONS off : -w ; +toolset.flags clang-darwin.compile OPTIONS on : -Wall ; +toolset.flags clang-darwin.compile OPTIONS all : -Wall -pedantic ; +toolset.flags clang-darwin.compile OPTIONS on : -Werror ; + +toolset.flags clang-darwin.compile OPTIONS on : -g ; +toolset.flags clang-darwin.compile OPTIONS on : -pg ; +toolset.flags clang-darwin.compile OPTIONS off : -fno-rtti ; + +actions compile.c { - local rule version-to-feature ( version + ) - { - switch $(version[1]) - { - case iphone* : - { - return $(version[1])-$(version[2-]:J=.) ; - } - case mac* : - { - return $(version[2-]:J=.) ; - } - case * : - { - return $(version:J=.) ; - } - } - } - - if $(version-feature) - { - if $(.debug-configuration) - { - ECHO notice: available sdk for $(condition)/$(version-feature) at $(sdk) ; - } - - # Add the version to the features for specifying them. - if ! $(version-feature) in [ feature.values macosx-version ] - { - feature.extend macosx-version : $(version-feature) ; - } - if ! $(version-feature) in [ feature.values macosx-version-min ] - { - feature.extend macosx-version-min : $(version-feature) ; - } - - # Set the flags the version needs to compile with, first - # generic options. - flags clang-darwin.compile OPTIONS $(condition)/$(version-feature) - : -isysroot $(sdk) ; - flags clang-darwin.link OPTIONS $(condition)/$(version-feature) - : -isysroot $(sdk) ; - - # Then device variation options. - switch $(version[1]) - { - case iphone* : - { - flags clang-darwin.compile OPTIONS $(version-feature) - : -miphoneos-version-min=$(version[2-]:J=.) ; - flags clang-darwin.link OPTIONS $(version-feature) - : -miphoneos-version-min=$(version[2-]:J=.) ; - } - - case mac* : - { - flags clang-darwin.compile OPTIONS $(version-feature) - : -miphoneos-version-min=$(version[2-]:J=.) ; - flags clang-darwin.link OPTIONS $(version-feature) - : -miphoneos-version-min=$(version[2-]:J=.) ; - } - } - - return $(version-feature) ; - } - else if $(version[4]) - { - # We have a patch version of an SDK. We want to set up - # both the specific patch version, and the minor version. - # So we recurse to set up the minor version. Plus the minor version. - return - [ init-sdk $(condition) : $(root) - : $(version[1-3]) : [ version-to-feature $(version[1-3]) ] ] - [ init-sdk $(condition) : $(root) - : $(version) : [ version-to-feature $(version) ] ] ; - } - else - { - # Yes, this is intentionally recursive. - return - [ init-sdk $(condition) : $(root) - : $(version) : [ version-to-feature $(version) ] ] ; - } + "$(CONFIG_COMMAND)" -x c $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } -# Determine the MacOSX SDK versions installed and their locations. -local rule init-available-sdk-versions ( condition * : root ? ) +actions compile.c++ { - root ?= /Developer ; - local sdks-root = $(root)/SDKs ; - local sdks = [ GLOB $(sdks-root) : MacOSX*.sdk iPhoneOS*.sdk iPhoneSimulator*.sdk ] ; - local result ; - for local sdk in $(sdks) - { - local sdk-match = [ MATCH ([^0-9]+)([0-9]+)[.]([0-9x]+)[.]?([0-9x]+)? : $(sdk:D=) ] ; - local sdk-platform = $(sdk-match[1]:L) ; - local sdk-version = $(sdk-match[2-]) ; - if $(sdk-version) - { - switch $(sdk-platform) - { - case macosx : - { - sdk-version = mac $(sdk-version) ; - } - case iphoneos : - { - sdk-version = iphone $(sdk-version) ; - } - case iphonesimulator : - { - sdk-version = iphonesim $(sdk-version) ; - } - case * : - { - sdk-version = $(sdk-version:J=-) ; - } - } - result += [ init-sdk $(condition) : $(sdk) : $(sdk-version) ] ; - } - } - return $(result) ; + "$(CONFIG_COMMAND)" -x c++ $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } -# Generic options. -flags clang-darwin.compile OPTIONS ; +flags clang-darwin ARFLAGS ; -# The following adds objective-c support to clang-darwin. -# Thanks to http://thread.gmane.org/gmane.comp.lib.boost.build/13759 +# Default value. Mostly for the sake of clang-linux +# that inherits from gcc, but does not has the same +# logic to set the .AR variable. We can put the same +# logic in clang-linux, but that's hardly worth the trouble +# as on Linux, 'ar' is always available. +.AR = ar ; -generators.register-c-compiler clang-darwin.compile.m : OBJECTIVE_C : OBJ : clang-darwin ; -generators.register-c-compiler clang-darwin.compile.mm : OBJECTIVE_CPP : OBJ : clang-darwin ; - -rule setup-address-model ( targets * : sources * : properties * ) +rule archive ( targets * : sources * : properties * ) { - local ps = [ property-set.create $(properties) ] ; - local arch = [ $(ps).get ] ; - local address-model = [ $(ps).get ] ; - local osx-version = [ $(ps).get ] ; - local clang-unix-version = [ $(ps).get ] ; - clang-unix-version = $(.real-version.$(clang-unix-version)) ; - local options ; - - local support-ppc64 = 1 ; - - osx-version ?= $(.host-osx-version) ; - - switch $(osx-version) - { - case iphone* : - { - support-ppc64 = ; - } - - case * : - if $(osx-version) && ! [ version.version-less [ regex.split $(osx-version) \\. ] : 10 6 ] - { - # When targeting 10.6: - # - clang-unix 4.2 will give a compiler errir if ppc64 compilation is requested - # - clang-unix 4.0 will compile fine, somehow, but then fail at link time - support-ppc64 = ; - } - } - switch $(arch) - { - case combined : - { - if $(address-model) = 32_64 { - if $(support-ppc64) { - options = -arch i386 -arch ppc -arch x86_64 -arch ppc64 ; - } else { - # Build 3-way binary - options = -arch i386 -arch ppc -arch x86_64 ; - } - } else if $(address-model) = 64 { - if $(support-ppc64) { - options = -arch x86_64 -arch ppc64 ; - } else { - errors.user-error "64-bit PPC compilation is not supported when targeting OSX 10.6 or later" ; - } - } else { - options = -arch i386 -arch ppc ; - } - } - - case x86 : - { - if $(address-model) = 32_64 { - options = -arch i386 -arch x86_64 ; - } else if $(address-model) = 64 { - options = -arch x86_64 ; - } else { - options = -arch i386 ; - } - } - - case power : - { - if ! $(support-ppc64) - && ( $(address-model) = 32_64 || $(address-model) = 64 ) - { - errors.user-error "64-bit PPC compilation is not supported when targeting OSX 10.6 or later" ; - } - - if $(address-model) = 32_64 { - options = -arch ppc -arch ppc64 ; - } else if $(address-model) = 64 { - options = -arch ppc64 ; - } else { - options = -arch ppc ; - } - } - - case arm : - { - options = -arch armv6 ; - } - } - - if $(options) - { - OPTIONS on $(targets) += $(options) ; - } + # Always remove archive and start again. Here's rationale from + # Andre Hentz: + # + # I had a file, say a1.c, that was included into liba.a. + # I moved a1.c to a2.c, updated my Jamfiles and rebuilt. + # My program was crashing with absurd errors. + # After some debugging I traced it back to the fact that a1.o was *still* + # in liba.a + # + # Rene Rivera: + # + # Originally removing the archive was done by splicing an RM + # onto the archive action. That makes archives fail to build on NT + # when they have many files because it will no longer execute the + # action directly and blow the line length limit. Instead we + # remove the file in a different action, just before the building + # of the archive. + # + local clean.a = $(targets[1])(clean) ; + TEMPORARY $(clean.a) ; + NOCARE $(clean.a) ; + LOCATE on $(clean.a) = [ on $(targets[1]) return $(LOCATE) ] ; + DEPENDS $(clean.a) : $(sources) ; + DEPENDS $(targets) : $(clean.a) ; + common.RmTemps $(clean.a) : $(targets) ; } -rule setup-threading ( targets * : sources * : properties * ) +actions piecemeal archive { - clang-unix.setup-threading $(targets) : $(sources) : $(properties) ; + "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" + "ranlib" -cs "$(<)" } -rule setup-fpic ( targets * : sources * : properties * ) -{ - clang-unix.setup-fpic $(targets) : $(sources) : $(properties) ; -} - -rule compile.m ( targets * : sources * : properties * ) -{ - LANG on $(<) = "-x objective-c" ; - clang-unix.setup-fpic $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; -} - -actions compile.m -{ - "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" -} - -rule compile.mm ( targets * : sources * : properties * ) -{ - LANG on $(<) = "-x objective-c++" ; - clang-unix.setup-fpic $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; -} - -actions compile.mm -{ - "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" -} - -# Set the max header padding to allow renaming of libs for installation. -flags clang-darwin.link.dll OPTIONS : -headerpad_max_install_names ; - -# To link the static runtime we need to link to all the core runtime libraries. -flags clang-darwin.link OPTIONS static - : -nodefaultlibs -shared-libgcc -lstdc++-static -lgcc_eh -lgcc -lSystem ; - -# Strip as much as possible when optimizing. -flags clang-darwin.link OPTIONS speed : -Wl,-dead_strip -no_dead_strip_inits_and_terms ; -flags clang-darwin.link OPTIONS space : -Wl,-dead_strip -no_dead_strip_inits_and_terms ; - -# Dynamic/shared linking. -flags clang-darwin.compile OPTIONS shared : -dynamic ; - -# Misc options. -flags clang-darwin.compile OPTIONS : -gdwarf-2 ; - -# Add the framework names to use. -flags clang-darwin.link FRAMEWORK ; - -# This is flag is useful for debugging the link step -# uncomment to see what libtool is doing under the hood -#~ flags clang-darwin.link.dll OPTIONS : -Wl,-v ; - -_ = " " ; - -# set up the -F option to include the paths to any frameworks used. -local rule prepare-framework-path ( target + ) -{ - # The -framework option only takes basename of the framework. - # The -F option specifies the directories where a framework - # is searched for. So, if we find feature - # with some path, we need to generate property -F option. - local framework-paths = [ on $(target) return $(FRAMEWORK:D) ] ; - - # Be sure to generate no -F if there's no path. - for local framework-path in $(framework-paths) - { - if $(framework-path) != "" - { - FRAMEWORK_PATH on $(target) += -F$(framework-path) ; - } - } -} +flags clang-darwin.link USER_OPTIONS ; +# Declare actions for linking rule link ( targets * : sources * : properties * ) { - setup-address-model $(targets) : $(sources) : $(properties) ; - prepare-framework-path $(<) ; + SPACE on $(targets) = " " ; + # Serialize execution of the 'link' action, since + # running N links in parallel is just slower. + JAM_SEMAPHORE on $(targets) = clang-darwin-link-semaphore ; } -# Note that using strip without any options was reported to result in broken -# binaries, at least on OS X 10.5.5, see: -# http://svn.boost.org/trac/boost/ticket/2347 -# So we pass -S -x. actions link bind LIBRARIES { - "$(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)-S $(NEED_STRIP)-x $(NEED_STRIP)"$(<)" -} - -rule link.dll ( targets * : sources * : properties * ) -{ - setup-address-model $(targets) : $(sources) : $(properties) ; - prepare-framework-path $(<) ; + "$(CONFIG_COMMAND)" $(USER_OPTIONS) -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(OPTIONS) } actions link.dll bind LIBRARIES { - "$(CONFIG_COMMAND)" -dynamiclib -Wl,-single_module -install_name "$(<:B)$(<:S)" -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(FRAMEWORK_PATH) -framework$(_)$(FRAMEWORK:D=:S=) $(OPTIONS) $(USER_OPTIONS) -} - -# We use libtool instead of ar to support universal binary linking -# TODO: Find a way to use the underlying tools, i.e. lipo, to do this. -actions piecemeal archive -{ - "$(.LIBTOOL)" -static -o "$(<:T)" $(ARFLAGS) "$(>:T)" + "$(CONFIG_COMMAND)" $(USER_OPTIONS) -L"$(LINKPATH)" -o "$(<)" -single_module -dynamiclib -install_name "$(<[1]:D=)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(OPTIONS) } diff --git a/v2/tools/clang-linux.jam b/v2/tools/clang-linux.jam new file mode 100644 index 000000000..cee88b27a --- /dev/null +++ b/v2/tools/clang-linux.jam @@ -0,0 +1,177 @@ +# Copyright (c) 2003 Michael Stevens +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +import toolset ; +import feature ; +import toolset : flags ; + +import clang ; +import gcc ; +import common ; +import errors ; +import generators ; +import type ; +import numbers ; + +feature.extend-subfeature toolset clang : platform : linux ; + +toolset.inherit-generators clang-linux + clang linux : gcc : gcc.mingw.link gcc.mingw.link.dll ; +generators.override clang-linux.prebuilt : builtin.lib-generator ; +generators.override clang-linux.prebuilt : builtin.prebuilt ; +generators.override clang-linux.searched-lib-generator : searched-lib-generator ; + +# Override default do-nothing generators. +generators.override clang-linux.compile.c.pch : pch.default-c-pch-generator ; +generators.override clang-linux.compile.c++.pch : pch.default-cpp-pch-generator ; + +type.set-generated-target-suffix PCH : clang linux : pchi ; + +toolset.inherit-rules clang-linux : gcc ; +toolset.inherit-flags clang-linux : gcc + : off on full + space speed + off all on + ; + +if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] +{ + .debug-configuration = true ; +} + +# Initializes the clang-linux toolset +# version in optional +# name (default clang++) is used to invoke the specified clanglinux complier +# compile and link options allow you to specify addition command line options for each version +rule init ( version ? : command * : options * ) +{ + command = [ common.get-invocation-command clang-linux : clang++ + : $(command) ] ; + + # Determine the version + local command-string = $(command:J=" ") ; + if $(command) + { + version ?= [ MATCH "^([0-9.]+)" + : [ SHELL "$(command-string) -dumpversion" ] ] ; + } + + local condition = [ common.check-init-parameters clang-linux + : version $(version) ] ; + + common.handle-options clang-linux : $(condition) : $(command) : $(options) ; + + gcc.init-link-flags clang-linux gnu $(condition) ; + +} + +SPACE = " " ; + +flags clang-linux.compile OPTIONS ; +flags clang-linux.compile OPTIONS ; +# flags clang-linux.compile INCLUDES ; + +# Declare flags and action for compilation. +toolset.flags clang-linux.compile OPTIONS off : -O0 ; +toolset.flags clang-linux.compile OPTIONS speed : -O3 ; +toolset.flags clang-linux.compile OPTIONS space : -Os ; + +toolset.flags clang-linux.compile OPTIONS off : -fno-inline ; +toolset.flags clang-linux.compile OPTIONS on : -Wno-inline ; +toolset.flags clang-linux.compile OPTIONS full : -finline-functions -Wno-inline ; + +toolset.flags clang-linux.compile OPTIONS off : -w ; +toolset.flags clang-linux.compile OPTIONS on : -Wall ; +toolset.flags clang-linux.compile OPTIONS all : -Wall -pedantic ; +toolset.flags clang-linux.compile OPTIONS on : -Werror ; + +toolset.flags clang-linux.compile OPTIONS on : -g ; +toolset.flags clang-linux.compile OPTIONS on : -pg ; +toolset.flags clang-linux.compile OPTIONS off : -fno-rtti ; + +rule compile.c++ ( targets * : sources * : properties * ) +{ + gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; + DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; +} + +actions compile.c++ bind PCH_FILE +{ + "$(CONFIG_COMMAND)" -c -x c++ $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -use-pch"$(PCH_FILE)" -c -o "$(<)" "$(>)" +} + +rule compile.c ( targets * : sources * : properties * ) +{ + gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; + DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; +} + +actions compile.c bind PCH_FILE +{ + "$(CONFIG_COMMAND)" -c -x c $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -use-pch"$(PCH_FILE)" -c -o "$(<)" "$(>)" +} + +rule compile.c++.pch ( targets * : sources * : properties * ) +{ + gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; +} +# +# Compiling a pch first deletes any existing *.pchi file, as Clang's compiler +# won't over-write an existing pch: instead it creates filename$1.pchi, filename$2.pchi +# etc - which appear not to do anything except take up disk space :-( +# +actions compile.c++.pch +{ + rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -pch-create "$(<)" "$(>)" +} + +rule compile.c.pch ( targets * : sources * : properties * ) +{ + gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-fpic $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; +} + +actions compile.c.pch +{ + rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -pch-create "$(<)" "$(>)" +} + +rule link ( targets * : sources * : properties * ) +{ + gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; + SPACE on $(targets) = " " ; + JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; +} + +actions link bind LIBRARIES +{ + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,-R$(SPACE)-Wl,"$(RPATH)" -Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(OPTIONS) $(USER_OPTIONS) +} + +rule link.dll ( targets * : sources * : properties * ) +{ + gcc.setup-threading $(targets) : $(sources) : $(properties) ; + gcc.setup-address-model $(targets) : $(sources) : $(properties) ; + SPACE on $(targets) = " " ; + JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; +} + +# Differ from 'link' above only by -shared. +actions link.dll bind LIBRARIES +{ + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,-R$(SPACE)-Wl,"$(RPATH)" -o "$(<)" -Wl,-soname$(SPACE)-Wl,$(<[1]:D=) -shared "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) $(OPTIONS) $(USER_OPTIONS) +} + + + diff --git a/v2/tools/clang-unix.jam b/v2/tools/clang-unix.jam deleted file mode 100644 index 5c55faf84..000000000 --- a/v2/tools/clang-unix.jam +++ /dev/null @@ -1,990 +0,0 @@ -# Copyright 2001 David Abrahams. -# Copyright 2002-2006 Rene Rivera. -# Copyright 2002-2003 Vladimir Prus. -# Copyright (c) 2005 Reece H. Dunn. -# Copyright 2006 Ilya Sokolov. -# Copyright 2007 Roland Schwarz -# Copyright 2007 Boris Gubenko. -# -# 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) - -import "class" : new ; -import common ; -import errors ; -import feature ; -import generators ; -import os ; -import pch ; -import property ; -import property-set ; -import toolset ; -import type ; -import rc ; -import regex ; -import set ; -import unix ; - - -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} - - -feature.extend toolset : clang-unix ; -# feature.subfeature toolset clang : flavor : : optional ; - -toolset.inherit-generators clang-unix : unix : unix.link unix.link.dll ; -toolset.inherit-flags clang-unix : unix ; -toolset.inherit-rules clang-unix : unix ; - -generators.override clang-unix.prebuilt : builtin.prebuilt ; -generators.override clang-unix.searched-lib-generator : searched-lib-generator ; - -# Make clang-unix toolset object files use the "o" suffix on all platforms. -type.set-generated-target-suffix OBJ : clang-unix : o ; -type.set-generated-target-suffix OBJ : clang-unix windows : o ; -type.set-generated-target-suffix OBJ : clang-unix cygwin : o ; - -# Initializes the clang-unix toolset for the given version. If necessary, command may -# be used to specify where the compiler is located. The parameter 'options' is a -# space-delimited list of options, each one specified as -# option-value. Valid option names are: cxxflags, linkflags and -# linker-type. Accepted linker-type values are aix, darwin, gnu, hpux, osf or -# sun and the default value will be selected based on the current OS. -# Example: -# using clang-unix : 1.5 : : foo bar sun ; -# -rule init ( version ? : command * : options * ) -{ - # Information about the clang command... - # The command. - local command = [ common.get-invocation-command clang-unix : clang++ : $(command) ] ; - # The root directory of the tool install. - local root = [ feature.get-values : $(options) ] ; - # The bin directory where to find the command to execute. - local bin ; - # The flavor of compiler. - local flavor = [ feature.get-values : $(options) ] ; - # Autodetect the root and bin dir if not given. - if $(command) - { - bin ?= [ common.get-absolute-tool-path $(command[-1]) ] ; - root ?= $(bin:D) ; - } - # The 'command' variable can have multiple elements. When calling - # the SHELL builtin we need a single string. - local command-string = $(command:J=" ") ; - # Autodetect the version and flavor if not given. - if $(command) - { - local machine = [ MATCH "^([^ ]+)" - : [ SHELL "$(command-string) -dumpmachine" ] ] ; - version ?= [ MATCH "^([0-9.]+)" - : [ SHELL "$(command-string) -dumpversion" ] ] ; - switch $(machine:L) - { - case *mingw* : flavor ?= mingw ; - } - } - - local condition ; - if $(flavor) - { - condition = [ common.check-init-parameters clang-unix - : version $(version) - : flavor $(flavor) - ] ; - } - else - { - condition = [ common.check-init-parameters clang-unix - : version $(version) - ] ; - condition = $(condition) ; #/ ; - } - - common.handle-options clang-unix : $(condition) : $(command) : $(options) ; - - local linker = [ feature.get-values : $(options) ] ; - # The logic below should actually be keyed on - if ! $(linker) - { - if [ os.name ] = OSF - { - linker = osf ; - } - else if [ os.name ] = HPUX - { - linker = hpux ; - } - else if [ os.name ] = AIX - { - linker = aix ; - } - else if [ os.name ] = SOLARIS - { - linker = sun ; - } - else - { - linker = gnu ; - } - } - init-link-flags clang-unix $(linker) $(condition) ; - - - # If clang is installed in non-standard location, we'd need to add - # LD_LIBRARY_PATH when running programs created with it (for unit-test/run - # rules). - if $(command) - { - # On multilib 64-bit boxes, there are both 32-bit and 64-bit libraries - # and all must be added to LD_LIBRARY_PATH. The linker will pick the - # right onces. Note that we don't provide a clean way to build 32-bit - # binary with 64-bit compiler, but user can always pass -m32 manually. - local lib_path = $(root)/bin $(root)/lib $(root)/lib32 $(root)/lib64 ; - if $(.debug-configuration) - { - ECHO notice: using clang libraries :: $(condition) :: $(lib_path) ; - } - toolset.flags clang-unix.link RUN_PATH $(condition) : $(lib_path) ; - } - - # If it's not a system clang install we should adjust the various programs as - # needed to prefer using the install specific versions. This is essential - # for correct use of MinGW and for cross-compiling. - - local nl = " -" ; - - # - The archive builder. - local archiver = [ common.get-invocation-command clang-unix - : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : [ SHELL "$(command-string) -print-prog-name=ar" ] ] ] - : [ feature.get-values : $(options) ] - : $(bin) - : search-path ] ; - toolset.flags clang-unix.archive .AR $(condition) : $(archiver[1]) ; - if $(.debug-configuration) - { - ECHO notice: using clang-unix archiver :: $(condition) :: $(archiver[1]) ; - } - - # - Ranlib - local ranlib = [ common.get-invocation-command clang-unix - : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : [ SHELL "$(command-string) -print-prog-name=ranlib" ] ] ] - : [ feature.get-values : $(options) ] - : $(bin) - : search-path ] ; - toolset.flags clang-unix.archive .RANLIB $(condition) : $(ranlib[1]) ; - if $(.debug-configuration) - { - ECHO notice: using clang-unix ranlib :: $(condition) :: $(ranlib[1]) ; - } - - - # - The resource compiler. - local rc = - [ common.get-invocation-command-nodefault clang-unix - : windres : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; - local rc-type = - [ feature.get-values : $(options) ] ; - rc-type ?= windres ; - if ! $(rc) - { - # If we can't find an RC compiler we fallback to a null RC compiler that - # creates empty object files. This allows the same Jamfiles to work - # across the board. The null RC uses the assembler to create the empty - # objects, so configure that. - rc = [ common.get-invocation-command clang-unix : as : : $(bin) : search-path ] ; - rc-type = null ; - } - rc.configure $(rc) : $(condition) : $(rc-type) ; -} - -if [ os.name ] = NT -{ - # This causes single-line command invocation to not go through .bat files, - # thus avoiding command-line length limitations. - JAMSHELL = % ; -} - -generators.register-c-compiler clang-unix.compile.c++ : CPP : OBJ : clang-unix ; -generators.register-c-compiler clang-unix.compile.c : C : OBJ : clang-unix ; -generators.register-c-compiler clang-unix.compile.asm : ASM : OBJ : clang-unix ; - -# pch support - -# The compiler looks for a precompiled header in each directory just before it -# looks for the include file in that directory. The name searched for is the -# name specified in the #include directive with ".gch" suffix appended. The -# logic in clang-unix-pch-generator will make sure that BASE_PCH suffix is appended to -# full name of the header. - -type.set-generated-target-suffix PCH : clang-unix : gch ; - -# clang-specific pch generator. -class clang-unix-pch-generator : pch-generator -{ - import project ; - import property-set ; - import type ; - - rule run-pch ( project name ? : property-set : sources + ) - { - # Find the header in sources. Ignore any CPP sources. - local header ; - for local s in $(sources) - { - if [ type.is-derived [ $(s).type ] H ] - { - header = $(s) ; - } - } - - # Error handling: Base header file name should be the same as the base - # precompiled header name. - local header-name = [ $(header).name ] ; - local header-basename = $(header-name:B) ; - if $(header-basename) != $(name) - { - local location = [ $(project).project-module ] ; - errors.user-error "in" $(location)": pch target name `"$(name)"' should be the same as the base name of header file `"$(header-name)"'" ; - } - - local pch-file = [ generator.run $(project) $(name) : $(property-set) - : $(header) ] ; - - # return result of base class and pch-file property as usage-requirements - return - [ property-set.create $(pch-file) -Winvalid-pch ] - $(pch-file) - ; - } - - # Calls the base version specifying source's name as the name of the created - # target. As result, the PCH will be named whatever.hpp.gch, and not - # whatever.gch. - rule generated-targets ( sources + : property-set : project name ? ) - { - name = [ $(sources[1]).name ] ; - return [ generator.generated-targets $(sources) - : $(property-set) : $(project) $(name) ] ; - } -} - -# Note: the 'H' source type will catch both '.h' header and '.hpp' header. The -# latter have HPP type, but HPP type is derived from H. The type of compilation -# is determined entirely by the destination type. -generators.register [ new clang-unix-pch-generator clang-unix.compile.c.pch : H : C_PCH : on clang-unix ] ; -generators.register [ new clang-unix-pch-generator clang-unix.compile.c++.pch : H : CPP_PCH : on clang-unix ] ; - -# Override default do-nothing generators. -generators.override clang-unix.compile.c.pch : pch.default-c-pch-generator ; -generators.override clang-unix.compile.c++.pch : pch.default-cpp-pch-generator ; - -toolset.flags clang-unix.compile PCH_FILE on : ; - -# Declare flags and action for compilation. -toolset.flags clang-unix.compile OPTIONS off : -O0 ; -toolset.flags clang-unix.compile OPTIONS speed : -O3 ; -toolset.flags clang-unix.compile OPTIONS space : -Os ; - -toolset.flags clang-unix.compile OPTIONS off : -fno-inline ; -toolset.flags clang-unix.compile OPTIONS on : -Wno-inline ; -toolset.flags clang-unix.compile OPTIONS full : -finline-functions -Wno-inline ; - -toolset.flags clang-unix.compile OPTIONS off : -w ; -toolset.flags clang-unix.compile OPTIONS on : -Wall ; -toolset.flags clang-unix.compile OPTIONS all : -Wall -pedantic ; -toolset.flags clang-unix.compile OPTIONS on : -Werror ; - -toolset.flags clang-unix.compile OPTIONS on : -g ; -toolset.flags clang-unix.compile OPTIONS on : -pg ; -toolset.flags clang-unix.compile OPTIONS off : -fno-rtti ; - -rule setup-fpic ( targets * : sources * : properties * ) -{ - local link = [ feature.get-values link : $(properties) ] ; - if $(link) = shared - { - local target = [ feature.get-values target-os : $(properties) ] ; - - # This logic will add -fPIC for all compilations: - # - # lib a : a.cpp b ; - # obj b : b.cpp ; - # exe c : c.cpp a d ; - # obj d : d.cpp ; - # - # This all is fine, except that 'd' will be compiled with -fPIC even though - # it is not needed, as 'd' is used only in exe. However, it is hard to - # detect where a target is going to be used. Alternatively, we can set -fPIC - # only when main target type is LIB but than 'b' would be compiled without - # -fPIC which would lead to link errors on x86-64. So, compile everything - # with -fPIC. - # - # Yet another alternative would be to create a propagated - # feature and set it when building shared libraries, but that would be hard - # to implement and would increase the target path length even more. - - # On Windows, fPIC is default, specifying -fPIC explicitly leads to - # a warning. - if $(target) != cygwin && $(target) != windows - { - OPTIONS on $(targets) += -fPIC ; - } - } -} - -rule setup-address-model ( targets * : sources * : properties * ) -{ - local model = [ feature.get-values address-model : $(properties) ] ; - if $(model) - { - local option ; - local os = [ feature.get-values target-os : $(properties) ] ; - if $(os) = aix - { - if $(model) = 32 - { - option = -maix32 ; - } - else - { - option = -maix64 ; - } - } - else - { - if $(model) = 32 - { - option = -m32 ; - } - else if $(model) = 64 - { - option = -m64 ; - } - # For darwin, the model can be 32_64. darwin.jam will handle that - # on its own. - } - OPTIONS on $(targets) += $(option) ; - } -} - - -# FIXME: this should not use os.name. -if [ os.name ] != NT && [ os.name ] != OSF && [ os.name ] != HPUX && [ os.name ] != AIX -{ - # OSF does have an option called -soname but it does not seem to work as - # expected, therefore it has been disabled. - HAVE_SONAME = "" ; - SONAME_OPTION = -h ; -} - -# HPUX, for some reason, seem to use '+h', not '-h'. -if [ os.name ] = HPUX -{ - HAVE_SONAME = "" ; - SONAME_OPTION = +h ; -} - -toolset.flags clang-unix.compile USER_OPTIONS ; -toolset.flags clang-unix.compile.c++ USER_OPTIONS ; -toolset.flags clang-unix.compile DEFINES ; -toolset.flags clang-unix.compile INCLUDES ; -toolset.flags clang-unix.compile.c++ TEMPLATE_DEPTH ; - -rule compile.c++.pch ( targets * : sources * : properties * ) -{ - setup-threading $(targets) : $(sources) : $(properties) ; - setup-fpic $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; -} - -actions compile.c++.pch -{ - "$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" -} - -rule compile.c.pch ( targets * : sources * : properties * ) -{ - setup-threading $(targets) : $(sources) : $(properties) ; - setup-fpic $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; -} - -actions compile.c.pch -{ - "$(CONFIG_COMMAND)" -x c-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" -} - -rule compile.c++ ( targets * : sources * : properties * ) -{ - setup-threading $(targets) : $(sources) : $(properties) ; - setup-fpic $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; - - # Some extensions are compiled as C++ by default. For others, we need to - # pass -x c++. We could always pass -x c++ but distcc does not work with it. - if ! $(>:S) in .cc .cp .cxx .cpp .c++ .C - { - LANG on $(<) = "-x c++" ; - } - DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; - - # Here we want to raise the template-depth parameter value to something - # higher than the default value of 17. Note that we could do this using the - # feature.set-default rule but we do not want to set the default value for - # all toolsets as well. - # - # TODO: This 'modified default' has been inherited from some 'older Boost - # Build implementation' and has most likely been added to make some Boost - # library parts compile correctly. We should see what exactly prompted this - # and whether we can get around the problem more locally. - local template-depth = [ on $(<) return $(TEMPLATE_DEPTH) ] ; - if ! $(template-depth) - { - TEMPLATE_DEPTH on $(<) = 128 ; - } -} - -rule compile.c ( targets * : sources * : properties * ) -{ - setup-threading $(targets) : $(sources) : $(properties) ; - setup-fpic $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; - - # If we use the name clang++ then default file suffix -> language mapping does - # not work. So have to pass -x option. Maybe, we can work around this by - # allowing the user to specify both C and C++ compiler names. - #if $(>:S) != .c - #{ - LANG on $(<) = "-x c" ; - #} - DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ; -} - -actions compile.c++ bind PCH_FILE -{ - "$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<:W)" "$(>:W)" -} - -actions compile.c bind PCH_FILE -{ - "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)" -} - -rule compile.asm -{ - LANG on $(<) = "-x assembler-with-cpp" ; -} - -actions compile.asm -{ - "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" -} - -# The class which check that we don't try to use the static -# property while creating or using shared library, since it's not supported by -# clang/libc. -class clang-unix-linking-generator : unix-linking-generator -{ - rule run ( project name ? : property-set : sources + ) - { - # TODO: Replace this with the use of a target-os property. - local no-static-link = ; - if [ modules.peek : UNIX ] - { - switch [ modules.peek : JAMUNAME ] - { - case * : no-static-link = true ; - } - } - - local properties = [ $(property-set).raw ] ; - local reason ; - if $(no-static-link) && static in $(properties) - { - if shared in $(properties) - { - reason = - "On clang, DLL can't be build with 'static'." ; - } - else if [ type.is-derived $(self.target-types[1]) EXE ] - { - for local s in $(sources) - { - local type = [ $(s).type ] ; - if $(type) && [ type.is-derived $(type) SHARED_LIB ] - { - reason = - "On clang, using DLLS together with the" - "static options is not possible " ; - } - } - } - } - if $(reason) - { - ECHO warning: - $(reason) ; - ECHO warning: - "It is suggested to use 'static' together" - "with 'static'." ; - return ; - } - else - { - local generated-targets = [ unix-linking-generator.run $(project) - $(name) : $(property-set) : $(sources) ] ; - return $(generated-targets) ; - } - } -} - -# The set of permissible input types is different on mingw. -# So, define two sets of generators, with mingw generators -# selected when target-os=windows. - -local g ; -g = [ new clang-unix-linking-generator clang-unix.mingw.link - : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB - : EXE - : clang-unix windows ] ; -$(g).set-rule-name clang-unix.link ; -generators.register $(g) ; - -g = [ new clang-unix-linking-generator clang-unix.mingw.link.dll - : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB - : IMPORT_LIB SHARED_LIB - : clang-unix windows ] ; -$(g).set-rule-name clang-unix.link.dll ; -generators.register $(g) ; - -generators.register - [ new clang-unix-linking-generator clang-unix.link - : LIB OBJ - : EXE - : clang-unix ] ; -generators.register - [ new clang-unix-linking-generator clang-unix.link.dll - : LIB OBJ - : SHARED_LIB - : clang-unix ] ; - -generators.override clang-unix.mingw.link : clang-unix.link ; -generators.override clang-unix.mingw.link.dll : clang-unix.link.dll ; - -# Cygwin is similar to msvc and mingw in that it uses import libraries. -# While in simple cases, it can directly link to a shared library, -# it is believed to be slower, and not always possible. Define cygwin-specific -# generators here. - -g = [ new clang-unix-linking-generator clang-unix.cygwin.link - : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB - : EXE - : clang-unix cygwin ] ; -$(g).set-rule-name clang-unix.link ; -generators.register $(g) ; - -g = [ new clang-unix-linking-generator clang-unix.cygwin.link.dll - : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB - : IMPORT_LIB SHARED_LIB - : clang-unix cygwin ] ; -$(g).set-rule-name clang-unix.link.dll ; -generators.register $(g) ; - -generators.override clang-unix.cygwin.link : clang-unix.link ; -generators.override clang-unix.cygwin.link.dll : clang-unix.link.dll ; - -# Declare flags for linking. -# First, the common flags. -toolset.flags clang-unix.link OPTIONS on : -g ; -toolset.flags clang-unix.link OPTIONS on : -pg ; -toolset.flags clang-unix.link USER_OPTIONS ; -toolset.flags clang-unix.link LINKPATH ; -toolset.flags clang-unix.link FINDLIBS-ST ; -toolset.flags clang-unix.link FINDLIBS-SA ; -toolset.flags clang-unix.link LIBRARIES ; - -toolset.flags clang-unix.link.dll .IMPLIB-COMMAND windows : "-Wl,--out-implib," ; -toolset.flags clang-unix.link.dll .IMPLIB-COMMAND cygwin : "-Wl,--out-implib," ; - -# For static we made sure there are no dynamic libraries in the -# link. On HP-UX not all system libraries exist as archived libraries (for -# example, there is no libunwind.a), so, on this platform, the -static option -# cannot be specified. -if [ os.name ] != HPUX -{ - toolset.flags clang-unix.link OPTIONS static : -static ; -} - -# Now, the vendor specific flags. -# The parameter linker can be either aix, darwin, gnu, hpux, osf or sun. -rule init-link-flags ( toolset linker condition ) -{ - switch $(linker) - { - case aix : - { - # - # On AIX we *have* to use the native linker. - # - # Using -brtl, the AIX linker will look for libraries with both the .a - # and .so extensions, such as libfoo.a and libfoo.so. Without -brtl, the - # AIX linker looks only for libfoo.a. Note that libfoo.a is an archived - # file that may contain shared objects and is different from static libs - # as on Linux. - # - # The -bnoipath strips the prepending (relative) path of libraries from - # the loader section in the target library or executable. Hence, during - # load-time LIBPATH (identical to LD_LIBRARY_PATH) or a hard-coded - # -blibpath (*similar* to -lrpath/-lrpath-link) is searched. Without - # this option, the prepending (relative) path + library name is - # hard-coded in the loader section, causing *only* this path to be - # searched during load-time. Note that the AIX linker does not have an - # -soname equivalent, this is as close as it gets. - # - # The above options are definately for AIX 5.x, and most likely also for - # AIX 4.x and AIX 6.x. For details about the AIX linker see: - # http://download.boulder.ibm.com/ibmdl/pub/software/dw/aix/es-aix_ll.pdf - # - - toolset.flags $(toolset).link OPTIONS : -Wl,-brtl -Wl,-bnoipath - : unchecked ; - } - - case darwin : - { - # On Darwin, the -s option to ld does not work unless we pass -static, - # and passing -static unconditionally is a bad idea. So, don't pass -s. - # at all, darwin.jam will use separate 'strip' invocation. - toolset.flags $(toolset).link RPATH $(condition) : : unchecked ; - toolset.flags $(toolset).link RPATH_LINK $(condition) : : unchecked ; - } - - case gnu : - { - # Strip the binary when no debugging is needed. We use --strip-all flag - # as opposed to -s since icc (intel's compiler) is generally - # option-compatible with and inherits from the clang toolset, but does not - # support -s. - toolset.flags $(toolset).link OPTIONS $(condition)/on : -Wl,--strip-all : unchecked ; - toolset.flags $(toolset).link RPATH $(condition) : : unchecked ; - toolset.flags $(toolset).link RPATH_LINK $(condition) : : unchecked ; - toolset.flags $(toolset).link START-GROUP $(condition) : -Wl,--start-group : unchecked ; - toolset.flags $(toolset).link END-GROUP $(condition) : -Wl,--end-group : unchecked ; - - # gnu ld has the ability to change the search behaviour for libraries - # referenced by -l switch. These modifiers are -Bstatic and -Bdynamic - # and change search for -l switches that follow them. The following list - # shows the tried variants. - # The search stops at the first variant that has a match. - # *nix: -Bstatic -lxxx - # libxxx.a - # - # *nix: -Bdynamic -lxxx - # libxxx.so - # libxxx.a - # - # windows (mingw,cygwin) -Bstatic -lxxx - # libxxx.a - # xxx.lib - # - # windows (mingw,cygwin) -Bdynamic -lxxx - # libxxx.dll.a - # xxx.dll.a - # libxxx.a - # xxx.lib - # cygxxx.dll (*) - # libxxx.dll - # xxx.dll - # libxxx.a - # - # (*) This is for cygwin - # Please note that -Bstatic and -Bdynamic are not a guarantee that a - # static or dynamic lib indeed gets linked in. The switches only change - # search patterns! - - # On *nix mixing shared libs with static runtime is not a good idea. - toolset.flags $(toolset).link FINDLIBS-ST-PFX $(condition)/shared - : -Wl,-Bstatic : unchecked ; - toolset.flags $(toolset).link FINDLIBS-SA-PFX $(condition)/shared - : -Wl,-Bdynamic : unchecked ; - - # On windows allow mixing of static and dynamic libs with static - # runtime. - toolset.flags $(toolset).link FINDLIBS-ST-PFX $(condition)/static/windows - : -Wl,-Bstatic : unchecked ; - toolset.flags $(toolset).link FINDLIBS-SA-PFX $(condition)/static/windows - : -Wl,-Bdynamic : unchecked ; - toolset.flags $(toolset).link OPTIONS $(condition)/static/windows - : -Wl,-Bstatic : unchecked ; - } - - case hpux : - { - toolset.flags $(toolset).link OPTIONS $(condition)/on - : -Wl,-s : unchecked ; - toolset.flags $(toolset).link OPTIONS $(condition)/shared - : -fPIC : unchecked ; - } - - case osf : - { - # No --strip-all, just -s. - toolset.flags $(toolset).link OPTIONS $(condition)/on - : -Wl,-s : unchecked ; - toolset.flags $(toolset).link RPATH $(condition) : - : unchecked ; - # This does not supports -R. - toolset.flags $(toolset).link RPATH_OPTION $(condition) : -rpath - : unchecked ; - # -rpath-link is not supported at all. - } - - case sun : - { - toolset.flags $(toolset).link OPTIONS $(condition)/on - : -Wl,-s : unchecked ; - toolset.flags $(toolset).link RPATH $(condition) : - : unchecked ; - # Solaris linker does not have a separate -rpath-link, but allows to use - # -L for the same purpose. - toolset.flags $(toolset).link LINKPATH $(condition) : - : unchecked ; - - # This permits shared libraries with non-PIC code on Solaris. - # VP, 2004/09/07: Now that we have -fPIC hardcode in link.dll, the - # following is not needed. Whether -fPIC should be hardcoded, is a - # separate question. - # AH, 2004/10/16: it is still necessary because some tests link against - # static libraries that were compiled without PIC. - toolset.flags $(toolset).link OPTIONS $(condition)/shared - : -mimpure-text : unchecked ; - } - - case * : - { - errors.user-error - "$(toolset) initialization: invalid linker '$(linker)'" : - "The value '$(linker)' specified for is not recognized." : - "Possible values are 'aix', 'darwin', 'gnu', 'hpux', 'osf' or 'sun'" ; - } - } -} - -# Enclose the RPATH variable on 'targets' in (double) quotes, -# unless it's already enclosed in single quotes. -# This special casing is done because it's common to pass -# '$ORIGIN' to linker -- and it has to have single quotes -# to prevent expansion by shell -- and if we add double -# quotes then preventing properties of single quotes disappear. -rule quote-rpath ( targets * ) -{ - local r = [ on $(targets[1]) return $(RPATH) ] ; - if ! [ MATCH "('.*')" : $(r) ] - { - r = "\"$(r)\"" ; - } - RPATH on $(targets) = $(r) ; -} - -# Declare actions for linking. -rule link ( targets * : sources * : properties * ) -{ - setup-threading $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; - SPACE on $(targets) = " " ; - # Serialize execution of the 'link' action, since running N links in - # parallel is just slower. For now, serialize only clang links, it might be a - # good idea to serialize all links. - JAM_SEMAPHORE on $(targets) = clang-unix-link-semaphore ; - quote-rpath $(targets) ; -} - -actions link bind LIBRARIES -{ - "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,$(RPATH) -Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" $(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) - -} - -# Default value. Mostly for the sake of intel-linux that inherits from clang, but -# does not have the same logic to set the .AR variable. We can put the same -# logic in intel-linux, but that's hardly worth the trouble as on Linux, 'ar' is -# always available. -.AR = ar ; -.RANLIB = ranlib ; - -toolset.flags clang-unix.archive AROPTIONS ; - -rule archive ( targets * : sources * : properties * ) -{ - # Always remove archive and start again. Here is the rationale from - # - # Andre Hentz: - # - # I had a file, say a1.c, that was included into liba.a. I moved a1.c to - # a2.c, updated my Jamfiles and rebuilt. My program was crashing with absurd - # errors. After some debugging I traced it back to the fact that a1.o was - # *still* in liba.a - # - # Rene Rivera: - # - # Originally removing the archive was done by splicing an RM onto the - # archive action. That makes archives fail to build on NT when they have - # many files because it will no longer execute the action directly and blow - # the line length limit. Instead we remove the file in a different action, - # just before building the archive. - # - local clean.a = $(targets[1])(clean) ; - TEMPORARY $(clean.a) ; - NOCARE $(clean.a) ; - LOCATE on $(clean.a) = [ on $(targets[1]) return $(LOCATE) ] ; - DEPENDS $(clean.a) : $(sources) ; - DEPENDS $(targets) : $(clean.a) ; - common.RmTemps $(clean.a) : $(targets) ; -} - -# Declare action for creating static libraries. -# The letter 'r' means to add files to the archive with replacement. Since we -# remove archive, we don't care about replacement, but there's no option "add -# without replacement". -# The letter 'c' suppresses the warning in case the archive does not exists yet. -# That warning is produced only on some platforms, for whatever reasons. -actions piecemeal archive -{ - "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" - "$(.RANLIB)" "$(<)" -} - -rule link.dll ( targets * : sources * : properties * ) -{ - setup-threading $(targets) : $(sources) : $(properties) ; - setup-address-model $(targets) : $(sources) : $(properties) ; - SPACE on $(targets) = " " ; - JAM_SEMAPHORE on $(targets) = clang-unix-link-semaphore ; - quote-rpath $(targets) ; -} - -# Differs from 'link' above only by -shared. -actions link.dll bind LIBRARIES -{ - "$(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)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) -} - -rule setup-threading ( targets * : sources * : properties * ) -{ - local threading = [ feature.get-values threading : $(properties) ] ; - if $(threading) = multi - { - local target = [ feature.get-values target-os : $(properties) ] ; - local option ; - local libs ; - - switch $(target) - { - case windows : - { - option = -mthreads ; - } - case cygwin : - { - option = -mthreads ; - } - case solaris : - { - option = -pthreads ; - libs = rt ; - } - case beos : - { - # BeOS has no threading options, so do not set anything here. - } - case *bsd : - { - option = -pthread ; - # There is no -lrt on BSD. - } - case sgi : - { - # clang on IRIX does not support multi-threading so do not set anything - # here. - } - case darwin : - { - # Darwin has no threading options so do not set anything here. - } - case * : - { - option = -pthread ; - libs = rt ; - } - } - - if $(option) - { - OPTIONS on $(targets) += $(option) ; - } - if $(libs) - { - FINDLIBS-SA on $(targets) += $(libs) ; - } - } -} - -local rule cpu-flags ( toolset variable : architecture : instruction-set + : values + : default ? ) -{ - if $(default) - { - toolset.flags $(toolset) $(variable) - $(architecture)/ - : $(values) ; - } - toolset.flags $(toolset) $(variable) - /$(instruction-set) - $(architecture)/$(instruction-set) - : $(values) ; -} - -# Set architecture/instruction-set options. -# -# x86 and compatible -cpu-flags clang-unix OPTIONS : x86 : native : -march=native : default ; -cpu-flags clang-unix OPTIONS : x86 : i386 : -march=i386 ; -cpu-flags clang-unix OPTIONS : x86 : i486 : -march=i486 ; -cpu-flags clang-unix OPTIONS : x86 : i586 : -march=i586 ; -cpu-flags clang-unix OPTIONS : x86 : i686 : -march=i686 ; -cpu-flags clang-unix OPTIONS : x86 : pentium : -march=pentium ; -cpu-flags clang-unix OPTIONS : x86 : pentium-mmx : -march=pentium-mmx ; -cpu-flags clang-unix OPTIONS : x86 : pentiumpro : -march=pentiumpro ; -cpu-flags clang-unix OPTIONS : x86 : pentium2 : -march=pentium2 ; -cpu-flags clang-unix OPTIONS : x86 : pentium3 : -march=pentium3 ; -cpu-flags clang-unix OPTIONS : x86 : pentium3m : -march=pentium3m ; -cpu-flags clang-unix OPTIONS : x86 : pentium-m : -march=pentium-m ; -cpu-flags clang-unix OPTIONS : x86 : pentium4 : -march=pentium4 ; -cpu-flags clang-unix OPTIONS : x86 : pentium4m : -march=pentium4m ; -cpu-flags clang-unix OPTIONS : x86 : prescott : -march=prescott ; -cpu-flags clang-unix OPTIONS : x86 : nocona : -march=nocona ; -cpu-flags clang-unix OPTIONS : x86 : core2 : -march=core2 ; -cpu-flags clang-unix OPTIONS : x86 : k6 : -march=k6 ; -cpu-flags clang-unix OPTIONS : x86 : k6-2 : -march=k6-2 ; -cpu-flags clang-unix OPTIONS : x86 : k6-3 : -march=k6-3 ; -cpu-flags clang-unix OPTIONS : x86 : athlon : -march=athlon ; -cpu-flags clang-unix OPTIONS : x86 : athlon-tbird : -march=athlon-tbird ; -cpu-flags clang-unix OPTIONS : x86 : athlon-4 : -march=athlon-4 ; -cpu-flags clang-unix OPTIONS : x86 : athlon-xp : -march=athlon-xp ; -cpu-flags clang-unix OPTIONS : x86 : athlon-mp : -march=athlon-mp ; -## -cpu-flags clang-unix OPTIONS : x86 : k8 : -march=k8 ; -cpu-flags clang-unix OPTIONS : x86 : opteron : -march=opteron ; -cpu-flags clang-unix OPTIONS : x86 : athlon64 : -march=athlon64 ; -cpu-flags clang-unix OPTIONS : x86 : athlon-fx : -march=athlon-fx ; -cpu-flags clang-unix OPTIONS : x86 : winchip-c6 : -march=winchip-c6 ; -cpu-flags clang-unix OPTIONS : x86 : winchip2 : -march=winchip2 ; -cpu-flags clang-unix OPTIONS : x86 : c3 : -march=c3 ; -cpu-flags clang-unix OPTIONS : x86 : c3-2 : -march=c3-2 ; - diff --git a/v2/tools/clang.jam b/v2/tools/clang.jam index 818ba8778..e0ac9a553 100644 --- a/v2/tools/clang.jam +++ b/v2/tools/clang.jam @@ -21,7 +21,7 @@ rule init ( * : * ) } else { - toolset.using clang-unix : + toolset.using clang-linux : $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; } } From 2844aa9b1dc270cbe1a82529518274af3f624bca Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 12 May 2010 04:32:43 +0000 Subject: [PATCH 187/198] Make the test reporting system handle the bcp tests properly [SVN r61925] --- v2/tools/testing.jam | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/v2/tools/testing.jam b/v2/tools/testing.jam index 25398ee72..c42075b78 100644 --- a/v2/tools/testing.jam +++ b/v2/tools/testing.jam @@ -205,12 +205,12 @@ rule dump-tests local rule get-library-name ( path ) { # Path is in normalized form, so all slashes are forward. - local match1 = [ MATCH /libs/(.*)/(test|example) : $(path) ] ; - local match2 = [ MATCH /libs/(.*)$ : $(path) ] ; + local match1 = [ MATCH /(tools|libs)/(.*)/(test|example) : $(path) ] ; + local match2 = [ MATCH /(tools|libs)/(.*)$ : $(path) ] ; local match3 = [ MATCH (/status$) : $(path) ] ; - if $(match1) { return $(match1[0]) ; } - else if $(match2) { return $(match2[0]) ; } + if $(match1) { return $(match1[2]) ; } + else if $(match2) { return $(match2[2]) ; } else if $(match3) { return "" ; } else if --dump-tests in [ modules.peek : ARGV ] { From c32f395396fe3ea50648d5330875056e8a1e442d Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Wed, 12 May 2010 06:35:30 +0000 Subject: [PATCH 188/198] fix clang/cygwin bbv2 problem (with Doug's blessing) [SVN r61928] --- v2/tools/clang-linux.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/clang-linux.jam b/v2/tools/clang-linux.jam index cee88b27a..ff477d4b6 100644 --- a/v2/tools/clang-linux.jam +++ b/v2/tools/clang-linux.jam @@ -19,7 +19,7 @@ import numbers ; feature.extend-subfeature toolset clang : platform : linux ; toolset.inherit-generators clang-linux - clang linux : gcc : gcc.mingw.link gcc.mingw.link.dll ; + clang linux : gcc : gcc.mingw.link gcc.mingw.link.dll gcc.cygwin.link gcc.cygwin.link.dll ; generators.override clang-linux.prebuilt : builtin.lib-generator ; generators.override clang-linux.prebuilt : builtin.prebuilt ; generators.override clang-linux.searched-lib-generator : searched-lib-generator ; From 71b989d78166c9629042850fb4b14a1d6ad5b8c8 Mon Sep 17 00:00:00 2001 From: "K. Noel Belcourt" Date: Fri, 14 May 2010 04:06:03 +0000 Subject: [PATCH 189/198] Remove non-blocking on child's pipe file descriptors. Dan Gohman asked that we not force child's pipe file descriptors to non-blocking so this commit removes those fcntls calls. I tested this change on Darwin, Linux and Solaris and saw no adverse impact to nightly build behavior. [SVN r61961] --- historic/jam/src/execunix.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/historic/jam/src/execunix.c b/historic/jam/src/execunix.c index 90736baf4..effd27138 100644 --- a/historic/jam/src/execunix.c +++ b/historic/jam/src/execunix.c @@ -190,13 +190,9 @@ void exec_cmd { if ( pipe( out ) < 0 ) exit( EXITBAD ); - fcntl( out[0], F_SETFL, O_NONBLOCK ); - fcntl( out[1], F_SETFL, O_NONBLOCK ); if ( pipe( err ) < 0 ) exit( EXITBAD ); - fcntl( err[0], F_SETFL, O_NONBLOCK ); - fcntl( err[1], F_SETFL, O_NONBLOCK ); } /* Start the command */ From d9f30df45d0b5a09adee9ac20510267841580350 Mon Sep 17 00:00:00 2001 From: "K. Noel Belcourt" Date: Fri, 14 May 2010 19:37:36 +0000 Subject: [PATCH 190/198] Serialize running of MPI tests to minimize overloading systems. Several in house complaints necessitated solution to running parallel Boost tests and MPI tests. Best short term option seems to be to serialize MPI tests using Boost.Build jam semaphore capability. [SVN r61970] --- v2/tools/mpi.jam | 3 +++ 1 file changed, 3 insertions(+) diff --git a/v2/tools/mpi.jam b/v2/tools/mpi.jam index 38012474e..f645d6ae6 100644 --- a/v2/tools/mpi.jam +++ b/v2/tools/mpi.jam @@ -493,6 +493,9 @@ rule capture-output ( target : sources * : properties * ) local num_processes = [ property.select : $(properties) ] ; num_processes = $(num_processes:G=) ; + # serialize the MPI tests to avoid overloading systems + JAM_SEMAPHORE on $(target) = mpi-run-semaphore ; + # We launch MPI processes using the "mpirun" equivalent specified by the user. LAUNCHER on $(target) = [ on $(target) return $(.mpirun) $(.mpirun_flags) $(num_processes) ] ; From 6f60fe9000009f907548d922a430a06caee933f6 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 25 May 2010 18:28:53 +0000 Subject: [PATCH 191/198] Better autoconfiguration for BoostBook [SVN r62210] --- v2/tools/boostbook-config.jam | 52 +------- v2/tools/boostbook.jam | 216 +++++++++++++++++++++++++--------- 2 files changed, 168 insertions(+), 100 deletions(-) diff --git a/v2/tools/boostbook-config.jam b/v2/tools/boostbook-config.jam index 0b91705f4..6e3f3ddc1 100644 --- a/v2/tools/boostbook-config.jam +++ b/v2/tools/boostbook-config.jam @@ -3,53 +3,11 @@ #~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) # Automatic configuration for BoostBook tools. To use, just import this module. +# +# This module is deprecated. +# using boostbook ; +# with no arguments now suffices. -import os ; import toolset : using ; -local docbook_xsl-path = [ modules.peek : DOCBOOK_XSL_DIR ] ; -local docbook_dtd-path = [ modules.peek : DOCBOOK_DTD_DIR ] ; -local boostbook-path = [ modules.peek : BOOSTBOOK_DIR ] ; -if [ os.name ] = NT -{ - # If installed by the Boost installer. - local boost-dir = ; - for local R in snapshot cvs 1.33.0 - { - boost-dir += [ W32_GETREG - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Boost.org\\$(R)" - : "InstallRoot" ] ; - } - # Plausible locations. - local root = [ PWD ] ; - while $(root) != $(root:D) { root = $(root:D) ; } - docbook_xsl-path ?= [ GLOB "$(boost-dir)\\share" "$(root)Boost\\share" : docbook-xsl* ] ; - docbook_dtd-path ?= [ GLOB "$(boost-dir)\\share" "$(root)Boost\\share" : docbook-xml* ] ; - boostbook-path ?= [ GLOB "$(boost-dir)\\share" "$(root)Boost\\share" : boostbook* ] ; - - docbook_xsl-path = $(docbook_xsl-path[1]) ; - docbook_dtd-path = $(docbook_dtd-path[1]) ; - boostbook-path = $(boostbook-path[1]) ; -} -else -{ - # Plausible locations. - docbook_xsl-path ?= [ GLOB "/usr/local/share" "/usr/share" "/opt/share" : docbook-xsl* ] ; - docbook_dtd-path ?= [ GLOB "/usr/local/share" "/usr/share" "/opt/share" : docbook-xml* ] ; - boostbook-path ?= [ GLOB "/usr/local/share" "/usr/share" "/opt/share" : boostbook* ] ; - # Ubuntu Linux - docbook_xsl-path ?= [ GLOB "/usr/share/xml/docbook/stylesheet" : nwalsh ] ; - docbook_dtd-path ?= [ GLOB "/usr/share/xml/docbook/schema/dtd" : 4.3 4.2 ] ; - - docbook_xsl-path = $(docbook_xsl-path[1]) ; - docbook_dtd-path = $(docbook_dtd-path[1]) ; - boostbook-path = $(boostbook-path[1]) ; -} -if $(docbook_xsl-path) && $(docbook_dtd-path) -{ - if --debug-configuration in [ modules.peek : ARGV ] - { - ECHO "notice:" using boostbook ":" $(docbook_xsl-path) ":" $(docbook_dtd-path) ":" $(boostbook-path) ; - } - using boostbook : $(docbook_xsl-path) : $(docbook_dtd-path) : $(boostbook-path) ; -} +using boostbook ; diff --git a/v2/tools/boostbook.jam b/v2/tools/boostbook.jam index 92296050a..237ad952e 100644 --- a/v2/tools/boostbook.jam +++ b/v2/tools/boostbook.jam @@ -22,6 +22,7 @@ import regex ; import scanner ; import sequence ; import make ; +import os ; import type ; import modules path project ; import build-system ; @@ -56,71 +57,26 @@ type.register BOOSTBOOK_MAIN ; # Initialize BoostBook support. The parameters are: # docbook-xsl-dir: The DocBook XSL stylesheet directory. If not provided, -# we use DOCBOOK_XSL_DIR from the environment (if available). Otherwise, -# we let the XML processor load the stylesheets remotely. +# we use DOCBOOK_XSL_DIR from the environment (if available) or look in +# standard locations. Otherwise, we let the XML processor load the +# stylesheets remotely. # # docbook-dtd-dir: The DocBook DTD directory. If not provided, we use -# DOCBOOK_DTD_DIR From the environment (if available). Otherwise, we let -# the XML processor load the DTD remotely. +# DOCBOOK_DTD_DIR From the environment (if available) or look in +# standard locations. Otherwise, we let the XML processor load the +# DTD remotely. # # boost-book-dir: The BoostBook directory with the DTD and XSL subdirs. # rule init ( docbook-xsl-dir ? : docbook-dtd-dir ? : boostbook-dir ? ) { - docbook-xsl-dir ?= [ modules.peek : DOCBOOK_XSL_DIR ] ; - - docbook-dtd-dir ?= [ modules.peek : DOCBOOK_DTD_DIR ] ; - if ! $(.initialized) { .initialized = true ; - if $(docbook-xsl-dir) - { - .docbook-xsl-dir = [ path.make $(docbook-xsl-dir) ] ; - } - if $(docbook-dtd-dir) - { - .docbook-dtd-dir = [ path.make $(docbook-dtd-dir) ] ; - } - - local boost-root = [ modules.peek : BOOST_ROOT ] ; - if $(boost-root) - { - boost-root = [ path.make $(boost-root) ] ; - } - local boost-build-root = [ build-system.location ] ; - local search-dirs = $(boostbook-dir) [ path.join $(boost-root) tools boostbook ] - $(boost-build-root)/../../boostbook ; - - - if --debug-configuration in [ modules.peek : ARGV ] - { - ECHO "notice: Boost.Book: searching XSLS/DTD in" ; - ECHO "notice:" [ sequence.transform path.native : $(search-dirs) ] ; - } - local boostbook-xsl-dir ; - for local dir in $(search-dirs) { - boostbook-xsl-dir += [ path.glob $(dir) : xsl ] ; - } - local boostbook-dtd-dir ; - for local dir in $(search-dirs) { - boostbook-dtd-dir += [ path.glob $(dir) : dtd ] ; - } - .boostbook-xsl-dir = $(boostbook-xsl-dir[1]) ; - .boostbook-dtd-dir = $(boostbook-dtd-dir[1]) ; - - - if ! $(.boostbook-xsl-dir) || ! $(.boostbook-dtd-dir) - { - errors.warning - "couldn't find BoostBook xsl or dtd directories;" - : please set \"BOOST_ROOT\" variable to the root directory of - your boost installation. Searched in: - : $(search-dirs:J=" -") ; - } + find-tools $(docbook-xsl-dir) : $(docbook-dtd-dir) : $(boostbook-dir) ; + # Register generators only if we've were called via "using boostbook ; " generators.register-standard boostbook.dtdxml-to-boostbook : DTDXML : XML ; generators.register-standard boostbook.boostbook-to-docbook : XML : DOCBOOK ; @@ -137,6 +93,160 @@ rule init ( docbook-xsl-dir ? : docbook-dtd-dir ? : boostbook-dir ? ) } } +rule find-boost-in-registry ( keys * ) +{ + local boost-root = ; + for local R in $(keys) + { + local installed-boost = [ W32_GETREG + "HKEY_LOCAL_MACHINE\\SOFTWARE\\$(R)" + : "InstallRoot" ] ; + if $(installed-boost) + { + boost-root += [ path.make $(installed-boost) ] ; + } + } + return $(boost-root) ; +} + +rule find-tools ( docbook-xsl-dir ? : docbook-dtd-dir ? : boostbook-dir ? ) +{ + docbook-xsl-dir ?= [ modules.peek : DOCBOOK_XSL_DIR ] ; + docbook-dtd-dir ?= [ modules.peek : DOCBOOK_DTD_DIR ] ; + boostbook-dir ?= [ modules.peek : BOOSTBOOK_DIR ] ; + + # Look for the boostbook stylesheets relative to BOOST_ROOT + # and Boost.Build. + local boost-build-root = [ path.make [ build-system.location ] ] ; + local boostbook-search-dirs = [ path.join $(boost-build-root) .. .. ] ; + + local boost-root = [ modules.peek : BOOST_ROOT ] ; + if $(boost-root) + { + boostbook-search-dirs += [ path.join [ path.make $(boost-root) ] tools ] ; + } + boostbook-dir ?= [ path.glob $(boostbook-search-dirs) : boostbook* ] ; + + # Try to find the tools in platform specific locations + if [ os.name ] = NT + { + # If installed by the Boost installer. + local boost-root = ; + + local boost-installer-versions = snapshot cvs 1.33.0 ; + local boost-consulting-installer-versions = 1.33.1 1.34.0 1.34.1 ; + local boostpro-installer-versions = + 1.35.0 1.36.0 1.37.0 1.38.0 1.39.0 1.40.0 1.41.0 1.42.0 + 1.43.0 1.44.0 1.45.0 1.46.0 1.47.0 1.48.0 1.49.0 1.50.0 ; + + local old-installer-root = [ find-boost-in-registry Boost.org\\$(boost-installer-versions) ] ; + + # Make sure that the most recent version is searched for first + boost-root += [ sequence.reverse + [ find-boost-in-registry + Boost-Consulting.com\\$(boost-consulting-installer-versions) + boostpro.com\\$(boostpro-installer-versions) ] ] ; + + # Plausible locations. + local root = [ PWD ] ; + while $(root) != $(root:D) { root = $(root:D) ; } + root = [ path.make $(root) ] ; + local search-dirs = ; + local docbook-search-dirs = ; + for local p in $(boost-root) { + search-dirs += [ path.join $(p) tools ] ; + } + for local p in $(old-installer-root) + { + search-dirs += [ path.join $(p) share ] ; + docbook-search-dirs += [ path.join $(p) share ] ; + } + search-dirs += [ path.join $(root) Boost tools ] ; + search-dirs += [ path.join $(root) Boost share ] ; + docbook-search-dirs += [ path.join $(root) Boost share ] ; + + docbook-xsl-dir ?= [ path.glob $(docbook-search-dirs) : docbook-xsl* ] ; + docbook-dtd-dir ?= [ path.glob $(docbook-search-dirs) : docbook-xml* ] ; + boostbook-dir ?= [ path.glob $(search-dirs) : boostbook* ] ; + } + else + { + # Plausible locations. + + local share = /usr/local/share /usr/share /opt/share ; + local dtd-versions = 4.2 ; + + docbook-xsl-dir ?= [ path.glob $(share) : docbook-xsl* ] ; + docbook-xsl-dir ?= [ path.glob $(share)/sgml/docbook : xsl-stylesheets ] ; + docbook-xsl-dir ?= [ path.glob $(share)/xsl : docbook* ] ; + + docbook-dtd-dir ?= [ path.glob $(share) : docbook-xml* ] ; + docbook-dtd-dir ?= [ path.glob $(share)/sgml/docbook : xml-dtd-$(dtd-versions)* ] ; + docbook-dtd-dir ?= [ path.glob $(share)/xml/docbook : $(dtd-versions) ] ; + + boostbook-dir ?= [ path.glob $(share) : boostbook* ] ; + + # Ubuntu Linux + docbook-xsl-dir ?= [ path.glob /usr/share/xml/docbook/stylesheet : nwalsh ] ; + docbook-dtd-dir ?= [ path.glob /usr/share/xml/docbook/schema/dtd : $(dtd-versions) ] ; + } + + if $(docbook-xsl-dir) + { + .docbook-xsl-dir = [ path.make $(docbook-xsl-dir[1]) ] ; + } + if $(docbook-dtd-dir) + { + .docbook-dtd-dir = [ path.make $(docbook-dtd-dir[1]) ] ; + } + + if --debug-configuration in [ modules.peek : ARGV ] + { + ECHO "notice: Boost.Book: searching XSL/DTD in" ; + ECHO "notice:" [ sequence.transform path.native : $(boostbook-dir) ] ; + } + local boostbook-xsl-dir ; + for local dir in $(boostbook-dir) { + boostbook-xsl-dir += [ path.glob $(dir) : xsl ] ; + } + local boostbook-dtd-dir ; + for local dir in $(boostbook-dir) { + boostbook-dtd-dir += [ path.glob $(dir) : dtd ] ; + } + .boostbook-xsl-dir = $(boostbook-xsl-dir[1]) ; + .boostbook-dtd-dir = $(boostbook-dtd-dir[1]) ; + + if --debug-configuration in [ modules.peek : ARGV ] + { + if $(.docbook-xsl-dir) + { + ECHO "notice: Boost.Book: found docbook XSL stylesheets in:" [ path.native $(.docbook-xsl-dir) ] ; + } + if $(.docbook-dtd-dir) + { + ECHO "notice: Boost.Book: found docbook DTD in:" [ path.native $(.docbook-dtd-dir) ] ; + } + if $(.boostbook-xsl-dir) + { + ECHO "notice: Boost.Book: found boostbook XSL stylesheets in:" [ path.native $(.boostbook-xsl-dir) ] ; + } + if $(.boostbook-dtd-dir) + { + ECHO "notice: Boost.Book: found boostbook DTD in:" [ path.native $(.boostbook-dtd-dir) ] ; + } + } + + if ! $(.boostbook-xsl-dir) || ! $(.boostbook-dtd-dir) + { + errors.warning + "couldn't find BoostBook xsl or dtd directories;" + : please set \"BOOST_ROOT\" variable to the root directory of + your boost installation. Searched in: + : $(boostbook-dir:J=" +") ; + } +} + rule xsl-dir { return $(.boostbook-xsl-dir) ; From b1f7e8dcc92ef069619d5dba6e63a76bc6eff9a1 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 25 May 2010 18:43:18 +0000 Subject: [PATCH 192/198] Always use forward slashes in arguments to xsltproc. The windows xsltproc can handle forward slashes, but the cygwin version chokes on backslashes [SVN r62212] --- v2/tools/xsltproc.jam | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/tools/xsltproc.jam b/v2/tools/xsltproc.jam index e0be7561c..7a150f8f0 100644 --- a/v2/tools/xsltproc.jam +++ b/v2/tools/xsltproc.jam @@ -87,7 +87,7 @@ local rule .xsltproc ( target : source stylesheet : properties * : dirname ? : a for local catalog in [ feature.get-values : $(properties) ] { - CATALOG = [ common.variable-setting-command XML_CATALOG_FILES : $(catalog) ] ; + CATALOG = [ common.variable-setting-command XML_CATALOG_FILES : $(catalog:T) ] ; } $(action) $(target) : $(source) ; @@ -108,11 +108,11 @@ rule xslt-dir ( target : source stylesheet : properties * : dirname ) actions xslt-xsltproc bind STYLESHEET { - $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<)" "$(STYLESHEET)" "$(>)" + $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<)" "$(STYLESHEET:T)" "$(>:T)" } actions xslt-xsltproc-dir bind STYLESHEET { - $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<:D)/" "$(STYLESHEET)" "$(>)" + $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<:D)/" "$(STYLESHEET:T)" "$(>:T)" } From eb163585c4ab63812a93e650a322c8a507b8c307 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 25 May 2010 21:15:18 +0000 Subject: [PATCH 193/198] Look for docbook in /opt/local/share since that's the default installation directory for macports [SVN r62216] --- v2/tools/boostbook.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/boostbook.jam b/v2/tools/boostbook.jam index 237ad952e..9a8c6b019 100644 --- a/v2/tools/boostbook.jam +++ b/v2/tools/boostbook.jam @@ -173,7 +173,7 @@ rule find-tools ( docbook-xsl-dir ? : docbook-dtd-dir ? : boostbook-dir ? ) { # Plausible locations. - local share = /usr/local/share /usr/share /opt/share ; + local share = /usr/local/share /usr/share /opt/share /opt/local/share ; local dtd-versions = 4.2 ; docbook-xsl-dir ?= [ path.glob $(share) : docbook-xsl* ] ; From fbcc338aa4667c39b915decae4077ca14e5d4daa Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 26 May 2010 00:31:42 +0000 Subject: [PATCH 194/198] Try to adjust the paths in boostbook_catalog.xml to be correct, whether we're using a windows or cygwin build of xsltproc [SVN r62221] --- v2/tools/boostbook.jam | 27 ++++++++++++++++++++++++--- v2/tools/xsltproc.jam | 30 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/v2/tools/boostbook.jam b/v2/tools/boostbook.jam index 9a8c6b019..90abf410c 100644 --- a/v2/tools/boostbook.jam +++ b/v2/tools/boostbook.jam @@ -315,6 +315,27 @@ rule docbook-to-fo ( target : source : properties * ) xslt $(target) : $(source) $(stylesheet) : $(properties) ; } +rule format-catalog-path ( path ) +{ + local result = $(path) ; + if [ xsltproc.is-cygwin ] + { + if [ os.name ] = NT + { + drive = [ MATCH ^/(.):(.*)$ : $(path) ] ; + result = /cygdrive/$(drive[1])$(drive[2]) ; + } + } + else + { + if [ os.name ] = CYGWIN + { + result = [ path.make $(path:W) ] ; + } + } + return [ regex.replace $(result) " " "%20" ] ; +} + rule generate-xml-catalog ( target : sources * : properties * ) { print.output $(target) ; @@ -323,7 +344,7 @@ rule generate-xml-catalog ( target : sources * : properties * ) local boostbook-dtd-dir = [ boostbook.dtd-dir ] ; if $(boostbook-dtd-dir) { - boostbook-dtd-dir = [ regex.replace $(boostbook-dtd-dir) " " "%20" ] ; + boostbook-dtd-dir = [ format-catalog-path $(boostbook-dtd-dir) ] ; } print.text @@ -348,7 +369,7 @@ rule generate-xml-catalog ( target : sources * : properties * ) } else { - docbook-xsl-dir = [ regex.replace $(docbook-xsl-dir) " " "%20" ] ; + docbook-xsl-dir = [ format-catalog-path $(docbook-xsl-dir) ] ; print.text " " ; } @@ -365,7 +386,7 @@ rule generate-xml-catalog ( target : sources * : properties * ) } else { - docbook-dtd-dir = [ regex.replace $(docbook-dtd-dir) " " "%20" ] ; + docbook-dtd-dir = [ format-catalog-path $(docbook-dtd-dir) ] ; print.text " " ; } diff --git a/v2/tools/xsltproc.jam b/v2/tools/xsltproc.jam index 7a150f8f0..96d47fd5e 100644 --- a/v2/tools/xsltproc.jam +++ b/v2/tools/xsltproc.jam @@ -13,6 +13,9 @@ import feature ; import regex ; import sequence ; import common ; +import os ; +import modules ; +import path ; feature.feature xsl:param : : free ; feature.feature xsl:path : : free ; @@ -33,9 +36,36 @@ rule init ( xsltproc ? ) { $(.initialized) = true ; .xsltproc = $(xsltproc) ; + .is-cygwin = [ .is-cygwin $(xsltproc) ] ; } } +# Returns a non-empty string if a cygwin xsltproc binary was specified. +rule is-cygwin ( ) +{ + return $(.is-cygwin) ; +} + +rule .is-cygwin ( xsltproc ) +{ + if [ os.on-windows ] + { + local file = [ path.make [ modules.binding $(__name__) ] ] ; + local dir = [ path.native + [ path.join [ path.parent $(file) ] xsltproc ] ] ; + if [ os.name ] = CYGWIN + { + cygdir = $(dir:W) ; + } + local command = + "\"$(xsltproc)\" \"$(dir)\\test.xsl\" \"$(dir)\\test.xml\" 2>&1" ; + local status = [ SHELL $(command) : no-output : exit-status ] ; + if $(status[2]) != "0" + { + return true ; + } + } +} rule compute-xslt-flags ( target : properties * ) { From 70a16158677eff1239150d3167958b62babbdc06 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 26 May 2010 03:39:15 +0000 Subject: [PATCH 195/198] Add some missing files [SVN r62227] --- v2/tools/xsltproc/included.xsl | 11 +++++++++++ v2/tools/xsltproc/test.xml | 2 ++ v2/tools/xsltproc/test.xsl | 12 ++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 v2/tools/xsltproc/included.xsl create mode 100644 v2/tools/xsltproc/test.xml create mode 100644 v2/tools/xsltproc/test.xsl diff --git a/v2/tools/xsltproc/included.xsl b/v2/tools/xsltproc/included.xsl new file mode 100644 index 000000000..ef86394a9 --- /dev/null +++ b/v2/tools/xsltproc/included.xsl @@ -0,0 +1,11 @@ + + + + diff --git a/v2/tools/xsltproc/test.xml b/v2/tools/xsltproc/test.xml new file mode 100644 index 000000000..57c8ba187 --- /dev/null +++ b/v2/tools/xsltproc/test.xml @@ -0,0 +1,2 @@ + + diff --git a/v2/tools/xsltproc/test.xsl b/v2/tools/xsltproc/test.xsl new file mode 100644 index 000000000..a142c91dd --- /dev/null +++ b/v2/tools/xsltproc/test.xsl @@ -0,0 +1,12 @@ + + + + + From ef1d433bd9de4f2d7466fe39df82e231d3df0a07 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 26 May 2010 12:18:46 +0000 Subject: [PATCH 196/198] Fix stupid bug, that broke using xsltproc ; [SVN r62238] --- v2/tools/xsltproc.jam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/tools/xsltproc.jam b/v2/tools/xsltproc.jam index 96d47fd5e..67dfda8e8 100644 --- a/v2/tools/xsltproc.jam +++ b/v2/tools/xsltproc.jam @@ -36,7 +36,7 @@ rule init ( xsltproc ? ) { $(.initialized) = true ; .xsltproc = $(xsltproc) ; - .is-cygwin = [ .is-cygwin $(xsltproc) ] ; + .is-cygwin = [ .is-cygwin $(xsltproc:E=xsltproc) ] ; } } From d1635d82fd1ce73b911dd31aabe5dd32512c40ac Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Wed, 26 May 2010 14:22:15 +0000 Subject: [PATCH 197/198] Import the out-xml rules to the global module so they can be found by __ACTION_RULE__. Somehow they stopped being globally available. This fixes the --out-xml=q.xml option. [SVN r62240] --- v2/build-system.jam | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/v2/build-system.jam b/v2/build-system.jam index 6a313108a..67def964d 100644 --- a/v2/build-system.jam +++ b/v2/build-system.jam @@ -921,6 +921,15 @@ local rule should-clean-project ( project ) __ACTION_RULE__ = build-system.out-xml.collect [ modules.peek build-system : .out-xml ] ; } + + IMPORT + build-system : + out-xml.collect + out-xml.generate-action + : : + build-system.out-xml.collect + build-system.out-xml.generate-action + ; } local j = [ option.get jobs ] ; @@ -998,4 +1007,3 @@ local rule should-clean-project ( project ) } } } - From 20bccaa3af1f3a93fd4600f48d5448b9563984d1 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 26 May 2010 18:06:16 +0000 Subject: [PATCH 198/198] Handle cygwin vs. windows paths for doxygen [SVN r62247] --- v2/tools/doxygen.jam | 82 ++++++++++++++++++- v2/tools/doxygen/windows-paths-check.doxyfile | 3 + v2/tools/doxygen/windows-paths-check.hpp | 0 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 v2/tools/doxygen/windows-paths-check.doxyfile create mode 100644 v2/tools/doxygen/windows-paths-check.hpp diff --git a/v2/tools/doxygen.jam b/v2/tools/doxygen.jam index 0c6682958..a443d8fe8 100644 --- a/v2/tools/doxygen.jam +++ b/v2/tools/doxygen.jam @@ -37,6 +37,7 @@ import alias ; import common ; import modules ; import project ; +import utility ; # Use to specify extra configuration paramters. These get translated @@ -140,6 +141,11 @@ rule init ( name ? ) ECHO "notice:" using doxygen ":" $(.doxygen) ; } + if [ .is-cygwin ] + { + .is-cygwin = true ; + } + .doxproc = [ modules.binding $(__name__) ] ; .doxproc = $(.doxproc:D)/doxproc.py ; @@ -170,6 +176,22 @@ rule name ( ) return $(.doxygen) ; } +rule .is-cygwin ( ) +{ + if [ os.on-windows ] + { + local file = [ path.make [ modules.binding $(__name__) ] ] ; + local dir = [ path.native + [ path.join [ path.parent $(file) ] doxygen ] ] ; + local command = + "cd \"$(dir)\" && \"$(.doxygen)\" windows-paths-check.doxyfile 2>&1" ; + result = [ SHELL $(command) ] ; + if [ MATCH "(Parsing file /)" : $(result) ] + { + return true ; + } + } +} # Runs Doxygen on the given Doxygen configuration file (the source) to generate # the Doxygen files. The output is dumped according to the settings in the @@ -192,6 +214,48 @@ actions doxproc } +rule translate-path ( path ) +{ + if [ os.on-windows ] + { + if [ os.name ] = CYGWIN + { + if $(.is-cygwin) + { + return $(path) ; + } + else + { + return $(path:W) ; + } + } + else + { + if $(.is-cygwin) + { + match = [ MATCH ^(.):(.*) : $(path) ] ; + if $(match) + { + return /cygdrive/$(match[1])$(match[2]:T) ; + } + else + { + return $(path:T) ; + } + } + else + { + return $(path) ; + } + } + } + else + { + return $(path) ; + } +} + + # Generates a doxygen configuration file (doxyfile) given a set of C++ sources # and a property list that may contain features. # @@ -205,20 +269,30 @@ rule headers-to-doxyfile ( target : sources * : properties * ) for local param in [ feature.get-values : $(properties) ] { local namevalue = [ regex.match ([^=]*)=(.*) : $(param) ] ; - text += "$(namevalue[1]) = $(namevalue[2])" ; if $(namevalue[1]) = OUTPUT_DIRECTORY { - output-dir = "$(namevalue[2])" ; + output-dir = [ translate-path + [ utility.unquote $(namevalue[2]) ] ] ; + text += "OUTPUT_DIRECTORY = \"$(output-dir)\"" ; + } + else + { + text += "$(namevalue[1]) = $(namevalue[2])" ; } } if ! $(output-dir) { - output-dir = [ on $(target) return $(LOCATE) ] ; + output-dir = [ translate-path [ on $(target) return $(LOCATE) ] ] ; text += "OUTPUT_DIRECTORY = \"$(output-dir)\"" ; } - local headers = \"$(sources:G=)\" ; + local headers = ; + for local header in $(sources:G=) + { + header = [ translate-path $(header) ] ; + headers += \"$(header)\" ; + } # Doxygen generates LaTex by default. So disable it unconditionally, or at # least until someone needs, and hence writes support for, LaTex output. diff --git a/v2/tools/doxygen/windows-paths-check.doxyfile b/v2/tools/doxygen/windows-paths-check.doxyfile new file mode 100644 index 000000000..9b969df9c --- /dev/null +++ b/v2/tools/doxygen/windows-paths-check.doxyfile @@ -0,0 +1,3 @@ +INPUT = windows-paths-check.hpp +GENERATE_HTML = NO +GENERATE_LATEX = NO diff --git a/v2/tools/doxygen/windows-paths-check.hpp b/v2/tools/doxygen/windows-paths-check.hpp new file mode 100644 index 000000000..e69de29bb