From 5843dec89ce643759ffe2df85ffe9fa806ef23c8 Mon Sep 17 00:00:00 2001 From: Aaron Boman Date: Wed, 23 Sep 2015 18:22:51 -0500 Subject: [PATCH] Expand command line properties as late as possible --- src/build-system.jam | 43 ++++++++++++----------- src/build/build-request.jam | 63 ++++++++++++++++++++++++++-------- src/build/build_request.py | 2 +- src/build_system.py | 25 +++++++++----- test/cli_property_expansion.py | 41 ++++++++++++++++++++++ 5 files changed, 130 insertions(+), 44 deletions(-) create mode 100644 test/cli_property_expansion.py diff --git a/src/build-system.jam b/src/build-system.jam index 247326a96..7b783336e 100644 --- a/src/build-system.jam +++ b/src/build-system.jam @@ -585,26 +585,6 @@ local rule should-clean-project ( project ) local properties = [ $(build-request).get-at 2 ] ; - # Expand properties specified on the command line into multiple property - # sets consisting of all legal property combinations. Each expanded property - # set will be used for a single build run. E.g. if multiple toolsets are - # specified then requested targets will be built with each of them. - if $(properties) - { - expanded = [ build-request.expand-no-defaults $(properties) ] ; - local xexpanded ; - for local e in $(expanded) - { - xexpanded += [ property-set.create [ feature.split $(e) ] ] ; - } - expanded = $(xexpanded) ; - } - else - { - expanded = [ property-set.empty ] ; - } - - # Check that we actually found something to build. if ! $(current-project) && ! $(target-ids) { @@ -695,6 +675,29 @@ local rule should-clean-project ( project ) configure.set-log-file $(first-build-build-dir)/config.log ; config-cache.load $(first-build-build-dir)/project-cache.jam ; + # Expand properties specified on the command line into multiple property + # sets consisting of all legal property combinations. Each expanded property + # set will be used for a single build run. E.g. if multiple toolsets are + # specified then requested targets will be built with each of them. + # The expansion is being performed as late as possible so that the feature + # validation is performed after all necessary modules (including project targets + # on the command line) have been loaded. + if $(properties) + { + expanded += [ build-request.convert-command-line-elements $(properties) ] ; + expanded = [ build-request.expand-no-defaults $(expanded) ] ; + local xexpanded ; + for local e in $(expanded) + { + xexpanded += [ property-set.create [ feature.split $(e) ] ] ; + } + expanded = $(xexpanded) ; + } + else + { + expanded = [ property-set.empty ] ; + } + # Now that we have a set of targets to build and a set of property sets to # build the targets with, we can start the main build process by using each # property set to generate virtual targets from all of our listed targets diff --git a/src/build/build-request.jam b/src/build/build-request.jam index 2a1bbb467..3110713b7 100644 --- a/src/build/build-request.jam +++ b/src/build/build-request.jam @@ -150,8 +150,7 @@ rule from-command-line ( command-line * ) if [ MATCH "(.*=.*)" : $(e) ] || [ looks-like-implicit-value $(e:D=) : $(feature-space) ] { - properties += [ convert-command-line-element $(e) : - $(feature-space) ] ; + properties += $(e) ; } else if $(e) { @@ -169,9 +168,22 @@ rule from-command-line ( command-line * ) } -# Converts one element of command line build request specification into internal +# Converts a list of elements of command line build request specification into internal # form. Expects all the project files to already be loaded. # +rule convert-command-line-elements ( elements * ) +{ + local result ; + for local e in $(elements) + { + result += [ convert-command-line-element $(e) ] ; + } + return $(result) ; +} + + +# Converts one element of command line build request specification into internal +# form. local rule convert-command-line-element ( e ) { local result ; @@ -286,37 +298,60 @@ rule __test__ ( ) local r ; - r = [ build-request.from-command-line bjam debug runtime-link=dynamic ] ; - assert.equal [ $(r).get-at 1 ] : ; - assert.equal [ $(r).get-at 2 ] : debug dynamic ; - try ; { - build-request.from-command-line bjam gcc/debug runtime-link=dynamic/static ; + r = [ build-request.from-command-line bjam gcc/debug runtime-link=dynamic/static ] ; + build-request.convert-command-line-elements [ $(r).get-at 2 ] ; } catch \"static\" is not an implicit feature value ; + r = [ build-request.from-command-line bjam debug runtime-link=dynamic ] ; + assert.equal [ $(r).get-at 1 ] : ; + assert.equal [ $(r).get-at 2 ] : debug runtime-link=dynamic ; + + assert.equal + [ build-request.convert-command-line-elements debug runtime-link=dynamic ] + : debug dynamic ; + r = [ build-request.from-command-line bjam -d2 --debug debug target runtime-link=dynamic ] ; assert.equal [ $(r).get-at 1 ] : target ; - assert.equal [ $(r).get-at 2 ] : debug dynamic ; + assert.equal [ $(r).get-at 2 ] : debug runtime-link=dynamic ; + + assert.equal + [ build-request.convert-command-line-elements debug runtime-link=dynamic ] + : debug dynamic ; r = [ build-request.from-command-line bjam debug runtime-link=dynamic,static ] ; assert.equal [ $(r).get-at 1 ] : ; - assert.equal [ $(r).get-at 2 ] : debug dynamic static ; + assert.equal [ $(r).get-at 2 ] : debug runtime-link=dynamic,static ; + + assert.equal + [ build-request.convert-command-line-elements debug runtime-link=dynamic,static ] + : debug dynamic static ; r = [ build-request.from-command-line bjam debug gcc/runtime-link=dynamic,static ] ; assert.equal [ $(r).get-at 1 ] : ; - assert.equal [ $(r).get-at 2 ] : debug gcc/dynamic - gcc/static ; + assert.equal [ $(r).get-at 2 ] : debug gcc/runtime-link=dynamic,static ; + + assert.equal + [ build-request.convert-command-line-elements debug gcc/runtime-link=dynamic,static ] + : debug gcc/dynamic gcc/static ; r = [ build-request.from-command-line bjam msvc gcc,borland/runtime-link=static ] ; assert.equal [ $(r).get-at 1 ] : ; - assert.equal [ $(r).get-at 2 ] : msvc gcc/static - borland/static ; + assert.equal [ $(r).get-at 2 ] : msvc gcc,borland/runtime-link=static ; + + assert.equal + [ build-request.convert-command-line-elements msvc gcc,borland/runtime-link=static ] + : msvc gcc/static borland/static ; r = [ build-request.from-command-line bjam gcc-3.0 ] ; assert.equal [ $(r).get-at 1 ] : ; assert.equal [ $(r).get-at 2 ] : gcc-3.0 ; + assert.equal + [ build-request.convert-command-line-elements gcc-3.0 ] + : gcc-3.0 ; + feature.finish-test build-request-test-temp ; } diff --git a/src/build/build_request.py b/src/build/build_request.py index 2716cf639..194251688 100644 --- a/src/build/build_request.py +++ b/src/build/build_request.py @@ -119,7 +119,7 @@ def from_command_line(command_line): # Build request spec either has "=" in it, or completely # consists of implicit feature values. if e.find("=") != -1 or looks_like_implicit_value(e.split("/")[0]): - properties += convert_command_line_element(e) + properties.append(e) elif e: targets.append(e) diff --git a/src/build_system.py b/src/build_system.py index 6bd05d1d9..b5a3b2775 100644 --- a/src/build_system.py +++ b/src/build_system.py @@ -509,15 +509,6 @@ def main_real(): # that all project files already be loaded. (target_ids, properties) = build_request.from_command_line(sys.argv[1:] + extra_properties) - # Expand properties specified on the command line into multiple property - # sets consisting of all legal property combinations. Each expanded property - # set will be used for a single build run. E.g. if multiple toolsets are - # specified then requested targets will be built with each of them. - if properties: - expanded = build_request.expand_no_defaults(properties) - else: - expanded = [property_set.empty()] - # Check that we actually found something to build. if not current_project and not target_ids: get_manager().errors()("no Jamfile in current directory found, and no target references specified.") @@ -595,6 +586,22 @@ def main_real(): global results_of_main_targets + # Expand properties specified on the command line into multiple property + # sets consisting of all legal property combinations. Each expanded property + # set will be used for a single build run. E.g. if multiple toolsets are + # specified then requested targets will be built with each of them. + # The expansion is being performed as late as possible so that the feature + # validation is performed after all necessary modules (including project targets + # on the command line) have been loaded. + if properties: + expanded = [] + for p in properties: + expanded.extend(build_request.convert_command_line_element(p)) + + expanded = build_request.expand_no_defaults(expanded) + else: + expanded = [property_set.empty()] + # Now that we have a set of targets to build and a set of property sets to # build the targets with, we can start the main build process by using each # property set to generate virtual targets from all of our listed targets diff --git a/test/cli_property_expansion.py b/test/cli_property_expansion.py new file mode 100644 index 000000000..24c821617 --- /dev/null +++ b/test/cli_property_expansion.py @@ -0,0 +1,41 @@ +#!/usr/bin/python + +# Copyright 2015 Aaron Boman +# 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) + +# Test that free property inside. + +import BoostBuild + +t = BoostBuild.Tester(use_test_config=False) + +t.write("jamroot.jam", "") +t.write( + "subdir/build.jam", + """ + import feature ; + feature.feature my-feature : : free ; + """ +) +t.write( + "subdir/subsubdir/build.jam", + """ + exe hello : hello.c ; + """ +) +t.write( + "subdir/subsubdir/hello.c", + r""" + #include + + int main(int argc, char **argv){ + printf("%s\n", "Hello, World!"); + } + """ +) + +# run from the root directory +t.run_build_system(['subdir/subsubdir', 'my-feature="some value"']) + +t.cleanup()