diff --git a/doc/user_guide/annex_cfg.html b/doc/user_guide/annex_cfg.html index a6c7afc1..399444ff 100644 --- a/doc/user_guide/annex_cfg.html +++ b/doc/user_guide/annex_cfg.html @@ -897,20 +897,24 @@

FCM Make Configuration: Build

description: Selects targets to build according to their - keys, categories, name-spaces and tasks.

+ keys, categories and tasks.

modifier: key (default if no modifier - specified), category, ns (i.e. name-space) or - task

+ specified), category or task

+ +

namespace: Not allowed if modifier is key. + Optional if modifier is category or task. If + specified, setting only applies to given name-spaces. Otherwise, setting + applies to all name-spaces.

value: A list of space-delimited items (according to the modifier).

example:

-build.target{category} = bin lib
-build.target{ns} = egg/fried ham
-build.target{task} = archive install link
+build.target{category}[etc/namelists etc/configs] = etc
+build.target{category}[src/hello src/greet src/world] = bin lib
+build.target{task}[src/public] = archive install
 build.target = egg.sh mince.py
 
diff --git a/doc/user_guide/make.html b/doc/user_guide/make.html index 9d5fd4cd..69641eab 100644 --- a/doc/user_guide/make.html +++ b/doc/user_guide/make.html @@ -1742,16 +1742,20 @@

Build Targets Selection and Rename

you to select targets according to their categories, source name-spaces, tasks and keys. The logic is demonstrated by the following example:

-# Selects targets matching these keys
+# Select targets matching these keys
 build.target = egg.bin ham.o bacon.sh
-# OR (
-#     those doing these tasks
+
+# Select all targets doing tasks "install" or "link"
 build.target{task} = install link
-#     AND in these categories
+
+# Select targets in name-space "foo" or "bar" doing tasks "link"
+build.target{task}[foo bar] = link
+
+# Select all targets in the "bin" category
 build.target{category} = bin
-#     AND in these name-spaces
-build.target{ns} = foo bar/baz
-# )
+
+# Select targets in name-space "foo" in the "etc" category
+build.target{category}[foo] = etc
 

There are times when an automatic target name is not what you want. In @@ -2193,8 +2197,10 @@

Preprocess: Basic

# ... some extract configuration +# Switch off preprocessing for all +preprocess.target{task} = # Only preprocess source files in these name-spaces -preprocess.target{ns} = foo/bar egg/fried.F90 +preprocess.target{task}[foo/bar egg/fried.F90] = process # Specifies the macro definitions for the Fortran preprocessor preprocess.prop{fpp.defs} = THING=stuff HIGH=tall # Specifies the macro definitions for the C preprocessor @@ -2268,7 +2274,6 @@

Preprocess Targets from Source Files

preprocess.target = preprocess.target{task} = process preprocess.target{category} = -preprocess.target{ns} =

Here is a list of what targets are available for each type of file:

