diff --git a/.gitignore b/.gitignore index 77cbb38df..500fe4bcf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ -*.pyc +*.py[co] /doc/bin /doc/html /b2 /bjam +/b2.exe +/bjam.exe /bin /bootstrap.log /test/test_results.txt diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 000000000..77a071dfe --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,161 @@ +:Copyright: + Copyright 2003, 2006 Vladimir Prus +:License: + 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) + +Boost.Build contributor guidelines +================================== + +Boost.Build is an open-source project. This means that we welcome and appreciate +all contributions --- be it ideas, bug reports, or patches. This document +contains guidelines which helps to assure that development goes on smoothly, and +changes are made quickly. + +The guidelines are not mandatory, and you can decide for yourself which one to +follow. But note, that 10 mins that you spare writing a comment, for example, +might lead to significally longer delay for everyone. + +Before contributing, make sure you are subscribed to our mailing list +at boost-build@lists.boost.org. + +Additional resources include + +- The issue tracker + + http://trac.lvk.cs.msu.su/boost.build/ + +- mailing list + + boost-build@lists.boost.org + + http://lists.boost.org/boost-build/ + +BUGS and PATCHES +---------------- + +Both bugs and patches can be send to our mailing list. + +When reporting a bug, please try to provide the following information. + +- What you did. + + - A minimal reproducible testcase is very much appreciated. + + - Shell script with some annotations is much better than verbose description + of the problem. + + - A regression test is the best (see test/test_system.html). + +- What you got. + +- What you expected. + +- What version of Boost.Build and Boost.Jam did you use. If possible, + please try to test with the CVS HEAD state. + +When submitting a patch, please: + +- make a single patch for a single logical change +- follow the policies and coding conventions below, +- send patches in unified diff format, (using either "cvs diff -u" or "diff -u") +- provide a log message together with the patch +- put the patch and the log message as attachment to your email. + +The purpose of log message serves to communicate what was changed, and *why*. +Without a good log message, you might spend a lot of time later, wondering where +a strange piece of code came from and why it was necessary. + +The good log message mentions each changed file and each rule/method, saying +what happend to it, and why. Consider, the following log message + +:: + + Better direct request handling. + + * new/build-request.jam + (directly-requested-properties-adjuster): Redo. + + * new/targets.jam + (main-target.generate-really): Adjust properties here. + + * new/virtual-target.jam + (register-actual-name): New rule. + (virtual-target.actualize-no-scanner): Call the above, to detected bugs, + where two virtual target correspond to one Jam target name. + +The log messages for the last two files are good. They tell what was changed. +The change to the first file is clearly undercommented. + +It's OK to use terse log messages for uninteresting changes, like ones induced +by interface changes elsewhere. + +POLICIES. +--------- + +1. Testing. + + All serious changes must be tested. New rules must be tested by the module where + they are declared. Test system (test/test_system.html) should be used to verify + user-observable behaviour. + +2. Documentation. + + It turns out that it's hard to have too much comments, but it's easy to have too + little. Please prepend each rule with a comment saying what the rule does and + what arguments mean. Stop for a minute and consider if the comment makes sense + for anybody else, and completely describes what the rules does. Generic phrases + like "adjusts properties" are really not enough. + +When applicable, make changes to the user documentation as well. + +CODING CONVENTIONS. +------------------- + +1. All names of rules and variables are lowercase with "-" to separate + words. + + :: + + rule call-me-ishmael ( ) ... + +2. Names with dots in them are "intended globals". Ordinary globals use a + dot prefix: + + :: + + .foobar + $(.foobar) + +3. Pseudofunctions or associations are .: + + :: + + $(argument).name = hello ; + $($(argument).name) + +4. Class attribute names are prefixed with "self.": + + :: + + self.x + $(self.x) + +5. Builtin rules are called via their ALL_UPPERCASE_NAMES: + + :: + + DEPENDS $(target) : $(sources) ; + +6. Opening and closing braces go on separate lines: + + :: + + if $(a) + { + # + } + else + { + # + } diff --git a/Jamroot.jam b/Jamroot.jam index 2f50eed09..d7f76b7c9 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -35,7 +35,9 @@ package.install-data boost-build-core $(SELF)/boost-build.jam $(SELF)/src/build-system.jam [ path.glob-tree $(SELF)/src/build : *.jam *.py ] + [ path.glob-tree $(SELF)/src/contrib : *.jam *.py ] [ path.glob-tree $(SELF)/src/kernel : *.jam *.py ] + [ path.glob-tree $(SELF)/src/options : *.jam *.py ] [ path.glob-tree $(SELF)/src/util : *.jam *.py ] [ path.glob-tree $(SELF)/src/tools : *.jam *.py *.xml *.xsl *.doxyfile *.hpp ] $(e2) diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..fe0a6f394 --- /dev/null +++ b/README.rst @@ -0,0 +1,6 @@ +Boost.Build +=========== + +See the Boost.Build website at https://boost.org/build. + +See the `guidelines for contributing <./CONTRIBUTING.rst>`__. diff --git a/bootstrap.sh b/bootstrap.sh index c99242ee8..bbb035063 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -57,7 +57,7 @@ my_dir="." if test "x$TOOLSET" = x; then guessed_toolset=`$my_dir/src/engine/build.sh --guess-toolset` case $guessed_toolset in - acc | darwin | gcc | como | mipspro | pathscale | pgi | qcc | vacpp | xlcpp ) + acc | darwin | gcc | como | mipspro | pathscale | pgi | qcc | vacpp | xlcpp | clang ) TOOLSET=$guessed_toolset ;; diff --git a/doc/bjam.qbk b/doc/bjam.qbk index 6b754d13d..27adf472a 100644 --- a/doc/bjam.qbk +++ b/doc/bjam.qbk @@ -364,7 +364,7 @@ The arguments starting with the "=--option=" forms are passed to the =build.jam= [[[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 =b2= sources.]] [[[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 =b2= sources.]] + [Enables use of the DUMA (Detect Unintended Memory Access) debugging memory allocator. The build expects to find the DUMA source files in a "duma" subdirectory from the =b2= sources.]] [[[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.]] [[[literal --show-locate-target]] diff --git a/doc/src/faq.xml b/doc/src/faq.xml index 9a7f9c458..3b6894b77 100644 --- a/doc/src/faq.xml +++ b/doc/src/faq.xml @@ -414,7 +414,7 @@ exe hello : hello.cpp /site-config//zlib ; external libraries it becomes problematic to remember which libraries are header only, and which ones you have to link to. However, with Boost.Build a header-only library can be declared as Boost.Build target and all - dependents can use such library without having to remeber whether it is a + dependents can use such library without having to remember whether it is a header-only library or not. diff --git a/doc/src/overview.xml b/doc/src/overview.xml index 1c2310b96..37bae1c96 100644 --- a/doc/src/overview.xml +++ b/doc/src/overview.xml @@ -107,7 +107,7 @@ a.o: a.c add_program ("a", "a.c") - This is a function call that creates the targets necessary to create a executable file + This is a function call that creates the targets necessary to create an executable file from the source file a.c. Depending on configured properties, different command lines may be used. However, add_program is higher-level, but rather thin level. All targets are created immediately when the build description @@ -675,7 +675,7 @@ b2 toolset=gcc variant=debug optimization=space Changes the build directories for all project roots being built. When this option is specified, all Jamroot files must declare a project name. - The build directory for the project root will be computed by concatanating + The build directory for the project root will be computed by concatenating the value of the option, the project name specified in Jamroot, and the build dir specified in Jamroot (or bin, if none is specified). @@ -782,12 +782,12 @@ b2 toolset=gcc variant=debug optimization=space - Enable cummulative debugging levels from 1 to n. Values are: + Enable cumulative debugging levels from 1 to n. Values are: Show the actions taken for building targets, as they are executed (the default). Show "quiet" actions and display all action text, as they are executed. Show dependency analysis, and target/source timestamps/paths. - Show arguments and timming of shell invocations. + Show arguments and timing of shell invocations. Show rule invocations and variable expansions. Show directory/header file/archive scans, and attempts at binding to targets. Show variable settings. @@ -1595,7 +1595,7 @@ b2 app1 lib1//lib1 gcc debug optimization=full Selecting the main target alternative to use. For each alternative we look how many properties are present both in alternative's requirements, and in build request. The - alternative with large number of matching properties is selected. + alternative with largest number of matching properties is selected. Determining "common" properties. diff --git a/doc/src/reference.xml b/doc/src/reference.xml index e258985ee..b46cea063 100644 --- a/doc/src/reference.xml +++ b/doc/src/reference.xml @@ -50,7 +50,7 @@ boost-build build-system ; automatically find the build system. The default bootstrap.jam, after loading some standard - definitions, loads two site-config.jam and user-config.jam. + definitions, loads both site-config.jam and user-config.jam. @@ -165,7 +165,7 @@ lib tools : [ glob *.cpp ] ; It is possible to also pass a second argument—the list of exclude patterns. The result will then include the list of - files patching any of include patterns, and not matching any + files matching any of include patterns, and not matching any of the exclude patterns. For example: lib tools : [ glob *.cpp : file_to_exclude.cpp bad*.cpp ] ; @@ -567,7 +567,7 @@ path-constant DATA : data/a.txt ; The value of those features is passed without modification to the corresponding tools. For cflags that is both the C and - C++ compilers, for cxxflags that is the C++ compiler + C++ compilers, for cxxflags that is the C++ compiler, and for linkflags that is the linker. The features are handy when you are trying to do something special that cannot be achieved by a higher-level feature in Boost.Build. @@ -672,7 +672,7 @@ path-constant DATA : data/a.txt ; Allowed values: on, off. The debug-symbols feature specifies if - produced object files, executables and libraries should include + produced object files, executables, and libraries should include debug information. Typically, the value of this feature is implicitly set by the variant feature, but it can be explicitly @@ -686,8 +686,8 @@ path-constant DATA : data/a.txt ; Allowed values: on, off. - The runtime-debugging feature specifies if - produced object files, executables and libraries should include + The runtime-debugging feature specifies + whether produced object files, executables, and libraries should include behaviour useful only for debugging, such as asserts. Typically, the value of this feature is implicitly set by the variant feature, but it can be explicitly @@ -711,7 +711,7 @@ path-constant DATA : data/a.txt ; The complete list of possible values for this feature is: - aix, bsd, cygwin, darwin, freebsd, hpux, iphone, linux, netbsd, + aix, appletv, bsd, cygwin, darwin, freebsd, hpux, iphone, linux, netbsd, openbsd, osf, qnx, qnxnto, sgi, solaris, unix, unixware, windows. @@ -930,6 +930,30 @@ using gcc : &toolset_ops; ; + + archiver + + + Specifies the archiver command that is used to produce static + libraries. Normally, it is autodetected using gcc + -print-prog-name option or defaulted to ar, + but in some cases you might want to override it, for example to explicitly + use a system version instead of one included with gcc. + + + + + ranlib + + + Specifies the ranlib command that is used to generated symbol table + for static libraries. Normally, it is autodetected using gcc + -print-prog-name option or defaulted to ranlib, + but in some cases you might want to override it, for example to explicitly + use a system version instead of one included with gcc. + + + rc @@ -997,6 +1021,9 @@ using gcc : &toolset_ops; ; C++ command-line tools on Microsoft Windows. The supported products and versions of command line tools are listed below: + Visual Studio 2015—14.0 + Visual Studio 2013—12.0 + Visual Studio 2012—11.0 Visual Studio 2010—10.0 Visual Studio 2008—9.0 Visual Studio 2005—8.0 @@ -1671,6 +1698,83 @@ using zlib : 1.2.7 : : <toolset>gcc ; +
+ bzip2 + bzip2 + + Provides support for the + bzip2 library. bzip2 + can be configured either to use precompiled binaries or to + build the library from source. + + bzip2 can be initialized using the following syntax + +using bzip2 : version : options : condition : is-default ; + + Options for using a prebuilt library: + + + search + + The directory containing the bzip2 binaries. + + + + name + + Overrides the default library name. + + + + include + + The directory containing the bzip2 headers. + + + + If none of these options is specified, then the environmental + variables BZIP2_LIBRARY_PATH, BZIP2_NAME, and BZIP2_INCLUDE will be + used instead. + Options for building bzip2 from source: + + + source + + The bzip2 source directory. Defaults to the + environmental variable BZIP2_SOURCE. + + + + tag + + Sets the tag + property to adjust the file name of the library. Ignored + when using precompiled binaries. + + + + build-name + + The base name to use for the compiled library. + Ignored when using precompiled binaries. + + + + Examples: + +# Find bzip in the default system location +using bzip2 ; +# Build bzip from source +using bzip2 : 1.0.6 : <source>/home/sergey/src/bzip2-1.0.6 ; +# Find bzip in /usr/local +using bzip2 : 1.0.6 : <include>/usr/local/include <search>/usr/local/lib ; +# Build bzip from source for msvc and find +# prebuilt binaries for gcc. +using bzip2 : 1.0.6 : <source>C:/Devel/src/bzip2-1.0.6 : <toolset>msvc ; +using bzip2 : 1.0.6 : : <toolset>gcc ; + +
+
@@ -2637,7 +2741,7 @@ exe hello : hello.cpp : <os>NT,<toolset>gcc:<link>static ; target. The syntax is: -target-id -> (project-id | target-name | file-name ) +target-id -> (target-name | file-name | project-id | directory-name) | (project-id | directory-name) "//" target-name project-id -> path target-name -> path @@ -2649,12 +2753,6 @@ directory-name -> path This grammar allows some elements to be recognized as either - - - project id (at this point, all project ids start with slash). - - - name of target declared in current Jamfile (note that target @@ -2668,17 +2766,30 @@ directory-name -> path project's sources location. + + + + project id (at this point, all project ids start with slash). + + + + + + the directory of another project, denoted by absolute name + or name relative to the current project's location. + + - To determine the real meaning a check is made if project-id - by the specified name exists, and then if main target of that - name exists. For example, valid target ids might be: + To determine the real meaning the possible interpretations + are checked in this order. For example, valid target ids might be: a -- target in current project lib/b.cpp -- regular file /boost/thread -- project "/boost/thread" /home/ghost/build/lr_library//parser -- target in specific project +../boost_1_61_0 -- project in specific directory diff --git a/doc/src/tasks.xml b/doc/src/tasks.xml index 8ff82f0b8..48f611a28 100644 --- a/doc/src/tasks.xml +++ b/doc/src/tasks.xml @@ -520,7 +520,7 @@ unit-test helpers_test By default, the executable is run directly. Sometimes, it is desirable to run the executable using some helper command. You - should use the this property to specify the name of the helper + should use this property to specify the name of the helper command. For example, if you write: unit-test helpers_test @@ -575,7 +575,7 @@ unit-test helpers_test Boost.Build's virtual targets. This is higher-level than the file names that the make rule operates with and allows you to create more than one target, create differently named targets depending on - properties or use more than one tool. + properties, or use more than one tool. @@ -764,7 +764,7 @@ exe main : main.cpp pch ; "parser.y". The latter source is converted into "parser.c" and "parser.h". Then, if "app.cpp" includes "parser.h", Boost.Build will detect this dependency. Moreover, since "parser.h" will be generated into a build - directory, the path to that directory will automatically added to include + directory, the path to that directory will automatically be added to the include path. @@ -800,14 +800,14 @@ using gcc : arm : arm-none-linux-gnueabi-g++ ; After that, if the host and target os are the same, for example Linux, you can - just request that this compiler version to be used: + just request that this compiler version be used: b2 toolset=gcc-arm - If you want to target different operating system from the host, you need + If you want to target a different operating system from the host, you need to additionally specify the value for the target-os feature, for example: @@ -823,7 +823,7 @@ b2 toolset=gcc-mingw target-os=windows - When using the msvc compiler, it's only possible to cross-compiler to a 64-bit system + When using the msvc compiler, it's only possible to cross-compile to a 64-bit system on a 32-bit host. Please see for details. diff --git a/doc/src/tutorial.xml b/doc/src/tutorial.xml index 3227a6718..e36ac1402 100644 --- a/doc/src/tutorial.xml +++ b/doc/src/tutorial.xml @@ -179,7 +179,7 @@ exe hello See - augments) the build request. + augment) the build request. @@ -384,7 +384,7 @@ project lib foo : foo.cpp ; Usage requirements are applied not to the target being declared but to its - dependants. In this case, <include>. will be + dependents. In this case, <include>. will be applied to all targets that directly depend on foo. @@ -417,7 +417,7 @@ exe app : app.cpp /library-example/foo//bar ; If you want all applications in some project to link to a certain - library, you can avoid having to specify it directly the sources of + library, you can avoid having to specify directly the sources of every target by using the <library> property. For example, if /boost/filesystem//fs should be linked to all applications in your project, you can add @@ -433,7 +433,7 @@ project
- Static and shared libaries + Static and shared libraries Libraries can be either static, which means they are @@ -621,8 +621,8 @@ lib lib2 exe app : app.cpp ../util/lib2//lib2 ; As with any target, the alternative selected depends on the properties - propagated from lib2's dependants. If we build the - release and debug versions of app will be linked + propagated from lib2's dependents. If we build the + release and debug versions of app it will be linked with lib2_release.a and lib2_debug.a , respectively. diff --git a/example/complex-testing/compile-fail.cpp b/example/complex-testing/compile-fail.cpp new file mode 100644 index 000000000..a219fa5c6 --- /dev/null +++ b/example/complex-testing/compile-fail.cpp @@ -0,0 +1,17 @@ +// Copyright (c) 2014 Rene Rivera +// +// 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) +// +// http://www.boost.org +// + +#include +#include + +int main() +{ + std::cout << "Bye!\n"; + return EXIT_FAILURE +} diff --git a/example/complex-testing/fail.cpp b/example/complex-testing/fail.cpp new file mode 100644 index 000000000..965661188 --- /dev/null +++ b/example/complex-testing/fail.cpp @@ -0,0 +1,17 @@ +// Copyright (c) 2014 Rene Rivera +// +// 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) +// +// http://www.boost.org +// + +#include +#include + +int main() +{ + std::cout << "Bye!\n"; + return EXIT_FAILURE; +} diff --git a/example/complex-testing/jamroot.jam b/example/complex-testing/jamroot.jam new file mode 100644 index 000000000..a5942a239 --- /dev/null +++ b/example/complex-testing/jamroot.jam @@ -0,0 +1,15 @@ +# Copyright 2016 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) + +using testing ; +import property-set ; +import path ; + +exe success : success.cpp ; + +run success : arg1 arg2 : : : success-a ; +run success : arg3 arg4 : : : success-b ; + +run post.cpp : : success-a : : post-a ; +run post.cpp : : success-b : : post-b ; diff --git a/example/complex-testing/post.cpp b/example/complex-testing/post.cpp new file mode 100644 index 000000000..6282e8f24 --- /dev/null +++ b/example/complex-testing/post.cpp @@ -0,0 +1,17 @@ +// Copyright (c) 2014 Rene Rivera +// +// 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) +// +// http://www.boost.org +// + +#include +#include + +int main(int argc, char *argv[]) +{ + std::cout << argv[1] << "\n"; + return EXIT_SUCCESS; +} diff --git a/example/complex-testing/success.cpp b/example/complex-testing/success.cpp new file mode 100644 index 000000000..a7e2b6ca0 --- /dev/null +++ b/example/complex-testing/success.cpp @@ -0,0 +1,17 @@ +// Copyright (c) 2014 Rene Rivera +// +// 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) +// +// http://www.boost.org +// + +#include +#include + +int main(int argc, char *argv[]) +{ + std::cout << "Hi!\n"; + return EXIT_SUCCESS; +} diff --git a/example/time/jamroot.jam b/example/time/jamroot.jam new file mode 100644 index 000000000..9419b4437 --- /dev/null +++ b/example/time/jamroot.jam @@ -0,0 +1,13 @@ +import feature ; +import toolset ; +import os ; +import testing ; + +path-constant HERE : . ; +make main.cpp : main_cpp.pro : @do-something ; +time main.time : main.cpp ; + +actions do-something +{ + sleep 2 && echo "$(<)" > "$(<)" +} diff --git a/example/time/main_cpp.pro b/example/time/main_cpp.pro new file mode 100644 index 000000000..237c8ce18 --- /dev/null +++ b/example/time/main_cpp.pro @@ -0,0 +1 @@ +int main() {} diff --git a/example/try_compile/Jamroot.jam b/example/try_compile/Jamroot.jam new file mode 100644 index 000000000..893a03fdc --- /dev/null +++ b/example/try_compile/Jamroot.jam @@ -0,0 +1,29 @@ + +# This example shows performing configure checks in Boost.Build, +# e.g. to check for some system function or compiler quirk. + +# First, declare a metatarget that we'll try to build. +obj foo : foo.cpp ; +# Make it explicit so that it's only built if used by a configure check +explicit foo ; + +# Declare a target that depends on configure check result. +exe main + : main.cpp + # The check-target-builds invocation in requirements section will + # - build the specified metatarget + # - if it builds OK, add the properties in the second parameter + # - otherwise, add the properties in the third parameter + : [ check-target-builds foo : FOO=1 : FOO=0 ] + ; + +# To test this: +# +# 1. Build with "b2". You should see a "foo builds: yes" message, and running +# the produced executable will show that FOO is set to 1. +# 2. Modify foo.cpp to contain a compile error, rebuild with +# "b2 -a --reconfigure". You should see a "foo builds: no" message, and running +# the produced executable should show that FOO is now set to 0. +# +# The output from the check is not shown on the console, instead it is +# redirected to the bin/config.log file diff --git a/example/try_compile/foo.cpp b/example/try_compile/foo.cpp new file mode 100644 index 000000000..c9107f937 --- /dev/null +++ b/example/try_compile/foo.cpp @@ -0,0 +1,6 @@ + + +int foo() +{ + return 0; +} \ No newline at end of file diff --git a/example/try_compile/main.cpp b/example/try_compile/main.cpp new file mode 100644 index 000000000..12f64995b --- /dev/null +++ b/example/try_compile/main.cpp @@ -0,0 +1,8 @@ + +#include +using namespace std; + +int main() +{ + std::cout << "Foo: " << FOO << "\n"; +} \ No newline at end of file diff --git a/example/user-config.jam b/example/user-config.jam index fbbf13fd0..df86176cd 100644 --- a/example/user-config.jam +++ b/example/user-config.jam @@ -22,7 +22,7 @@ # This file uses Jam language syntax to describe available tools. Mostly, # there are 'using' lines, that contain the name of the used tools, and -# parameters to pass to those tools -- where paremeters are separated by +# parameters to pass to those tools -- where parameters are separated by # semicolons. Important syntax notes: # # - Both ':' and ';' must be separated from other tokens by whitespace diff --git a/index.html b/index.html index fd08ee93b..a42dc05f9 100644 --- a/index.html +++ b/index.html @@ -37,8 +37,7 @@

Boost.Build makes it easy to build C++ projects, everywhere.

- You - name you executables and libraries and list their sources. Boost.Build + You name your executables and libraries and list their sources. Boost.Build takes care about compiling your sources with right options, creating static and shared libraries, making executables, and other chores — whether you're using gcc, msvc, or a dozen more supported C++ diff --git a/notes/hacking.txt b/notes/hacking.txt deleted file mode 100644 index 3c059173b..000000000 --- a/notes/hacking.txt +++ /dev/null @@ -1,138 +0,0 @@ -Copyright 2003, 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) - - - ---------------------------------- - Boost.Build contributor guidelines - ---------------------------------- - -Boost.Build is an open-source project. This means that we welcome and appreciate -all contributions --- be it ideas, bug reports, or patches. This document -contains guidelines which helps to assure that development goes on smoothly, and -changes are made quickly. - -The guidelines are not mandatory, and you can decide for yourself which one to -follow. But note, that 10 mins that you spare writing a comment, for example, -might lead to significally longer delay for everyone. - -Before contributing, make sure you are subscribed to our mailing list - - boost-build@lists.boost.org - -Additional resources include - - - The issue tracker - http://trac.lvk.cs.msu.su/boost.build/ - - - mailing list: - boost-build@lists.boost.org - http://lists.boost.org/boost-build/ - - -BUGS and PATCHES - -Both bugs and patches can be send to our mailing list. - -When reporting a bug, please try to provide the following information. - - - What you did. A minimal reproducible testcase is very much appreciated. - Shell script with some annotations is much better than verbose description - of the problem. A regression test is the best (see test/test_system.html). - - What you got. - - What you expected. - - What version of Boost.Build and Boost.Jam did you use. If possible, - please try to test with the CVS HEAD state. - -When submitting a patch, please: - - - make a single patch for a single logical change - - follow the policies and coding conventions below, - - send patches in unified diff format, - (using either "cvs diff -u" or "diff -u") - - provide a log message together with the patch - - put the patch and the log message as attachment to your email. - -The purpose of log message serves to communicate what was changed, and *why*. -Without a good log message, you might spend a lot of time later, wondering where -a strange piece of code came from and why it was necessary. - -The good log message mentions each changed file and each rule/method, saying -what happend to it, and why. Consider, the following log message - - Better direct request handling. - - * new/build-request.jam - (directly-requested-properties-adjuster): Redo. - - * new/targets.jam - (main-target.generate-really): Adjust properties here. - - * new/virtual-target.jam - (register-actual-name): New rule. - (virtual-target.actualize-no-scanner): Call the above, to detected bugs, - where two virtual target correspond to one Jam target name. - -The log messages for the last two files are good. They tell what was changed. -The change to the first file is clearly undercommented. - -It's OK to use terse log messages for uninteresting changes, like ones induced -by interface changes elsewhere. - - -POLICIES. - -1. Testing. - -All serious changes must be tested. New rules must be tested by the module where -they are declared. Test system (test/test_system.html) should be used to verify -user-observable behaviour. - -2. Documentation. - -It turns out that it's hard to have too much comments, but it's easy to have too -little. Please prepend each rule with a comment saying what the rule does and -what arguments mean. Stop for a minute and consider if the comment makes sense -for anybody else, and completely describes what the rules does. Generic phrases -like "adjusts properties" are really not enough. - -When applicable, make changes to the user documentation as well. - - -CODING CONVENTIONS. - - 1. All names of rules and variables are lowercase with "-" to separate - words. - - rule call-me-ishmael ( ) ... - - 2. Names with dots in them are "intended globals". Ordinary globals use a - dot prefix: - - .foobar - $(.foobar) - - 3. Pseudofunctions or associations are .: - - $(argument).name = hello ; - $($(argument).name) - - 4. Class attribute names are prefixed with "self.": - - self.x - $(self.x) - - 5. Builtin rules are called via their ALL_UPPERCASE_NAMES: - - DEPENDS $(target) : $(sources) ; - - 6. Opening and closing braces go on separate lines: - - if $(a) - { - # - } - else - { - # - } diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/build-system.jam b/src/build-system.jam index 76db2d377..185526c70 100644 --- a/src/build-system.jam +++ b/src/build-system.jam @@ -971,7 +971,15 @@ local rule should-clean-project ( project ) DEPENDS all : $(actual-targets) ; if UPDATE_NOW in [ RULENAMES ] { - local ok = [ UPDATE_NOW all $(.out-xml) ] ; + local ok = [ UPDATE_NOW all ] ; + # Force sequence updating of regular targets, then the xml + # log output target. To ensure the output records all built + # as otherwise if could execute out-of-sequence when + # doing parallel builds. + if $(.out-xml) + { + UPDATE_NOW $(.out-xml) : : ignore-minus-n ; + } if $(.post-build-hook) { $(.post-build-hook) $(ok) ; diff --git a/src/build/ac.jam b/src/build/ac.jam index c4bd6b7e1..c01dc5997 100644 --- a/src/build/ac.jam +++ b/src/build/ac.jam @@ -26,7 +26,7 @@ rule generate-include ( target : sources * : properties * ) { local header = [ property.select : $(properties) ] ; print.output $(target) ; - print.text "#include <$(header:G=)>" : true ; + print.text "#include <$(header:G=)>\n" : true ; } rule generate-main ( target : sources * : properties * ) @@ -76,14 +76,6 @@ rule construct-library ( name : property-set : provided-path ? ) rule find-library ( properties : names + : provided-path ? ) { local result ; - if ! $(.main.cpp) - { - local a = [ class.new action : ac.generate-main : - [ property-set.empty ] ] ; - .main.cpp = [ virtual-target.register - [ class.new file-target main.cpp exact - : CPP : $(.project) : $(a) ] ] ; - } if [ $(properties).get ] = shared { link-opts = shared static ; @@ -100,8 +92,12 @@ rule find-library ( properties : names + : provided-path ? ) { local name = $(names-iter[1]) ; local lib = [ construct-library $(name) : $(properties) : $(provided-path) ] ; + local a = [ class.new action : ac.generate-main : + [ property-set.empty ] ] ; + local main.cpp = [ virtual-target.register + [ class.new file-target main-$(name).cpp exact : CPP : $(.project) : $(a) ] ] ; local test = [ generators.construct $(.project) $(name) : EXE - : [ $(properties).add $(lib[1]) ] : $(.main.cpp) $(lib[2-]) + : [ $(properties).add $(lib[1]) ] : $(main.cpp) $(lib[2-]) : true ] ; local jam-targets ; for t in $(test[2-]) diff --git a/src/build/build_request.py b/src/build/build_request.py index 194251688..4fa54072f 100644 --- a/src/build/build_request.py +++ b/src/build/build_request.py @@ -56,7 +56,7 @@ def __x_product_aux (property_sets, seen_features): these_features = set() for p in property_sets[0].non_free(): - these_features.add(p.feature()) + these_features.add(p.feature) # Note: the algorithm as implemented here, as in original Jam code, appears to # detect conflicts based on features, not properties. For example, if command diff --git a/src/build/engine.py b/src/build/engine.py index 4c2c97eaf..6e49a8b5e 100644 --- a/src/build/engine.py +++ b/src/build/engine.py @@ -16,21 +16,23 @@ from b2.util import set_jam_action, is_iterable class BjamAction(object): """Class representing bjam action defined from Python.""" - def __init__(self, action_name, function): + def __init__(self, action_name, function, has_command=False): assert isinstance(action_name, basestring) assert callable(function) or function is None self.action_name = action_name self.function = function + self.has_command = has_command def __call__(self, targets, sources, property_set_): assert is_iterable(targets) assert is_iterable(sources) assert isinstance(property_set_, property_set.PropertySet) - # 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, []) + if self.has_command: + # 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, []) if self.function: self.function(targets, sources, property_set_) @@ -134,8 +136,11 @@ class Engine: assert isinstance(variable, basestring) assert is_iterable(value) - for target in targets: - self.do_set_target_variable (target, variable, value, append) + if targets: + if append: + bjam_interface.call("set-target-variable", targets, variable, value, "true") + else: + bjam_interface.call("set-target-variable", targets, variable, value) def set_update_action (self, action_name, targets, sources, properties=None): """ Binds a target to the corresponding update action. @@ -158,7 +163,7 @@ class Engine: self.do_set_update_action (action_name, targets, sources, properties) - def register_action (self, action_name, command, bound_list = [], flags = [], + def register_action (self, action_name, command='', bound_list = [], flags = [], function = None): """Creates a new build engine action. @@ -190,7 +195,8 @@ class Engine: if command: bjam_interface.define_action(action_name, command, bound_list, bjam_flags) - self.actions[action_name] = BjamAction(action_name, function) + self.actions[action_name] = BjamAction( + action_name, function, has_command=bool(command)) def register_bjam_action (self, action_name, function=None): """Informs self that 'action_name' is declared in bjam. @@ -206,7 +212,7 @@ class Engine: # action is already registered. assert isinstance(action_name, basestring) assert function is None or callable(function) - if not self.actions.has_key(action_name): + if action_name not in self.actions: self.actions[action_name] = BjamNativeAction(action_name, function) # Overridables diff --git a/src/build/errors.py b/src/build/errors.py index 69d8a37d3..dd517395f 100644 --- a/src/build/errors.py +++ b/src/build/errors.py @@ -123,5 +123,13 @@ class Errors: stack=traceback.extract_stack()) - - +def nearest_user_location(): + """ + Returns: + tuple: the filename and line number of the nearest user location + """ + bt = bjam.backtrace() + if not bt: + return None + last = bt[-1] + return last[0], last[1] diff --git a/src/build/feature.py b/src/build/feature.py index 6cf81a1a1..85f6579ca 100644 --- a/src/build/feature.py +++ b/src/build/feature.py @@ -9,6 +9,7 @@ import re +from b2.manager import get_manager from b2.util import utility, bjam_signature, is_iterable_typed import b2.util.set from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, to_seq @@ -18,108 +19,79 @@ __re_split_subfeatures = re.compile ('<(.*):(.*)>') __re_no_hyphen = re.compile ('^([^:]+)$') __re_slash_or_backslash = re.compile (r'[\\/]') +VALID_ATTRIBUTES = { + 'implicit', + 'composite', + 'optional', + 'symmetric', + 'free', + 'incidental', + 'path', + 'dependency', + 'propagated', + 'link-incompatible', + 'subfeature', + 'order-sensitive' +} + + class Feature(object): - - # Map from string attribute names to integers bit flags. - # This will be initialized after declaration of the class. - _attribute_name_to_integer = {} - def __init__(self, name, values, attributes): assert isinstance(name, basestring) assert is_iterable_typed(values, basestring) assert is_iterable_typed(attributes, basestring) - self._name = name - self._values = values - self._default = None - self._attributes = 0 - for a in attributes: - self._attributes = self._attributes | Feature._attribute_name_to_integer[a] - self._attributes_string_list = attributes - self._subfeatures = [] - self._parent = None + self.name = name + self.values = values + self.default = None + self.subfeatures = [] + self.parent = None + self.attributes_string_list = [] + self._hash = hash(self.name) - def name(self): - return self._name - - def values(self): - return self._values + for attr in attributes: + self.attributes_string_list.append(attr) + attr = attr.replace("-", "_") + setattr(self, attr, True) def add_values(self, values): assert is_iterable_typed(values, basestring) - self._values.extend(values) - - def attributes(self): - return self._attributes + self.values.extend(values) def set_default(self, value): assert isinstance(value, basestring) for attr in ('free', 'optional'): - if getattr(self, attr)(): + if getattr(self, attr): get_manager().errors()('"{}" feature "<{}>" cannot have a default value.' - .format(attr, self._name)) + .format(attr, self.name)) - self._default = value - - def default(self): - return self._default - - # FIXME: remove when we fully move to using classes for features/properties - def attributes_string_list(self): - return self._attributes_string_list - - def subfeatures(self): - return self._subfeatures + self.default = value def add_subfeature(self, name): assert isinstance(name, Feature) - self._subfeatures.append(name) - - def parent(self): - """For subfeatures, return pair of (parent_feature, value). - - Value may be None if this subfeature is not specific to any - value of the parent feature. - """ - return self._parent + self.subfeatures.append(name) def set_parent(self, feature, value): assert isinstance(feature, Feature) assert isinstance(value, basestring) - self._parent = (feature, value) + self.parent = (feature, value) + + def __hash__(self): + return self._hash def __str__(self): - return self._name + return self.name 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_from_value, __all_top_features, __free_features + global __subfeature_from_value, __all_top_features, __free_features global __all_subfeatures - # The list with all attribute names. - __all_attributes = [ 'implicit', - 'composite', - 'optional', - 'symmetric', - 'free', - 'incidental', - 'path', - 'dependency', - 'propagated', - 'link-incompatible', - 'subfeature', - 'order-sensitive' - ] - i = 1 - for a in __all_attributes: - setattr(Feature, a.upper(), i) - Feature._attribute_name_to_integer[a] = i - def probe(self, flag=i): - return getattr(self, "_attributes") & flag - setattr(Feature, a.replace("-", "_"), probe) - i = i << 1 + # sets the default value of False for each valid attribute + for attr in VALID_ATTRIBUTES: + setattr(Feature, attr.replace("-", "_"), False) # A map containing all features. The key is the feature name. # The value is an instance of Feature class. @@ -135,10 +107,6 @@ def reset (): # and the value is a list of Property instances __composite_properties = {} - __features_with_attributes = {} - for attribute in __all_attributes: - __features_with_attributes [attribute] = [] - # Maps a value to the corresponding subfeature name. __subfeature_from_value = {} @@ -178,9 +146,6 @@ def feature (name, values, attributes = []): # Temporary measure while we have not fully moved from 'gristed strings' __all_features["<" + name + ">"] = feature - for attribute in attributes: - __features_with_attributes [attribute].append (name) - name = add_grist(name) if 'subfeature' in attributes: @@ -203,19 +168,18 @@ def set_default (feature, value): value: the default value to assign """ f = __all_features[feature] - attributes = f.attributes() bad_attribute = None - if attributes & Feature.FREE: + if f.free: bad_attribute = "free" - elif attributes & Feature.OPTIONAL: + elif f.optional: bad_attribute = "optional" if bad_attribute: - raise InvalidValue ("%s property %s cannot have a default" % (bad_attribute, feature.name())) + raise InvalidValue ("%s property %s cannot have a default" % (bad_attribute, f.name)) - if not value in f.values(): - raise InvalidValue ("The specified default value, '%s' is invalid.\n" % value + "allowed values are: %s" % f.values()) + if value not in f.values: + raise InvalidValue ("The specified default value, '%s' is invalid.\n" % value + "allowed values are: %s" % f.values) f.set_default(value) @@ -228,8 +192,8 @@ def defaults(features): result = [] for f in features: - if not f.free() and not f.optional() and f.default(): - result.append(property.Property(f, f.default())) + if not f.free and not f.optional and f.default: + result.append(property.Property(f, f.default)) return result @@ -246,26 +210,26 @@ def attributes (feature): """ Returns the attributes of the given feature. """ assert isinstance(feature, basestring) - return __all_features[feature].attributes_string_list() + return __all_features[feature].attributes_string_list def values (feature): """ Return the values of the given feature. """ assert isinstance(feature, basestring) validate_feature (feature) - return __all_features[feature].values() + return __all_features[feature].values def is_implicit_value (value_string): """ Returns true iff 'value_string' is a value_string of an implicit feature. """ assert isinstance(value_string, basestring) - if __implicit_features.has_key(value_string): + if value_string in __implicit_features: return __implicit_features[value_string] v = value_string.split('-') - if not __implicit_features.has_key(v[0]): + if v[0] not in __implicit_features: return False feature = __implicit_features[v[0]] @@ -282,7 +246,7 @@ def implied_feature (implicit_value): assert isinstance(implicit_value, basestring) components = implicit_value.split('-') - if not __implicit_features.has_key(components[0]): + if components[0] not in __implicit_features: raise InvalidValue ("'%s' is not a value of an implicit feature" % implicit_value) return __implicit_features[components[0]] @@ -318,11 +282,12 @@ def validate_feature (name): """ Checks if all name is a valid feature. Otherwise, raises an exception. """ assert isinstance(name, basestring) - if not __all_features.has_key(name): + if name not in __all_features: raise InvalidFeature ("'%s' is not a valid feature name" % name) else: return __all_features[name] + # Uses Property def __expand_subfeatures_aux (property_, dont_validate = False): """ Helper for expand_subfeatures. @@ -343,8 +308,8 @@ def __expand_subfeatures_aux (property_, dont_validate = False): assert isinstance(property_, property.Property) assert isinstance(dont_validate, int) # matches bools - f = property_.feature() - v = property_.value() + f = property_.feature + v = property_.value if not dont_validate: validate_value_string(f, v) @@ -394,7 +359,7 @@ def expand_subfeatures(properties, dont_validate = False): result = [] for p in properties: # Don't expand subfeatures in subfeatures - if p.feature().subfeature(): + if p.feature.subfeature: result.append (p) else: result.extend(__expand_subfeatures_aux (p, dont_validate)) @@ -420,7 +385,7 @@ def expand_subfeatures(properties, dont_validate = False): # Now, the specific rule must be called, depending on the desired operation: # extend_feature # extend_subfeature - +@bjam_signature([['name'], ['values', '*']]) def extend (name, values): """ Adds the given values to the given feature. """ @@ -430,14 +395,14 @@ def extend (name, values): __validate_feature (name) feature = __all_features [name] - if feature.implicit(): + if feature.implicit: for v in values: - if __implicit_features.has_key(v): + if v in __implicit_features: raise BaseException ("'%s' is already associated with the feature '%s'" % (v, __implicit_features [v])) __implicit_features[v] = feature - if values and not feature.values() and not(feature.free() or feature.optional()): + if values and not feature.values and not(feature.free or feature.optional): # This is the first value specified for this feature, # take it as default value feature.set_default(values[0]) @@ -449,20 +414,20 @@ def validate_value_string (f, value_string): """ assert isinstance(f, Feature) assert isinstance(value_string, basestring) - if f.free() or value_string in f.values(): + if f.free or value_string in f.values: return values = [value_string] - if f.subfeatures(): - if not value_string in f.values() and \ - not value_string in f.subfeatures(): + if f.subfeatures: + if not value_string in f.values and \ + not value_string in 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 f.optional()): - raise InvalidValue ("'%s' is not a known value of feature '%s'\nlegal values: '%s'" % (values [0], f.name(), f.values())) + if not values[0] in f.values and \ + (values[0] or not f.optional): + raise InvalidValue ("'%s' is not a known value of feature '%s'\nlegal values: '%s'" % (values [0], f.name, f.values)) for v in values [1:]: # this will validate any subfeature values in value-string @@ -503,11 +468,11 @@ def extend_subfeature (feature_name, value_string, subfeature_name, subvalues): if value_string == None: value_string = '' - if not __subfeature_from_value.has_key(feature): - __subfeature_from_value [feature] = {} + if feature not in __subfeature_from_value: + __subfeature_from_value[feature] = {} - if not __subfeature_from_value[feature].has_key(value_string): - __subfeature_from_value [feature][value_string] = {} + if value_string not in __subfeature_from_value[feature]: + __subfeature_from_value[feature][value_string] = {} for subvalue in subvalues: __subfeature_from_value [feature][value_string][subvalue] = subfeature @@ -529,7 +494,7 @@ def subfeature (feature_name, value_string, subfeature, subvalues, attributes = # 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(): + 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) @@ -554,17 +519,17 @@ def compose (composite_property_s, component_properties_s): component_properties_s = to_seq (component_properties_s) composite_property = property.create_from_string(composite_property_s) - f = composite_property.feature() + f = composite_property.feature if len(component_properties_s) > 0 and isinstance(component_properties_s[0], property.Property): component_properties = component_properties_s else: component_properties = [property.create_from_string(p) for p in component_properties_s] - if not f.composite(): + if not f.composite: raise BaseException ("'%s' is not a composite feature" % f) - if __composite_properties.has_key(property): + if property in __composite_properties: raise BaseException ('components of "%s" already set: %s' % (composite_property, str (__composite_properties[composite_property]))) if composite_property in component_properties: @@ -578,7 +543,7 @@ def expand_composite(property_): from .property import Property assert isinstance(property_, Property) result = [ property_ ] - if __composite_properties.has_key(property_): + if property_ in __composite_properties: for p in __composite_properties[property_]: result.extend(expand_composite(p)) return result @@ -608,7 +573,7 @@ def expand_composites (properties): if __debug__: from .property import Property assert is_iterable_typed(properties, Property) - explicit_features = set(p.feature() for p in properties) + explicit_features = set(p.feature for p in properties) result = [] @@ -618,23 +583,23 @@ def expand_composites (properties): for x in expanded: if not x in result: - f = x.feature() + f = x.feature - if f.free(): + if f.free: result.append (x) elif not x in properties: # x is the result of expansion if not f in explicit_features: # not explicitly-specified - if any(r.feature() == f for r in result): + if any(r.feature == f for r in result): raise FeatureConflict( "expansions of composite features result in " "conflicting values for '%s'\nvalues: '%s'\none contributing composite property was '%s'" % - (f.name(), [r.value() for r in result if r.feature() == f] + [x.value()], p)) + (f.name, [r.value for r in result if r.feature == f] + [x.value], p)) else: result.append (x) - elif any(r.feature() == f for r in result): + elif any(r.feature == f for r in result): raise FeatureConflict ("explicitly-specified values of non-free feature '%s' conflict\n" "existing values: '%s'\nvalue from expanding '%s': '%s'" % (f, - [r.value() for r in result if r.feature() == f], p, x.value())) + [r.value for r in result if r.feature == f], p, x.value)) else: result.append (x) @@ -651,20 +616,20 @@ def is_subfeature_of (parent_property, f): assert isinstance(parent_property, Property) assert isinstance(f, Feature) - if not f.subfeature(): + if not f.subfeature: return False - p = f.parent() + p = f.parent if not p: return False parent_feature = p[0] parent_value = p[1] - if parent_feature != parent_property.feature(): + if parent_feature != parent_property.feature: return False - if parent_value and parent_value != parent_property.value(): + if parent_value and parent_value != parent_property.value: return False return True @@ -676,7 +641,7 @@ def __is_subproperty_of (parent_property, p): from .property import Property assert isinstance(parent_property, Property) assert isinstance(p, Property) - return is_subfeature_of (parent_property, p.feature()) + return is_subfeature_of (parent_property, p.feature) # Returns true iff the subvalue is valid for the feature. When the @@ -735,28 +700,23 @@ def add_defaults (properties): if __debug__: from .property import Property assert is_iterable_typed(properties, Property) + # create a copy since properties will be modified + result = list(properties) - result = [x for x in properties] - - handled_features = set() - for p in properties: - # We don't add default for conditional properties. We don't want - # debug:DEBUG to be takes as specified value for - if not p.condition(): - handled_features.add(p.feature()) + # We don't add default for conditional properties. We don't want + # debug:DEBUG to be takes as specified value for + handled_features = set(p.feature for p in properties if not p.condition) missing_top = [f for f in __all_top_features if not f in handled_features] more = defaults(missing_top) result.extend(more) - for p in more: - handled_features.add(p.feature()) + handled_features.update(p.feature for p in more) # Add defaults for subfeatures of features which are present for p in result[:]: - s = p.feature().subfeatures() - more = defaults([s for s in p.feature().subfeatures() if not s in handled_features]) - for p in more: - handled_features.add(p.feature()) + subfeatures = [s for s in p.feature.subfeatures if not s in handled_features] + more = defaults(__select_subfeatures(p, subfeatures)) + handled_features.update(h.feature for h in more) result.extend(more) return result @@ -775,29 +735,33 @@ def minimize (properties): assert is_iterable_typed(properties, Property) # remove properties implied by composite features components = [] + component_features = set() for property in properties: - if __composite_properties.has_key (property): - components.extend(__composite_properties[property]) + if property in __composite_properties: + cs = __composite_properties[property] + components.extend(cs) + component_features.update(c.feature for c in cs) + properties = b2.util.set.difference (properties, components) # handle subfeatures and implicit features # move subfeatures to the end of the list - properties = [p for p in properties if not p.feature().subfeature()] +\ - [p for p in properties if p.feature().subfeature()] + properties = [p for p in properties if not p.feature.subfeature] +\ + [p for p in properties if p.feature.subfeature] result = [] while properties: p = properties[0] - f = p.feature() + f = p.feature # locate all subproperties of $(x[1]) in the property set - subproperties = __select_subproperties (p, properties) + subproperties = [x for x in properties if is_subfeature_of(p, x.feature)] if subproperties: # reconstitute the joined property name subproperties.sort () - joined = b2.build.property.Property(p.feature(), p.value() + '-' + '-'.join ([sp.value() for sp in subproperties])) + joined = b2.build.property.Property(p.feature, p.value + '-' + '-'.join ([sp.value for sp in subproperties])) result.append(joined) properties = b2.util.set.difference(properties[1:], subproperties) @@ -811,12 +775,8 @@ def minimize (properties): # 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 p.value() != f.default() or f.symmetric(): + if p.value != f.default or f.symmetric or f in component_features: result.append (p) - #\ - #or get_grist (fullp) in get_grist (components): - # FIXME: restore above - properties = properties[1:] @@ -872,18 +832,18 @@ def compress_subproperties (properties): matched_subs = set() all_subs = set() for p in properties: - f = p.feature() + f = p.feature - if not f.subfeature(): - subs = __select_subproperties (p, properties) + if not f.subfeature: + subs = [x for x in properties if is_subfeature_of(p, x.feature)] if subs: matched_subs.update(subs) - subvalues = '-'.join (sub.value() for sub in subs) + subvalues = '-'.join (sub.value for sub in subs) result.append(Property( - p.feature(), p.value() + '-' + subvalues, - p.condition())) + p.feature, p.value + '-' + subvalues, + p.condition)) else: result.append(p) @@ -920,7 +880,7 @@ def __validate_feature_attributes (name, attributes): assert isinstance(name, basestring) assert is_iterable_typed(attributes, basestring) for attribute in attributes: - if not attribute in __all_attributes: + if attribute not in VALID_ATTRIBUTES: raise InvalidAttribute ("unknown attributes: '%s' in feature declaration: '%s'" % (str (b2.util.set.difference (attributes, __all_attributes)), name)) if name in __all_features: @@ -935,7 +895,7 @@ def __validate_feature (feature): """ Generates an error if the feature is unknown. """ assert isinstance(feature, basestring) - if not __all_features.has_key (feature): + if feature not in __all_features: raise BaseException ('unknown feature "%s"' % feature) diff --git a/src/build/generators.py b/src/build/generators.py index f1c514556..a4a39d94d 100644 --- a/src/build/generators.py +++ b/src/build/generators.py @@ -53,9 +53,10 @@ import os.path from virtual_target import Subvariant from . import virtual_target, type, property_set, property +from b2.exceptions import BaseBoostBuildException from b2.util.logger import * from b2.util.utility import * -from b2.util import set as set_, is_iterable_typed, is_iterable +from b2.util import set as set_, is_iterable_typed, is_iterable, bjam_signature from b2.util.sequence import unique import b2.util.sequence as sequence from b2.manager import get_manager @@ -138,7 +139,7 @@ def invalidate_extendable_viable_source_target_type_cache(): __vstg_cached_generators = [] for g in generators_with_cached_source_types: - if __viable_source_types_cache.has_key(g): + if g in __viable_source_types_cache: if __viable_source_types_cache[g] == ["*"]: __vstg_cached_generators.append(g) else: @@ -148,7 +149,7 @@ def invalidate_extendable_viable_source_target_type_cache(): types_with_cached_sources_types = __vst_cached_types __vst_cached_types = [] for t in types_with_cached_sources_types: - if __viable_source_types_cache.has_key(t): + if t in __viable_source_types_cache: if __viable_source_types_cache[t] == ["*"]: __vst_cached_types.append(t) else: @@ -158,6 +159,13 @@ def dout(message): if debug(): print __indent + message + +class InvalidTargetSource(BaseBoostBuildException): + """ + Should be raised when a target contains a source that is invalid. + """ + + class Generator: """ Creates a generator. manager: the build manager. @@ -336,11 +344,16 @@ class Generator: assert isinstance(name, basestring) or name is None assert isinstance(prop_set, property_set.PropertySet) assert is_iterable_typed(sources, virtual_target.VirtualTarget) - 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 sources: + s = 'An empty source list was passed in to the "{}" generator'.format(self.id_) + if name: + s += ' for target "{}"'.format(name) + raise InvalidTargetSource(s) + if not self.composing_ and len (sources) > 1 and len (self.source_types_) > 1: raise BaseException ("Unsupported source/source_type combination") @@ -368,24 +381,22 @@ class Generator: assert isinstance(prop_set, property_set.PropertySet) assert is_iterable_typed(sources, virtual_target.VirtualTarget) # 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) + consumed = self.convert_multiple_sources_to_consumable_types (project, prop_set, sources) else: - (consumed, bypassed) = self.convert_to_consumable_types (project, name, prop_set, sources) + consumed = 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") + project.manager ().logger ().log (__name__, " FAILURE") return result @@ -412,12 +423,9 @@ class Generator: 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)) + result.extend(self.generated_targets([r], prop_set, project, name)) + elif consumed: + result.extend(self.generated_targets(consumed, prop_set, project, name)) return result @@ -527,7 +535,6 @@ class Generator: Returns a pair: consumed: all targets that can be consumed. - bypassed: all targets that cannot be consumed. """ if __debug__: from .targets import ProjectTarget @@ -537,7 +544,6 @@ class Generator: assert is_iterable_typed(sources, virtual_target.VirtualTarget) assert isinstance(only_one, bool) consumed = [] - bypassed = [] missing_types = [] if len (sources) > 1: @@ -574,59 +580,45 @@ class Generator: 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) + return consumed def convert_multiple_sources_to_consumable_types (self, project, prop_set, sources): """ Converts several files to consumable types. """ - consumed = [] - bypassed = [] if __debug__: from .targets import ProjectTarget assert isinstance(project, ProjectTarget) assert isinstance(prop_set, property_set.PropertySet) assert is_iterable_typed(sources, virtual_target.VirtualTarget) + if not self.source_types_: + return list(sources) - assert isinstance(project, ProjectTarget) - assert isinstance(prop_set, property_set.PropertySet) - assert is_iterable_typed(sources, virtual_target.VirtualTarget) - # 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) + acceptable_types = set() + for t in self.source_types_: + acceptable_types.update(type.all_derived(t)) + + result = [] + for source in sources: + if source.type() not in acceptable_types: + transformed = construct_types( + project, None,self.source_types_, prop_set, [source]) + # construct_types returns [prop_set, [targets]] + for t in transformed[1]: + if t.type() in self.source_types_: + result.append(t) + if not transformed: + project.manager().logger().log(__name__, " failed to convert ", source) + else: + result.append(source) + + result = sequence.unique(result, stable=True) + return result - consumed.extend (c) - bypassed.extend (b) - return (consumed, bypassed) def consume_directly (self, source): assert isinstance(source, virtual_target.VirtualTarget) @@ -642,7 +634,7 @@ class Generator: for st in 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) + consumed = [source] else: missing_types.append (st) @@ -726,6 +718,7 @@ def check_register_types(fn): return wrapper +@bjam_signature([['id'], ['source_types', '*'], ['target_types', '*'], ['requirements', '*']]) @check_register_types def register_standard (id, source_types, target_types, requirements = []): """ Creates new instance of the 'generator' class and registers it. @@ -831,7 +824,7 @@ def viable_source_types (target_type): """ Helper rule, caches the result of '__viable_source_types_real'. """ assert isinstance(target_type, basestring) - if not __viable_source_types_cache.has_key(target_type): + if target_type not in __viable_source_types_cache: __vst_cached_types.append(target_type) __viable_source_types_cache [target_type] = __viable_source_types_real (target_type) return __viable_source_types_cache [target_type] @@ -867,7 +860,7 @@ def viable_source_types_for_generator (generator): """ Caches the result of 'viable_source_types_for_generator'. """ assert isinstance(generator, Generator) - if not __viable_source_types_cache.has_key(generator): + if generator not in __viable_source_types_cache: __vstg_cached_generators.append(generator) __viable_source_types_cache[generator] = viable_source_types_for_generator_real (generator) diff --git a/src/build/project.ann.py b/src/build/project.ann.py deleted file mode 100644 index 349f54955..000000000 --- a/src/build/project.ann.py +++ /dev/null @@ -1,996 +0,0 @@ -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/src/build/project.py b/src/build/project.py index ea8fe0106..9ec2ccb79 100644 --- a/src/build/project.py +++ b/src/build/project.py @@ -184,7 +184,7 @@ class ProjectRegistry: 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." + print "error: Did not find Jamfile.jam or Jamroot.jam in any parent directory." sys.exit(1) return self.load(os.path.dirname(found[0])) @@ -298,16 +298,17 @@ Please consult the documentation at 'http://boost.org/boost-build2'.""" # See if the Jamfile is where it should be. is_jamroot = False jamfile_to_load = b2.util.path.glob([dir], self.JAMROOT) - if not jamfile_to_load: - jamfile_to_load = self.find_jamfile(dir) - else: + if jamfile_to_load: if len(jamfile_to_load) > 1: - get_manager().errors()("Multiple Jamfiles found at '%s'\n" +\ - "Filenames are: %s" - % (dir, [os.path.basename(j) for j in jamfile_to_load])) - + get_manager().errors()( + "Multiple Jamfiles found at '{}'\n" + "Filenames are: {}" + .format(dir, ' '.join(os.path.basename(j) for j in jamfile_to_load)) + ) is_jamroot = True jamfile_to_load = jamfile_to_load[0] + else: + jamfile_to_load = self.find_jamfile(dir) dir = os.path.dirname(jamfile_to_load) if not dir: @@ -321,47 +322,67 @@ Please consult the documentation at 'http://boost.org/boost-build2'.""" # same project we're loading now. Checking inside .jamfile-modules # prevents that second attempt from messing up. if not jamfile_module in self.jamfile_modules: - self.jamfile_modules[jamfile_module] = True - + previous_project = self.current_project # Initialize the jamfile module before loading. - # self.initialize(jamfile_module, dir, os.path.basename(jamfile_to_load)) - saved_project = self.current_project + if not jamfile_module in self.jamfile_modules: + saved_project = self.current_project + self.jamfile_modules[jamfile_module] = True - bjam.call("load", jamfile_module, jamfile_to_load) - basename = os.path.basename(jamfile_to_load) + bjam.call("load", jamfile_module, jamfile_to_load) - if is_jamroot: - jamfile = self.find_jamfile(dir, no_errors=True) - if jamfile: - bjam.call("load", jamfile_module, jamfile) + if is_jamroot: + jamfile = self.find_jamfile(dir, no_errors=True) + if jamfile: + bjam.call("load", jamfile_module, jamfile) - # Now do some checks - if self.current_project != saved_project: + # Now do some checks + if self.current_project != saved_project: + from textwrap import dedent + self.manager.errors()(dedent( + """ + 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) + )) + + self.end_load(previous_project) + + if self.global_build_dir: + id = self.attributeDefault(jamfile_module, "id", None) + 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 end_load(self, previous_project=None): + if not self.current_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)) + 'Ending project loading requested when there was no project currently ' + 'being loaded.' + ) - if self.global_build_dir: - id = self.attributeDefault(jamfile_module, "id", None) - 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" + if not previous_project and self.saved_current_project: + self.manager.errors()( + 'Ending project loading requested with no "previous project" when there ' + 'other projects still being loaded recursively.' + ) + self.current_project = previous_project def load_standalone(self, jamfile_module, file): """Loads 'file' as standalone project that has no location @@ -388,51 +409,20 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) else: return 0 - def initialize(self, module_name, location=None, basename=None): + def initialize(self, module_name, location=None, basename=None, standalone_path=''): """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, standalone project will be initialized + standalone_path is the path to the source-location. + this should only be called from the python side. """ assert isinstance(module_name, basestring) assert isinstance(location, basestring) or location is None assert isinstance(basename, basestring) or basename is None - 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 = "" - - attributes = ProjectAttributes(self.manager, location, module_name) - self.module2attributes[module_name] = attributes - - python_standalone = False - if location: - attributes.set("source-location", [location], exact=1) - elif not module_name in ["test-config", "site-config", "user-config", "project-config"]: - # This is a standalone project with known location. Set source location - # so that it can declare targets. This is intended so that you can put - # a .jam file in your sources and use it via 'using'. Standard modules - # (in 'tools' subdir) may not assume source dir is set. - attributes.set("source-location", self.loaded_tool_module_path_[module_name], exact=1) - python_standalone = True - - attributes.set("requirements", property_set.empty(), exact=True) - attributes.set("usage-requirements", property_set.empty(), exact=True) - attributes.set("default-build", property_set.empty(), 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, python_standalone) - jamroot = False - - parent_module = None; + parent_module = None if module_name == "test-config": # No parent pass @@ -447,37 +437,74 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) # --- i.e # if the project is not standalone. parent_module = self.load_parent(location) - else: + elif location: # It's either jamroot, or standalone project. # If it's jamroot, inherit from user-config. + # If project-config module exist, inherit from it. + parent_module = 'user-config' + if 'project-config' in self.module2attributes: + parent_module = 'project-config' + jamroot = True + + # 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 = "" + + # the call to load_parent() above can end up loading this module again + # make sure we don't reinitialize the module's attributes + if module_name not in self.module2attributes: + if "--debug-loading" in self.manager.argv(): + print "Initializing project '%s'" % module_name + attributes = ProjectAttributes(self.manager, location, module_name) + self.module2attributes[module_name] = attributes + + python_standalone = False if location: - # If project-config module exist, inherit from it. - if self.module2attributes.has_key("project-config"): - parent_module = "project-config" - else: - parent_module = "user-config" ; + attributes.set("source-location", [location], exact=1) + elif not module_name in ["test-config", "site-config", "user-config", "project-config"]: + # This is a standalone project with known location. Set source location + # so that it can declare targets. This is intended so that you can put + # a .jam file in your sources and use it via 'using'. Standard modules + # (in 'tools' subdir) may not assume source dir is set. + source_location = standalone_path + if not source_location: + source_location = self.loaded_tool_module_path_.get(module_name) + if not source_location: + self.manager.errors()('Standalone module path not found for "{}"' + .format(module_name)) + attributes.set("source-location", [source_location], exact=1) + python_standalone = True - jamroot = True ; + attributes.set("requirements", property_set.empty(), exact=True) + attributes.set("usage-requirements", property_set.empty(), exact=True) + attributes.set("default-build", property_set.empty(), exact=True) + attributes.set("projects-to-build", [], exact=True) + attributes.set("project-root", None, exact=True) + attributes.set("build-dir", None, exact=True) - if parent_module: - self.inherit_attributes(module_name, parent_module) - attributes.set("parent-module", parent_module, exact=1) + self.project_rules_.init_project(module_name, python_standalone) - if jamroot: - attributes.set("project-root", location, exact=1) + if parent_module: + self.inherit_attributes(module_name, parent_module) + attributes.set("parent-module", parent_module, exact=1) - parent = None - if parent_module: - parent = self.target(parent_module) + if jamroot: + attributes.set("project-root", location, exact=1) - 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 + parent = None + if parent_module: + parent = self.target(parent_module) + + if module_name not in self.module2target: + 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) @@ -529,6 +556,11 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) def current(self): """Returns the project which is currently being loaded.""" + if not self.current_project: + get_manager().errors()( + 'Reference to the project currently being loaded requested ' + 'when there was no project module being loaded.' + ) return self.current_project def set_current(self, c): @@ -547,8 +579,10 @@ actual value %s""" % (jamfile_module, saved_project, 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] + if self.saved_current_project: + self.current_project = self.saved_current_project.pop() + else: + self.current_project = None def attributes(self, project): """Returns the project-attribute instance for the @@ -564,7 +598,7 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) try: return self.module2attributes[project].get(attribute) except: - raise BaseException("No attribute '%s' for project" % (attribute, project)) + raise BaseException("No attribute '%s' for project %s" % (attribute, project)) def attributeDefault(self, project, attribute, default): """Returns the value of the specified attribute in the @@ -577,7 +611,7 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) def target(self, project_module): """Returns the project target corresponding to the 'project-module'.""" assert isinstance(project_module, basestring) - if not self.module2target.has_key(project_module): + if project_module not in self.module2target: self.module2target[project_module] = \ b2.build.targets.ProjectTarget(project_module, project_module, self.attribute(project_module, "requirements")) @@ -595,12 +629,12 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) 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.has_key(id) and self.id2module[id] != project_module: + if id in self.id2module and self.id2module[id] != project_module: self.manager.errors()( """Attempt to redeclare already existing project id '%s' at location '%s'""" % (id, location)) self.id2module[id] = project_module - self.current_module = saved_project + self.current_project = saved_project def add_rule(self, name, callable_): """Makes rule 'name' available to all subsequently loaded Jamfiles. @@ -725,15 +759,21 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) # find_module is used so that the pyc's can be used. # an ImportError is raised if not found f, location, description = imp.find_module(name, paths) + except ImportError: + # if the module is not found in the b2 package, + # this error will be handled later + pass + else: + # we've found the module, now let's try loading it. + # it's possible that the module itself contains an ImportError + # which is why we're loading it in this else clause so that the + # proper error message is shown to the end user. + # TODO: does this module name really need to be mangled like this? mname = name + "__for_jamfile" self.loaded_tool_module_path_[mname] = location module = imp.load_module(mname, f, location, description) self.loaded_tool_modules_[name] = module return module - except ImportError: - # if the module is not found in the b2 package, - # this error will be handled later - pass # the cache is created here due to possibly importing packages # that end up calling get_manager() which might fail @@ -750,12 +790,17 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project)) # the module exists within the BOOST_BUILD_PATH, # load it. elif mname: - # __import__ can be used here since the module - # is guaranteed to be found under the `b2` namespace. + # in some cases, self.loaded_tool_module_path_ needs to + # have the path to the file during the import + # (project.initialize() for example), + # so the path needs to be set *before* importing the module. + path = os.path.join(b2.__path__[0], *mname.split('.')[1:]) + self.loaded_tool_module_path_[mname] = path + # mname is guaranteed to be importable since it was + # found within the cache __import__(mname) module = sys.modules[mname] self.loaded_tool_modules_[name] = module - self.loaded_tool_module_path_[mname] = module.__file__ return module self.manager.errors()("Cannot find module '%s'" % name) @@ -1104,8 +1149,8 @@ attribute is allowed only for top-level 'project' invocations""") assert is_iterable_typed(name, basestring) assert is_iterable_typed(value, basestring) if len(value) > 1: - self.registry.manager.error()("path constant should have one element") - self.registry.current().add_constant(name[0], value[0], path=1) + self.registry.manager.errors()("path constant should have one element") + 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 @@ -1128,7 +1173,7 @@ attribute is allowed only for top-level 'project' invocations""") def always(self, target_names): assert is_iterable_typed(target_names, basestring) - self.registry.current().mark_targets_as_alays(target_names) + self.registry.current().mark_targets_as_always(target_names) def glob(self, wildcards, excludes=None): assert is_iterable_typed(wildcards, basestring) @@ -1167,7 +1212,7 @@ attribute is allowed only for top-level 'project' invocations""") location = current.get('location') m = self.registry.load_module(toolset[0], [location]) - if not m.__dict__.has_key("init"): + if "init" not in m.__dict__: self.registry.manager.errors()( "Tool module '%s' does not define the 'init' method" % toolset[0]) m.init(*args) diff --git a/src/build/property.py b/src/build/property.py index 11a18ff38..dff82865c 100644 --- a/src/build/property.py +++ b/src/build/property.py @@ -9,12 +9,15 @@ import re import sys +from functools import total_ordering + from b2.util.utility import * from b2.build import feature from b2.util import sequence, qualify_jam_action, is_iterable_typed import b2.util.set from b2.manager import get_manager + __re_two_ampersands = re.compile ('&&') __re_comma = re.compile (',') __re_split_condition = re.compile ('(.*):(<.*)') @@ -23,50 +26,138 @@ __re_colon = re.compile (':') __re_has_condition = re.compile (r':<') __re_separate_condition_and_property = re.compile (r'(.*):(<.*)') -__not_applicable_feature='not-applicable-in-this-context' -feature.feature(__not_applicable_feature, [], ['free']) +_not_applicable_feature='not-applicable-in-this-context' +feature.feature(_not_applicable_feature, [], ['free']) __abbreviated_paths = False + +class PropertyMeta(type): + """ + This class exists to implement the isinstance() and issubclass() + hooks for the Property class. Since we've introduce the concept of + a LazyProperty, isinstance(p, Property) will fail when p is a LazyProperty. + Implementing both __instancecheck__ and __subclasscheck__ will allow + LazyProperty instances to pass the isinstance() and issubclass check for + the Property class. + + Additionally, the __call__ method intercepts the call to the Property + constructor to ensure that calling Property with the same arguments + will always return the same Property instance. + """ + _registry = {} + current_id = 1 + + def __call__(mcs, f, value, condition=None): + """ + This intercepts the call to the Property() constructor. + + This exists so that the same arguments will always return the same Property + instance. This allows us to give each instance a unique ID. + """ + from b2.build.feature import Feature + if not isinstance(f, Feature): + f = feature.get(f) + if condition is None: + condition = [] + key = (f, value) + tuple(sorted(condition)) + if key not in mcs._registry: + instance = super(PropertyMeta, mcs).__call__(f, value, condition) + mcs._registry[key] = instance + return mcs._registry[key] + + @staticmethod + def check(obj): + return (hasattr(obj, 'feature') and + hasattr(obj, 'value') and + hasattr(obj, 'condition')) + + def __instancecheck__(self, instance): + return self.check(instance) + + def __subclasscheck__(self, subclass): + return self.check(subclass) + + +@total_ordering class Property(object): - __slots__ = ('_feature', '_value', '_condition') + __slots__ = ('feature', 'value', 'condition', '_to_raw', '_hash', 'id') + __metaclass__ = PropertyMeta - def __init__(self, f, value, condition = []): - if type(f) == type(""): - f = feature.get(f) - # At present, single property has a single value. - assert type(value) != type([]) - assert(f.free() or value.find(':') == -1) - self._feature = f - self._value = value - self._condition = condition + def __init__(self, f, value, condition=None): + assert(f.free or ':' not in value) + if condition is None: + condition = [] - def feature(self): - return self._feature + self.feature = f + self.value = value + self.condition = condition + self._hash = hash((self.feature, self.value) + tuple(sorted(self.condition))) + self.id = PropertyMeta.current_id + # increment the id counter. + # this allows us to take a list of Property + # instances and use their unique integer ID + # to create a key for PropertySet caching. This is + # much faster than string comparison. + PropertyMeta.current_id += 1 - def value(self): - return self._value + condition_str = '' + if condition: + condition_str = ",".join(str(p) for p in self.condition) + ':' - def condition(self): - return self._condition + self._to_raw = '{}<{}>{}'.format(condition_str, f.name, value) def to_raw(self): - result = "<" + self._feature.name() + ">" + str(self._value) - if self._condition: - result = ",".join(str(p) for p in self._condition) + ':' + result - return result + return self._to_raw def __str__(self): - return self.to_raw() + + return self._to_raw def __hash__(self): - # FIXME: consider if this class should be value-is-identity one - return hash((self._feature, self._value, tuple(self._condition))) + return self._hash - def __cmp__(self, other): - return cmp((self._feature.name(), self._value, self._condition), - (other._feature.name(), other._value, other._condition)) + def __eq__(self, other): + return self._hash == other._hash + + def __lt__(self, other): + return (self.feature.name, self.value) < (other.feature.name, other.value) + + +@total_ordering +class LazyProperty(object): + def __init__(self, feature_name, value, condition=None): + if condition is None: + condition = [] + + self.__property = Property( + feature.get(_not_applicable_feature), feature_name + value, condition=condition) + self.__name = feature_name + self.__value = value + self.__condition = condition + self.__feature = None + + def __getattr__(self, item): + if self.__feature is None: + try: + self.__feature = feature.get(self.__name) + self.__property = Property(self.__feature, self.__value, self.__condition) + except KeyError: + pass + return getattr(self.__property, item) + + def __hash__(self): + return hash(self.__property) + + def __str__(self): + return self.__property._to_raw + + def __eq__(self, other): + return self.__property == other + + def __lt__(self, other): + return (self.feature.name, self.value) < (other.feature.name, other.value) def create_from_string(s, allow_condition=False,allow_missing_value=False): @@ -89,17 +180,24 @@ def create_from_string(s, allow_condition=False,allow_missing_value=False): # FIXME: break dependency cycle from b2.manager import get_manager + if condition: + condition = [create_from_string(x) for x in condition.split(',')] + feature_name = get_grist(s) if not feature_name: if feature.is_implicit_value(s): f = feature.implied_feature(s) value = s + p = Property(f, value, condition=condition) else: raise get_manager().errors()("Invalid property '%s' -- unknown feature" % s) else: + value = get_value(s) + if not value and not allow_missing_value: + get_manager().errors()("Invalid property '%s' -- no value specified" % s) + if feature.valid(feature_name): - f = feature.get(feature_name) - value = get_value(s) + p = Property(feature.get(feature_name), value, condition=condition) else: # In case feature name is not known, it is wrong to do a hard error. # Feature sets change depending on the toolset. So e.g. @@ -112,17 +210,9 @@ def create_from_string(s, allow_condition=False,allow_missing_value=False): # The underlying cause for this problem is that python port Property # is more strict than its Jam counterpart and must always reference # a valid feature. - f = feature.get(__not_applicable_feature) - value = s + p = LazyProperty(feature_name, value, condition=condition) - if not value and not allow_missing_value: - get_manager().errors()("Invalid property '%s' -- no value specified" % s) - - - if condition: - condition = [create_from_string(x) for x in condition.split(',')] - - return Property(f, value, condition) + return p def create_from_strings(string_list, allow_condition=False): assert is_iterable_typed(string_list, basestring) @@ -141,6 +231,9 @@ reset () def set_abbreviated_paths(on=True): global __abbreviated_paths + if on == 'off': + on = False + on = bool(on) __abbreviated_paths = on @@ -199,19 +292,19 @@ def refine (properties, requirements): # Record them so that we can handle 'properties'. for r in requirements: # Don't consider conditional requirements. - if not r.condition(): - required[r.feature()] = r + if not r.condition: + required[r.feature] = r for p in properties: # Skip conditional properties - if p.condition(): + if p.condition: result.add(p) # No processing for free properties - elif p.feature().free(): + elif p.feature.free: result.add(p) else: - if required.has_key(p.feature()): - result.add(required[p.feature()]) + if p.feature in required: + result.add(required[p.feature]) else: result.add(p) @@ -222,17 +315,18 @@ def translate_paths (properties, path): The property values are assumed to be in system-specific form, and will be translated into normalized form. """ + assert is_iterable_typed(properties, Property) result = [] for p in properties: - if p.feature().path(): - values = __re_two_ampersands.split(p.value()) + if p.feature.path: + values = __re_two_ampersands.split(p.value) - new_value = "&&".join(os.path.join(path, v) for v in values) + new_value = "&&".join(os.path.normpath(os.path.join(path, v)) for v in values) - if new_value != p.value(): - result.append(Property(p.feature(), new_value, p.condition())) + if new_value != p.value: + result.append(Property(p.feature, new_value, p.condition)) else: result.append(p) @@ -250,10 +344,10 @@ def translate_indirect(properties, context_module): assert isinstance(context_module, basestring) result = [] for p in properties: - if p.value()[0] == '@': - q = qualify_jam_action(p.value()[1:], context_module) + if p.value[0] == '@': + q = qualify_jam_action(p.value[1:], context_module) get_manager().engine().register_bjam_action(q) - result.append(Property(p.feature(), '@' + q, p.condition())) + result.append(Property(p.feature, '@' + q, p.condition)) else: result.append(p) @@ -274,22 +368,25 @@ def expand_subfeatures_in_conditions (properties): result = [] for p in properties: - if not p.condition(): + if not p.condition: result.append(p) else: expanded = [] - for c in p.condition(): + for c in p.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 don't validate. + expanded.extend(feature.expand_subfeatures ([c], True)) - if c.feature().name().startswith("toolset") or c.feature().name() == "os": - # 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. - expanded.extend(feature.expand_subfeatures ([c], True)) - else: - expanded.extend(feature.expand_subfeatures([c])) - - result.append(Property(p.feature(), p.value(), expanded)) + # we need to keep LazyProperties lazy + if isinstance(p, LazyProperty): + value = p.value + feature_name = get_grist(value) + value = value.replace(feature_name, '') + result.append(LazyProperty(feature_name, value, condition=expanded)) + else: + result.append(Property(p.feature, p.value, expanded)) return result @@ -341,7 +438,7 @@ def evaluate_conditionals_in_context (properties, context): conditional = [] for p in properties: - if p.condition(): + if p.condition: conditional.append (p) else: base.append (p) @@ -351,8 +448,8 @@ def evaluate_conditionals_in_context (properties, context): # Evaluate condition # FIXME: probably inefficient - if all(x in context for x in p.condition()): - result.append(Property(p.feature(), p.value())) + if all(x in context for x in p.condition): + result.append(Property(p.feature, p.value)) return result @@ -389,8 +486,8 @@ def __validate1 (property): assert isinstance(property, Property) msg = None - if not property.feature().free(): - feature.validate_value_string (property.feature(), property.value()) + if not property.feature.free: + feature.validate_value_string (property.feature, property.value) ################################################################### @@ -460,10 +557,10 @@ def translate_dependencies(properties, project_id, location): result = [] for p in properties: - if not p.feature().dependency(): + if not p.feature.dependency: result.append(p) else: - v = p.value() + v = p.value m = re.match("(.*)//(.*)", v) if m: rooted = m.group(1) @@ -473,12 +570,12 @@ def translate_dependencies(properties, project_id, location): else: rooted = os.path.join(os.getcwd(), location, rooted) - result.append(Property(p.feature(), rooted + "//" + m.group(2), p.condition())) + result.append(Property(p.feature, rooted + "//" + m.group(2), p.condition)) elif os.path.isabs(v): result.append(p) else: - result.append(Property(p.feature(), project_id + "//" + v, p.condition())) + result.append(Property(p.feature, project_id + "//" + v, p.condition)) return result diff --git a/src/build/property_set.py b/src/build/property_set.py index 494a5b1b7..3fc86de27 100644 --- a/src/build/property_set.py +++ b/src/build/property_set.py @@ -44,14 +44,18 @@ def create (raw_properties = []): x = raw_properties else: x = [property.create_from_string(ps) for ps in raw_properties] - x.sort() - x = unique(x, stable=True) - # FIXME: can we do better, e.g. by directly computing - # hash value of the list? - key = tuple(x) + # These two lines of code are optimized to the current state + # of the Property class. Since this function acts as the caching + # frontend to the PropertySet class modifying these two lines + # could have a severe performance penalty. Be careful. + # It would be faster to sort by p.id, but some projects may rely + # on the fact that the properties are ordered alphabetically. So, + # we maintain alphabetical sorting so as to maintain backward compatibility. + x = sorted(set(x), key=lambda p: (p.feature.name, p.value, p.condition)) + key = tuple(p.id for p in x) - if not __cache.has_key (key): + if key not in __cache: __cache [key] = PropertySet(x) return __cache [key] @@ -154,16 +158,13 @@ class PropertySet: - several operations, like and refine and as_path are provided. They all use caching whenever possible. """ - def __init__ (self, properties = []): + def __init__ (self, properties=None): + if properties is None: + properties = [] assert is_iterable_typed(properties, property.Property) - raw_properties = [] - for p in properties: - raw_properties.append(p.to_raw()) - self.all_ = properties - self.all_raw_ = raw_properties - self.all_set_ = set(properties) + self._all_set = {p.id for p in properties} self.incidental_ = [] self.free_ = [] @@ -207,39 +208,42 @@ class PropertySet: # 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)) - - if 'propagated' in att: - self.propagated_.append (p) - - if 'link_incompatible' in att: - self.link_incompatible.append (p) + # stores the list of LazyProperty instances. + # these are being kept separate from the normal + # Property instances so that when this PropertySet + # tries to return one of its attributes, it + # will then try to evaluate the LazyProperty instances + # first before returning. + self.lazy_properties = [] for p in properties: - + f = p.feature + if isinstance(p, property.LazyProperty): + self.lazy_properties.append(p) # A feature can be both incidental and free, # in which case we add it to incidental. - if p.feature().incidental(): + elif f.incidental: self.incidental_.append(p) - elif p.feature().free(): + elif f.free: self.free_.append(p) else: self.base_.append(p) - if p.condition(): + if p.condition: self.conditional_.append(p) else: self.non_conditional_.append(p) - if p.feature().dependency(): + if f.dependency: self.dependency_.append (p) - else: + elif not isinstance(p, property.LazyProperty): self.non_dependency_.append (p) + if f.propagated: + self.propagated_.append(p) + if f.link_incompatible: + self.link_incompatible.append(p) + def all(self): return self.all_ @@ -247,33 +251,48 @@ class PropertySet: def raw (self): """ Returns the list of stored properties. """ - return self.all_raw_ + # create a new list due to the LazyProperties. + # this gives them a chance to evaluate to their + # true Property(). This approach is being + # taken since calculations should not be using + # PropertySet.raw() + return [p._to_raw for p in self.all_] def __str__(self): - return ' '.join(str(p) for p in self.all_) + return ' '.join(p._to_raw for p in self.all_) def base (self): """ Returns properties that are neither incidental nor free. """ - return self.base_ + result = [p for p in self.lazy_properties + if not(p.feature.incidental or p.feature.free)] + result.extend(self.base_) + return result def free (self): """ Returns free properties which are not dependency properties. """ - return self.free_ + result = [p for p in self.lazy_properties + if not p.feature.incidental and p.feature.free] + result.extend(self.free_) + return result def non_free(self): - return self.base_ + self.incidental_ + return self.base() + self.incidental() def dependency (self): """ Returns dependency properties. """ + result = [p for p in self.lazy_properties if p.feature.dependency] + result.extend(self.dependency_) return self.dependency_ def non_dependency (self): """ Returns properties that are not dependencies. """ - return self.non_dependency_ + result = [p for p in self.lazy_properties if not p.feature.dependency] + result.extend(self.non_dependency_) + return result def conditional (self): """ Returns conditional properties. @@ -288,13 +307,15 @@ class PropertySet: def incidental (self): """ Returns incidental properties. """ - return self.incidental_ + result = [p for p in self.lazy_properties if p.feature.incidental] + result.extend(self.incidental_) + return result def refine (self, requirements): """ Refines this set's properties using the requirements passed as an argument. """ assert isinstance(requirements, PropertySet) - if not self.refined_.has_key (requirements): + if requirements not in self.refined_: r = property.refine(self.all_, requirements.all_) self.refined_[requirements] = create(r) @@ -317,7 +338,7 @@ class PropertySet: if not context: context = self - if not self.evaluated_.has_key(context): + if context not in self.evaluated_: # FIXME: figure why the call messes up first parameter self.evaluated_[context] = create( property.evaluate_conditionals_in_context(self.all(), context)) @@ -342,13 +363,13 @@ class PropertySet: def path_order (p1, p2): - i1 = p1.feature().implicit() - i2 = p2.feature().implicit() + i1 = p1.feature.implicit + i2 = p2.feature.implicit if i1 != i2: return i2 - i1 else: - return cmp(p1.feature().name(), p2.feature().name()) + return cmp(p1.feature.name, p2.feature.name) # trim redundancy properties = feature.minimize(self.base_) @@ -358,15 +379,16 @@ class PropertySet: components = [] for p in properties: - if p.feature().implicit(): - components.append(p.value()) + f = p.feature + if f.implicit: + components.append(p.value) else: - value = p.feature().name() + "-" + p.value() + value = f.name.replace(':', '-') + "-" + p.value if property.get_abbreviated_paths(): value = abbreviate_dashed(value) components.append(value) - self.as_path_ = '/'.join (components) + self.as_path_ = '/'.join(components) return self.as_path_ @@ -421,7 +443,7 @@ class PropertySet: plus the ones of the property set passed as argument. """ assert isinstance(ps, PropertySet) - if not self.added_.has_key(ps): + if ps not in self.added_: self.added_[ps] = create(self.all_ + ps.all()) return self.added_[ps] @@ -441,13 +463,13 @@ class PropertySet: feature = b2.build.feature.get(feature) assert isinstance(feature, b2.build.feature.Feature) - if not self.feature_map_: + if self.feature_map_ is None: self.feature_map_ = {} for v in self.all_: - if not self.feature_map_.has_key(v.feature()): - self.feature_map_[v.feature()] = [] - self.feature_map_[v.feature()].append(v.value()) + if v.feature not in self.feature_map_: + self.feature_map_[v.feature] = [] + self.feature_map_[v.feature].append(v.value) return self.feature_map_.get(feature, []) @@ -460,12 +482,12 @@ class PropertySet: result = [] for p in self.all_: - if p.feature() == feature: + if p.feature == feature: result.append(p) return result def __contains__(self, item): - return item in self.all_set_ + return item.id in self._all_set def hash(p): m = hashlib.md5() diff --git a/src/build/scanner.py b/src/build/scanner.py index ada5d8325..ec3b31b40 100644 --- a/src/build/scanner.py +++ b/src/build/scanner.py @@ -28,11 +28,9 @@ # 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 import os -from b2.exceptions import * from b2.manager import get_manager from b2.util import is_iterable_typed @@ -65,7 +63,7 @@ def register(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)) + return str(scanner_class) in __scanners def get(scanner_class, properties): """ Returns an instance of previously registered scanner @@ -83,7 +81,7 @@ def get(scanner_class, properties): scanner_id = scanner_name + '.' + '-'.join(r) - if not __scanner_cache.has_key(scanner_id): + if scanner_id not in __scanner_cache: __scanner_cache[scanner_id] = scanner_class(r) return __scanner_cache[scanner_id] @@ -99,7 +97,7 @@ class Scanner: """ raise BaseException ("method must be overriden") - def process (self, target, matches): + def process (self, target, matches, binding): """ Establish necessary relationship between targets, given actual target beeing scanned, and a list of pattern matches in that file. @@ -141,7 +139,7 @@ class ScannerRegistry: assert isinstance(vtarget, basestring) engine = self.manager_.engine() engine.set_target_variable(target, "HDRSCAN", scanner.pattern()) - if not self.exported_scanners_.has_key(scanner): + if scanner not in self.exported_scanners_: exported_name = "scanner_" + str(self.count_) self.count_ = self.count_ + 1 self.exported_scanners_[scanner] = exported_name diff --git a/src/build/targets.py b/src/build/targets.py index 043d90666..6f71177c3 100644 --- a/src/build/targets.py +++ b/src/build/targets.py @@ -82,6 +82,7 @@ from virtual_target import Subvariant from b2.exceptions import * from b2.util.sequence import unique from b2.util import path, bjam_signature, safe_isinstance, is_iterable_typed +from b2.build import errors from b2.build.errors import user_error_checkpoint import b2.build.build_request as build_request @@ -155,6 +156,8 @@ class TargetRegistry: 'project' is the project where the main taret is to be declared.""" assert is_iterable_typed(specification, basestring) assert isinstance(project, ProjectTarget) + # create a copy since the list is being modified + specification = list(specification) specification.extend(toolset.requirements()) requirements = property_set.refine_from_user_input( @@ -202,7 +205,7 @@ class TargetRegistry: """ Helper rules to detect cycles in main target references. """ assert isinstance(main_target_instance, MainTarget) - if self.targets_being_built_.has_key(id(main_target_instance)): + if id(main_target_instance) in self.targets_being_built_: names = [] for t in self.targets_being_built_.values() + [main_target_instance]: names.append (t.full_name()) @@ -213,7 +216,7 @@ class TargetRegistry: def end_building (self, main_target_instance): assert isinstance(main_target_instance, MainTarget) - assert (self.targets_being_built_.has_key (id (main_target_instance))) + assert (id(main_target_instance) in self.targets_being_built_) del self.targets_being_built_ [id (main_target_instance)] def create_typed_target (self, type, project, name, sources, requirements, default_build, usage_requirements): @@ -308,6 +311,7 @@ class AbstractTarget: self.name_ = name self.project_ = project + self.location_ = errors.nearest_user_location() def manager (self): return self.manager_ @@ -499,7 +503,7 @@ class ProjectTarget (AbstractTarget): if not self.built_main_targets_: self.build_main_targets() - return self.main_target_.has_key(name) + return name in self.main_target_ def create_main_target (self, name): """ Returns a 'MainTarget' class instance corresponding to the 'name'. @@ -527,8 +531,16 @@ class ProjectTarget (AbstractTarget): target_part = None if split: - project_part = split.group (1) - target_part = split.group (2) + project_part = split.group(1) + target_part = split.group(2) + if not target_part: + get_manager().errors()( + 'Project ID, "{}", is not a valid target reference. There should ' + 'be either a target name after the "//" or the "//" should be removed ' + 'from the target reference.' + .format(id) + ) + project_registry = self.project_.manager ().projects () @@ -595,7 +607,7 @@ class ProjectTarget (AbstractTarget): for a in self.alternatives_: name = a.name () - if not self.main_target_.has_key (name): + if name not in self.main_target_: t = MainTarget (name, self.project_) self.main_target_ [name] = t @@ -613,7 +625,7 @@ class ProjectTarget (AbstractTarget): to the location of project. """ assert isinstance(name, basestring) - assert isinstance(value, basestring) + assert is_iterable_typed(value, basestring) assert isinstance(path, int) # will also match bools if path: l = self.location_ @@ -625,10 +637,10 @@ class ProjectTarget (AbstractTarget): # targets in config files, but that's for later. l = self.get('source-location') - value = os.path.join(l, value) + value = os.path.join(l, value[0]) # Now make the value absolute path. Constants should be in # platform-native form. - value = os.path.normpath(os.path.join(os.getcwd(), value)) + value = [os.path.normpath(os.path.join(os.getcwd(), value))] self.constants_[name] = value bjam.call("set-variable", self.project_module(), name, value) @@ -658,12 +670,13 @@ class MainTarget (AbstractTarget): def __init__ (self, name, project): AbstractTarget.__init__ (self, name, project) self.alternatives_ = [] + self.best_alternative = None self.default_build_ = property_set.empty () def add_alternative (self, target): """ Add a new alternative for this target. """ - assert isinstance(target, AbstractTarget) + assert isinstance(target, BasicTarget) d = target.default_build () if self.alternatives_ and self.default_build_ != d: @@ -769,6 +782,7 @@ class MainTarget (AbstractTarget): """ assert isinstance(prop_set, property_set.PropertySet) best_alternative = self.__select_alternatives (prop_set, debug=0) + self.best_alternative = best_alternative if not best_alternative: # FIXME: revive. @@ -881,9 +895,9 @@ class BasicTarget (AbstractTarget): """ def __init__ (self, name, project, sources, requirements = None, default_build = None, usage_requirements = None): assert is_iterable_typed(sources, basestring) - assert isinstance(requirements, property_set.PropertySet) - assert isinstance(default_build, property_set.PropertySet) - assert isinstance(usage_requirements, property_set.PropertySet) + assert isinstance(requirements, property_set.PropertySet) or requirements is None + assert isinstance(default_build, property_set.PropertySet) or default_build is None + assert isinstance(usage_requirements, property_set.PropertySet) or usage_requirements is None AbstractTarget.__init__ (self, name, project) for s in sources: @@ -955,14 +969,14 @@ class BasicTarget (AbstractTarget): free_unconditional = [] other = [] for p in requirements.all(): - if p.feature().free() and not p.condition() and p.feature().name() != 'conditional': + if p.feature.free and not p.condition and p.feature.name != 'conditional': free_unconditional.append(p) else: other.append(p) other = property_set.create(other) key = (build_request, other) - if not self.request_cache.has_key(key): + if key not in self.request_cache: self.request_cache[key] = self.__common_properties2 (build_request, other) return self.request_cache[key].add_raw(free_unconditional) @@ -1143,10 +1157,10 @@ class BasicTarget (AbstractTarget): usage_requirements = [] for p in properties: - result = generate_from_reference(p.value(), self.project_, ps) + result = generate_from_reference(p.value, self.project_, ps) for t in result.targets(): - result_properties.append(property.Property(p.feature(), t)) + result_properties.append(property.Property(p.feature, t)) usage_requirements += result.usage_requirements().all() @@ -1179,7 +1193,7 @@ class BasicTarget (AbstractTarget): self.manager().targets().push_target(self) - if not self.generated_.has_key(ps): + if ps not in self.generated_: # Apply free features form the command line. If user # said @@ -1313,9 +1327,18 @@ class BasicTarget (AbstractTarget): # they are propagated only to direct dependents. We might need # a more general mechanism, but for now, only those two # features are special. - removed_pch = filter(lambda prop: prop.feature().name() not in ['', ''], subvariant.sources_usage_requirements().all()) - result = result.add(property_set.PropertySet(removed_pch)) + properties = [] + for p in subvariant.sources_usage_requirements().all(): + if p.feature.name not in ('pch-header', 'pch-file'): + properties.append(p) + if 'shared' in rproperties.get('link'): + new_properties = [] + for p in properties: + if p.feature.name != 'library': + new_properties.append(p) + properties = new_properties + result = result.add_raw(properties) return result def create_subvariant (self, root_targets, all_targets, @@ -1398,11 +1421,9 @@ def apply_default_build(property_set_, default_build): assert isinstance(property_set_, property_set.PropertySet) assert isinstance(default_build, property_set.PropertySet) - specified_features = set(p.feature() for p in property_set_.all()) - defaults_to_apply = [] for d in default_build.all(): - if not d.feature() in specified_features: + if not property_set_.get(d.feature): defaults_to_apply.append(d) # 2. If there's any defaults to be applied, form the new @@ -1426,10 +1447,16 @@ def apply_default_build(property_set_, default_build): # be an indication that # build_request.expand-no-defaults is the wrong rule # to use here. - compressed = feature.compress_subproperties(property_set_.all()) + properties = build_request.expand_no_defaults( + [property_set.create([p]) for p in + feature.compress_subproperties(property_set_.all()) + defaults_to_apply] + ) - result = build_request.expand_no_defaults( - b2.build.property_set.create(feature.expand([p])) for p in (compressed + defaults_to_apply)) + if properties: + for p in properties: + result.append(property_set.create(feature.expand(p.all()))) + else: + result = [property_set.empty()] else: result.append (property_set_) diff --git a/src/build/toolset.py b/src/build/toolset.py index 672d18f5a..cf2b24b2e 100644 --- a/src/build/toolset.py +++ b/src/build/toolset.py @@ -9,6 +9,7 @@ """ Support for toolset definition. """ +import sys import feature, property, generators, property_set import b2.util.set @@ -16,13 +17,14 @@ import bjam from b2.util import cached, qualify_jam_action, is_iterable_typed, is_iterable from b2.util.utility import * -from b2.util import bjam_signature +from b2.util import bjam_signature, sequence from b2.manager import get_manager __re_split_last_segment = re.compile (r'^(.+)\.([^\.])*') __re_two_ampersands = re.compile ('(&&)') __re_first_segment = re.compile ('([^.]*).*') __re_first_group = re.compile (r'[^.]*\.(.*)') +_ignore_toolset_requirements = '--ignore-toolset-requirements' not in sys.argv # Flag is a mechanism to set a value # A single toolset flag. Specifies that when certain @@ -76,8 +78,10 @@ reset () # FIXME: --ignore-toolset-requirements def using(toolset_module, *args): - loaded_toolset_module= get_manager().projects().load_module(toolset_module, [os.getcwd()]); - loaded_toolset_module.init(*args) + if isinstance(toolset_module, (list, tuple)): + toolset_module = toolset_module[0] + loaded_toolset_module= get_manager().projects().load_module(toolset_module, [os.getcwd()]); + loaded_toolset_module.init(*args) # FIXME push-checking-for-flags-module .... # FIXME: investigate existing uses of 'hack-hack' parameter @@ -182,16 +186,14 @@ def find_satisfied_condition(conditions, ps): 'properties', or an empty list if no such element exists.""" assert is_iterable_typed(conditions, property_set.PropertySet) assert isinstance(ps, property_set.PropertySet) - features = set(p.feature() for p in ps.all()) for condition in conditions: found_all = True for i in condition.all(): - found = False - if i.value(): - found = i.value() in ps.get(i.feature()) + if i.value: + found = i.value in ps.get(i.feature) else: # Handle value-less properties like '' (compare with # 'x86'). @@ -204,7 +206,7 @@ def find_satisfied_condition(conditions, ps): # foo foo foo no match # foo foo foo no match # foo foo foo foo match - found = not i.feature() in features + found = not ps.get(i.feature) found_all = found_all and found @@ -337,12 +339,12 @@ def __handle_flag_value (manager, value, ps): for value in values: - if f.dependency(): + if f.dependency: # the value of a dependency feature is a target # and must be actualized result.append(value.actualize()) - elif f.path() or f.free(): + elif f.path or f.free: # Treat features with && in the value # specially -- each &&-separated element is considered @@ -354,11 +356,11 @@ def __handle_flag_value (manager, value, ps): else: result.extend(value.split ('&&')) else: - result.append (ungristed) + result.append (value) else: result.append (value) - return result + return sequence.unique(result, stable=True) def __add_flag (rule_or_module, variable_name, condition, values): """ Adds a new flag setting with the specified values. @@ -393,10 +395,9 @@ def add_requirements(requirements): be conditional or indirect conditional.""" assert is_iterable_typed(requirements, basestring) - #if ! $(.ignore-requirements) - #{ - __requirements.extend(requirements) - #} + if _ignore_toolset_requirements: + __requirements.extend(requirements) + # Make toolset 'toolset', defined in a module of the same name, # inherit from 'base' @@ -409,7 +410,7 @@ def add_requirements(requirements): def inherit(toolset, base): assert isinstance(toolset, basestring) assert isinstance(base, basestring) - get_manager().projects().load_module(base, []); + get_manager().projects().load_module(base, ['.']); inherit_generators(toolset, [], base) inherit_flags(toolset, base) diff --git a/src/build/type.py b/src/build/type.py index c8d6334c7..3d9b7c1b2 100644 --- a/src/build/type.py +++ b/src/build/type.py @@ -71,14 +71,19 @@ def register (type, suffixes = [], base_type = None): if __re_hyphen.search (type): raise BaseException ('type name "%s" contains a hyphen' % type) - if __types.has_key (type): + # it's possible for a type to be registered with a + # base type that hasn't been registered yet. in the + # check for base_type below and the following calls to setdefault() + # the key `type` will be added to __types. When the base type + # actually gets registered, it would fail after the simple check + # of "type in __types"; thus the check for "'base' in __types[type]" + if type in __types and 'base' in __types[type]: raise BaseException ('Type "%s" is already registered.' % type) - entry = {} - entry ['base'] = base_type - entry ['derived'] = [] - entry ['scanner'] = None - __types [type] = entry + entry = __types.setdefault(type, {}) + entry['base'] = base_type + entry.setdefault('derived', []) + entry.setdefault('scanner', None) if base_type: __types.setdefault(base_type, {}).setdefault('derived', []).append(type) @@ -122,7 +127,7 @@ def register_suffixes (suffixes, type): assert is_iterable_typed(suffixes, basestring) assert isinstance(type, basestring) for s in suffixes: - if __suffixes_to_types.has_key (s): + if s in __suffixes_to_types: 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)) @@ -133,7 +138,7 @@ def registered (type): """ Returns true iff type has been registered. """ assert isinstance(type, basestring) - return __types.has_key (type) + return type in __types def validate (type): """ Issues an error if 'type' is unknown. @@ -250,16 +255,23 @@ def generated_target_suffix(type, properties): assert isinstance(properties, PropertySet) 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. -@bjam_signature((["type"], ["properties", "*"], ["suffix"])) + +@bjam_signature((["type"], ["properties", "*"], ["prefix"])) def set_generated_target_prefix(type, properties, prefix): + """ + Sets a file prefix to be used when generating a target of 'type' with the + specified properties. Can be called with no properties if no prefix has + already been specified for the 'type'. The 'prefix' parameter can be an empty + string ("") to indicate that no prefix should be used. + + Note that this does not cause files with 'prefix' to be automatically + recognized as being of 'type'. Two different types can use the same prefix for + their generated files but only one type can be auto-detected for a file with + that prefix. User should explicitly specify which one using the + register-prefixes rule. + + Usage example: library names use the "lib" prefix on unix. + """ set_generated_target_ps(0, type, properties, prefix) # Change the prefix previously registered for this type/properties combination. @@ -349,7 +361,7 @@ def type(filename): if not suffix: return None suffix = suffix[1:] - if __suffixes_to_types.has_key(suffix): + if suffix in __suffixes_to_types: return __suffixes_to_types[suffix] # NOTE: moved from tools/types/register diff --git a/src/build/version.py b/src/build/version.py index 1efe3b5c5..88299060e 100644 --- a/src/build/version.py +++ b/src/build/version.py @@ -10,12 +10,12 @@ from b2.manager import get_manager MANAGER = get_manager() ERROR_HANDLER = MANAGER.errors() -_major = "2014" -_minor = "03" +_major = "2015" +_minor = "07" def boost_build(): - return "{}.{}-svn".format(_major, _minor) + return "{}.{}-git".format(_major, _minor) def verify_engine_version(): diff --git a/src/build/virtual_target.py b/src/build/virtual_target.py index ea4b24d82..e5a130476 100644 --- a/src/build/virtual_target.py +++ b/src/build/virtual_target.py @@ -117,7 +117,7 @@ class VirtualTargetRegistry: signature = "-" + target.name() result = None - if not self.cache_.has_key (signature): + if signature not in self.cache_: self.cache_ [signature] = [] for t in self.cache_ [signature]: @@ -166,7 +166,7 @@ class VirtualTargetRegistry: path = os.path.join(os.getcwd(), file_location, file) path = os.path.normpath(path) - if self.files_.has_key (path): + if path in self.files_: return self.files_ [path] file_type = b2.build.type.type (file) @@ -200,7 +200,7 @@ class VirtualTargetRegistry: def register_actual_name (self, actual_name, virtual_target): assert isinstance(actual_name, basestring) assert isinstance(virtual_target, VirtualTarget) - if self.actual_.has_key (actual_name): + if actual_name in self.actual_: cs1 = self.actual_ [actual_name].creating_subvariant () cs2 = virtual_target.creating_subvariant () cmt1 = cs1.main_target () @@ -218,10 +218,12 @@ class VirtualTargetRegistry: p2 = p2.raw () properties_removed = set.difference (p1, p2) - if not properties_removed: properties_removed = "none" + if not properties_removed: + properties_removed = ["none"] properties_added = set.difference (p2, p1) - if not properties_added: properties_added = "none" + if not properties_added: + properties_added = ["none"] # FIXME: Revive printing of real location. get_manager().errors()( @@ -230,13 +232,14 @@ class VirtualTargetRegistry: "created from '%s'\n" "another virtual target '%s'\n" "created from '%s'\n" - "added properties: '%s'\n" - "removed properties: '%s'\n" + "added properties:\n%s\n" + "removed properties:\n%s\n" % (actual_name, - self.actual_ [actual_name], "loc", #cmt1.location (), + self.actual_ [actual_name], cmt1.project().location(), virtual_target, - "loc", #cmt2.location (), - properties_added, properties_removed)) + cmt2.project().location(), + '\n'.join('\t' + p for p in properties_added), + '\n'.join('\t' + p for p in properties_removed))) else: self.actual_ [actual_name] = virtual_target @@ -334,7 +337,7 @@ class VirtualTarget: name = replace_grist (actual_name, '<' + g + '>') - if not self.made_.has_key (name): + if name not in self.made_: self.made_ [name] = True self.project_.manager ().engine ().add_dependency (name, actual_name) @@ -949,9 +952,9 @@ class NonScanningAction(Action): #be removed? -- Steven Watanabe Action.__init__(self, b2.manager.get_manager(), sources, action_name, property_set) - def actualize_source_type(self, sources, property_set): + def actualize_source_type(self, sources, ps=None): assert is_iterable_typed(sources, VirtualTarget) - assert isinstance(property_set, property_set.PropertySet) + assert isinstance(ps, property_set.PropertySet) or ps is None result = [] for s in sources: result.append(s.actualize()) @@ -1099,8 +1102,8 @@ class Subvariant: or as dependency properties. Targets referred with dependency property are returned a properties, not targets.""" if __debug__: - from .targets import GenerateResult - assert isinstance(result, GenerateResult) + from .property import Property + assert is_iterable_typed(result, (VirtualTarget, Property)) # Find directly referenced targets. deps = self.build_properties().dependency() all_targets = self.sources_ + deps @@ -1111,7 +1114,7 @@ class Subvariant: if not e in result: result.add(e) if isinstance(e, property.Property): - t = e.value() + t = e.value else: t = e diff --git a/src/build_system.py b/src/build_system.py index b5a3b2775..6e78ced50 100644 --- a/src/build_system.py +++ b/src/build_system.py @@ -8,6 +8,15 @@ # 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 sys +import re + +import bjam + +# set this early on since some of the following modules +# require looking at the sys.argv +sys.argv = bjam.variable("ARGV") from b2.build.engine import Engine @@ -16,13 +25,11 @@ 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 from b2.build.toolset import using -import b2.build.project as project import b2.build.virtual_target as virtual_target import b2.build.build_request as build_request @@ -32,13 +39,6 @@ from b2.manager import get_manager from b2.util import cached from b2.util import option - -import bjam - -import os -import sys -import re - ################################################################################ # # Module global data. @@ -327,6 +327,8 @@ def load_configuration_files(): initialize_config_module('project-config', os.path.dirname(file[0])) load_config('project-config', "project-config.jam", [os.path.dirname(file[0])], True) + get_manager().projects().end_load() + # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or # toolset=xx,yy,...zz in the command line. May return additional properties to @@ -421,8 +423,6 @@ def should_clean_project(project): def main(): - sys.argv = bjam.variable("ARGV") - # FIXME: document this option. if "--profiling" in sys.argv: import cProfile @@ -624,191 +624,6 @@ def main_real(): for t in virtual_targets: actual_targets.append(t.actualize()) - - # FIXME: restore -## # If XML data output has been requested prepare additional rules and targets -## # so we can hook into Jam to collect build data while its building and have -## # it trigger the final XML report generation after all the planned targets -## # have been built. -## if $(.out-xml) -## { -## # Get a qualified virtual target name. -## rule full-target-name ( target ) -## { -## local name = [ $(target).name ] ; -## local project = [ $(target).project ] ; -## local project-path = [ $(project).get location ] ; -## return $(project-path)//$(name) ; -## } - -## # Generate an XML file containing build statistics for each constituent. -## # -## rule out-xml ( xml-file : constituents * ) -## { -## # Prepare valid XML header and footer with some basic info. -## local nl = " -## " ; -## local jam = [ version.jam ] ; -## local os = [ modules.peek : OS OSPLAT JAMUNAME ] "" ; -## local timestamp = [ modules.peek : JAMDATE ] ; -## local cwd = [ PWD ] ; -## local command = $(.sys.argv) ; -## local bb-version = [ version.boost-build ] ; -## .header on $(xml-file) = -## "" -## "$(nl)" -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " -## ; -## .footer on $(xml-file) = -## "$(nl)" ; - -## # Generate the target dependency graph. -## .contents on $(xml-file) += -## "$(nl) " ; -## for local t in [ virtual-target.all-targets ] -## { -## local action = [ $(t).action ] ; -## if $(action) -## # If a target has no action, it has no dependencies. -## { -## local name = [ full-target-name $(t) ] ; -## local sources = [ $(action).sources ] ; -## local dependencies ; -## for local s in $(sources) -## { -## dependencies += [ full-target-name $(s) ] ; -## } - -## local path = [ $(t).path ] ; -## local jam-target = [ $(t).actual-name ] ; - -## .contents on $(xml-file) += -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " -## ; -## } -## } -## .contents on $(xml-file) += -## "$(nl) " ; - -## # Build $(xml-file) after $(constituents). Do so even if a -## # constituent action fails and regenerate the xml on every bjam run. -## INCLUDES $(xml-file) : $(constituents) ; -## ALWAYS $(xml-file) ; -## __ACTION_RULE__ on $(xml-file) = build-system.out-xml.generate-action ; -## out-xml.generate $(xml-file) ; -## } - -## # The actual build actions are here; if we did this work in the actions -## # clause we would have to form a valid command line containing the -## # result of @(...) below (the name of the XML file). -## # -## rule out-xml.generate-action ( args * : xml-file -## : command status start end user system : output ? ) -## { -## local contents = -## [ on $(xml-file) return $(.header) $(.contents) $(.footer) ] ; -## local f = @($(xml-file):E=$(contents)) ; -## } - -## # Nothing to do here; the *real* actions happen in -## # out-xml.generate-action. -## actions quietly out-xml.generate { } - -## # Define the out-xml file target, which depends on all the targets so -## # that it runs the collection after the targets have run. -## out-xml $(.out-xml) : $(actual-targets) ; - -## # Set up a global __ACTION_RULE__ that records all the available -## # statistics about each actual target in a variable "on" the --out-xml -## # target. -## # -## rule out-xml.collect ( xml-file : target : command status start end user -## system : output ? ) -## { -## local nl = " -## " ; -## # Open the action with some basic info. -## .contents on $(xml-file) += -## "$(nl) " ; - -## # If we have an action object we can print out more detailed info. -## local action = [ on $(target) return $(.action) ] ; -## if $(action) -## { -## local action-name = [ $(action).action-name ] ; -## local action-sources = [ $(action).sources ] ; -## local action-props = [ $(action).properties ] ; - -## # The qualified name of the action which we created the target. -## .contents on $(xml-file) += -## "$(nl) " ; - -## # The sources that made up the target. -## .contents on $(xml-file) += -## "$(nl) " ; -## for local source in $(action-sources) -## { -## local source-actual = [ $(source).actual-name ] ; -## .contents on $(xml-file) += -## "$(nl) " ; -## } -## .contents on $(xml-file) += -## "$(nl) " ; - -## # The properties that define the conditions under which the -## # target was built. -## .contents on $(xml-file) += -## "$(nl) " ; -## for local prop in [ $(action-props).raw ] -## { -## local prop-name = [ MATCH ^<(.*)>$ : $(prop:G) ] ; -## .contents on $(xml-file) += -## "$(nl) " ; -## } -## .contents on $(xml-file) += -## "$(nl) " ; -## } - -## local locate = [ on $(target) return $(LOCATE) ] ; -## locate ?= "" ; -## .contents on $(xml-file) += -## "$(nl) " -## "$(nl) " -## "$(nl) " -## "$(nl) " ; -## .contents on $(xml-file) += -## "$(nl) " ; -## } - -## # When no __ACTION_RULE__ is set "on" a target, the search falls back to -## # the global module. -## module -## { -## __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 -## ; -## } - j = option.get("jobs") if j: bjam.call("set-variable", 'PARALLELISM', j) diff --git a/src/engine/build.bat b/src/engine/build.bat index e0e742da2..34caf37c9 100644 --- a/src/engine/build.bat +++ b/src/engine/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, vc10, vc11, vc12, vc14 +ECHO ### intel-win32, metrowerks, mingw, msvc, vc7, vc8, vc9, vc10, vc11, vc12, vc14, vc15 ECHO ### call :Set_Error endlocal @@ -100,6 +100,26 @@ call :Clear_Error call :Test_Empty %ProgramFiles% if not errorlevel 1 set ProgramFiles=C:\Program Files +call :Clear_Error +if NOT "_%VS150COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET=vc15" + set "BOOST_JAM_TOOLSET_ROOT=%VS150COMNTOOLS%..\..\VC\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ( + set "BOOST_JAM_TOOLSET=vc15" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\VC\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\Build\vcvarsall.bat" ( + set "BOOST_JAM_TOOLSET=vc15" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\VC\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" ( + set "BOOST_JAM_TOOLSET=vc15" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\VC\" + goto :eof) call :Clear_Error if NOT "_%VS140COMNTOOLS%_" == "__" ( set "BOOST_JAM_TOOLSET=vc14" @@ -446,6 +466,28 @@ set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" set "_known_=1" :Skip_VC14 +if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc15_" goto Skip_VC15 +if NOT "_%VS150COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%VS150COMNTOOLS%..\..\VC\" + ) + +REM vc15 vsvarsall requires the architecture as a parameter. +set BOOST_JAM_ARCH=x86 +if NOT "_%PROCESSOR_ARCHITECTURE%_" == "__" set BOOST_JAM_ARCH=%PROCESSOR_ARCHITECTURE% +if NOT "_%Platform%_" == "__" set BOOST_JAM_ARCH=%Platform% +set BOOST_JAM_ARGS=%BOOST_JAM_ARGS% %BOOST_JAM_ARCH% + +if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%Auxiliary\Build\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_VC15 if NOT "_%BOOST_JAM_TOOLSET%_" == "_borland_" goto Skip_BORLAND if "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( call :Test_Path bcc32.exe ) diff --git a/src/engine/build.jam b/src/engine/build.jam index a0f1ea30f..1f6f64c1e 100644 --- a/src/engine/build.jam +++ b/src/engine/build.jam @@ -304,7 +304,7 @@ toolset qcc qcc : "-o " : -D ## Qlogic Pathscale 2.4 toolset pathscale pathcc : "-o " : -D : - [ opt --release : -s -Ofast -O3 ] + [ opt --release : -s -O3 ] [ opt --debug : -g ] -I$(--python-include) -I$(--extra-include) : -L$(--python-lib[1]) -l$(--python-lib[2]) ; @@ -394,12 +394,20 @@ toolset vc12 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++ 2015 toolset vc14 cl : /Fe /Fe /Fd /Fo : -D : /nologo [ opt --release : /GL /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]) ; +## Microsoft Visual C++ 2017 +toolset vc15 cl : /Fe /Fe /Fd /Fo : -D + : /nologo + [ opt --release : /GL /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) diff --git a/src/engine/build.sh b/src/engine/build.sh index 1470b480e..1d0dcc892 100755 --- a/src/engine/build.sh +++ b/src/engine/build.sh @@ -77,6 +77,7 @@ Guess_Toolset () BOOST_JAM_TOOLSET=vacpp fi elif test_uname AIX && test_path xlc; then BOOST_JAM_TOOLSET=vacpp + elif test_uname FreeBSD && test_path freebsd-version && test_path clang; then BOOST_JAM_TOOLSET=clang elif test_path gcc ; then BOOST_JAM_TOOLSET=gcc elif test_path icc ; then BOOST_JAM_TOOLSET=intel-linux elif test -r /opt/intel/cc/9.0/bin/iccvars.sh ; then diff --git a/src/engine/debug.c b/src/engine/debug.c index b4601066f..3dc353392 100644 --- a/src/engine/debug.c +++ b/src/engine/debug.c @@ -1,5 +1,5 @@ /* - * Copyright 2005. Rene Rivera + * Copyright 2005, 2016. Rene Rivera * 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) @@ -9,6 +9,7 @@ #include "debug.h" #include "output.h" #include "hash.h" +#include static profile_frame * profile_stack = 0; @@ -28,7 +29,7 @@ void profile_enter( OBJECT * rulename, profile_frame * frame ) { if ( DEBUG_PROFILE ) { - clock_t start = clock(); + double start = profile_clock(); profile_info * p; if ( !profile_hash && rulename ) @@ -53,15 +54,15 @@ void profile_enter( OBJECT * rulename, profile_frame * frame ) p = &profile_other; } - ++p->num_entries; - ++p->stack_count; + p->num_entries += 1; + p->stack_count += 1; frame->info = p; frame->caller = profile_stack; profile_stack = frame; - frame->entry_time = clock(); + frame->entry_time = profile_clock(); frame->overhead = 0; frame->subrules = 0; @@ -76,7 +77,7 @@ void profile_memory( long mem ) { if ( DEBUG_PROFILE ) if ( profile_stack && profile_stack->info ) - profile_stack->info->memory += mem; + profile_stack->info->memory += ((double)mem) / 1024; } @@ -85,7 +86,7 @@ void profile_exit( profile_frame * frame ) if ( DEBUG_PROFILE ) { /* Cumulative time for this call. */ - clock_t const t = clock() - frame->entry_time - frame->overhead; + double t = profile_clock() - frame->entry_time - frame->overhead; /* If this rule is already present on the stack, do not add the time for * this instance. */ @@ -111,22 +112,17 @@ void profile_exit( profile_frame * frame ) static void dump_profile_entry( void * p_, void * ignored ) { profile_info * p = (profile_info *)p_; - unsigned long mem_each = ( p->memory / ( p->num_entries ? p->num_entries : 1 + double mem_each = ( p->memory / ( p->num_entries ? p->num_entries : 1 ) ); - double cumulative = p->cumulative; - double net = p->net; double q = p->net; - q /= ( p->num_entries ? p->num_entries : 1 ); - cumulative /= CLOCKS_PER_SEC; - net /= CLOCKS_PER_SEC; - q /= CLOCKS_PER_SEC; + if (p->num_entries) q /= p->num_entries; if ( !ignored ) { profile_total.cumulative += p->net; profile_total.memory += p->memory; } - out_printf( "%10ld %12.6f %12.6f %12.8f %10ld %10ld %s\n", p->num_entries, - cumulative, net, q, p->memory, mem_each, object_str( p->name ) ); + out_printf( "%10ld %12.6f %12.6f %12.8f %10.2f %10.2f %s\n", p->num_entries, + p->cumulative, p->net, q, p->memory, mem_each, object_str( p->name ) ); } @@ -143,3 +139,20 @@ void profile_dump() dump_profile_entry( &profile_total, (void *)1 ); } } + +double profile_clock() +{ + return ((double) clock()) / CLOCKS_PER_SEC; +} + +OBJECT * profile_make_local( char const * scope ) +{ + if ( DEBUG_PROFILE ) + { + return object_new( scope ); + } + else + { + return 0; + } +} diff --git a/src/engine/debug.h b/src/engine/debug.h index 4151d27fa..b4e8472c9 100644 --- a/src/engine/debug.h +++ b/src/engine/debug.h @@ -1,5 +1,5 @@ /* - * Copyright 2005. Rene Rivera + * Copyright 2005, 2016. Rene Rivera * 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) @@ -10,23 +10,22 @@ #include "constants.h" #include "object.h" -#include typedef struct profile_info { /* name of rule being called */ OBJECT * name; - /* cumulative time spent in rule */ - clock_t cumulative; - /* time spent in rule proper */ - clock_t net; + /* cumulative time spent in rule, in seconds */ + double cumulative; + /* time spent in rule proper, in seconds */ + double net; /* number of time rule was entered */ unsigned long num_entries; /* number of the times this function is present in stack */ unsigned long stack_count; - /* bytes of memory allocated by the call */ - unsigned long memory; + /* memory allocated by the call, in KiB */ + double memory; } profile_info; typedef struct profile_frame @@ -34,13 +33,13 @@ typedef struct profile_frame /* permanent storage where data accumulates */ profile_info * info; /* overhead for profiling in this call */ - clock_t overhead; + double overhead; /* time of last entry to rule */ - clock_t entry_time; + double entry_time; /* stack frame of caller */ struct profile_frame * caller; /* time spent in subrules */ - clock_t subrules; + double subrules; } profile_frame; profile_frame * profile_init( OBJECT * rulename, profile_frame * ); @@ -48,8 +47,16 @@ void profile_enter( OBJECT * rulename, profile_frame * ); void profile_memory( long mem ); void profile_exit( profile_frame * ); void profile_dump(); +double profile_clock(); #define PROFILE_ENTER( scope ) profile_frame PROF_ ## scope, *PROF_ ## scope ## _p = profile_init( constant_ ## scope, &PROF_ ## scope ) #define PROFILE_EXIT( scope ) profile_exit( PROF_ ## scope ## _p ) +OBJECT * profile_make_local( char const * ); +#define PROFILE_ENTER_LOCAL( scope ) \ + static OBJECT * constant_LOCAL_##scope = 0; \ + if (DEBUG_PROFILE && !constant_LOCAL_##scope) constant_LOCAL_##scope = profile_make_local( #scope ); \ + PROFILE_ENTER( LOCAL_##scope ) +#define PROFILE_EXIT_LOCAL( scope ) PROFILE_EXIT( LOCAL_##scope ) + #endif diff --git a/src/engine/execunix.c b/src/engine/execunix.c index 8ee4d9dbf..21a223d9e 100644 --- a/src/engine/execunix.c +++ b/src/engine/execunix.c @@ -64,8 +64,6 @@ static int get_free_cmdtab_slot(); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ static clock_t tps; -static int old_time_initialized; -static struct tms old_time; /* We hold stdout & stderr child process information in two element arrays * indexed as follows. @@ -178,13 +176,6 @@ void exec_cmd exit( EXITBAD ); } - /* Initialize old_time only once. */ - if ( !old_time_initialized ) - { - times( &old_time ); - old_time_initialized = 1; - } - /* Start the command */ timestamp_current( &cmdtab[ slot ].start_dt ); @@ -513,6 +504,7 @@ void exec_wait() int status; int rstat; timing_info time_info; + struct rusage cmd_usage; /* We found a terminated child process - our search is done. */ finished = 1; @@ -523,7 +515,7 @@ void exec_wait() close_streams( i, ERR ); /* Reap the child and release resources. */ - while ( ( pid = waitpid( cmdtab[ i ].pid, &status, 0 ) ) == -1 ) + while ( ( pid = wait4( cmdtab[ i ].pid, &status, 0, &cmd_usage ) ) == -1 ) if ( errno != EINTR ) break; if ( pid != cmdtab[ i ].pid ) @@ -539,15 +531,10 @@ void exec_wait() : EXIT_OK; { - struct tms new_time; - times( &new_time ); - time_info.system = (double)( new_time.tms_cstime - - old_time.tms_cstime ) / CLOCKS_PER_SEC; - time_info.user = (double)( new_time.tms_cutime - - old_time.tms_cutime ) / CLOCKS_PER_SEC; + time_info.system = ((double)(cmd_usage.ru_stime.tv_sec)*1000000.0+(double)(cmd_usage.ru_stime.tv_usec))/1000000.0; + time_info.user = ((double)(cmd_usage.ru_utime.tv_sec)*1000000.0+(double)(cmd_usage.ru_utime.tv_usec))/1000000.0; timestamp_copy( &time_info.start, &cmdtab[ i ].start_dt ); timestamp_current( &time_info.end ); - old_time = new_time; } /* Drive the completion. */ diff --git a/src/engine/filent.c b/src/engine/filent.c index 4f19a56b5..9802dcb0a 100644 --- a/src/engine/filent.c +++ b/src/engine/filent.c @@ -227,10 +227,7 @@ int try_file_query_root( file_info_t * const info ) } else if ( pathstr[ 1 ] == ':' ) { - if ( !pathstr[ 2 ] ) - { - } - else if ( !pathstr[ 2 ] || ( pathstr[ 2 ] == '\\' && !pathstr[ 3 ] ) ) + if ( !pathstr[ 2 ] || ( pathstr[ 2 ] == '\\' && !pathstr[ 3 ] ) ) { buf[ 0 ] = pathstr[ 0 ]; buf[ 1 ] = ':'; diff --git a/src/engine/function.c b/src/engine/function.c index 53fe1fe69..e623ddd89 100644 --- a/src/engine/function.c +++ b/src/engine/function.c @@ -1,5 +1,6 @@ /* * Copyright 2011 Steven Watanabe + * Copyright 2016 Rene Rivera * 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) @@ -27,6 +28,17 @@ #include #include +/* * +#define FUNCTION_DEBUG_PROFILE +/* */ + +#ifndef FUNCTION_DEBUG_PROFILE +#undef PROFILE_ENTER_LOCAL +#define PROFILE_ENTER_LOCAL(x) static int unused_LOCAL_##x = 0 +#undef PROFILE_EXIT_LOCAL +#define PROFILE_EXIT_LOCAL(x) +#endif + int glob( char const * s, char const * c ); void backtrace( FRAME * ); void backtrace_line( FRAME * ); @@ -576,9 +588,9 @@ static LIST * function_call_member_rule( JAM_FUNCTION * function, FRAME * frame, } } + list_free( first ); result = evaluate_rule( rule, real_rulename, inner ); frame_free( inner ); - object_free( rulename ); object_free( real_rulename ); return result; } @@ -2894,7 +2906,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) { compile_parse( parse->left, c, RESULT_RETURN ); compile_emit_cleanups( c, 0 ); - compile_emit( c, INSTR_RETURN, 0 ); + compile_emit( c, INSTR_RETURN, 0 ); /* 0 for return in the middle of a function. */ } else if ( parse->type == PARSE_BREAK ) { @@ -2966,7 +2978,7 @@ FUNCTION * function_compile( PARSE * parse ) JAM_FUNCTION * result; compiler_init( c ); compile_parse( parse, c, RESULT_RETURN ); - compile_emit( c, INSTR_RETURN, 0 ); + compile_emit( c, INSTR_RETURN, 1 ); result = compile_to_function( c ); compiler_free( c ); result->file = object_copy( parse->file ); @@ -2986,7 +2998,7 @@ FUNCTION * function_compile_actions( char const * actions, OBJECT * file, compiler_init( c ); var_parse_actions_compile( parse, c ); var_parse_actions_free( parse ); - compile_emit( c, INSTR_RETURN, 0 ); + compile_emit( c, INSTR_RETURN, 1 ); result = compile_to_function( c ); compiler_free( c ); result->file = object_copy( file ); @@ -3617,7 +3629,9 @@ FUNCTION * function_bind_variables( FUNCTION * f, module_t * module, case INSTR_SET: op_code = INSTR_SET_FIXED; break; case INSTR_APPEND: op_code = INSTR_APPEND_FIXED; break; case INSTR_DEFAULT: op_code = INSTR_DEFAULT_FIXED; break; - case INSTR_RETURN: return (FUNCTION *)new_func; + case INSTR_RETURN: + if( code->arg == 1 ) return (FUNCTION *)new_func; + else continue; case INSTR_CALL_MEMBER_RULE: case INSTR_CALL_RULE: ++i; continue; case INSTR_PUSH_MODULE: @@ -3834,12 +3848,15 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) LIST * result = L0; void * saved_stack = s->data; + PROFILE_ENTER_LOCAL(function_run); + #ifdef JAM_DEBUGGER frame->function = function_; #endif if ( function_->type == FUNCTION_BUILTIN ) { + PROFILE_ENTER_LOCAL(function_run_FUNCTION_BUILTIN); BUILTIN_FUNCTION const * const f = (BUILTIN_FUNCTION *)function_; if ( function_->formal_arguments ) argument_list_check( function_->formal_arguments, @@ -3848,16 +3865,21 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) debug_on_enter_function( frame, f->base.rulename, NULL, -1 ); result = f->func( frame, f->flags ); debug_on_exit_function( f->base.rulename ); + PROFILE_EXIT_LOCAL(function_run_FUNCTION_BUILTIN); + PROFILE_EXIT_LOCAL(function_run); return result; } #ifdef HAVE_PYTHON else if ( function_->type == FUNCTION_PYTHON ) { + PROFILE_ENTER_LOCAL(function_run_FUNCTION_PYTHON); PYTHON_FUNCTION * f = (PYTHON_FUNCTION *)function_; debug_on_enter_function( frame, f->base.rulename, NULL, -1 ); result = call_python_function( f, frame ); debug_on_exit_function( f->base.rulename ); + PROFILE_EXIT_LOCAL(function_run_FUNCTION_PYTHON); + PROFILE_EXIT_LOCAL(function_run); return result; } #endif @@ -3881,31 +3903,50 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) */ case INSTR_PUSH_EMPTY: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_EMPTY); stack_push( s, L0 ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_EMPTY); break; + } case INSTR_PUSH_CONSTANT: { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_CONSTANT); OBJECT * value = function_get_constant( function, code->arg ); stack_push( s, list_new( object_copy( value ) ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_CONSTANT); break; } case INSTR_PUSH_ARG: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_ARG); stack_push( s, frame_get_local( frame, code->arg ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_ARG); break; + } case INSTR_PUSH_VAR: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_VAR); stack_push( s, function_get_variable( function, frame, code->arg ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_VAR); break; + } case INSTR_PUSH_VAR_FIXED: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_VAR_FIXED); stack_push( s, list_copy( frame->module->fixed_variables[ code->arg ] ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_VAR_FIXED); break; + } case INSTR_PUSH_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_GROUP); LIST * value = L0; LISTITER iter; LISTITER end; @@ -3916,121 +3957,183 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) function, frame, list_item( iter ) ) ); list_free( l ); stack_push( s, value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_GROUP); break; } case INSTR_PUSH_APPEND: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_APPEND); r = stack_pop( s ); l = stack_pop( s ); stack_push( s, list_append( l, r ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_APPEND); break; + } case INSTR_SWAP: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_SWAP); l = stack_top( s ); stack_set( s, 0, stack_at( s, code->arg ) ); stack_set( s, code->arg, l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_SWAP); break; + } case INSTR_POP: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_POP); list_free( stack_pop( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_POP); break; + } /* * Branch instructions */ case INSTR_JUMP: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP); code += code->arg; + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP); break; + } case INSTR_JUMP_EMPTY: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_EMPTY); l = stack_pop( s ); if ( !list_cmp( l, L0 ) ) code += code->arg; list_free( l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_EMPTY); break; + } case INSTR_JUMP_NOT_EMPTY: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_NOT_EMPTY); l = stack_pop( s ); if ( list_cmp( l, L0 ) ) code += code->arg; list_free( l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_NOT_EMPTY); break; + } case INSTR_JUMP_LT: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_LT); r = stack_pop( s ); l = stack_pop( s ); if ( list_cmp( l, r ) < 0 ) code += code->arg; list_free( l ); list_free( r ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_LT); break; + } case INSTR_JUMP_LE: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_LE); r = stack_pop( s ); l = stack_pop( s ); if ( list_cmp( l, r ) <= 0 ) code += code->arg; list_free( l ); list_free( r ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_LE); break; + } case INSTR_JUMP_GT: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_GT); r = stack_pop( s ); l = stack_pop( s ); if ( list_cmp( l, r ) > 0 ) code += code->arg; list_free( l ); list_free( r ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_GT); break; + } case INSTR_JUMP_GE: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_GE); r = stack_pop( s ); l = stack_pop( s ); if ( list_cmp( l, r ) >= 0 ) code += code->arg; list_free( l ); list_free( r ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_GE); break; + } case INSTR_JUMP_EQ: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_EQ); r = stack_pop( s ); l = stack_pop( s ); if ( list_cmp( l, r ) == 0 ) code += code->arg; list_free( l ); list_free( r ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_EQ); break; + } case INSTR_JUMP_NE: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_NE); r = stack_pop(s); l = stack_pop(s); if ( list_cmp(l, r) != 0 ) code += code->arg; list_free(l); list_free(r); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_NE); break; + } case INSTR_JUMP_IN: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_IN); r = stack_pop(s); l = stack_pop(s); if ( list_is_sublist( l, r ) ) code += code->arg; list_free(l); list_free(r); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_IN); break; + } case INSTR_JUMP_NOT_IN: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_NOT_IN); r = stack_pop( s ); l = stack_pop( s ); if ( !list_is_sublist( l, r ) ) code += code->arg; list_free( l ); list_free( r ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_NOT_IN); break; + } /* * For */ case INSTR_FOR_INIT: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_FOR_INIT); l = stack_top( s ); *(LISTITER *)stack_allocate( s, sizeof( LISTITER ) ) = list_begin( l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_FOR_INIT); break; + } case INSTR_FOR_LOOP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_FOR_LOOP); LISTITER iter = *(LISTITER *)stack_get( s ); stack_deallocate( s, sizeof( LISTITER ) ); l = stack_top( s ); @@ -4046,13 +4149,16 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) *(LISTITER *)stack_allocate( s, sizeof( LISTITER ) ) = iter; stack_push( s, r ); } + PROFILE_EXIT_LOCAL(function_run_INSTR_FOR_LOOP); break; } case INSTR_FOR_POP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_FOR_POP); stack_deallocate( s, sizeof( LISTITER ) ); list_free( stack_pop( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_FOR_POP); break; } @@ -4062,6 +4168,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) case INSTR_JUMP_NOT_GLOB: { + PROFILE_ENTER_LOCAL(function_run_INSTR_JUMP_NOT_GLOB); char const * pattern; char const * match; l = stack_pop( s ); @@ -4073,6 +4180,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) else list_free( stack_pop( s ) ); list_free( l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_JUMP_NOT_GLOB); break; } @@ -4081,20 +4189,29 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) */ case INSTR_SET_RESULT: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_SET_RESULT); list_free( result ); if ( !code->arg ) result = stack_pop( s ); else result = list_copy( stack_top( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_SET_RESULT); break; + } case INSTR_PUSH_RESULT: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_RESULT); stack_push( s, result ); result = L0; + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_RESULT); break; + } case INSTR_RETURN: { + PROFILE_ENTER_LOCAL(function_run_INSTR_RETURN); if ( function_->formal_arguments ) argument_list_pop( function_->formal_arguments, function_->num_formal_arguments, frame, s ); @@ -4111,6 +4228,8 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) #endif assert( saved_stack == s->data ); debug_on_exit_function( function->base.rulename ); + PROFILE_EXIT_LOCAL(function_run_INSTR_RETURN); + PROFILE_EXIT_LOCAL(function_run); return result; } @@ -4120,38 +4239,49 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) case INSTR_PUSH_LOCAL: { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_LOCAL); LIST * value = stack_pop( s ); stack_push( s, function_swap_variable( function, frame, code->arg, value ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_LOCAL); break; } case INSTR_POP_LOCAL: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_POP_LOCAL); function_set_variable( function, frame, code->arg, stack_pop( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_POP_LOCAL); break; + } case INSTR_PUSH_LOCAL_FIXED: { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_LOCAL_FIXED); LIST * value = stack_pop( s ); LIST * * ptr = &frame->module->fixed_variables[ code->arg ]; assert( code->arg < frame->module->num_fixed_variables ); stack_push( s, *ptr ); *ptr = value; + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_LOCAL_FIXED); break; } case INSTR_POP_LOCAL_FIXED: { + PROFILE_ENTER_LOCAL(function_run_INSTR_POP_LOCAL_FIXED); LIST * value = stack_pop( s ); LIST * * ptr = &frame->module->fixed_variables[ code->arg ]; assert( code->arg < frame->module->num_fixed_variables ); list_free( *ptr ); *ptr = value; + PROFILE_EXIT_LOCAL(function_run_INSTR_POP_LOCAL_FIXED); break; } case INSTR_PUSH_LOCAL_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_LOCAL_GROUP); LIST * const value = stack_pop( s ); LISTITER iter; LISTITER end; @@ -4162,11 +4292,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_item( iter ), list_copy( value ) ) ); list_free( value ); stack_push( s, l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_LOCAL_GROUP); break; } case INSTR_POP_LOCAL_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_POP_LOCAL_GROUP); LISTITER iter; LISTITER end; r = stack_pop( s ); @@ -4177,6 +4309,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) function_set_named_variable( function, frame, list_item( iter ), stack_pop( s ) ); list_free( l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_POP_LOCAL_GROUP); break; } @@ -4186,6 +4319,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) case INSTR_PUSH_ON: { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_ON); LIST * targets = stack_top( s ); if ( !list_empty( targets ) ) { @@ -4202,11 +4336,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) stack_push( s, L0 ); code += code->arg; } + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_ON); break; } case INSTR_POP_ON: { + PROFILE_ENTER_LOCAL(function_run_INSTR_POP_ON); LIST * result = stack_pop( s ); LIST * targets = stack_pop( s ); if ( !list_empty( targets ) ) @@ -4216,11 +4352,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) } list_free( targets ); stack_push( s, result ); + PROFILE_EXIT_LOCAL(function_run_INSTR_POP_ON); break; } case INSTR_SET_ON: { + PROFILE_ENTER_LOCAL(function_run_INSTR_SET_ON); LIST * targets = stack_pop( s ); LIST * value = stack_pop( s ); LIST * vars = stack_pop( s ); @@ -4239,11 +4377,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_free( vars ); list_free( targets ); stack_push( s, value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_SET_ON); break; } case INSTR_APPEND_ON: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPEND_ON); LIST * targets = stack_pop( s ); LIST * value = stack_pop( s ); LIST * vars = stack_pop( s ); @@ -4262,11 +4402,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_free( vars ); list_free( targets ); stack_push( s, value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPEND_ON); break; } case INSTR_DEFAULT_ON: { + PROFILE_ENTER_LOCAL(function_run_INSTR_DEFAULT_ON); LIST * targets = stack_pop( s ); LIST * value = stack_pop( s ); LIST * vars = stack_pop( s ); @@ -4285,12 +4427,14 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_free( vars ); list_free( targets ); stack_push( s, value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_DEFAULT_ON); break; } /* [ on $(target) return $(variable) ] */ case INSTR_GET_ON: { + PROFILE_ENTER_LOCAL(function_run_INSTR_GET_ON); LIST * targets = stack_pop( s ); LIST * result = L0; if ( !list_empty( targets ) ) @@ -4313,7 +4457,9 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) result = var_get( frame->module, varname ) ; } } + list_free( targets ); stack_push( s, list_copy( result ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_GET_ON); break; } @@ -4322,39 +4468,56 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) */ case INSTR_SET: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_SET); function_set_variable( function, frame, code->arg, stack_pop( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_SET); break; + } case INSTR_APPEND: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPEND); function_append_variable( function, frame, code->arg, stack_pop( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPEND); break; + } case INSTR_DEFAULT: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_DEFAULT); function_default_variable( function, frame, code->arg, stack_pop( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_DEFAULT); break; + } case INSTR_SET_FIXED: { + PROFILE_ENTER_LOCAL(function_run_INSTR_SET_FIXED); LIST * * ptr = &frame->module->fixed_variables[ code->arg ]; assert( code->arg < frame->module->num_fixed_variables ); list_free( *ptr ); *ptr = stack_pop( s ); + PROFILE_EXIT_LOCAL(function_run_INSTR_SET_FIXED); break; } case INSTR_APPEND_FIXED: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPEND_FIXED); LIST * * ptr = &frame->module->fixed_variables[ code->arg ]; assert( code->arg < frame->module->num_fixed_variables ); *ptr = list_append( *ptr, stack_pop( s ) ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPEND_FIXED); break; } case INSTR_DEFAULT_FIXED: { + PROFILE_ENTER_LOCAL(function_run_INSTR_DEFAULT_FIXED); LIST * * ptr = &frame->module->fixed_variables[ code->arg ]; LIST * value = stack_pop( s ); assert( code->arg < frame->module->num_fixed_variables ); @@ -4362,11 +4525,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) *ptr = value; else list_free( value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_DEFAULT_FIXED); break; } case INSTR_SET_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_SET_GROUP); LIST * value = stack_pop( s ); LIST * vars = stack_pop( s ); LISTITER iter = list_begin( vars ); @@ -4376,11 +4541,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_copy( value ) ); list_free( vars ); list_free( value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_SET_GROUP); break; } case INSTR_APPEND_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPEND_GROUP); LIST * value = stack_pop( s ); LIST * vars = stack_pop( s ); LISTITER iter = list_begin( vars ); @@ -4390,11 +4557,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) ), list_copy( value ) ); list_free( vars ); list_free( value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPEND_GROUP); break; } case INSTR_DEFAULT_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_DEFAULT_GROUP); LIST * value = stack_pop( s ); LIST * vars = stack_pop( s ); LISTITER iter = list_begin( vars ); @@ -4404,6 +4573,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) iter ), list_copy( value ) ); list_free( vars ); list_free( value ); + PROFILE_EXIT_LOCAL(function_run_INSTR_DEFAULT_GROUP); break; } @@ -4413,31 +4583,43 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) case INSTR_CALL_RULE: { + PROFILE_ENTER_LOCAL(function_run_INSTR_CALL_RULE); char const * unexpanded = object_str( function_get_constant( function, code[ 1 ].op_code ) ); LIST * result = function_call_rule( function, frame, s, code->arg, unexpanded, function->file, code[ 1 ].arg ); stack_push( s, result ); ++code; + PROFILE_EXIT_LOCAL(function_run_INSTR_CALL_RULE); break; } case INSTR_CALL_MEMBER_RULE: { + PROFILE_ENTER_LOCAL(function_run_INSTR_CALL_MEMBER_RULE); OBJECT * rule_name = function_get_constant( function, code[1].op_code ); LIST * result = function_call_member_rule( function, frame, s, code->arg, rule_name, function->file, code[1].arg ); stack_push( s, result ); ++code; + PROFILE_EXIT_LOCAL(function_run_INSTR_CALL_MEMBER_RULE); break; } case INSTR_RULE: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_RULE); function_set_rule( function, frame, s, code->arg ); + PROFILE_EXIT_LOCAL(function_run_INSTR_RULE); break; + } case INSTR_ACTIONS: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_ACTIONS); function_set_actions( function, frame, s, code->arg ); + PROFILE_EXIT_LOCAL(function_run_INSTR_ACTIONS); break; + } /* * Variable expansion @@ -4445,6 +4627,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) case INSTR_APPLY_MODIFIERS: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPLY_MODIFIERS); int n; int i; l = stack_pop( s ); @@ -4456,18 +4639,24 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) for ( i = 0; i < code->arg; ++i ) list_free( stack_pop( s ) ); /* pop modifiers */ stack_push( s, l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPLY_MODIFIERS); break; } case INSTR_APPLY_INDEX: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPLY_INDEX); l = apply_subscript( s ); list_free( stack_pop( s ) ); list_free( stack_pop( s ) ); stack_push( s, l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPLY_INDEX); break; + } case INSTR_APPLY_INDEX_MODIFIERS: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPLY_INDEX_MODIFIERS); int i; int n; l = stack_pop( s ); @@ -4482,11 +4671,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) for ( i = 0; i < code->arg; ++i ) list_free( stack_pop( s ) ); /* pop modifiers */ stack_push( s, l ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPLY_INDEX_MODIFIERS); break; } case INSTR_APPLY_MODIFIERS_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPLY_MODIFIERS_GROUP); int i; LIST * const vars = stack_pop( s ); int const n = expand_modifiers( s, code->arg ); @@ -4505,11 +4696,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) for ( i = 0; i < code->arg; ++i ) list_free( stack_pop( s ) ); /* pop modifiers */ stack_push( s, result ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPLY_MODIFIERS_GROUP); break; } case INSTR_APPLY_INDEX_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPLY_INDEX_GROUP); LIST * vars = stack_pop( s ); LIST * result = L0; LISTITER iter = list_begin( vars ); @@ -4524,11 +4717,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_free( vars ); list_free( stack_pop( s ) ); stack_push( s, result ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPLY_INDEX_GROUP); break; } case INSTR_APPLY_INDEX_MODIFIERS_GROUP: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPLY_INDEX_MODIFIERS_GROUP); int i; LIST * const vars = stack_pop( s ); LIST * const r = stack_pop( s ); @@ -4551,11 +4746,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) for ( i = 0; i < code->arg; ++i ) list_free( stack_pop( s ) ); /* pop modifiers */ stack_push( s, result ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPLY_INDEX_MODIFIERS_GROUP); break; } case INSTR_COMBINE_STRINGS: { + PROFILE_ENTER_LOCAL(function_run_INSTR_COMBINE_STRINGS); size_t const buffer_size = code->arg * sizeof( expansion_item ); LIST * * const stack_pos = stack_get( s ); expansion_item * items = stack_allocate( s, buffer_size ); @@ -4568,11 +4765,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) for ( i = 0; i < code->arg; ++i ) list_free( stack_pop( s ) ); stack_push( s, result ); + PROFILE_EXIT_LOCAL(function_run_INSTR_COMBINE_STRINGS); break; } case INSTR_GET_GRIST: { + PROFILE_ENTER_LOCAL(function_run_INSTR_GET_GRIST); LIST * vals = stack_pop( s ); LIST * result = L0; LISTITER iter, end; @@ -4598,11 +4797,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_free( vals ); stack_push( s, result ); + PROFILE_EXIT_LOCAL(function_run_INSTR_GET_GRIST); break; } case INSTR_INCLUDE: { + PROFILE_ENTER_LOCAL(function_run_INSTR_INCLUDE); LIST * nt = stack_pop( s ); if ( !list_empty( nt ) ) { @@ -4634,6 +4835,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) frame->function = function_; #endif } + PROFILE_EXIT_LOCAL(function_run_INSTR_INCLUDE); break; } @@ -4643,6 +4845,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) case INSTR_PUSH_MODULE: { + PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_MODULE); LIST * const module_name = stack_pop( s ); module_t * const outer_module = frame->module; frame->module = !list_empty( module_name ) @@ -4651,19 +4854,23 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) list_free( module_name ); *(module_t * *)stack_allocate( s, sizeof( module_t * ) ) = outer_module; + PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_MODULE); break; } case INSTR_POP_MODULE: { + PROFILE_ENTER_LOCAL(function_run_INSTR_POP_MODULE); module_t * const outer_module = *(module_t * *)stack_get( s ); stack_deallocate( s, sizeof( module_t * ) ); frame->module = outer_module; + PROFILE_EXIT_LOCAL(function_run_INSTR_POP_MODULE); break; } case INSTR_CLASS: { + PROFILE_ENTER_LOCAL(function_run_INSTR_CLASS); LIST * bases = stack_pop( s ); LIST * name = stack_pop( s ); OBJECT * class_module = make_class_module( name, bases, frame ); @@ -4674,25 +4881,33 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) *(module_t * *)stack_allocate( s, sizeof( module_t * ) ) = outer_module; + PROFILE_EXIT_LOCAL(function_run_INSTR_CLASS); break; } case INSTR_BIND_MODULE_VARIABLES: + { + PROFILE_ENTER_LOCAL(function_run_INSTR_BIND_MODULE_VARIABLES); module_bind_variables( frame->module ); + PROFILE_EXIT_LOCAL(function_run_INSTR_BIND_MODULE_VARIABLES); break; + } case INSTR_APPEND_STRINGS: { + PROFILE_ENTER_LOCAL(function_run_INSTR_APPEND_STRINGS); string buf[ 1 ]; string_new( buf ); combine_strings( s, code->arg, buf ); stack_push( s, list_new( object_new( buf->value ) ) ); string_free( buf ); + PROFILE_EXIT_LOCAL(function_run_INSTR_APPEND_STRINGS); break; } case INSTR_WRITE_FILE: { + PROFILE_ENTER_LOCAL(function_run_INSTR_WRITE_FILE); string buf[ 1 ]; char const * out; OBJECT * tmp_filename = 0; @@ -4798,14 +5013,17 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) object_free( tmp_filename ); if ( out_debug ) out_putc( '\n' ); + PROFILE_EXIT_LOCAL(function_run_INSTR_WRITE_FILE); break; } case INSTR_OUTPUT_STRINGS: { + PROFILE_ENTER_LOCAL(function_run_INSTR_OUTPUT_STRINGS); string * const buf = *(string * *)( (char *)stack_get( s ) + ( code->arg * sizeof( LIST * ) ) ); combine_strings( s, code->arg, buf ); + PROFILE_EXIT_LOCAL(function_run_INSTR_OUTPUT_STRINGS); break; } @@ -4818,6 +5036,8 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) } ++code; } + + PROFILE_EXIT_LOCAL(function_run); } diff --git a/src/engine/hash.c b/src/engine/hash.c index 772087a22..b2685a860 100644 --- a/src/engine/hash.c +++ b/src/engine/hash.c @@ -24,7 +24,7 @@ #include -/* */ +/* * #define HASH_DEBUG_PROFILE 1 /* */ diff --git a/src/engine/make1.c b/src/engine/make1.c index 7dbf7c8da..8282279cf 100644 --- a/src/engine/make1.c +++ b/src/engine/make1.c @@ -689,7 +689,7 @@ static void call_timing_rule( TARGET * target, timing_info const * const time ) if ( !list_empty( timing_rule ) ) { - /* rule timing-rule ( args * : target : start end user system ) */ + /* rule timing-rule ( args * : target : start end user system clock ) */ /* Prepare the argument list. */ FRAME frame[ 1 ]; @@ -703,12 +703,14 @@ static void call_timing_rule( TARGET * target, timing_info const * const time ) /* target :: the name of the target */ lol_add( frame->args, list_new( object_copy( target->name ) ) ); - /* start end user system :: info about the action command */ - lol_add( frame->args, list_push_back( list_push_back( list_push_back( list_new( + /* start end user system clock :: info about the action command */ + lol_add( frame->args, list_push_back( list_push_back( list_push_back( list_push_back( list_new( outf_time( &time->start ) ), outf_time( &time->end ) ), outf_double( time->user ) ), - outf_double( time->system ) ) ); + outf_double( time->system ) ), + outf_double( timestamp_delta_seconds(&time->start, &time->end) ) ) + ); /* Call the rule. */ evaluate_rule( bindrule( rulename , root_module() ), rulename, frame ); @@ -854,7 +856,9 @@ static void make1c_closure { call_timing_rule( t, time ); if ( DEBUG_EXECCMD ) - out_printf( "%f sec system; %f sec user\n", time->system, time->user ); + out_printf( "%f sec system; %f sec user; %f sec clock\n", + time->system, time->user, + timestamp_delta_seconds(&time->start, &time->end) ); /* Assume -p0 is in effect, i.e. cmd_stdout contains merged output. */ call_action_rule( t, status_orig, time, cmd->buf->value, cmd_stdout ); diff --git a/src/engine/modules/property-set.c b/src/engine/modules/property-set.c index 21e35d5ab..aee1f2ff0 100644 --- a/src/engine/modules/property-set.c +++ b/src/engine/modules/property-set.c @@ -82,6 +82,7 @@ static void ps_map_destroy( struct ps_map * map ) for ( pos = map->table[ i ]; pos; ) { struct ps_map_entry * tmp = pos->next; + object_free( pos->value ); BJAM_FREE( pos ); pos = tmp; } @@ -164,7 +165,7 @@ LIST * property_set_create( FRAME * frame, int flags ) list_new( object_new( "property-set" ) ), 0 ); LISTITER iter, end; object_free( rulename ); - pos->value = list_front( val ); + pos->value = object_copy( list_front( val ) ); var_set( bindmodule( pos->value ), varname, unique, VAR_SET ); object_free( varname ); diff --git a/src/engine/object.c b/src/engine/object.c index 02440d2d1..07866baf4 100644 --- a/src/engine/object.c +++ b/src/engine/object.c @@ -259,7 +259,9 @@ OBJECT * object_new_range( char const * const string, int const size ) strtotal += size + 1; memcpy( m->data, string, size ); m->data[ size ] = '\0'; +#ifndef NDEBUG m->header.magic = OBJECT_MAGIC; +#endif return (OBJECT *)m->data; } #else diff --git a/src/engine/parse.c b/src/engine/parse.c index afa487531..42f64f06e 100644 --- a/src/engine/parse.c +++ b/src/engine/parse.c @@ -55,6 +55,8 @@ static void parse_impl( FRAME * frame ) list_free( function_run( func, frame, stack_global() ) ); function_free( func ); } + + yyfdone(); } diff --git a/src/engine/pathnt.c b/src/engine/pathnt.c index 077dc3b1b..bde5cd0b8 100644 --- a/src/engine/pathnt.c +++ b/src/engine/pathnt.c @@ -16,8 +16,8 @@ * pathnt.c - NT specific path manipulation support */ +#include "jam.h" #include "pathsys.h" - #include "hash.h" #define WIN32_LEAN_AND_MEAN @@ -345,7 +345,8 @@ static int translate_path_cyg2win( string * path ) if ( result ) { - string_copy( path, result ); + string_truncate( path, 0 ); + string_append( path, result ); translated = 1; } diff --git a/src/engine/pathvms.c b/src/engine/pathvms.c index 01f3f90d9..5882d40af 100644 --- a/src/engine/pathvms.c +++ b/src/engine/pathvms.c @@ -174,15 +174,25 @@ static int translate_path_posix2vms( string * path ) && stat(as_file->value, &statbuf ) > 0 && ( statbuf.st_mode & S_IFREG ) ) { - string_copy( path, as_file->value ); + string_truncate( path, 0 ); + string_append( path, as_file->value ); } else { - string_copy( path, as_dir->value ); + string_truncate( path, 0 ); + string_append( path, as_dir->value ); } } - else if ( file_count ) { string_copy( path, as_file->value ); } - else if ( dir_count ) { string_copy( path, as_dir->value ); } + else if ( file_count ) + { + string_truncate( path, 0 ); + string_append( path, as_file->value ); + } + else if ( dir_count ) + { + string_truncate( path, 0 ); + string_append( path, as_dir->value ); + } else { /* error: unable to translate path to native format */ diff --git a/src/engine/scan.c b/src/engine/scan.c index ab858b0ee..d50a20cb1 100644 --- a/src/engine/scan.c +++ b/src/engine/scan.c @@ -113,6 +113,22 @@ void yysparse( OBJECT * name, const char * * lines ) } +/* + * yyfdone() - cleanup after we're done parsing a file. + */ +void yyfdone( void ) +{ + include * const i = incp; + incp = i->next; + + /* Close file, free name. */ + if(i->file && (i->file != stdin)) + fclose(i->file); + object_free(i->fname); + BJAM_FREE((char *)i); +} + + /* * yyline() - read new line and return first character. * @@ -166,18 +182,10 @@ int yyline() } } - /* This include is done. Free it up and return EOF so yyparse() returns to + /* This include is done. Return EOF so yyparse() returns to * parse_file(). */ - incp = i->next; - - /* Close file, free name. */ - if ( i->file && ( i->file != stdin ) ) - fclose( i->file ); - object_free( i->fname ); - BJAM_FREE( (char *)i ); - return EOF; } diff --git a/src/engine/scan.h b/src/engine/scan.h index 3da8d1461..03fd8f3a7 100644 --- a/src/engine/scan.h +++ b/src/engine/scan.h @@ -51,6 +51,7 @@ void yymode( int n ); void yyerror( char const * s ); int yyanyerrors(); void yyfparse( OBJECT * s ); +void yyfdone( void ); void yysparse( OBJECT * name, const char * * lines ); int yyline(); int yylex(); diff --git a/src/engine/timestamp.c b/src/engine/timestamp.c index 17510bcd0..b6fd906ef 100644 --- a/src/engine/timestamp.c +++ b/src/engine/timestamp.c @@ -261,3 +261,11 @@ void timestamp_done() hashdone( bindhash ); } } + +/* + * timestamp_delta_seconds() - seconds from time a to b. + */ +double timestamp_delta_seconds( timestamp const * const a , timestamp const * const b ) +{ + return ((b->secs*1000000.0+b->nsecs)-(a->secs*1000000.0+a->nsecs))/1000000.0; +} diff --git a/src/engine/timestamp.h b/src/engine/timestamp.h index ecedb5f92..e9b41753c 100644 --- a/src/engine/timestamp.h +++ b/src/engine/timestamp.h @@ -42,5 +42,6 @@ void timestamp_from_filetime( timestamp * const, FILETIME const * const ); #endif void timestamp_done(); +double timestamp_delta_seconds( timestamp const * const, timestamp const * const ); #endif diff --git a/src/exceptions.py b/src/exceptions.py index 5750abfe3..70d4d9831 100644 --- a/src/exceptions.py +++ b/src/exceptions.py @@ -2,43 +2,54 @@ # 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 BaseBoostBuildException(Exception): + """A base Exception class for all other Boost.Build exceptions to inherit from.""" -class UserError (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) -class FeatureConflict (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) +class UserError(BaseBoostBuildException): + pass -class InvalidSource (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) -class InvalidFeature (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) +class FeatureConflict(BaseBoostBuildException): + pass -class InvalidProperty (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) -class InvalidValue (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) +class InvalidSource(BaseBoostBuildException): + pass -class InvalidAttribute (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) -class AlreadyDefined (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) +class InvalidFeature(BaseBoostBuildException): + pass -class IllegalOperation (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) -class Recursion (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) +class InvalidProperty(BaseBoostBuildException): + pass -class NoBestMatchingAlternative (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) -class NoAction (BaseException): - def __init__ (self, message = ''): BaseException.__init__ (self, message) +class InvalidValue(BaseBoostBuildException): + pass + + +class InvalidAttribute(BaseBoostBuildException): + pass + + +class AlreadyDefined(BaseBoostBuildException): + pass + + +class IllegalOperation(BaseBoostBuildException): + pass + + +class Recursion(BaseBoostBuildException): + pass + + +class NoBestMatchingAlternative(BaseBoostBuildException): + pass + + +class NoAction(BaseBoostBuildException): + pass diff --git a/src/kernel/bootstrap.jam b/src/kernel/bootstrap.jam index c4320dc29..74ee5d34f 100644 --- a/src/kernel/bootstrap.jam +++ b/src/kernel/bootstrap.jam @@ -140,7 +140,6 @@ if ! $(dont-build) } else { - ECHO "Boost.Build V2 Python port (experimental)" ; # Define additional interface exposed to Python code. Python code will # also have access to select bjam builtins in the 'bjam' module, but diff --git a/src/tools/builtin.jam b/src/tools/builtin.jam index f39fb7018..1236120fe 100644 --- a/src/tools/builtin.jam +++ b/src/tools/builtin.jam @@ -40,7 +40,7 @@ import convert ; import generate ; -.os-names = aix android bsd cygwin darwin freebsd haiku hpux iphone linux netbsd +.os-names = aix android appletv bsd cygwin darwin freebsd haiku hpux iphone linux netbsd openbsd osf qnx qnxnto sgi solaris unix unixware windows vms 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 diff --git a/src/tools/builtin.py b/src/tools/builtin.py index a149a3332..ee6474b73 100644 --- a/src/tools/builtin.py +++ b/src/tools/builtin.py @@ -82,7 +82,7 @@ def variant (name, parents_or_properties, explicit_properties = []): feature.compose ("" + name, explicit_properties.all()) __os_names = """ - amiga aix bsd cygwin darwin dos emx freebsd hpux iphone linux netbsd + amiga aix appletv 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() @@ -147,7 +147,7 @@ def register_globals (): feature.feature ('exception-handling', ['on', 'off'], ['propagated']) # Whether there is support for asynchronous EH (e.g. catching SEGVs). - feature.feature ('asynch-exceptions', ['on', 'off'], ['propagated']) + feature.feature ('asynch-exceptions', ['off', 'on'], ['propagated']) # Whether all extern "C" functions are considered nothrow by default. feature.feature ('extern-c-nothrow', ['off', 'on'], ['propagated']) @@ -367,11 +367,21 @@ class CScanner (scanner.Scanner): return r'#[ \t]*include[ ]*(<(.*)>|"(.*)")' def process (self, target, matches, binding): + # since it's possible for this function to be called + # thousands to millions of times (depending on how many + # header files there are), as such, there are some + # optimizations that have been used here. Anything that + # is slightly out of the ordinary for Python code + # has been commented. + angle = [] + quoted = [] + for match in matches: + if '<' in match: + angle.append(match.strip('<>')) + elif '"' in match: + quoted.append(match.strip('"')) - angle = regex.transform (matches, "<(.*)>") - quoted = regex.transform (matches, '"(.*)"') - - g = str(id(self)) + g = id(self) b = os.path.normpath(os.path.dirname(binding[0])) # Attach binding of including file to included targets. @@ -382,27 +392,38 @@ class CScanner (scanner.Scanner): # 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 + # Note: string interpolation is slightly faster + # than .format() + g2 = '<%s#%s>' % (g, b) + g = "<%s>" % g - 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) + # each include in self.includes_ looks something like this: + # path/to/somewhere + # calling get_value(include) is super slow, + # calling .replace('', '') is much faster + # however, i[9:] is the fastest way of stripping off the "" + # substring. + include_paths = [i[9:] for i in self.includes_] + engine = get_manager().engine() - engine.set_target_variable(angle, "SEARCH", get_value(self.includes_)) - engine.set_target_variable(quoted, "SEARCH", [b] + get_value(self.includes_)) + engine.set_target_variable(angle, "SEARCH", include_paths) + engine.set_target_variable(quoted, "SEARCH", [b] + include_paths) # Just propagate current scanner to includes, in a hope # that includes do not change scanners. - get_manager().scanners().propagate(self, angle + quoted) + get_manager().scanners().propagate(self, all) scanner.register (CScanner, 'include') type.set_scanner ('CPP', CScanner) type.set_scanner ('C', CScanner) +type.set_scanner('H', CScanner) +type.set_scanner('HPP', CScanner) # Ported to trunk@47077 class LibGenerator (generators.Generator): @@ -598,6 +619,8 @@ class LinkingGenerator (generators.Generator): assert isinstance(prop_set, property_set.PropertySet) assert is_iterable_typed(sources, virtual_target.VirtualTarget) + # create a copy since sources is being modified + sources = list(sources) sources.extend(prop_set.get('')) # Add properties for all searched libraries @@ -729,11 +752,27 @@ class ArchiveGenerator (generators.Generator): 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 ('') + assert isinstance(project, targets.ProjectTarget) + assert isinstance(name, basestring) or name is None + assert isinstance(prop_set, property_set.PropertySet) + assert is_iterable_typed(sources, virtual_target.VirtualTarget) + + # create a copy since this modifies the sources list + sources = list(sources) + sources.extend(prop_set.get('')) result = generators.Generator.run (self, project, name, prop_set, sources) - return result + usage_requirements = [] + link = prop_set.get('') + if 'static' in link: + for t in sources: + if type.is_derived(t.type(), 'LIB'): + usage_requirements.append(property.Property('', t)) + + usage_requirements = property_set.create(usage_requirements) + + return usage_requirements, result def register_archiver(id, source_types, target_types, requirements): diff --git a/src/tools/bzip2.jam b/src/tools/bzip2.jam index a994a70ef..ae9a03909 100644 --- a/src/tools/bzip2.jam +++ b/src/tools/bzip2.jam @@ -20,6 +20,9 @@ import path ; import modules ; import errors ; import indirect ; +import make ; +import os ; +import print ; import property ; import property-set ; @@ -134,13 +137,11 @@ rule init ( [ property.select : $(options) ] "and" [ property.select : $(options) ] ; } - else - { - no-build-from-source = true ; - } } - - source-path ?= [ modules.peek : BZIP2_SOURCE ] ; + else + { + source-path ?= [ modules.peek : BZIP2_SOURCE ] ; + } if $(.configured.$(condition)) { @@ -157,7 +158,7 @@ rule init ( } return ; } - else if $(source-path) && ! $(no-build-from-source) + else if $(source-path) { build-name ?= bz2 ; library-id = [ CALC $(library-id) + 1 ] ; @@ -167,6 +168,7 @@ rule init ( tag = [ indirect.make $(tag) : [ $(caller).project-module ] ] ; } sources = [ path.glob $(source-path) : $(sources) ] ; + def-file = [ path.glob $(source-path) : libbz2.def ] ; if $(.debug) { ECHO "notice: [bzip2] Building bzip from source as $(build-name)" ; @@ -184,7 +186,15 @@ rule init ( } } local target ; - if $(sources) { + if $(sources) + { + if ! $(.def-file-target) + { + .def-file-target = [ targets.create-metatarget make-target-class + : $(.project) : libbz2.def : $(def-file) + : @bzip2.make-bz2-def-file ] + ; + } target = [ targets.create-typed-target LIB : $(.project) : $(build-name).$(library-id) : $(sources) @@ -193,7 +203,7 @@ rule init ( $(source-path) msvc:_CRT_SECURE_NO_DEPRECATE msvc:_SCL_SECURE_NO_DEPRECATE - shared:BZIP2_DLL + shared:libbz2.def : : $(source-path) ] ; } @@ -206,7 +216,9 @@ rule init ( $(mt).set-target $(target) ; } targets.main-target-alternative $(mt) ; - } else { + } + else + { if $(.debug) { ECHO "notice: [bzip2] Using pre-installed library" ; @@ -217,10 +229,44 @@ rule init ( } local mt = [ new ac-library bzip2 : $(.project) : $(condition) : - $(include-path) : $(library-path) : $(library-name) : $(root) ] ; + $(include-path) : $(library-path) : $(library-name) ] ; $(mt).set-header $(header) ; $(mt).set-default-names $(names) ; targets.main-target-alternative $(mt) ; } .configured.$(condition) = true ; } + +if [ os.name ] = NT +{ + local rule read-file ( file ) + { + return [ SPLIT_BY_CHARACTERS [ SHELL "type \"$(file:G=)\" 2>nul" ] : "\n" ] ; + } +} +else if [ os.name ] = VMS +{ + local rule read-file ( file ) + { + return [ SPLIT_BY_CHARACTERS [ SHELL "PIPE TYPE $(file:W) 2>NL:" ] : "\n" ] ; + } +} +else +{ + local rule read-file ( file ) + { + return [ SPLIT_BY_CHARACTERS [ SHELL "cat \"$(file:G=)\" 2>/dev/null" ] : "\n" ] ; + } +} + +rule make-bz2-def-file ( target : source : properties * ) +{ + print.output $(target) ; + for local line in [ read-file $(source) ] + { + if ! [ MATCH "(LIBRARY[ \t]+LIBBZ2)" : $(line) ] + { + print.text $(line) : yes ; + } + } +} diff --git a/src/tools/cast.py b/src/tools/cast.py index 0d21edb0b..cf1a87627 100644 --- a/src/tools/cast.py +++ b/src/tools/cast.py @@ -25,7 +25,7 @@ # > cast, as defining a new target type + generator for that type is somewhat # > simpler than defining a main target rule. -from b2.build import targets, virtual_target, property_set +from b2.build import targets, virtual_target, property_set, type as type_ from b2.manager import get_manager from b2.util import bjam_signature, is_iterable_typed @@ -52,6 +52,7 @@ class CastTargetClass(targets.TypedTarget): return property_set.empty(), result + @bjam_signature((["name", "type"], ["sources", "*"], ["requirements", "*"], ["default_build", "*"], ["usage_requirements", "*"])) def cast(name, type, sources, requirements, default_build, usage_requirements): @@ -61,8 +62,11 @@ def cast(name, type, sources, requirements, default_build, usage_requirements): project = get_manager().projects().current() + real_type = type_.type_from_rule_name(type) + if not real_type: + real_type = type return t.main_target_alternative( - CastTargetClass(name, project, type, + CastTargetClass(name, project, real_type, t.main_target_sources(sources, name), t.main_target_requirements(requirements, project), t.main_target_default_build(default_build, project), diff --git a/src/tools/clang-linux.jam b/src/tools/clang-linux.jam index f6dcda9f3..f977c8c72 100644 --- a/src/tools/clang-linux.jam +++ b/src/tools/clang-linux.jam @@ -153,7 +153,7 @@ rule compile.c++.pch ( targets * : sources * : properties * ) { } actions compile.c++.pch { - rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -Xclang -emit-pth -o "$(<)" "$(>)" + rm -f "$(<)" && "$(CONFIG_COMMAND)" -c -x c++-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -Xclang -emit-pth -o "$(<)" "$(>)" } rule compile.c.pch ( targets * : sources * : properties * ) { @@ -164,7 +164,7 @@ rule compile.c.pch ( targets * : sources * : properties * ) { actions compile.c.pch { - rm -f "$(<)" && "$(CONFIG_COMMAND)" -x c-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -Xclang -emit-pth -o "$(<)" "$(>)" + rm -f "$(<)" && "$(CONFIG_COMMAND)" -c -x c-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -Xclang -emit-pth -o "$(<)" "$(>)" } ############################################################################### diff --git a/src/tools/clang.jam b/src/tools/clang.jam index e0ac9a553..2d89f3807 100644 --- a/src/tools/clang.jam +++ b/src/tools/clang.jam @@ -3,7 +3,7 @@ # 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. +# forwards either to 'clang-linux' or 'clang-darwin' modules. import feature ; import os ; diff --git a/src/tools/common.jam b/src/tools/common.jam index ca9480be9..04b87accb 100644 --- a/src/tools/common.jam +++ b/src/tools/common.jam @@ -339,6 +339,10 @@ rule get-absolute-tool-path ( command ) # rule find-tool ( name : additional-paths * : path-last ? ) { + if $(name:D) + { + return [ check-tool-aux $(name) ] ; + } local path = [ path.programs-path ] ; local match = [ path.glob $(path) : $(name) $(name).exe ] ; local additional-match = [ path.glob $(additional-paths) : $(name) diff --git a/src/tools/common.py b/src/tools/common.py index 3f30baa56..87b942c6d 100644 --- a/src/tools/common.py +++ b/src/tools/common.py @@ -182,7 +182,7 @@ def check_init_parameters(toolset, requirement, *args): The return value from this rule is a condition to be used for flags settings. """ assert isinstance(toolset, basestring) - assert is_iterable_typed(requirement, basestring) + assert is_iterable_typed(requirement, basestring) or requirement is None from b2.build import toolset as b2_toolset if requirement is None: requirement = [] @@ -204,7 +204,7 @@ def check_init_parameters(toolset, requirement, *args): ### if $(value)-is-not-empty if value is not None: condition = condition + '-' + value - if __had_unspecified_value.has_key(str_toolset_name): + if str_toolset_name in __had_unspecified_value: raise BaseException("'%s' initialization: parameter '%s' inconsistent\n" \ "no value was specified in earlier initialization\n" \ "an explicit value is specified now" % (toolset, name)) @@ -220,8 +220,8 @@ def check_init_parameters(toolset, requirement, *args): if m: t = m.group(1) - if not __had_value.has_key(str_toolset_name): - if not __declared_subfeature.has_key(str((t, name))): + if str_toolset_name not in __had_value: + if str((t, name)) not in __declared_subfeature: feature.subfeature('toolset', t, name, [], ['propagated']) __declared_subfeature[str((t, name))] = True @@ -231,7 +231,7 @@ def check_init_parameters(toolset, requirement, *args): subcondition += ['' + value ] else: - if __had_value.has_key(str_toolset_name): + if str_toolset_name in __had_value: 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)) @@ -247,7 +247,7 @@ def check_init_parameters(toolset, requirement, *args): if requirement: sig += '-' + '-'.join(requirement) - if __all_signatures.has_key(sig): + if sig in __all_signatures: message = "duplicate initialization of '%s' with the following parameters: " % toolset for arg in args: @@ -314,9 +314,8 @@ def get_invocation_command_nodefault( #FIXME #ECHO "warning: initialized from" [ errors.nearest-user-location ] command = [] - command = ' '.join(command) - - assert(isinstance(command, str)) + if command: + command = ' '.join(command) return command @@ -542,9 +541,16 @@ def prepend_path_variable_command(variable, paths): """ assert isinstance(variable, basestring) assert is_iterable_typed(paths, basestring) + return path_variable_setting_command( + variable, paths + [expand_variable(variable)]) + + +def expand_variable(variable): + """Produce a string that expands the shell variable.""" + if os.name == 'nt': + return '%{}%'.format(variable) + return '${%s}' % variable - return path_variable_setting_command(variable, - paths + os.environ.get(variable, "").split(os.pathsep)) def file_creation_command(): """ @@ -575,10 +581,7 @@ def mkdir(engine, target): __mkdir_set.add(target) # Schedule the mkdir build action. - if os_name() == 'NT': - engine.set_update_action("common.MkDir1-quick-fix-for-windows", target, []) - else: - engine.set_update_action("common.MkDir1-quick-fix-for-unix", target, []) + engine.set_update_action("common.MkDir", target, []) # Prepare a Jam 'dirs' target that can be used to make the build only # construct all the target directories. @@ -816,62 +819,37 @@ def runtime_tag(name, target_type, prop_set ): 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): + global __RM, __CP, __IGNORE, __LN 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 "$(<)"') - + # register the make() and alias() rules globally import b2.tools.make import b2.build.alias - global __RM, __CP, __IGNORE, __LN + windows_hack = '' # ported from trunk@47281 if os_name() == 'NT': __RM = 'del /f /q' - __CP = 'copy' + __CP = 'copy /b' + windows_hack = '+ this-file-does-not-exist-A698EE7806899E69' __IGNORE = '2>nul >nul & setlocal' __LN = __CP #if not __LN: # __LN = CP + MKDIR = 'if not exist "$(<)\\" mkdir "$(<)"' else: __RM = 'rm -f' __CP = 'cp' __IGNORE = '' __LN = 'ln' + MKDIR = 'mkdir -p "$(<)"' - engine.register_action("common.Clean", __RM + ' "$(>)"', - flags=['piecemeal', 'together', 'existing']) - engine.register_action("common.copy", __CP + ' "$(>)" "$(<)"') + engine.register_action("common.MkDir", MKDIR + __IGNORE) + + engine.register_action( + "common.Clean", __RM + ' "$(>)"', flags=['piecemeal', 'together', 'existing']) + engine.register_action("common.copy", '{} "$(>)" {} "$(<)"'.format(__CP, windows_hack)) engine.register_action("common.RmTemps", __RM + ' "$(>)" ' + __IGNORE, flags=['quietly', 'updated', 'piecemeal', 'together']) diff --git a/src/tools/cygwin.jam b/src/tools/cygwin.jam new file mode 100644 index 000000000..7c091c015 --- /dev/null +++ b/src/tools/cygwin.jam @@ -0,0 +1,74 @@ +# Copyright 2004 Vladimir Prus. +# Copyright 2016 Steven Watanabe +# 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) + +# Provides utility functions for handling cygwin paths + +import regex ; + +.cygwin-drive-letter-re = ^/cygdrive/([a-z])/(.*) ; + +# Like W32_GETREG, except prepend HKEY_CURRENT_USER\SOFTWARE and +# HKEY_LOCAL_MACHINE\SOFTWARE to the first argument, returning the first result +# found. Also accounts for the fact that on 64-bit machines, 32-bit software has +# its own area, under SOFTWARE\Wow6432node. +# +local rule software-registry-value ( path : data ? ) +{ + local result ; + for local root in HKEY_CURRENT_USER HKEY_LOCAL_MACHINE + { + for local x64elt in "" Wow6432node\\ # Account for 64-bit windows + { + if ! $(result) + { + result = [ W32_GETREG $(root)\\SOFTWARE\\$(x64elt)$(path) : $(data) ] ; + } + } + + } + return $(result) ; +} + +# :W only works in Cygwin builds of bjam. This one works on NT builds as well. +# +rule cygwin-to-windows-path ( path ) +{ + path = $(path:R="") ; # strip any trailing slash + + local drive-letter = [ SUBST $(path) $(.cygwin-drive-letter-re) $1:/$2 ] ; + if $(drive-letter) + { + path = $(drive-letter) ; + } + else if $(path:R=/x) = $(path) # already rooted? + { + # Look for a cygwin mount that includes each head sequence in $(path). + local head = $(path) ; + local tail = "" ; + + while $(head) + { + local root = [ software-registry-value + "Cygnus Solutions\\Cygwin\\mounts v2\\"$(head) : native ] ; + + if $(root) + { + path = $(tail:R=$(root)) ; + head = ; + } + tail = $(tail:R=$(head:D=)) ; + + if $(head) = / + { + head = ; + } + else + { + head = $(head:D) ; + } + } + } + return [ regex.replace $(path:R="") / \\ ] ; +} diff --git a/src/tools/darwin.jam b/src/tools/darwin.jam index edd6b7a4e..a610603f0 100644 --- a/src/tools/darwin.jam +++ b/src/tools/darwin.jam @@ -66,7 +66,8 @@ toolset.inherit-flags darwin : gcc x86/ power/32 power/64 - power/ ; + power/ + full ; # Options: # @@ -149,6 +150,12 @@ rule init ( version ? : command * : options * : requirement * ) # - GCC on Darwin with -pedantic, suppress unsupported long long warning flags darwin.compile OPTIONS $(condition)/all : -Wno-long-long ; + # - GCC on El Capitan (10.11) does not suport -finline-functions + if "10.11.0" <= $(.host-osx-version) + { + flags darwin.compile OPTIONS $(condition)/full : -Wno-inline ; + } + # - Set the link flags common with the GCC toolset. gcc.init-link-flags darwin darwin $(condition) ; @@ -216,6 +223,10 @@ local rule init-sdk ( condition * : root ? : version + : version-feature ? ) { switch $(version[1]) { + case appletv* : + { + return $(version[1])-$(version[2-]:J=.) ; + } case iphone* : { return $(version[1])-$(version[2-]:J=.) ; @@ -258,6 +269,30 @@ local rule init-sdk ( condition * : root ? : version + : version-feature ? ) # Then device variation options. switch $(version[1]) { + case appletvsim* : + { + local N = $(version[2]) ; + if ! $(version[3]) { N += 00 ; } + else if [ regex.match (..) : $(version[3]) ] { N += $(version[3]) ; } + else { N += 0$(version[3]) ; } + if ! $(version[4]) { N += 00 ; } + else if [ regex.match (..) : $(version[4]) ] { N += $(version[4]) ; } + else { N += 0$(version[4]) ; } + N = $(N:J=) ; + flags darwin.compile OPTIONS $(version-feature) + : -D__IPHONE_OS_VERSION_MIN_REQUIRED=$(N) ; + flags darwin.link OPTIONS $(version-feature) + : -D__IPHONE_OS_VERSION_MIN_REQUIRED=$(N) ; + } + + case appletv* : + { + flags darwin.compile OPTIONS $(version-feature) + : -mtvos-version-min=$(version[2-]:J=.) ; + flags darwin.link OPTIONS $(version-feature) + : -mtvos-version-min=$(version[2-]:J=.) ; + } + case iphonesim* : { local N = $(version[2]) ; @@ -273,7 +308,7 @@ local rule init-sdk ( condition * : root ? : version + : version-feature ? ) flags darwin.link OPTIONS $(version-feature) : -D__IPHONE_OS_VERSION_MIN_REQUIRED=$(N) ; } - + case iphone* : { flags darwin.compile OPTIONS $(version-feature) @@ -333,7 +368,7 @@ 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 sdks = [ GLOB $(sdks-root) : MacOSX*.sdk AppleTVOS*.sdk AppleTVSimulator*.sdk iPhoneOS*.sdk iPhoneSimulator*.sdk ] ; local result ; for local sdk in $(sdks) { @@ -348,6 +383,14 @@ local rule init-available-sdk-versions ( condition * : root ? ) { sdk-version = mac $(sdk-version) ; } + case appletvos : + { + sdk-version = appletv $(sdk-version) ; + } + case appletvsimulator : + { + sdk-version = appletvsim $(sdk-version) ; + } case iphoneos : { sdk-version = iphone $(sdk-version) ; diff --git a/src/tools/doxproc.py b/src/tools/doxproc.py index c41d7fde1..d415133e1 100644 --- a/src/tools/doxproc.py +++ b/src/tools/doxproc.py @@ -50,7 +50,7 @@ def get_args( argv = sys.argv[1:] ): ( option_pairs, other ) = getopt.getopt( argv, '', spec ) map( lambda x: options.__setitem__( x[0], x[1] ), option_pairs ) - if options.has_key( '--help' ): + if '--help' in options: usage() sys.exit(1) @@ -59,7 +59,7 @@ def get_args( argv = sys.argv[1:] ): 'output' : options['--output'], 'id' : options['--id'], 'title' : options['--title'], - 'index' : options.has_key('--enable-index') + 'index' : '--enable-index' in options } def if_attribute(node, attribute, true_value, false_value=None): @@ -151,13 +151,13 @@ class Doxygen2BoostBook: #~ BoostBook references. def _rewriteIDs( self, node ): if node.nodeName in ('link'): - if (self.idmap.has_key(node.getAttribute('linkend'))): + if node.getAttribute('linkend') in self.idmap: #~ A link, and we have someplace to repoint it at. node.setAttribute('linkend',self.idmap[node.getAttribute('linkend')]) else: #~ A link, but we don't have a generated target for it. node.removeAttribute('linkend') - elif hasattr(node,'hasAttribute') and node.hasAttribute('id') and self.idmap.has_key(node.getAttribute('id')): + elif hasattr(node,'hasAttribute') and node.hasAttribute('id') and node.getAttribute('id') in self.idmap: #~ Simple ID, and we have a translation. node.setAttribute('id',self.idmap[node.getAttribute('id')]) #~ Recurse, and iterate, depth-first traversal which turns out to be @@ -333,7 +333,7 @@ class Doxygen2BoostBook: 'detailed' : self._getChildData('detaileddescription',root=node), 'parsed' : False } - if self.symbols.has_key(namespace['name']): + if namespace['name'] in self.symbols: if not self.symbols[namespace['name']]['parsed']: self.symbols[namespace['name']]['parsed'] = True #~ for n in node.childNodes: @@ -385,7 +385,7 @@ class Doxygen2BoostBook: #~ Translate a ..., def _translate_compounddef_includes_( self, node, target=None, **kwargs ): name = node.firstChild.data - if not self.symbols.has_key(name): + if name not in self.symbols: self._setID(node.getAttribute('refid'),name) self.symbols[name] = { 'kind' : 'header', @@ -800,7 +800,7 @@ class Doxygen2BoostBook: namespace = '::'.join(result['namespace']) while ( len(result['namespace']) > 0 and ( - not self.symbols.has_key(namespace) or + namespace not in self.symbols or self.symbols[namespace]['kind'] != 'namespace') ): result['name'] = result['namespace'].pop()+'::'+result['name'] diff --git a/src/tools/emscripten.jam b/src/tools/emscripten.jam new file mode 100644 index 000000000..c9a0009db --- /dev/null +++ b/src/tools/emscripten.jam @@ -0,0 +1,113 @@ +# Copyright Rene Rivera 2016 +# 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 feature ; +import os ; +import toolset ; +import common ; +import gcc ; +import type ; + +feature.feature embind : off on : propagated ; +feature.feature closure : off on full : propagated ; +feature.feature link-optimization : off on full : propagated ; + +rule init ( version ? : command * : options * ) +{ + command = [ common.get-invocation-command emscripten + : emcc + : $(command) ] ; + + # Determine the version + if $(command) + { + local command-string = \"$(command)\" ; + command-string = $(command-string:J=" ") ; + version ?= [ MATCH "([0-9.]+)" + : [ SHELL "$(command-string) --version" ] ] ; + } + + local condition = [ common.check-init-parameters emscripten + : version $(version) ] ; + + common.handle-options emscripten : $(condition) : $(command) : $(options) ; +} + +feature.extend toolset : emscripten ; + +toolset.inherit-generators emscripten emscripten + : gcc + : gcc.mingw.link gcc.mingw.link.dll gcc.compile.c.pch gcc.compile.c++.pch + ; +toolset.inherit-rules emscripten : gcc ; +toolset.inherit-flags emscripten : gcc + : + off speed space + off on + off on full + off all on + off on + off on + off on + ; + +type.set-generated-target-suffix EXE : emscripten : "js" ; +type.set-generated-target-suffix OBJ : emscripten : "bc" ; +type.set-generated-target-suffix STATIC_LIB : emscripten : "bc" ; + +toolset.flags emscripten.compile OPTIONS ; +toolset.flags emscripten.compile OPTIONS ; +toolset.flags emscripten.compile.c++ OPTIONS ; + +toolset.flags emscripten.compile OPTIONS off : -O0 ; +toolset.flags emscripten.compile OPTIONS speed : -O3 ; +toolset.flags emscripten.compile OPTIONS space : -Oz ; +toolset.flags emscripten.link OPTIONS off : -O0 ; +toolset.flags emscripten.link OPTIONS speed : -O3 ; +toolset.flags emscripten.link OPTIONS space : -O3 ; + +toolset.flags emscripten.compile OPTIONS on : --profiling-funcs ; + +toolset.flags emscripten.compile OPTIONS off : -fno-inline ; +toolset.flags emscripten.compile OPTIONS on : -Wno-inline ; +toolset.flags emscripten.compile OPTIONS full : -Wno-inline ; + +toolset.flags emscripten.compile OPTIONS off : -w ; +toolset.flags emscripten.compile OPTIONS on : -Wall ; +toolset.flags emscripten.compile OPTIONS all : -Wall -pedantic ; +toolset.flags emscripten.compile OPTIONS on : -Werror ; + +toolset.flags emscripten OPTIONS off : -g0 ; +toolset.flags emscripten OPTIONS on : -g4 -s DEMANGLE_SUPPORT=1 ; +toolset.flags emscripten OPTIONS off : -fno-rtti ; + +toolset.flags emscripten.link OPTIONS on : --bind ; +toolset.flags emscripten.link OPTIONS on : --closure 1 ; +toolset.flags emscripten.link OPTIONS full : --closure 2 ; +toolset.flags emscripten.link OPTIONS off : --llvm-lto 0 ; +toolset.flags emscripten.link OPTIONS on : --llvm-lto 1 ; +toolset.flags emscripten.link OPTIONS full : --llvm-lto 3 ; + +actions compile.c +{ + "$(CONFIG_COMMAND)" -x c $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +actions compile.c++ +{ + "$(CONFIG_COMMAND)" -x c++ $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +actions archive +{ + "$(CONFIG_COMMAND)" $(AROPTIONS) -o "$(<)" "$(>)" +} + +toolset.flags emscripten.link USER_OPTIONS ; + +actions link bind LIBRARIES +{ + "$(CONFIG_COMMAND)" $(USER_OPTIONS) -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" $(START-GROUP) $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) +} diff --git a/src/tools/gcc.jam b/src/tools/gcc.jam index e94eced20..0f346a501 100644 --- a/src/tools/gcc.jam +++ b/src/tools/gcc.jam @@ -12,6 +12,7 @@ import "class" : new ; import common ; +import cygwin ; import feature ; import fortran ; import generators ; @@ -231,13 +232,9 @@ rule init ( version ? : command * : options * ) # programs as needed to prefer using their installation specific versions. # This is essential for correct use of MinGW and for cross-compiling. - local nl = " -" ; - # - Archive builder. local archiver = [ common.get-invocation-command gcc - : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : - [ SHELL "$(command-string) -print-prog-name=ar" ] ] ] + : [ .get-prog-name $(command-string) : ar : $(flavor) ] : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; @@ -249,8 +246,7 @@ rule init ( version ? : command * : options * ) # - Ranlib. local ranlib = [ common.get-invocation-command gcc - : [ NORMALIZE_PATH [ MATCH "(.*)[$(nl)]+" : - [ SHELL "$(command-string) -print-prog-name=ranlib" ] ] ] + : [ .get-prog-name $(command-string) : ranlib : $(flavor) ] : [ feature.get-values : $(options) ] : $(bin) : search-path ] ; @@ -286,6 +282,19 @@ if [ os.name ] = NT JAMSHELL = % ; } +# Uses -print-prog-name to get the name of the tool. +# Converts the path to native form if using cygwin. +rule .get-prog-name ( command-string : tool : flavor ? ) +{ + local prog-name = [ NORMALIZE_PATH [ MATCH "(.*)[\n]+" : + [ SHELL "$(command-string) -print-prog-name=$(tool)" ] ] ] ; + if $(flavor) != mingw && [ os.name ] = NT + { + prog-name = [ cygwin.cygwin-to-windows-path $(prog-name) ] ; + } + return $(prog-name) ; +} + generators.register-c-compiler gcc.compile.c++.preprocess : CPP : PREPROCESSED_CPP : gcc ; generators.register-c-compiler gcc.compile.c.preprocess : C : PREPROCESSED_C : gcc ; generators.register-c-compiler gcc.compile.c++ : CPP : OBJ : gcc ; diff --git a/src/tools/gcc.py b/src/tools/gcc.py index bc810e489..270ca97b6 100644 --- a/src/tools/gcc.py +++ b/src/tools/gcc.py @@ -103,11 +103,13 @@ def init(version = None, command = None, options = None): # The command. command = to_seq(common.get_invocation_command('gcc', 'g++', command)) # The root directory of the tool install. - root = feature.get_values('', options) ; + root = feature.get_values('', options) + root = root[0] if root else '' # The bin directory where to find the command to execute. bin = None # The flavor of compiler. flavor = feature.get_values('', options) + flavor = flavor[0] if flavor else '' # Autodetect the root and bin dir if not given. if command: if not bin: @@ -198,7 +200,7 @@ def init(version = None, command = None, options = None): # 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) + rc.configure([rc_command], condition, ['' + rc_type]) ###if [ os.name ] = NT ###{ @@ -209,6 +211,8 @@ def init(version = None, command = None, options = None): #FIXME: when register_c_compiler is moved to # generators, these should be updated +builtin.register_c_compiler('gcc.compile.c++.preprocess', ['CPP'], ['PREPROCESSED_CPP'], ['gcc']) +builtin.register_c_compiler('gcc.compile.c.preprocess', ['C'], ['PREPROCESSED_C'], ['gcc']) 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']) @@ -376,6 +380,24 @@ engine.register_action( function=gcc_compile_c, bound_list=['PCH_FILE']) +engine.register_action( + 'gcc.compile.c++.preprocess', + function=gcc_compile_cpp, + bound_list=['PCH_FILE'], + command=""" + $(CONFIG_COMMAND) $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" "$(>:W)" -E >"$(<:W)" + """ +) + +engine.register_action( + 'gcc.compile.c.preprocess', + function=gcc_compile_c, + bound_list=['PCH_FILE'], + command=""" + $(CONFIG_COMMAND) $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" "$(>)" -E >$(<) + """ +) + def gcc_compile_asm(targets, sources, properties): get_manager().engine().set_target_variable(targets, 'LANG', '-x assembler-with-cpp') diff --git a/src/tools/intel-win.jam b/src/tools/intel-win.jam index d50116796..21392e7bf 100644 --- a/src/tools/intel-win.jam +++ b/src/tools/intel-win.jam @@ -475,6 +475,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] .iclvars-14.0-supported-vcs = "12.0 11.0 10.0 9.0" ; .iclvars-15.0-supported-vcs = "12.0 11.0 10.0 9.0" ; .iclvars-16.0-supported-vcs = "14.0 12.0 11.0 10.0 9.0" ; +.iclvars-17.0-supported-vcs = "14.0 12.0 11.0 10.0" ; .iclvars-version-alias-vc14 = vs2015 ; .iclvars-version-alias-vc12 = vs2013 ; .iclvars-version-alias-vc11 = vs2012 ; diff --git a/src/tools/link.jam b/src/tools/link.jam index 944911b3e..50ec485c6 100644 --- a/src/tools/link.jam +++ b/src/tools/link.jam @@ -488,7 +488,7 @@ if [ os.name ] = NT actions junction { - if exist "$(<)" del "$(<)" + if exist "$(<)" rmdir "$(<)" mklink /J "$(<)" "$(>)" } diff --git a/src/tools/mc.py b/src/tools/mc.py index 9992c36c1..d8b970194 100644 --- a/src/tools/mc.py +++ b/src/tools/mc.py @@ -34,7 +34,7 @@ feature('mc-set-customer-bit', ['no', 'yes'], ['free']) flags('mc.compile', 'MCFLAGS', ['ansi'], ['-a']) flags('mc.compile', 'MCFLAGS', ['unicode'], ['-u']) -flags('mc.compile', 'MCFLAGS', ['ansi'], '-A') +flags('mc.compile', 'MCFLAGS', ['ansi'], ['-A']) flags('mc.compile', 'MCFLAGS', ['unicode'], ['-U']) flags('mc.compile', 'MCFLAGS', ['no'], []) flags('mc.compile', 'MCFLAGS', ['yes'], ['-c']) diff --git a/src/tools/message.py b/src/tools/message.py index 5ec3efc76..3f276f93e 100644 --- a/src/tools/message.py +++ b/src/tools/message.py @@ -15,9 +15,10 @@ from b2.manager import get_manager class MessageTargetClass(targets.BasicTarget): - def __init__(self, name, project, *args): - - targets.BasicTarget.__init__(self, name, project, []) + def __init__(self, name, project, sources, requirements, default_build, + usage_requirements, *args): + targets.BasicTarget.__init__( + self, name, project, sources, requirements, default_build, usage_requirements) self.args = args self.built = False @@ -38,9 +39,16 @@ def message(name, *args): name = name[0] t = get_manager().targets() - project = get_manager().projects().current() - return t.main_target_alternative(MessageTargetClass(*((name, project) + args))) + return t.main_target_alternative( + MessageTargetClass( + name, project, + t.main_target_sources([], name), + t.main_target_requirements([], project), + t.main_target_default_build([], project), + t.main_target_usage_requirements([], project), + *args + )) get_manager().projects().add_rule("message", message) diff --git a/src/tools/mpi.jam b/src/tools/mpi.jam index a161101bc..1ccc54133 100644 --- a/src/tools/mpi.jam +++ b/src/tools/mpi.jam @@ -3,33 +3,33 @@ # (C) Copyright 2005, 2006 Trustees of Indiana University # (C) Copyright 2005 Douglas Gregor # -# Distributed under the Boost Software License, Version 1.0. (See accompanying +# 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.) # # Authors: Douglas Gregor # Andrew Lumsdaine # # ==== MPI Configuration ==== -# -# For many users, MPI support can be enabled simply by adding the following +# +# For many users, MPI support can be enabled simply by adding the following # line to your user-config.jam file: # # using mpi ; # -# This should auto-detect MPI settings based on the MPI wrapper compiler in +# This should auto-detect MPI settings based on the MPI wrapper compiler in # your path, e.g., "mpic++". If the wrapper compiler is not in your path, or # has a different name, you can pass the name of the wrapper compiler as the # first argument to the mpi module: # # using mpi : /opt/mpich2-1.0.4/bin/mpiCC ; # -# If your MPI implementation does not have a wrapper compiler, or the MPI +# If your MPI implementation does not have a wrapper compiler, or the MPI # auto-detection code does not work with your MPI's wrapper compiler, -# you can pass MPI-related options explicitly via the second parameter to the +# you can pass MPI-related options explicitly via the second parameter to the # mpi module: # # using mpi : : lammpio lammpi++ -# mpi lam +# mpi lam # dl ; # # To see the results of MPI auto-detection, pass "--debug-configuration" on @@ -41,17 +41,17 @@ # to this to run tests and tell the program to expect the number of # processors to follow (default: "-np"). With the default parameters, # for instance, the test harness will execute, e.g., -# +# # mpirun -np 4 all_gather_test # # ==== Linking Against the MPI Libraries === # -# To link against the MPI libraries, import the "mpi" module and add the +# To link against the MPI libraries, import the "mpi" module and add the # following requirement to your target: -# -# /mpi//mpi # -# Since MPI support is not always available, you should check +# /mpi//mpi +# +# Since MPI support is not always available, you should check # "mpi.configured" before trying to link against the MPI libraries. import "class" : new ; @@ -82,7 +82,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] # value rest-of-cmdline # # This is a subroutine of cmdline_to_features -rule add_feature ( prefix name cmdline ) +rule add_feature ( prefix name cmdline ) { local match = [ MATCH "^$(prefix)([^\" ]+|\"[^\"]+\") *(.*)$" : $(cmdline) ] ; @@ -142,7 +142,7 @@ rule cmdline_to_features ( cmdline : unknown-features ? ) executable = $(match[1]) ; cmdline = $(match[2]) ; - # List the prefix/feature pairs that we will be able to transform. + # List the prefix/feature pairs that we will be able to transform. # Every kind of parameter not mentioned here will be placed in both # cxxflags and linkflags, because we don't know where they should go. local feature_kinds-D = "define" ; @@ -171,13 +171,13 @@ rule cmdline_to_features ( cmdline : unknown-features ? ) # uses -lpthread as opposed to -pthread. We do want to # set multi, instead of -lpthread. result += "multi" ; - MPI_EXTRA_REQUIREMENTS += "multi" ; + MPI_EXTRA_REQUIREMENTS += "multi" ; } else - { - result += $(add[1]) ; + { + result += $(add[1]) ; } - + cmdline = $(add[2]) ; matched = yes ; } @@ -204,7 +204,7 @@ rule cmdline_to_features ( cmdline : unknown-features ? ) # conflicts when BBv2 determines which "common" properties to # apply to a target. In our case, the single property # gets propagated from the common properties to Boost.MPI - # targets, even though multi is in the usage + # targets, even though multi is in the usage # requirements of /mpi//mpi. MPI_EXTRA_REQUIREMENTS += "multi" ; } @@ -235,63 +235,97 @@ local rule safe-shell-command ( cmdline ) return [ MATCH ".*(SSCOK).*" : $(result) ] ; } -# Initialize the MPI module. +# Initialize the MPI module. rule init ( mpicxx ? : options * : mpirun-with-options * ) { if ! $(options) && $(.debug-configuration) { ECHO "===============MPI Auto-configuration===============" ; } - + if ! $(mpicxx) && [ os.on-windows ] - { - # Try to auto-configure to the Microsoft Compute Cluster Pack + { + # Paths for Microsoft MPI + local ms_mpi_path_native = "C:\\Program Files\\Microsoft MPI" ; + local ms_mpi_sdk_path_native = "C:\\Program Files (x86)\\Microsoft SDKs\\MPI" ; + + # Path for Microsoft Compute Cluster Pack local cluster_pack_path_native = "C:\\Program Files\\Microsoft Compute Cluster Pack" ; - local cluster_pack_path = [ path.make $(cluster_pack_path_native) ] ; - if [ GLOB $(cluster_pack_path_native)\\Include : mpi.h ] + + # Try to auto-configure Microsoft MPI + if [ GLOB $(ms_mpi_path_native)\\Bin : mpiexec.exe ] && + [ GLOB $(ms_mpi_sdk_path_native)\\Include : mpi.h ] + { + if $(.debug-configuration) + { + ECHO "Found Microsoft MPI: $(ms_mpi_path_native)" ; + ECHO "Found Microsoft MPI SDK: $(ms_mpi_sdk_path_native)" ; + } + + local ms_mpi_sdk_path = [ path.make $(ms_mpi_sdk_path_native) ] ; + + # Pick up either the 32-bit or 64-bit library, depending on which address + # model the user has selected. Default to 32-bit. + options = $(ms_mpi_sdk_path)/Include + 64:$(ms_mpi_sdk_path)/Lib/x64 + $(ms_mpi_sdk_path)/Lib/x86 + msmpi + msvc:_SECURE_SCL=0 + ; + + # Setup the "mpirun" equivalent (mpiexec) + .mpirun = "\"$(ms_mpi_path_native)\\Bin\\mpiexec.exe"\" ; + .mpirun_flags = -n ; + } + # Try to auto-configure to the Microsoft Compute Cluster Pack + else if [ GLOB $(cluster_pack_path_native)\\Include : mpi.h ] { if $(.debug-configuration) { ECHO "Found Microsoft Compute Cluster Pack: $(cluster_pack_path_native)" ; } - + + local cluster_pack_path = [ path.make $(cluster_pack_path_native) ] ; + # Pick up either the 32-bit or 64-bit library, depending on which address # model the user has selected. Default to 32-bit. - options = $(cluster_pack_path)/Include + options = $(cluster_pack_path)/Include 64:$(cluster_pack_path)/Lib/amd64 $(cluster_pack_path)/Lib/i386 msmpi msvc:_SECURE_SCL=0 ; - + # Setup the "mpirun" equivalent (mpiexec) .mpirun = "\"$(cluster_pack_path_native)\\Bin\\mpiexec.exe"\" ; .mpirun_flags = -n ; } else if $(.debug-configuration) { + ECHO "Did not find Microsoft MPI in $(ms_mpi_path_native)" ; + ECHO " and/or Microsoft MPI SDK in $(ms_mpi_sdk_path_native)." ; ECHO "Did not find Microsoft Compute Cluster Pack in $(cluster_pack_path_native)." ; } - } - + } + if ! $(options) - { + { # Try to auto-detect options based on the wrapper compiler local command = [ common.get-invocation-command mpi : mpic++ : $(mpicxx) ] ; - if ! $(mpicxx) && ! $(command) + if ! $(mpicxx) && ! $(command) { - # Try "mpiCC", which is used by MPICH + # Try "mpiCC", which is used by MPICH command = [ common.get-invocation-command mpi : mpiCC ] ; } - if ! $(mpicxx) && ! $(command) + if ! $(mpicxx) && ! $(command) { # Try "mpicxx", which is used by OpenMPI and MPICH2 command = [ common.get-invocation-command mpi : mpicxx ] ; } - if ! $(mpicxx) && ! $(command) + if ! $(mpicxx) && ! $(command) { # Try "CC", which is used by Cray command = [ common.get-invocation-command mpi : CC ] ; @@ -302,10 +336,10 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) local link_flags ; if ! $(command) - { + { # Do nothing: we'll complain later } - # OpenMPI and newer versions of LAM-MPI have -showme:compile and + # OpenMPI and newer versions of LAM-MPI have -showme:compile and # -showme:link. else if [ safe-shell-command "$(command) -showme:compile" ] && [ safe-shell-command "$(command) -showme:link" ] @@ -317,8 +351,8 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) compile_flags = [ SHELL "$(command) -showme:compile" ] ; link_flags = [ SHELL "$(command) -showme:link" ] ; - - # Prepend COMPILER as the executable name, to match the format of + + # Prepend COMPILER as the executable name, to match the format of # other compilation commands. compile_flags = "COMPILER $(compile_flags) -DOMPI_SKIP_MPICXX " ; link_flags = "COMPILER $(link_flags)" ; @@ -379,7 +413,7 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) ECHO "Found IBM MPI wrapper compiler: $(command)" ; } - # + # compile_flags = [ SHELL "$(command) -c -v 2>/dev/null" ] ; compile_flags = [ MATCH "(.*)exec: export.*" : $(compile_flags) ] ; local front = [ MATCH "(.*)-v" : $(compile_flags) ] ; @@ -402,7 +436,7 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) compile_flags = "$(compile_flags) $(f_flags)" ; } } - # Cray + # Cray else if [ safe-shell-command "$(command) -v" ] { compile_flags = [ safe-shell-command "$(command) -###" ] ; @@ -411,8 +445,8 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) # ECHO "Noel: link_flags: $(link_flags)" ; result = " " ; } - - # Prepend COMPILER as the executable name, to match the format of + + # Prepend COMPILER as the executable name, to match the format of if $(result) || $(compile_flags) && $(link_flags) { @@ -421,17 +455,17 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) result = [ strip-eol $(result) ] ; options = [ cmdline_to_features $(result) ] ; } - else - { + else + { compile_flags = [ strip-eol $(compile_flags) ] ; link_flags = [ strip-eol $(link_flags) ] ; # Separately process compilation and link features, then combine # them at the end. - local compile_features = [ cmdline_to_features $(compile_flags) - : "" ] ; - local link_features = [ cmdline_to_features $(link_flags) - : "" ] ; + local compile_features = [ cmdline_to_features $(compile_flags) + : "" ] ; + local link_features = [ cmdline_to_features $(link_flags) + : "" ] ; options = $(compile_features) $(link_features) ; } @@ -444,44 +478,44 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) } else { - local match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$" + local match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$" : $(compile_flags) ] ; ECHO "MPI compilation flags: $(match[2])" ; - local match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$" + local match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$" : $(link_flags) ] ; ECHO "MPI link flags: $(match[2])" ; } } - } - else + } + else { if $(command) { ECHO "MPI auto-detection failed: unknown wrapper compiler $(command)" ; ECHO "Please report this error to the Boost mailing list: http://www.boost.org" ; - } + } else if $(mpicxx) { ECHO "MPI auto-detection failed: unable to find wrapper compiler $(mpicxx)" ; - } + } else { ECHO "MPI auto-detection failed: unable to find wrapper compiler `mpic++' or `mpiCC'" ; } ECHO "You will need to manually configure MPI support." ; } - + } # Find mpirun (or its equivalent) and its flags if ! $(.mpirun) { - .mpirun = + .mpirun = [ common.get-invocation-command mpi : mpirun : $(mpirun-with-options[1]) ] ; .mpirun_flags = $(mpirun-with-options[2-]) ; .mpirun_flags ?= -np ; } - + if $(.debug-configuration) { if $(options) @@ -494,15 +528,15 @@ rule init ( mpicxx ? : options * : mpirun-with-options * ) { echo "MPI launcher: $(.mpirun) $(.mpirun_flags)" ; } - + ECHO "====================================================" ; } - if $(options) + if $(options) { .configured = true ; - # Set up the "mpi" alias + # Set up the "mpi" alias alias mpi : : : : $(options) ; } } @@ -537,21 +571,21 @@ class mpi-test-generator : generator } rule run ( project name ? : property-set : sources * : multiple ? ) - { + { # Generate an executable from the sources. This is the executable we will run. - local executable = + local executable = [ generators.construct $(project) $(name) : EXE : $(property-set) : $(sources) ] ; - result = + result = [ construct-result $(executable[2-]) : $(project) $(name)-run : $(property-set) ] ; } } # Use mpi-test-generator to generate MPI tests from sources -generators.register +generators.register [ new mpi-test-generator mpi.capture-output : : RUN_MPI_OUTPUT ] ; -generators.register-standard testing.expect-success +generators.register-standard testing.expect-success : RUN_MPI_OUTPUT : RUN_MPI ; # The number of processes to spawn when executing an MPI test. @@ -574,25 +608,25 @@ rule capture-output ( target : sources * : properties * ) JAM_SEMAPHORE on $(target) = mpi-run-semaphore ; # We launch MPI processes using the "mpirun" equivalent specified by the user. - LAUNCHER on $(target) = + LAUNCHER on $(target) = [ on $(target) return $(.mpirun) $(.mpirun_flags) $(num_processes) ] ; } -# Creates a set of test cases to be run through the MPI launcher. The name, sources, -# and requirements are the same as for any other test generator. However, schedule is -# a list of numbers, which indicates how many processes each test run will use. For +# Creates a set of test cases to be run through the MPI launcher. The name, sources, +# and requirements are the same as for any other test generator. However, schedule is +# a list of numbers, which indicates how many processes each test run will use. For # example, passing 1 2 7 will run the test with 1 process, then 2 processes, then 7 -# 7 processes. The name provided is just the base name: the actual tests will be -# the name followed by a hypen, then the number of processes. +# 7 processes. The name provided is just the base name: the actual tests will be +# the name followed by a hypen, then the number of processes. rule mpi-test ( name : sources * : requirements * : schedule * ) -{ +{ sources ?= $(name).cpp ; schedule ?= 1 2 3 4 7 8 13 17 ; local result ; for processes in $(schedule) - { - result += [ testing.make-test + { + result += [ testing.make-test run-mpi : $(sources) /boost/mpi//boost_mpi : $(requirements) msvc:static $(processes) : $(name)-$(processes) ] ; } diff --git a/src/tools/msvc.jam b/src/tools/msvc.jam index f3561db20..3f8f49e68 100644 --- a/src/tools/msvc.jam +++ b/src/tools/msvc.jam @@ -793,7 +793,7 @@ actions write-setup-script # Local helper rule to create the vcvars setup command for given architecture # and options. # -local rule generate-setup-cmd ( version : command : parent : options * : cpu : global-setup : default-global-setup-options : default-setup ) +local rule generate-setup-cmd ( version : command : parent : options * : cpu : global-setup ? : default-global-setup-options : default-setup ) { local setup-prefix = "call " ; local setup-suffix = " >nul"$(.nl) ; @@ -907,7 +907,11 @@ 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 14)" : $(command) ] + if [ MATCH "(Microsoft Visual Studio 15)" : $(command) ] + { + version = 15.0 ; + } + else if [ MATCH "(Microsoft Visual Studio 14)" : $(command) ] { version = 14.0 ; } @@ -960,7 +964,7 @@ local rule configure-really ( version ? : options * ) { cpu = i386 ; } - if $(below-11.0) + else if $(below-11.0) { cpu = i386 amd64 ia64 ; } @@ -1591,7 +1595,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] armv7 armv7s ; # Known toolset versions, in order of preference. -.known-versions = 14.0 12.0 11.0 10.0 10.0express 9.0 9.0express 8.0 8.0express 7.1 +.known-versions = 15.0 14.0 12.0 11.0 10.0 10.0express 9.0 9.0express 8.0 8.0express 7.1 7.1toolkit 7.0 6.0 ; # Version aliases. @@ -1604,6 +1608,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] .version-alias-11 = 11.0 ; .version-alias-12 = 12.0 ; .version-alias-14 = 14.0 ; +.version-alias-15 = 15.0 ; # Names of registry keys containing the Visual C++ installation path (relative # to "HKEY_LOCAL_MACHINE\SOFTWARE\\Microsoft"). @@ -1619,6 +1624,7 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] .version-11.0-reg = "VisualStudio\\11.0\\Setup\\VC" ; .version-12.0-reg = "VisualStudio\\12.0\\Setup\\VC" ; .version-14.0-reg = "VisualStudio\\14.0\\Setup\\VC" ; +.version-15.0-reg = "VisualStudio\\15.0\\Setup\\VC" ; # Visual C++ Toolkit 2003 does not store its installation path in the registry. # The environment variable 'VCToolkitInstallDir' and the default installation diff --git a/src/tools/msvc.py b/src/tools/msvc.py index 8cdc273e0..efd3a908a 100644 --- a/src/tools/msvc.py +++ b/src/tools/msvc.py @@ -344,7 +344,7 @@ class SetupAction: def register_setup_action(action_name,setup_function,function=None): global engine - if engine.actions.has_key(action_name): + if action_name in engine.actions: raise "Bjam action %s is already defined" % action_name engine.actions[action_name] = SetupAction(setup_function, function) @@ -380,12 +380,12 @@ def setup_preprocess_c_cpp_action(targets, sources, properties): return 'preprocess-c-c++' register_setup_action( - 'msvc.preprocess.c', + 'msvc.compile.c.preprocess', setup_preprocess_c_cpp_action, function=compile_c_preprocess) register_setup_action( - 'msvc.preprocess.c++', + 'msvc.compile.c++.preprocess', setup_preprocess_c_cpp_action, function=compile_cpp_preprocess) @@ -623,6 +623,102 @@ def auto_detect_toolset_versions(): register_configuration(i,default_path(i)) +def maybe_rewrite_setup(toolset, setup_script, setup_options, version, rewrite_setup='off'): + """ + Helper rule to generate a faster alternative to MSVC setup scripts. + + We used to call MSVC setup scripts directly in every action, however in + newer MSVC versions (10.0+) they make long-lasting registry queries + which have a significant impact on build time. + """ + result = '"{}" {}'.format(setup_script, setup_options) + + # At the moment we only know how to rewrite scripts with cmd shell. + if os.name == 'nt' and rewrite_setup != 'off': + basename = os.path.basename(setup_script) + filename, _ = os.path.splitext(basename) + setup_script_id = 'b2_{}_{}_{}'.format(toolset, version, filename) + if setup_options: + setup_script_id = '{}_{}'.format(setup_script_id, setup_options) + + tempdir = os.environ.get('TEMP') + replacement = os.path.join(tempdir, setup_script_id + '.cmd') + if rewrite_setup == 'always' or not os.path.exists(replacement): + import subprocess + # call the setup script and print the environment after doing so + p = subprocess.Popen([ + setup_script, setup_options, '>', 'nul', '&&', 'set', + ], stdout=subprocess.PIPE, shell=True + ) + stdout, _ = p.communicate() + + diff_vars = [] + for var in stdout.splitlines(): + # returns a tuple of ('var-name', '=', 'value'). + # partition is being used here (over something like .split()) + # for two reasons: + # 1) an environment variable may have a value that contains an '='; + # .partition() will still return the correct key and value pair. + # 2) if the line doesn't contain an '=' at all, then the returned + # tuple will contain only empty strings rather than raising + # an exception. + key, _, value = var.partition('=') + # os.environ handles casing differences here. Usually the + # call to "set" above will produce pascal-cased environment + # variable names, so a normal python dict can't be used here. + # check for the existence of key in case the partitioning() above + # returned an empty key value pair. + if key and os.environ.get(key) != value: + diff_vars.append('SET {}={}'.format(key, value)) + + if diff_vars: + with open(replacement, 'wb') as f: + f.write(os.linesep.join(diff_vars)) + + result = '"{}"'.format(replacement) + else: + result = '"{}"'.format(replacement) + + return result + + +def generate_setup_cmd(version, command, parent, options, cpu, global_setup, + default_global_setup_options, default_setup): + setup_prefix = "call " + setup_suffix = """ >nul\n""" + if on_cygwin(): + setup_prefix = "cmd.exe /S /C call " + setup_suffix = " \">nul\" \"&&\" " + + setup_options = '' + setup_cpu = feature.get_values(''.format(cpu), options) + + if not setup_cpu: + if global_setup: + setup_cpu = global_setup + # If needed we can easily add using configuration flags + # here for overriding which options get passed to the + # global setup command for which target platform: + # setup_options = feature.get_values(''.format(cpu),options) + if not setup_options: + setup_options = default_global_setup_options[cpu] + else: + setup_cpu = locate_default_setup(command, parent, default_setup[cpu]) + else: + setup_cpu = setup_cpu[0] + + # Cygwin to Windows path translation. + # setup-$(c) = "\""$(setup-$(c):W)"\"" ; + + # Append setup options to the setup name and add the final setup + # prefix & suffix. + rewrite = feature.get_values('', options) + rewrite = rewrite[0] if rewrite else '' + setup = maybe_rewrite_setup( + 'msvc', setup_cpu, setup_options, version, rewrite) + return '{}{}{}'.format(setup_prefix, setup, setup_suffix) + + # Worker rule for toolset version configuration. Takes an explicit version id or # nothing in case it should configure the default toolset version (the first # registered one or a new 'default' one in case no toolset versions have been @@ -676,7 +772,9 @@ def configure_really(version=None, options=[]): # version from the path. # FIXME: We currently detect both Microsoft Visual Studio 9.0 and # 9.0express as 9.0 here. - if re.search("Microsoft Visual Studio 14", command): + if re.search("Microsoft Visual Studio[\/\\]2017", command): + version = '15.0' + elif re.search("Microsoft Visual Studio 14", command): version = '14.0' elif re.search("Microsoft Visual Studio 12", command): version = '12.0' @@ -785,39 +883,15 @@ def configure_really(version=None, options=[]): elif somehow_detect_the_itanium_platform: default_global_setup_options[ 'ia64' ] = 'ia64' - setup_prefix = "call " - setup_suffix = """ >nul\n""" - if on_cygwin(): - setup_prefix = "cmd.exe /S /C call " - setup_suffix = " \">nul\" \"&&\" " - for c in cpu: - setup_options = None - setup_cpu = feature.get_values(''.format(c),options) - - if not setup_cpu: - if global_setup: - setup_cpu = global_setup - # If needed we can easily add using configuration flags - # here for overriding which options get passed to the - # global setup command for which target platform: - # setup_options = feature.get_values(''.format(c),options) - if not setup_options: - setup_options = default_global_setup_options[ c ] - else: - setup_cpu = locate_default_setup(command, parent, default_setup[ c ]) - - # Cygwin to Windows path translation. - # setup-$(c) = "\""$(setup-$(c):W)"\"" ; - - # Append setup options to the setup name and add the final setup - # prefix & suffix. - setup_scripts[ c ] = '{}"{}" {}{}'.format(setup_prefix, setup_cpu, setup_options, setup_suffix) + setup_scripts[c] = generate_setup_cmd( + version, command, parent, options, c, global_setup, + default_global_setup_options, default_setup + ) # Get tool names (if any) and finish setup. compiler = feature.get_values("", options) - if not compiler: - compiler = "cl" + compiler = compiler[0] if compiler else 'cl' linker = feature.get_values("", options) if not linker: @@ -965,7 +1039,7 @@ def register_toolset_really(): feature.extend('toolset', ['msvc']) # Intel and msvc supposedly have link-compatible objects. - feature.subfeature( 'toolset', 'msvc', 'vendor', 'intel', ['propagated', 'optional']) + feature.subfeature( 'toolset', 'msvc', 'vendor', ['intel'], ['propagated', 'optional']) # Inherit MIDL flags. toolset.inherit_flags('msvc', 'midl') @@ -1191,7 +1265,7 @@ __cpu_type_itanium2 = ['itanium2', 'mckinley'] # Known toolset versions, in order of preference. -_known_versions = ['14.0', '12.0', '11.0', '10.0', '10.0express', '9.0', '9.0express', '8.0', '8.0express', '7.1', '7.1toolkit', '7.0', '6.0'] +_known_versions = ['15.0', '14.0', '12.0', '11.0', '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' @@ -1203,6 +1277,7 @@ __version_alias_10 = '10.0' __version_alias_11 = '11.0' __version_alias_12 = '12.0' __version_alias_14 = '14.0' +__version_alias_15 = '15.0' # Names of registry keys containing the Visual C++ installation path (relative # to "HKEY_LOCAL_MACHINE\SOFTWARE\\Microsoft"). @@ -1218,6 +1293,7 @@ __version_10_0express_reg = "VCExpress\\10.0\\Setup\\VC" __version_11_0_reg = "VisualStudio\\11.0\\Setup\\VC" __version_12_0_reg = "VisualStudio\\12.0\\Setup\\VC" __version_14_0_reg = "VisualStudio\\14.0\\Setup\\VC" +__version_15_0_reg = "VisualStudio\\15.0\\Setup\\VC" # Visual C++ Toolkit 2003 does not store its installation path in the registry. # The environment variable 'VCToolkitInstallDir' and the default installation diff --git a/src/tools/python.jam b/src/tools/python.jam index cb4a4c3e0..b8968848f 100644 --- a/src/tools/python.jam +++ b/src/tools/python.jam @@ -797,18 +797,14 @@ local rule configure ( version ? : cmd-or-prefix ? : includes * : libraries ? : } } - # Anything left to compute? - if $(includes) && $(libraries) + # Check whether configuration succeeded. + if ! ( $(includes) && $(libraries) ) { - .configured = true ; - } - else - { - version ?= $(fallback-version) ; - version ?= 2.5 ; - exec-prefix ?= $(prefix) ; - compute-default-paths $(target-os) : $(version) : $(prefix:E=) ; + debug-message Python headers and libraries not found. ; + return ; } + + .configured = true ; if ! $(interpreter-cmd) { @@ -833,6 +829,27 @@ local rule configure ( version ? : cmd-or-prefix ? : includes * : libraries ? : debug-message " DLL search path:" \"$(exec-prefix:E=)\" ; } + # + # Discover the presence of NumPy + # + debug-message "Checking for NumPy..." ; + local full-cmd = "import sys; sys.stderr = sys.stdout; import numpy; print(numpy.get_include())" ; + local full-cmd = $(interpreter-cmd)" -c \"$(full-cmd)\"" ; + debug-message "running command '$(full-cmd)'" ; + local result = [ SHELL $(full-cmd) : strip-eol : exit-status ] ; + if $(result[2]) = 0 + { + .numpy = true ; + .numpy-include = $(result[1]) ; + debug-message "NumPy enabled" ; + } + else + { + debug-message "NumPy disabled. Reason:" ; + debug-message " $(full-cmd) aborted with " ; + debug-message " $(result[1])" ; + } + # # End autoconfiguration sequence. # @@ -904,6 +921,19 @@ local rule configure ( version ? : cmd-or-prefix ? : includes * : libraries ? : toolset.add-requirements $(target-requirements:J=,):$(interpreter-cmd) ; + # We also set a default requirement that assigns the first python configured + # for a particular target OS as the default. This makes it so that we can + # select a python interpreter with only knowledge of the target OS. And hence + # can configure different Pythons based on the target OS only. + local toolset-requirements = [ toolset.requirements ] ; + local toolset-target-os-requirements + = [ property.evaluate-conditionals-in-context + [ $(toolset-requirements).raw ] : $(target-os) ] ; + if ! in $(toolset-target-os-requirements:G) + { + toolset.add-requirements $(target-os):$(version:E=default) ; + } + # Register the right suffix for extensions. register-extension-suffix $(extension-suffix) : $(target-requirements) ; @@ -1003,6 +1033,16 @@ rule configured ( ) return $(.configured) ; } +rule numpy ( ) +{ + return $(.numpy) ; +} + +rule numpy-include ( ) +{ + return $(.numpy-include) ; +} + type.register PYTHON_EXTENSION : : SHARED_LIB ; @@ -1058,19 +1098,6 @@ rule python-extension ( name : sources * : requirements * : default-build * : IMPORT python : python-extension : : python-extension ; -rule py2to3 -{ - common.copy $(<) : $(>) ; - 2to3 $(<) ; -} - -actions 2to3 -{ - 2to3 -wn --no-diffs "$(<)" - 2to3 -dwn --no-diffs "$(<)" -} - - # Support for testing. type.register PY : py ; type.register RUN_PYD_OUTPUT ; @@ -1093,22 +1120,6 @@ class python-test-generator : generator local python ; local other-pythons ; - # Make new target that converting Python source by 2to3 when running with Python 3. - local rule make-2to3-source ( source ) - { - if $(pyversion) >= 3.0 - { - local a = [ new action $(source) : python.py2to3 : $(property-set) ] ; - local t = [ utility.basename [ $(s).name ] ] ; - local p = [ new file-target $(t) : PY : $(project) : $(a) ] ; - return $(p) ; - } - else - { - return $(source) ; - } - } - for local s in $(sources) { if [ $(s).type ] = PY @@ -1116,13 +1127,13 @@ class python-test-generator : generator if ! $(python) { # First Python source ends up on command line. - python = [ make-2to3-source $(s) ] ; + python = $(s) ; } else { # Other Python sources become dependencies. - other-pythons += [ make-2to3-source $(s) ] ; + other-pythons += $(s) ; } } } @@ -1212,7 +1223,7 @@ local rule pyd-pythonpath ( source ) # The flag settings on testing.capture-output do not apply to python.capture # output at the moment. Redo this explicitly. toolset.flags python.capture-output ARGS ; - +toolset.flags python.capture-output INPUT_FILES ; rule capture-output ( target : sources * : properties * ) { @@ -1226,8 +1237,10 @@ rule capture-output ( target : sources * : properties * ) PYTHONPATH += [ feature.get-values pythonpath : $(properties) ] ; # After test is run, we remove the Python module, but not the Python script. + local targets-to-remove = $(sources[2-]) ; + targets-to-remove ?= none ; testing.capture-output $(target) : $(sources[1]) : $(properties) : - $(sources[2-]) ; + $(targets-to-remove) ; # PYTHONPATH is different; it will be interpreted by whichever Python is # invoked and so must follow path rules for the target os. The only OSes @@ -1257,5 +1270,20 @@ rule bpl-test ( name : sources * : requirements * ) : $(requirements) : $(name) ] ; } +# The same as bpl-test but additionally require (and link to) boost_numpy. +# Masked whenever NumPy is not enabled. +rule numpy-test ( name : sources * : requirements * ) +{ + numpy-include = [ python.numpy-include ] ; + # yuk ! + if ! $(.numpy) { requirements += no ; } + sources ?= $(name).py $(name).cpp ; + name = [ regex.replace $(name) "[/]" "~" ] ; + return [ testing.make-test run-pyd + : $(sources) /boost/python//boost_numpy /boost/python//boost_python + : $(requirements) $(numpy-include) + : $(name) ] ; +} IMPORT $(__name__) : bpl-test : : bpl-test ; +IMPORT $(__name__) : numpy-test : : numpy-test ; diff --git a/src/tools/qcc.jam b/src/tools/qcc.jam index 3b3557894..faa353064 100644 --- a/src/tools/qcc.jam +++ b/src/tools/qcc.jam @@ -234,5 +234,5 @@ rule link.dll ( targets * : sources * : properties * ) # actions link.dll bind LIBRARIES { - "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,-R$(SPACE)-Wl,"$(RPATH)" -o "$(<)" $(HAVE_SONAME)-Wl,-h$(SPACE)-Wl,$(<[1]:D=) -shared "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-ST) -l$(FINDLIBS-SA) $(OPTIONS) + "$(CONFIG_COMMAND)" -L"$(LINKPATH)" -Wl,-R$(SPACE)-Wl,"$(RPATH)" -o "$(<)" -Wl,-h$(SPACE)-Wl,$(<[1]:D=) -shared "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-ST) -l$(FINDLIBS-SA) $(OPTIONS) } diff --git a/src/tools/qt5.jam b/src/tools/qt5.jam index 6eac4ad3f..464066da8 100644 --- a/src/tools/qt5.jam +++ b/src/tools/qt5.jam @@ -417,7 +417,7 @@ rule init ( prefix : version ? : condition * : namespace ? : infix ? : full_bin } else { - alias QtAngle + alias QtAngle : # sources : # requirements $(target-requirements) @@ -475,9 +475,21 @@ rule init ( prefix : version ? : condition * : namespace ? : infix ? : full_bin add-shared-library QtQuickParticles : QtQml : : $(target-requirements) ; add-shared-library QtQuickTest : QtQml : : $(target-requirements) ; + add-shared-library QtSerialPort : QtCore : QT_SERIALPORT_LIB : $(target-requirements) ; + # QtLocation (since 5.4) add-shared-library QtLocation : QtQuick QtPositioning : QT_LOCATION_LIB : $(target-requirements) ; + # Webengine support (since 5.4) + add-shared-library QtWebEngine : QtGui : QT_WEBENGINE_LIB : $(target-requirements) ; + add-shared-library QtWebEngineCore : QtWebEngine : QT_WEBENGINECORE_LIB : $(target-requirements) ; + add-shared-library QtWebEngineWidgets : QtWebEngineCore QtWidgets : QT_WEBENGINEWIDGETS_LIB : $(target-requirements) ; + + add-shared-library QtWebChannel : QtQml : QT_WEBCHANNEL_LIB : $(target-requirements) ; + add-shared-library QtWebSockets : QtNetwork : QT_WEBSOCKETS_LIB : $(target-requirements) ; + + add-shared-library QtWebView : QtWebEngineCore QtWebChannel : QT_WEBVIEW_LIB : $(target-requirements) ; + # Qt3d libraries (since 5.6) add-shared-library Qt3DCore : QtGui : QT_3DCORE_LIB : $(target-requirements) ; add-shared-library Qt3DRender : Qt3DCore QtConcurrent : QT_3DRENDER_LIB : $(target-requirements) ; @@ -487,6 +499,27 @@ rule init ( prefix : version ? : condition * : namespace ? : infix ? : full_bin # QtCharts (since 5.7) add-shared-library QtCharts : QtWidgets : QT_CHARTS_LIB : $(target-requirements) ; + # 3D data visualization (since 5.7) + add-shared-library QtDataVisualization : QtGui : QT_DATAVISUALIZATION_LIB : $(target-requirements) ; + + # In-App purchase API (since 5.7) + add-shared-library QtPurchasing : QtCore : QT_PURCHASING_LIB : $(target-requirements) ; + + # Qt Connectivity (since 5.3) + add-shared-library QtBluetooth : QtCore : QT_BLUETOOTH_LIB : $(target-requirements) ; + add-shared-library QtNfc : QtCore : QT_NFC_LIB : $(target-requirements) ; + + # Gamepad (since 5.7) + add-shared-library QtGamepad : QtCore : QT_GAMEPAD_LIB : $(target-requirements) ; + + # SCXML state machine (since 5.7) + add-shared-library QtScxml : QtCore : QT_SCXML_LIB : $(target-requirements) ; + + # Tech Preview QtQuick + # SerialBus (since 5.7) + add-shared-library QtSerialBus : QtCore : QT_SERIALBUS_LIB : $(target-requirements) ; + + # Platform dependent libraries # Regular expression support add-shared-library QtV8 : QtCore : : $(target-requirements) ; diff --git a/src/tools/rc.py b/src/tools/rc.py index 5bdebb9be..c7a02dbb6 100644 --- a/src/tools/rc.py +++ b/src/tools/rc.py @@ -68,7 +68,7 @@ def configure (command = None, condition = None, options = None): 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', '.RC_TYPE', condition, [rc_type.lower()]) flags('rc.compile.resource', 'DEFINES', [], ['']) flags('rc.compile.resource', 'INCLUDES', [], ['']) if debug(): @@ -91,12 +91,13 @@ class RCAction: # 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): + if action_name in engine.actions: raise AlreadyDefined("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') + rc_type = rc_type[0] if rc_type else '' global engine engine.set_update_action('rc.compile.resource.' + rc_type, targets, sources, properties) diff --git a/src/tools/stage.py b/src/tools/stage.py index 76b10f65a..2a15dd18a 100644 --- a/src/tools/stage.py +++ b/src/tools/stage.py @@ -60,7 +60,7 @@ class InstallTargetClass(targets.BasicTarget): # properties. if build_ps.get('hardcode-dll-paths') != ['true']: - properties = [p for p in properties if p.feature().name() != 'dll-path'] + properties = [p for p in properties if p.feature.name != 'dll-path'] # If any properties were specified for installing, add # them. @@ -75,7 +75,7 @@ class InstallTargetClass(targets.BasicTarget): # sources, then we shall get virtual targets with the # property set. properties = [p for p in properties - if not p.feature().name() in ['tag', 'location']] + if not p.feature.name in ['tag', 'location']] properties.extend(build_ps.get_properties('dependency')) @@ -90,7 +90,7 @@ class InstallTargetClass(targets.BasicTarget): # making the path absolute will help. if d: p = d[0] - properties.append(property.Property(p.feature(), os.path.abspath(p.value()))) + properties.append(property.Property(p.feature, os.path.abspath(p.value))) return property_set.create(properties) @@ -180,8 +180,8 @@ class InstallTargetClass(targets.BasicTarget): for r in result: if isinstance(r, property.Property): - if r.feature().name() != 'use': - result2.append(r.value()) + if r.feature.name != 'use': + result2.append(r.value) else: result2.append(r) result2 = unique(result2) diff --git a/src/tools/testing-aux.jam b/src/tools/testing-aux.jam index a264e4c35..30309fbb8 100644 --- a/src/tools/testing-aux.jam +++ b/src/tools/testing-aux.jam @@ -1,3 +1,5 @@ +import feature ; + # This module is imported by testing.py. The definitions here are # too tricky to do in Python diff --git a/src/tools/testing.jam b/src/tools/testing.jam index 7fbb692a1..0f6359d20 100644 --- a/src/tools/testing.jam +++ b/src/tools/testing.jam @@ -750,20 +750,37 @@ generators.register-composing testing.time : : TIME ; # there are multiple actions operating on the same target in sequence. One such # example are msvc exe targets first created by a linker action and then updated # with an embedded manifest file by a separate action. -rule record-time ( target : source : start end user system ) +rule record-time ( target : source : start end user system clock ) { local src-string = [$(source:G=:J=",")"] " ; USER_TIME on $(target) += $(src-string)$(user) ; SYSTEM_TIME on $(target) += $(src-string)$(system) ; + CLOCK_TIME on $(target) += $(src-string)$(clock) ; # We need the following variables because attempting to perform such # variable expansion in actions would not work due to quotes getting treated # as regular characters. USER_TIME_SECONDS on $(target) += $(src-string)$(user)" seconds" ; SYSTEM_TIME_SECONDS on $(target) += $(src-string)$(system)" seconds" ; + CLOCK_TIME_SECONDS on $(target) += $(src-string)$(clock)" seconds" ; } +# Support for generating timing information for any main target. To use +# declare a custom make target that uses the testing.time generator rule +# specified here. For example: +# +# make main.cpp : main_cpp.pro : @do-something ; +# time main.time : main.cpp ; +# actions do-something +# { +# sleep 2 && echo "$(<)" > "$(<)" +# } +# +# The above will generate a "main.time", and echo to output, timing +# information for the action of source "main.cpp". + + IMPORT testing : record-time : : testing.record-time ; @@ -774,7 +791,14 @@ IMPORT testing : record-time : : testing.record-time ; rule time ( target : sources + : properties * ) { # Set up rule for recording timing information. - __TIMING_RULE__ on $(sources) = testing.record-time $(target) ; + local action = [ on $(target) return $(.action) ] ; + for local action.source in [ $(action).sources ] + { + # Yes, this uses the private "actual-name" of the target action. + # But it's the only way to get at the real name of the sources + # given the context of header scanners. + __TIMING_RULE__ on [ $(action.source).actual-name ] = testing.record-time $(target) ; + } # Make sure the sources get rebuilt any time we need to retrieve that # information. @@ -786,9 +810,11 @@ actions time { echo user: $(USER_TIME) echo system: $(SYSTEM_TIME) + echo clock: $(CLOCK_TIME) echo user: $(USER_TIME_SECONDS) > "$(<)" echo system: $(SYSTEM_TIME_SECONDS) >> "$(<)" + echo clock: $(CLOCK_TIME_SECONDS) >> "$(<)" } if [ os.name ] = VMS @@ -797,8 +823,10 @@ if [ os.name ] = VMS { WRITE SYS$OUTPUT "user: ", "$(USER_TIME)" WRITE SYS$OUTPUT "system: ", "(SYSTEM_TIME)" + WRITE SYS$OUTPUT "clock: ", "(CLOCK_TIME)" PIPE WRITE SYS$OUTPUT "user: ", "$(USER_TIME_SECONDS)" | TYPE SYS$INPUT /OUT=$(<:W) PIPE WRITE SYS$OUTPUT "system: ", "$(SYSTEM_TIME_SECONDS)" | APPEND /NEW SYS$INPUT $(<:W) + PIPE WRITE SYS$OUTPUT "clock: ", "$(CLOCK_TIME_SECONDS)" | APPEND /NEW SYS$INPUT $(<:W) } } diff --git a/src/tools/testing.py b/src/tools/testing.py index 868905a05..b634bea28 100644 --- a/src/tools/testing.py +++ b/src/tools/testing.py @@ -113,7 +113,7 @@ def make_test(target_type, sources, requirements, target_name=None): # The alias to the real target, per period replacement above. if real_name != target_name: - get_manager().projects().project_rules().all_names_["alias"]( + get_manager().projects().project_rules().rules["alias"]( target_name, [t]) # Remember the test (for --dump-tests). A good way would be to collect all diff --git a/src/tools/types/asm.py b/src/tools/types/asm.py index a4b4aee61..d9a30152e 100644 --- a/src/tools/types/asm.py +++ b/src/tools/types/asm.py @@ -27,7 +27,7 @@ def set_asm_type(type_, sources, name=''): name = name if name else _project_types[project.name() + type_] type_ += '.asm' - cast(name, type_.upper(), sources, [], [], []) + return cast(name, type_.upper(), sources, [], [], []) PROJECT_REGISTRY.add_rule("set-asm-type", set_asm_type) diff --git a/src/tools/types/cpp.py b/src/tools/types/cpp.py index 22f4dece4..50797bae4 100644 --- a/src/tools/types/cpp.py +++ b/src/tools/types/cpp.py @@ -1,84 +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) -import os -import re +from b2.build import type as type_ -import bjam - -from b2.build import type as type_, scanner -from b2.manager import get_manager -from b2.util.utility import replace_grist - - -MANAGER = get_manager() -ENGINE = MANAGER.engine() -SCANNERS = MANAGER.scanners() - - -class CScanner(scanner.Scanner): - def __init__(self, includes): - scanner.Scanner.__init__(self) - self.includes = [] - for include in includes: - self.includes.extend(replace_grist(include, '').split('&&')) - - def pattern(self): - return '#\s*include\s*(<(.*)>|"(.*)")' - - def process(self, target, matches, binding): - # create a single string so that findall - # can be used since it returns a list of - # all grouped matches - match_str = ' '.join(matches) - # the question mark makes the regexes non-greedy - angles = re.findall(r'<(.*?)>', match_str) - quoted = re.findall(r'"(.*?)"', match_str) - - # CONSIDER: the new scoping rules seem to defeat "on target" variables. - g = ENGINE.get_target_variable(target, 'HDRGRIST') - b = os.path.normpath(os.path.dirname(binding)) - - # Attach binding of including file to included targets. When a target is - # directly created from a 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 the including file (we can not get literal "." in the - # include path). - # local g2 = $(g)"#"$(b) ; - g2 = g + '#' + b - - angles = [replace_grist(angle, g) for angle in angles] - quoted = [replace_grist(quote, g2) for quote in quoted] - - includes = angles + quoted - - bjam.call('INCLUDES', target, includes) - bjam.call('NOCARE', includes) - ENGINE.set_target_variable(angles, 'SEARCH', self.includes) - ENGINE.set_target_variable(quoted, 'SEARCH', [b] + self.includes) - - # Just propagate the current scanner to includes, in hope that includes - # do not change scanners. - SCANNERS.propagate(self, includes) - - bjam.call('ISFILE', includes) - - -scanner.register(CScanner, 'include') type_.register_type('CPP', ['cpp', 'cxx', 'cc']) type_.register_type('H', ['h']) type_.register_type('HPP', ['hpp'], 'H') type_.register_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', CScanner) -type_.set_scanner('C', CScanner) -# 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', CScanner) -type_.set_scanner('HPP', CScanner) diff --git a/src/tools/unix.py b/src/tools/unix.py index 681a87202..298fc1dc5 100644 --- a/src/tools/unix.py +++ b/src/tools/unix.py @@ -46,8 +46,13 @@ class UnixArchiveGenerator (builtin.ArchiveGenerator): builtin.ArchiveGenerator.__init__ (self, id, composing, source_types, target_types_and_names, requirements) def run (self, project, name, prop_set, sources): + from b2.build.property_set import PropertySet result = builtin.ArchiveGenerator.run(self, project, name, prop_set, sources) - set_library_order(project.manager(), sources, prop_set, result) + if result and isinstance(result[0], PropertySet): + _, targets = result + else: + targets = result + set_library_order(project.manager(), sources, prop_set, targets) return result class UnixSearchedLibGenerator (builtin.SearchedLibGenerator): @@ -130,7 +135,7 @@ def set_library_order (manager, sources, prop_set, result): used_libraries = [] deps = prop_set.dependency () - sources.extend(d.value() for d in deps) + sources.extend(d.value for d in deps) sources = sequence.unique(sources) for l in sources: diff --git a/src/tools/zlib.jam b/src/tools/zlib.jam index 8095eeeee..5a7d5fbdf 100644 --- a/src/tools/zlib.jam +++ b/src/tools/zlib.jam @@ -135,13 +135,11 @@ rule init ( [ property.select : $(options) ] "and" [ property.select : $(options) ] ; } - else - { - no-build-from-source = true ; - } } - - source-path ?= [ modules.peek : ZLIB_SOURCE ] ; + else + { + source-path ?= [ modules.peek : ZLIB_SOURCE ] ; + } if $(.configured.$(condition)) { @@ -158,7 +156,7 @@ rule init ( } return ; } - else if $(source-path) && ! $(no-build-from-source) + else if $(source-path) { build-name ?= z ; library-id = [ CALC $(library-id) + 1 ] ; @@ -185,7 +183,8 @@ rule init ( } } local target ; - if $(sources) { + if $(sources) + { target = [ targets.create-typed-target LIB : $(.project) : $(build-name).$(library-id) : $(sources) @@ -207,7 +206,9 @@ rule init ( $(mt).set-target $(target) ; } targets.main-target-alternative $(mt) ; - } else { + } + else + { if $(.debug) { ECHO "notice: [zlib] Using pre-installed library" ; @@ -218,7 +219,7 @@ rule init ( } local mt = [ new ac-library zlib : $(.project) : $(condition) : - $(include-path) : $(library-path) : $(library-name) : $(root) ] ; + $(include-path) : $(library-path) : $(library-name) ] ; $(mt).set-header $(header) ; $(mt).set-default-names $(names) ; targets.main-target-alternative $(mt) ; diff --git a/src/util/option.py b/src/util/option.py index 120c2a32c..b23a7257c 100644 --- a/src/util/option.py +++ b/src/util/option.py @@ -29,7 +29,7 @@ def get(name, default_value=None, implied_value=None): m = b2.util.regex.transform(sys.argv, "--(" + re.escape(name) + ")") if m and implied_value: return implied_value - elif options.has_key(name) and options[name] != None: + elif options.get(name) is not None: return options[name] else: return default_value diff --git a/src/util/os_j.py b/src/util/os_j.py index f44cca620..f5dff1f90 100644 --- a/src/util/os_j.py +++ b/src/util/os_j.py @@ -8,6 +8,7 @@ # Copyright 2003, 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) +import os import bjam @@ -17,3 +18,7 @@ __OS = bjam.call("peek", [], "OS")[0] # when faced with Python naming def name(): return __OS + + +def environ(keys): + return [os.environ[key] for key in keys if key in os.environ] diff --git a/src/util/path.py b/src/util/path.py index 7b9032073..e451c2cbe 100644 --- a/src/util/path.py +++ b/src/util/path.py @@ -881,7 +881,7 @@ def glob_tree(roots, patterns, exclude_patterns=None): exclude_patterns = [] result = glob(roots, patterns, exclude_patterns) - subdirs = [s for s in glob(roots, ["*"]) if s != "." and s != ".." and os.path.isdir(s)] + subdirs = [s for s in glob(roots, ["*"], exclude_patterns) if s != "." and s != ".." and os.path.isdir(s)] if subdirs: result.extend(glob_tree(subdirs, patterns, exclude_patterns)) diff --git a/src/util/regex.py b/src/util/regex.py index 6348c6fb1..37eeb6d88 100644 --- a/src/util/regex.py +++ b/src/util/regex.py @@ -38,7 +38,16 @@ def replace(s, pattern, replacement): pattern (str): the search expression replacement (str): the string to replace each match with """ - return re.sub(pattern, replacement, s) + # the replacement string may contain invalid backreferences (like \1 or \g) + # which will cause python's regex to blow up. Since this should emulate + # the jam version exactly and the jam version didn't support + # backreferences, this version shouldn't either. re.sub + # allows replacement to be a callable; this is being used + # to simply return the replacement string and avoid the hassle + # of worrying about backreferences within the string. + def _replacement(matchobj): + return replacement + return re.sub(pattern, _replacement, s) @bjam_signature((['items', '*'], ['match'], ['replacement'])) diff --git a/src/util/set.py b/src/util/set.py index f2239a021..98b1d17f5 100644 --- a/src/util/set.py +++ b/src/util/set.py @@ -10,13 +10,11 @@ from .utility import to_seq def difference (b, a): """ Returns the elements of B that are not in A. """ - assert is_iterable(b) - assert is_iterable(a) + a = set(a) result = [] - for element in b: - if not element in a: - result.append (element) - + for item in b: + if item not in a: + result.append(item) return result def intersection (set1, set2): diff --git a/src/util/utility.py b/src/util/utility.py index 162a57be4..ded3e5bcd 100644 --- a/src/util/utility.py +++ b/src/util/utility.py @@ -59,17 +59,28 @@ def replace_grist (features, new_grist): """ assert is_iterable_typed(features, basestring) or isinstance(features, basestring) assert isinstance(new_grist, basestring) - 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) + # this function is used a lot in the build phase and the original implementation + # was extremely slow; thus some of the weird-looking optimizations for this function. + single_item = False + if isinstance(features, str): + features = [features] + single_item = True - if isinstance (features, str): - return replace_grist_one (features, new_grist) - else: - return [ replace_grist_one (feature, new_grist) for feature in features ] + result = [] + for feature in features: + # 'value' -> ('', 'value') + # 'something' -> ('something', '', '') + # 'msvc/value' -> ('', 'msvc/value') + grist, split, value = feature.partition('>') + # if a partition didn't occur, then grist is just 'something' + # set the value to be the grist + if not value and not split: + value = grist + result.append(new_grist + value) + + if single_item: + return result[0] + return result def get_value (property): """ Gets the value of a property, that is, the part following the grist, if any. @@ -124,7 +135,7 @@ def forward_slashes (s): """ Converts all backslashes to forward slashes. """ assert isinstance(s, basestring) - return __re_backslash.sub ('/', s) + return s.replace('\\', '/') def split_action_id (id): diff --git a/test/BoostBuild.py b/test/BoostBuild.py index 8c51cf05f..55f3bc221 100644 --- a/test/BoostBuild.py +++ b/test/BoostBuild.py @@ -452,7 +452,12 @@ class Tester(TestCmd.TestCmd): if ignore_toolset_requirements: kw["program"].append("--ignore-toolset-requirements") if "--python" in sys.argv: - kw["program"].append("--python") + # -z disables Python optimization mode. + # this enables type checking (all assert + # and if __debug__ statements). + kw["program"].extend(["--python", "-z"]) + if "--stacktrace" in sys.argv: + kw["program"].append("--stacktrace") kw["chdir"] = subdir self.last_program_invocation = kw["program"] build_time_start = time.time() diff --git a/test/bzip2.py b/test/bzip2.py index b966eb718..4e74c6023 100755 --- a/test/bzip2.py +++ b/test/bzip2.py @@ -39,7 +39,7 @@ t.rm('bzip2') common_stuff = ''' source_file('test.cpp', 'test.cpp') source_file('main.cpp', 'int main() {}') -source_file('bzlib.h.cpp', '#include ') +source_file('bzlib.h.cpp', '#include \\n') action('-c -x c++ $main.cpp -o $main.o') ''' t.write('test.cpp', 'test.cpp') diff --git a/test/conditionals_multiple.py b/test/conditionals_multiple.py index 91b8f30d7..dd1169081 100755 --- a/test/conditionals_multiple.py +++ b/test/conditionals_multiple.py @@ -147,7 +147,7 @@ rule init ( version ? ) { } from b2.build import feature feature.extend('toolset', ["%(toolset)s"]) feature.subfeature('toolset', "%(toolset)s", "version", ['0','1']) -def init ( version ): pass +def init (version=''): pass """ % {"toolset": toolset}) t.write("jamroot.jam", """\ diff --git a/test/core-language/test.jam b/test/core-language/test.jam index 2853c736d..b0ac767ef 100644 --- a/test/core-language/test.jam +++ b/test/core-language/test.jam @@ -1457,9 +1457,9 @@ if $(NT) local sound = "Beep" "ExtendedSounds" ; local r1 = [ W32_GETREGNAMES "HKEY_CURRENT_USER\\Control Panel\\Sound" : values ] ; - check-equal w32_getregnames : $(sound:L) : $(r1:L) ; + check-equal w32_getregnames : $(sound:L) : [ SORT $(r1:L) ] ; local r2 = [ W32_GETREGNAMES "HKCU\\Control Panel\\Sound" : values ] ; - check-equal w32_getregnames : $(sound:L) : $(r2:L) ; + check-equal w32_getregnames : $(sound:L) : [ SORT $(r2:L) ] ; # Some Windows platforms may have additional keys under # 'CurrentControlSet' which we then remove here so they would not be diff --git a/test/dependency_property.py b/test/dependency_property.py index cdd8055b0..bcced6ad9 100644 --- a/test/dependency_property.py +++ b/test/dependency_property.py @@ -10,7 +10,6 @@ # targets ended up being in the same location. import BoostBuild -import string t = BoostBuild.Tester() @@ -31,6 +30,10 @@ void foo() {} """) t.run_build_system(["--no-error-backtrace"], status=1) -t.fail_test(string.find(t.stdout(), "Tried to build the target twice") == -1) +output = t.stdout() +t.fail_test( + "Tried to build the target twice" not in output and + "Duplicate name of actual target" not in output +) t.cleanup() diff --git a/test/inherit_toolset.py b/test/inherit_toolset.py index af1878003..95884647e 100644 --- a/test/inherit_toolset.py +++ b/test/inherit_toolset.py @@ -25,6 +25,36 @@ actions compile { yfc1-compile } actions link { yfc1-link } """) +t.write( + 'yfc1.py', +""" +from b2.build import feature, generators +from b2.manager import get_manager + +MANAGER = get_manager() +ENGINE = MANAGER.engine() + +feature.extend('toolset', ['yfc1']) + +generators.register_standard('yfc1.compile', ['CPP'], ['OBJ'], ['yfc1']) +generators.register_standard('yfc1.link', ['OBJ'], ['EXE'], ['yfc1']) + +ENGINE.register_action( + 'yfc1.compile', + 'yfc1-compile' +) + +ENGINE.register_action( + 'yfc1.link', + 'yfc1-link' +) + +def init(*args): + pass + +""" +) + t.write("yfc2.jam", """\ import feature ; import toolset ; @@ -36,6 +66,25 @@ rule init ( ) { } actions link { yfc2-link } """) +t.write( + 'yfc2.py', +""" +from b2.build import feature, toolset +from b2.manager import get_manager + +MANAGER = get_manager() +ENGINE = MANAGER.engine() + +feature.extend('toolset', ['yfc2']) +toolset.inherit('yfc2', 'yfc1') + +ENGINE.register_action('yfc2.link', 'yfc2-link') + +def init(*args): + pass +""" +) + t.write("jamfile.jam", "exe a : a.cpp ;") t.write("jamroot.jam", "using yfc1 ;") diff --git a/test/qt5/jamroot.jam b/test/qt5/jamroot.jam index 7e50faf01..782922557 100644 --- a/test/qt5/jamroot.jam +++ b/test/qt5/jamroot.jam @@ -48,8 +48,20 @@ if [ qt5.initialized ] # QtQuick version2 [ run qtquick.cpp /qt5//QtQuick : "--" -platform offscreen : $(CWD)/qtquick.qml ] + [ run qtwebengine.cpp /qt5//QtWebEngine ] + [ run qtwebenginewidgets.cpp /qt5//QtWebEngineWidgets ] + + # QtSerialPort + [ run qtserialport.cpp /qt5//QtSerialPort ] + [ run qtlocation.cpp /qt5//QtLocation ] + [ run qtwebchannel.cpp /qt5//QtWebChannel ] + [ run qtwebsockets.cpp /qt5//QtWebSockets ] + [ run qtwebview.cpp /qt5//QtWebView ] + + [ run qtpurchasing.cpp /qt5//QtPurchasing ] + [ run qtcharts.cpp /qt5//QtCharts ] [ run qt3dcore.cpp /qt5//Qt3DCore ] @@ -57,6 +69,19 @@ if [ qt5.initialized ] [ run qt3dinput.cpp /qt5//Qt3DInput ] [ run qt3dlogic.cpp /qt5//Qt3DLogic ] + [ run qtdatavisualization.cpp /qt5//QtDataVisualization ] + + # Qt Connectivity + [ run qtbluetooth.cpp /qt5//QtBluetooth ] + [ run qtnfc.cpp /qt5//QtNfc ] + + [ run qtgamepad.cpp /qt5//QtGamepad ] + + [ run qtscxml.cpp /qt5//QtScxml ] + + [ run qtserialbus.cpp /qt5//QtSerialBus ] + + # Help systems. [ link qthelp.cpp /qt5//QtHelp ] diff --git a/test/qt5/qtbluetooth.cpp b/test/qt5/qtbluetooth.cpp new file mode 100644 index 000000000..53beff17b --- /dev/null +++ b/test/qt5/qtbluetooth.cpp @@ -0,0 +1,34 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtBluetooth + +#include + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_BLUETOOTH_LIB), true); +} + +/*! + Try to detect a device + */ +BOOST_AUTO_TEST_CASE( bluetooth ) +{ + QList localAdapters = QBluetoothLocalDevice::allDevices(); + + if (!localAdapters.empty()) + { + QBluetoothLocalDevice adapter(localAdapters.at(0).address()); + adapter.setHostMode(QBluetoothLocalDevice::HostDiscoverable); + } + else + { + BOOST_TEST(localAdapters.size() == 0); + } +} diff --git a/test/qt5/qtdatavisualization.cpp b/test/qt5/qtdatavisualization.cpp new file mode 100644 index 000000000..bc35c04c5 --- /dev/null +++ b/test/qt5/qtdatavisualization.cpp @@ -0,0 +1,31 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtDataVisualization + +#include + +#include + +#include + +BOOST_AUTO_TEST_CASE (defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_DATAVISUALIZATION_LIB), true); +} + +BOOST_AUTO_TEST_CASE( datavisualization ) +{ + QGuiApplication app(boost::unit_test::framework::master_test_suite().argc, + boost::unit_test::framework::master_test_suite().argv); + + QtDataVisualization::Q3DBars graph; + + graph.setShadowQuality(QtDataVisualization::QAbstract3DGraph::ShadowQualitySoftMedium); + graph.activeTheme()->setBackgroundEnabled(false); + graph.activeTheme()->setLabelBackgroundEnabled(true); +} diff --git a/test/qt5/qtgamepad.cpp b/test/qt5/qtgamepad.cpp new file mode 100644 index 000000000..c6c6aea50 --- /dev/null +++ b/test/qt5/qtgamepad.cpp @@ -0,0 +1,29 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtGamepad + +#include + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GAMEPAD_LIB), true); +} + +/*! + Try to detect a device + */ +BOOST_AUTO_TEST_CASE( gamepad ) +{ + auto gamepads = QGamepadManager::instance()->connectedGamepads(); + if (gamepads.isEmpty()) { + return; + } + + QGamepad gamepad(*gamepads.begin()); +} diff --git a/test/qt5/qtnfc.cpp b/test/qt5/qtnfc.cpp new file mode 100644 index 000000000..df3805f67 --- /dev/null +++ b/test/qt5/qtnfc.cpp @@ -0,0 +1,28 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtNfc + +#include + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_NFC_LIB), true); +} + +/*! + Try to detect a device + */ +BOOST_AUTO_TEST_CASE( nfc ) +{ + QNearFieldManager manager; + if (!manager.isAvailable()) + { + BOOST_TEST_MESSAGE("No Nfc"); + } +} diff --git a/test/qt5/qtpurchasing.cpp b/test/qt5/qtpurchasing.cpp new file mode 100644 index 000000000..9a49ed2cc --- /dev/null +++ b/test/qt5/qtpurchasing.cpp @@ -0,0 +1,44 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtPurchasing + +#include + +#include + +BOOST_AUTO_TEST_CASE (defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_PURCHASING_LIB), true); +} + +class DummyProduct : public QInAppProduct +{ +public: + + DummyProduct() : QInAppProduct{QStringLiteral("One"), + QString{}, + QString{}, + Consumable, + QStringLiteral("DummyProduct"), + nullptr} {}; + void purchase() override {}; +}; + +std::ostream& +operator << (std::ostream& stream, QString const& string) +{ + stream << qPrintable(string); + return stream; +} + +BOOST_AUTO_TEST_CASE (purchase) +{ + DummyProduct product; + + BOOST_TEST(product.price() == QLatin1String("One")); + BOOST_TEST(product.identifier() == QLatin1String("DummyProduct")); +} diff --git a/test/qt5/qtscxml.cpp b/test/qt5/qtscxml.cpp new file mode 100644 index 000000000..9e423a18a --- /dev/null +++ b/test/qt5/qtscxml.cpp @@ -0,0 +1,33 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtScxml + +#include + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SCXML_LIB), true); +} + +std::ostream& +operator << (std::ostream& stream, QString const& string) +{ + stream << qPrintable(string); + return stream; +} + +/*! + */ +BOOST_AUTO_TEST_CASE( scxml ) +{ + QString sessionId = QScxmlStateMachine::generateSessionId(QStringLiteral("dummy")); + + BOOST_TEST(sessionId.isEmpty() == false); + BOOST_TEST(sessionId == QString{"dummy1"}); +} diff --git a/test/qt5/qtserialbus.cpp b/test/qt5/qtserialbus.cpp new file mode 100644 index 000000000..5849351d8 --- /dev/null +++ b/test/qt5/qtserialbus.cpp @@ -0,0 +1,25 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtSerialBus + +#include + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SERIALBUS_LIB), true); +} + +/*! + create a canbus instance + */ +BOOST_AUTO_TEST_CASE( serialBus ) +{ + auto canbus = QCanBus::instance(); + Q_UNUSED(canbus); +} diff --git a/test/qt5/qtserialport.cpp b/test/qt5/qtserialport.cpp new file mode 100644 index 000000000..fd24ed92b --- /dev/null +++ b/test/qt5/qtserialport.cpp @@ -0,0 +1,22 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtSerialPort + +#include + +#include + +BOOST_AUTO_TEST_CASE (defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SERIALPORT_LIB), true); +} + +BOOST_AUTO_TEST_CASE( serialport ) +{ + QSerialPort serialPort; + serialPort.setPortName(QStringLiteral("test serialport")); +} diff --git a/test/qt5/qtwebchannel.cpp b/test/qt5/qtwebchannel.cpp new file mode 100644 index 000000000..e4f05b7f2 --- /dev/null +++ b/test/qt5/qtwebchannel.cpp @@ -0,0 +1,29 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtWebChannel + +#include + +#include + +#include + +BOOST_AUTO_TEST_CASE (defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBCHANNEL_LIB), true); +} + +BOOST_AUTO_TEST_CASE( webchannel ) +{ + QGuiApplication app(boost::unit_test::framework::master_test_suite().argc, + boost::unit_test::framework::master_test_suite().argv); + + QWebChannel channel; + QObject dummy; + channel.registerObject(QStringLiteral("dummy"), &dummy); +} diff --git a/test/qt5/qtwebengine.cpp b/test/qt5/qtwebengine.cpp new file mode 100644 index 000000000..d4c1b0726 --- /dev/null +++ b/test/qt5/qtwebengine.cpp @@ -0,0 +1,30 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtWebEngine + +#include +#include + + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINE_LIB), true); +} + +/*! + Just call the global initialization function + */ +BOOST_AUTO_TEST_CASE( webengine ) +{ + QGuiApplication app(boost::unit_test::framework::master_test_suite().argc, + boost::unit_test::framework::master_test_suite().argv); + + QtWebEngine::initialize(); +} diff --git a/test/qt5/qtwebenginewidgets.cpp b/test/qt5/qtwebenginewidgets.cpp new file mode 100644 index 000000000..f0c3c2d8b --- /dev/null +++ b/test/qt5/qtwebenginewidgets.cpp @@ -0,0 +1,40 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtWebEngineWidgets + +#include + +#include +#include +#include + +#include + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WIDGETS_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINECORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINEWIDGETS_LIB), true); +} + +/*! + Also tests the core library + */ +BOOST_AUTO_TEST_CASE( webengine_widgets ) +{ + QApplication app(boost::unit_test::framework::master_test_suite().argc, + boost::unit_test::framework::master_test_suite().argv); + + QWebEngineSettings *defaultSettings = QWebEngineSettings::globalSettings(); + QWebEngineProfile *defaultProfile = QWebEngineProfile::defaultProfile(); + + defaultSettings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); + defaultProfile->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); +} diff --git a/test/qt5/qtwebsocket.cpp b/test/qt5/qtwebsocket.cpp new file mode 100644 index 000000000..f46aa58b3 --- /dev/null +++ b/test/qt5/qtwebsocket.cpp @@ -0,0 +1,26 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtWebSockets + +#include + +#include + +#include + +BOOST_AUTO_TEST_CASE (defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBSOCKETS_LIB), true); +} + +BOOST_AUTO_TEST_CASE( websocket ) +{ + QCoreApplication app(boost::unit_test::framework::master_test_suite().argc, + boost::unit_test::framework::master_test_suite().argv); + + QWebSocket socket; +} diff --git a/test/qt5/qtwebsockets.cpp b/test/qt5/qtwebsockets.cpp new file mode 100644 index 000000000..9829ce916 --- /dev/null +++ b/test/qt5/qtwebsockets.cpp @@ -0,0 +1,24 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtWebSockets + +#include + +#include + +BOOST_AUTO_TEST_CASE (defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBSOCKETS_LIB), true); +} + +BOOST_AUTO_TEST_CASE( websocket ) +{ + QWebSocket socket; + socket.setPauseMode(QAbstractSocket::PauseNever); + + BOOST_TEST(socket.isValid() == false); +} diff --git a/test/qt5/qtwebview.cpp b/test/qt5/qtwebview.cpp new file mode 100644 index 000000000..dfd130f37 --- /dev/null +++ b/test/qt5/qtwebview.cpp @@ -0,0 +1,31 @@ +// (c) Copyright Juergen Hunold 2016 +// 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) + +#define BOOST_TEST_MODULE QtWebView + +#include + +#include + +#include + +BOOST_AUTO_TEST_CASE( defines) +{ + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINECORE_LIB), true); + BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBVIEW_LIB), true); +} + +/*! + Just call the global initialization function + */ +BOOST_AUTO_TEST_CASE( webview ) +{ + QGuiApplication app(boost::unit_test::framework::master_test_suite().argc, + boost::unit_test::framework::master_test_suite().argv); + + QtWebView::initialize(); +} diff --git a/test/scanner_causing_rebuilds.py b/test/scanner_causing_rebuilds.py index 2b5fc501c..d1ff66bfd 100755 --- a/test/scanner_causing_rebuilds.py +++ b/test/scanner_causing_rebuilds.py @@ -68,6 +68,54 @@ actions foo $(.touch) "$(<[2])" } """) + +t.write( + 'foo.py', +""" +import os + +from b2.build import type as type_, generators +from b2.tools import common +from b2.manager import get_manager + +MANAGER = get_manager() +ENGINE = MANAGER.engine() + +type_.register('FOO', ['foo']) +type_.register('BAR', ['bar']) +generators.register_standard('foo.foo', ['FOO'], ['CPP', 'BAR']) + +def sleep_cmd(delay): + if os.name == 'nt': + return 'ping 127.0.0.1 -n {} -w 1000 >NUL'.format(delay) + return 'sleep {}'.format(delay) + +def foo(targets, sources, properties): + cpp, bar = targets + foo = sources[0] + # We add the INCLUDE relationship between our generated CPP & BAR targets + # explicitly instead of relying on Boost Jam's internal implementation + # detail - automatically adding such relationships between all files + # generated by the same action. This way our test will continue to function + # correctly even if the related Boost Jam implementation detail changes. + # Note that adding this relationship by adding an #include directive in our + # generated CPP file is not good enough as such a relationship would get + # added only after the scanner target's relationships have already been + # established and they (as affected by our initial INCLUDE relationship) are + # the original reason for this test failing. + bjam.call('INCLUDES', cpp, bar) + +ENGINE.register_action( + 'foo.foo', + ''' + {touch} "$(<[1])" + {sleep} + {touch} "$(<[2])" + '''.format(touch=common.file_creation_command(), sleep=sleep_cmd(2)) +) +""" +) + t.write("x.foo", "") t.write("jamroot.jam", """\ import foo ; diff --git a/test/source_order.py b/test/source_order.py index f42f4ccae..af8fd54bc 100755 --- a/test/source_order.py +++ b/test/source_order.py @@ -27,6 +27,37 @@ actions check-order generators.register-composing check-order.check-order : C : ORDER_TEST ; """) +t.write( + 'check-order.py', +""" +import bjam + +from b2.build import type as type_, generators +from b2.tools import common +from b2.manager import get_manager + +MANAGER = get_manager() +ENGINE = MANAGER.engine() + +type_.register('ORDER_TEST', ['order-test']) + +generators.register_composing('check-order.check-order', ['C'], ['ORDER_TEST']) + +def check_order(targets, sources, properties): + ENGINE.set_target_variable(targets, 'SPACE', ' ') + ENGINE.set_target_variable(targets, 'nl', '\\n') + +ENGINE.register_action( + 'check-order.check-order', + function=check_order, + command=''' + echo$(SPACE)$(>[1])>$(<[1]) + echo$(SPACE)$(>[2-])>>$(<[1])$(nl) + ''' +) +""" +) + # The aliases are necessary for this test, since # the targets were sorted by virtual target # id, not by file name. diff --git a/test/test_rc.py b/test/test_rc.py index 510b47275..1ffac15b0 100755 --- a/test/test_rc.py +++ b/test/test_rc.py @@ -73,6 +73,44 @@ set-generated-obj-suffix windows ; set-generated-obj-suffix cygwin ; """ % toolsetName) + t.write( + toolsetName + '.py', +""" +from b2.build import feature, type as type_ +from b2.manager import get_manager +from b2.tools import rc, common + +MANAGER = get_manager() +ENGINE = MANAGER.engine() + +toolset_name = "{0}" + +feature.extend('toolset', [toolset_name]) + +def init(*args): + pass + +rc.configure(['dummy-rc-command'], ['' + toolset_name], ['dummy']) + +ENGINE.register_action( + 'rc.compile.resource.dummy', + ''' + %s "$(<)" + ''' % common.file_creation_command() +) + +def set_generated_obj_suffix(target_os=''): + requirements = ['' + toolset_name] + if target_os: + requirements.append('' + target_os) + type_.set_generated_target_suffix('OBJ', requirements, 'obj') + +set_generated_obj_suffix() +set_generated_obj_suffix('windows') +set_generated_obj_suffix('cygwin') +""".format(toolsetName) + ) + # Prepare project source files. t.write("jamroot.jam", """\ ECHO {{{ [ modules.peek : XXX ] [ modules.peek : NOEXEC ] }}} ; diff --git a/test/timedata.py b/test/timedata.py index 20a95c2a2..2bfc3ef31 100644 --- a/test/timedata.py +++ b/test/timedata.py @@ -49,14 +49,15 @@ rule time actions time { - echo $(>) user: $(__USER_TIME__) system: $(__SYSTEM_TIME__) + echo $(>) user: $(__USER_TIME__) system: $(__SYSTEM_TIME__) clock: $(__CLOCK_TIME__) echo timed from $(>) >> $(<) } -rule record_time ( target : source : start end user system ) +rule record_time ( target : source : start end user system clock ) { __USER_TIME__ on $(target) = $(user) ; __SYSTEM_TIME__ on $(target) = $(system) ; + __CLOCK_TIME__ on $(target) = $(clock) ; } rule make @@ -80,7 +81,7 @@ make bar : baz ; \.\.\.updating 2 targets\.\.\. make bar time foo -bar +user: [0-9\.]+ +system: +[0-9\.]+ * +bar +user: [0-9\.]+ +system: +[0-9\.]+ +clock: +[0-9\.]+ * \.\.\.updated 2 targets\.\.\.$ """ @@ -125,6 +126,8 @@ time my-time : my-exe ; "user: *[0-9] seconds") t.expect_content_lines("bin/$toolset/debug/my-time.time", "system: *[0-9] seconds") + t.expect_content_lines("bin/$toolset/debug/my-time.time", + "clock: *[0-9] seconds") t.cleanup() diff --git a/test/zlib.py b/test/zlib.py index 2821bd744..0bb8269ad 100755 --- a/test/zlib.py +++ b/test/zlib.py @@ -39,7 +39,7 @@ t.rm('zlib') common_stuff = ''' source_file('test.cpp', 'test.cpp') source_file('main.cpp', 'int main() {}') -source_file('zlib.h.cpp', '#include ') +source_file('zlib.h.cpp', '#include \\n') action('-c -x c++ $main.cpp -o $main.o') ''' t.write('test.cpp', 'test.cpp') diff --git a/tutorial.html b/tutorial.html index c500d2085..f50586e4f 100644 --- a/tutorial.html +++ b/tutorial.html @@ -1433,7 +1433,7 @@ exe hello : hello.cpp : <library>/boost//thread ; <target-os> - aix, bsd, cygwin, darwin, freebsd, hpux, iphone, linux, + aix, appletv, bsd, cygwin, darwin, freebsd, hpux, iphone, linux, netbsd, openbsd, osf, qnx, qnxnto, sgi, solaris, unix, unixware, windows