diff --git a/lib/FCM/System/Make/Build.pm b/lib/FCM/System/Make/Build.pm index cbff538f..24bbd2e3 100644 --- a/lib/FCM/System/Make/Build.pm +++ b/lib/FCM/System/Make/Build.pm @@ -60,7 +60,11 @@ our @FILE_TYPE_UTILS = ( ); # Default target selection -our %TARGET_SELECT_BY = (task => {}); +our %TARGET_SELECT_BY = ( + 'category' => {}, + 'key' => {}, + 'task' => {}, +); # Configuration parser label to action map our %CONFIG_PARSER_OF = ( @@ -181,8 +185,16 @@ sub _config_parse_inherit_hook { while (my ($key, $value) = each(%{$i_ctx->get_target_key_of()})) { $ctx->get_target_key_of()->{$key} = $value; } - while (my ($key, $value) = each(%{$i_ctx->get_target_select_by()})) { - $ctx->get_target_select_by()->{$key} = dclone($value); + while (my ($key, $item_ref) = each(%{$i_ctx->get_target_select_by()})) { + while (my ($key2, $attr_set) = each(%{$item_ref})) { + if (ref($attr_set)) { + $ctx->get_target_select_by()->{$key}{$key2} = {%{$attr_set}}; + } + else { + # Backward compat, $key2 is an $attr + $ctx->get_target_select_by()->{$key}{q{}}{$key2} = 1; + } + } } _config_parse_inherit_hook_prop($attrib_ref, $ctx, $i_ctx); } @@ -211,15 +223,24 @@ sub _config_parse_source { sub _config_parse_target { my ($attrib_ref, $ctx, $entry) = @_; my %modifier_of = %{$entry->get_modifier_of()}; - if (!keys(%modifier_of)) { + if (!(%modifier_of)) { %modifier_of = (key => 1); } + my @ns_list = map {$_ eq q{/} ? q{} : $_} @{$entry->get_ns_list()}; + if (exists($modifier_of{'key'}) && grep {$_} @ns_list) { + return $E->throw($E->CONFIG_NS, $entry); + } + if (!@ns_list) { + @ns_list = (q{}); + } while (my $name = each(%modifier_of)) { - if (!grep {$_ eq $name} qw{category key ns task}) { + if (!grep {$_ eq $name} qw{category key task}) { return $E->throw($E->CONFIG_MODIFIER, $entry); } - $ctx->get_target_select_by()->{$name} - = {map {$_ eq q{/} ? (q{} => 1) : ($_ => 1)} $entry->get_values()}; + my %attr_set = map {($_ => 1)} $entry->get_values(); + for my $ns (@ns_list) { + $ctx->get_target_select_by()->{$name}{$ns} = \%attr_set; + } } } @@ -282,13 +303,20 @@ sub _config_unparse { : () ), ( map { - FCM::Context::ConfigEntry->new({ - label => $LABEL_OF{'target'}, - modifier_of => {$_ => 1}, - value => _config_unparse_join( - keys(%{$ctx->get_target_select_by()->{$_}}), - ), - }); + my $modifier = $_; + map { + my $ns = $_; + my @values = sort(keys( + %{$ctx->get_target_select_by()->{$modifier}{$ns}} + )); + FCM::Context::ConfigEntry->new({ + label => $LABEL_OF{'target'}, + modifier_of => {$modifier => 1}, + ns_list => [$ns], + value => _config_unparse_join(@values), + }); + } + sort keys(%{$ctx->get_target_select_by()->{$modifier}}); } sort keys(%{$ctx->get_target_select_by()}) ), @@ -1035,18 +1063,23 @@ sub _targets_select { my %target_set; my %has_ns_in; # available sets of name-spaces for my $target (@{$targets_ref}) { - if ( exists($select_by{key}{$target->get_key()}) - || ( !exists($select_by{category}) - || exists($select_by{category}{$target->get_category()}) - ) - && ( !exists($select_by{task}) - || exists($select_by{task}{$target->get_task()}) - ) - && ( !exists($select_by{ns}) - || $UTIL->ns_in_set($target->get_ns(), $select_by{ns}) - ) + ATTR_NAME: + for ( + #$attr_name, $attr_func + ['key' , sub {$_[0]->get_key()}], + ['category', sub {$_[0]->get_category()}], + ['task' , sub {$_[0]->get_task()}], ) { - $target_set{$target->get_key()} = 1; + my ($attr_name, $attr_func) = @{$_}; + for my $ns (sort keys(%{$select_by{$attr_name}})) { + my %attr_set = %{$select_by{$attr_name}->{$ns}}; + if ( exists($attr_set{$attr_func->($target)}) + && (!$ns || $UTIL->ns_in_set($target->get_ns(), {$ns => 1})) + ) { + $target_set{$target->get_key()} = 1; + last ATTR_NAME; + } + } } if (exists($target_of{$target->get_key()})) { if (!exists($targets_of{$target->get_key()})) { @@ -1250,8 +1283,8 @@ sub _targets_select { if (keys(%missing_deps_in)) { return $E->throw($E->BUILD_TARGET_DEP, \%missing_deps_in); } - if (exists($select_by{key})) { - my @bad_keys = grep {!exists($state_of{$_})} keys(%{$select_by{key}}); + if (exists($select_by{key}{q{}})) { + my @bad_keys = grep {!exists($state_of{$_})} keys(%{$select_by{key}{q{}}}); if (@bad_keys) { return $E->throw($E->BUILD_TARGET_BAD, \@bad_keys); } diff --git a/lib/FCM/System/Make/Preprocess.pm b/lib/FCM/System/Make/Preprocess.pm index aebec0e7..13766029 100644 --- a/lib/FCM/System/Make/Preprocess.pm +++ b/lib/FCM/System/Make/Preprocess.pm @@ -27,7 +27,11 @@ use FCM::System::Make::Build::FileType::FPP; use FCM::System::Make::Build::FileType::H ; # Default target selection -our %TARGET_SELECT_BY = (task => {'process' => 1}); +our %TARGET_SELECT_BY = ( + 'category' => {}, + 'key' => {}, + 'task' => {q{} => {'process' => 1}}, +); # Classes for working with typed source files our @FILE_TYPE_UTILS = ( diff --git a/t/fcm-make/47-build-target-modifier-ns.t b/t/fcm-make/47-build-target-modifier-ns.t new file mode 100755 index 00000000..60987e53 --- /dev/null +++ b/t/fcm-make/47-build-target-modifier-ns.t @@ -0,0 +1,93 @@ +#!/bin/bash +#------------------------------------------------------------------------------- +# (C) British Crown Copyright 2006-15 Met Office. +# +# This file is part of FCM, tools for managing and building source code. +# +# FCM is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# FCM is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with FCM. If not, see . +#------------------------------------------------------------------------------- +# Test "fcm make", build.target{category} and build.target{task} with namespace. +#------------------------------------------------------------------------------- +. "$(dirname "$0")/test_header" +#------------------------------------------------------------------------------- +tests 12 +mkdir 'i0' +cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* 'i0' +#------------------------------------------------------------------------------- +TEST_KEY="${TEST_KEY_BASE}" +run_pass "${TEST_KEY}" fcm make -C 'i0' + +grep -F 'build.target{category}' 'i0/.fcm-make/config-on-success.cfg' \ + >'edited-config-on-success.cfg' +file_cmp "${TEST_KEY}-edited-config-on-success.cfg" \ + 'edited-config-on-success.cfg' <<'__CFG__' +build.target{category}[hello] = bin +__CFG__ + +find 'i0/build' -type f | sort >'find-i0-build.out' +file_cmp "${TEST_KEY}-find-i0-build.out" 'find-i0-build.out' <<'__FIND__' +i0/build/bin/hello +i0/build/o/hello.o +__FIND__ + +"${PWD}/i0/build/bin/hello" <<<'&world_nl /' >'hello.out' +file_cmp "${TEST_KEY}-hello.out" 'hello.out' <<<'Hello World' +#------------------------------------------------------------------------------- +TEST_KEY="${TEST_KEY_BASE}-inherit" +run_pass "${TEST_KEY}" fcm make -C 'i1' \ + "use=${PWD}/i0" \ + 'build.target{category}[greet] = etc' + +grep -F 'build.target{category}' 'i1/.fcm-make/config-on-success.cfg' \ + >'edited-config-on-success.cfg' +file_cmp "${TEST_KEY}-edited-config-on-success.cfg" \ + 'edited-config-on-success.cfg' <<'__CFG__' +build.target{category}[greet] = etc +build.target{category}[hello] = bin +__CFG__ + +find 'i1/build' -type f | sort >'find-i1-build.out' +file_cmp "${TEST_KEY}-find-i1-build.out" 'find-i1-build.out' <<'__FIND__' +i1/build/bin/hello +i1/build/etc/greet/.etc +i1/build/etc/greet/world.nl +__FIND__ + +"${PWD}/i1/build/bin/hello" <'i1/build/etc/greet/world.nl' >'hello.out' +file_cmp "${TEST_KEY}-hello.out" 'hello.out' <<<'Hello Earth' +#------------------------------------------------------------------------------- +TEST_KEY="${TEST_KEY_BASE}-inherit-incr" +touch 'before' +run_pass "${TEST_KEY}" fcm make -C 'i1' \ + "use=${PWD}/i0" \ + 'build.target{category}[greet] = bin etc' + +grep -F 'build.target{category}' 'i1/.fcm-make/config-on-success.cfg' \ + >'edited-config-on-success.cfg' +file_cmp "${TEST_KEY}-edited-config-on-success.cfg" \ + 'edited-config-on-success.cfg' <<'__CFG__' +build.target{category}[greet] = bin etc +build.target{category}[hello] = bin +__CFG__ + +find 'i1/build' -type f -newer 'before' | sort >'find-i1-build.out' +file_cmp "${TEST_KEY}-find-i1-build.out" 'find-i1-build.out' <<'__FIND__' +i1/build/bin/greet +i1/build/o/greet.o +__FIND__ + +"${PWD}/i1/build/bin/greet" <'i1/build/etc/greet/world.nl' >'greet.out' +file_cmp "${TEST_KEY}-greet.out" 'greet.out' <<<'Greet Earth' +#------------------------------------------------------------------------------- +exit 0 diff --git a/t/fcm-make/47-build-target-modifier-ns/fcm-make.cfg b/t/fcm-make/47-build-target-modifier-ns/fcm-make.cfg new file mode 100644 index 00000000..22b9e6ce --- /dev/null +++ b/t/fcm-make/47-build-target-modifier-ns/fcm-make.cfg @@ -0,0 +1,4 @@ +steps=build +build.source=$HERE/src +build.prop{file-ext.bin}= +build.target{category}[hello] = bin diff --git a/t/fcm-make/47-build-target-modifier-ns/src/greet/greet.f90 b/t/fcm-make/47-build-target-modifier-ns/src/greet/greet.f90 new file mode 100644 index 00000000..0f2cad45 --- /dev/null +++ b/t/fcm-make/47-build-target-modifier-ns/src/greet/greet.f90 @@ -0,0 +1,6 @@ +program greet +character(31) :: world = 'World' +namelist /world_nl/ world +read(*, nml=world_nl) +write(*, '(a,1x,a)') 'Greet', trim(world) +end program greet diff --git a/t/fcm-make/47-build-target-modifier-ns/src/greet/world.nl b/t/fcm-make/47-build-target-modifier-ns/src/greet/world.nl new file mode 100644 index 00000000..c20aea5b --- /dev/null +++ b/t/fcm-make/47-build-target-modifier-ns/src/greet/world.nl @@ -0,0 +1,3 @@ +&world_nl +world='Earth', +/ diff --git a/t/fcm-make/47-build-target-modifier-ns/src/hello/hello.f90 b/t/fcm-make/47-build-target-modifier-ns/src/hello/hello.f90 new file mode 100644 index 00000000..bfed8974 --- /dev/null +++ b/t/fcm-make/47-build-target-modifier-ns/src/hello/hello.f90 @@ -0,0 +1,6 @@ +program hello +character(31) :: world = 'World' +namelist /world_nl/ world +read(*, nml=world_nl) +write(*, '(a,1x,a)') 'Hello', trim(world) +end program hello diff --git a/t/fcm-make/47-build-target-modifier-ns/src/hello/world.nl b/t/fcm-make/47-build-target-modifier-ns/src/hello/world.nl new file mode 100644 index 00000000..072e5895 --- /dev/null +++ b/t/fcm-make/47-build-target-modifier-ns/src/hello/world.nl @@ -0,0 +1,3 @@ +&world_nl +world='Jupiter', +/