diff options
author | Joerg Bornemann <joerg.bornemann@qt.io> | 2020-07-16 08:15:04 (GMT) |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@qt.io> | 2020-07-21 15:16:26 (GMT) |
commit | 2cdaf43d966f8407f0fb75ceb131fdca16617915 (patch) | |
tree | 15e3284091c62ca73b22fa15ea1a93770c97305d | |
parent | c7b7547d8da6b9a4225d111440d0cf6c2f55914d (diff) | |
download | CMake-2cdaf43d966f8407f0fb75ceb131fdca16617915.zip CMake-2cdaf43d966f8407f0fb75ceb131fdca16617915.tar.gz CMake-2cdaf43d966f8407f0fb75ceb131fdca16617915.tar.bz2 |
Allow generator expressions in the EXCLUDE_FROM_ALL target property
This allows for setting EXCLUDE_FROM_ALL, conditional on the build
configuration. However, only the Ninja Multi-Config generator supports
different property values per config. All other multi-config
generators will yield an error in that situation.
Fixes: #20923
20 files changed, 168 insertions, 14 deletions
diff --git a/Help/prop_tgt/EXCLUDE_FROM_ALL.rst b/Help/prop_tgt/EXCLUDE_FROM_ALL.rst index c9ece22..f0200f3 100644 --- a/Help/prop_tgt/EXCLUDE_FROM_ALL.rst +++ b/Help/prop_tgt/EXCLUDE_FROM_ALL.rst @@ -19,3 +19,10 @@ If a target has ``EXCLUDE_FROM_ALL`` set to true, it may still be listed in an :command:`install(TARGETS)` command, but the user is responsible for ensuring that the target's build artifacts are not missing or outdated when an install is performed. + +This property may use "generator expressions" with the syntax ``$<...>``. See +the :manual:`cmake-generator-expressions(7)` manual for available expressions. + +Only the "Ninja Multi-Config" generator supports a property value that varies by +configuration. For all other generators the value of this property must be the +same for all configurations. diff --git a/Source/cmGlobalCommonGenerator.cxx b/Source/cmGlobalCommonGenerator.cxx index 9dc86f4..5eff3b8 100644 --- a/Source/cmGlobalCommonGenerator.cxx +++ b/Source/cmGlobalCommonGenerator.cxx @@ -5,8 +5,12 @@ #include <memory> #include <utility> +#include <cmext/algorithm> + +#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmLocalGenerator.h" +#include "cmMakefile.h" #include "cmProperty.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -31,6 +35,8 @@ cmGlobalCommonGenerator::ComputeDirectoryTargets() const lg->GetStateSnapshot().GetDirectory().GetCurrentBinary()); DirectoryTarget& dirTarget = dirTargets[currentBinaryDir]; dirTarget.LG = lg.get(); + const std::vector<std::string>& configs = + lg->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); // The directory-level rule should depend on the target-level rules // for all targets in the directory. @@ -46,11 +52,18 @@ cmGlobalCommonGenerator::ComputeDirectoryTargets() const } DirectoryTarget::Target t; t.GT = gt.get(); - if (cmProp exclude = gt->GetProperty("EXCLUDE_FROM_ALL")) { - if (cmIsOn(*exclude)) { - // This target has been explicitly excluded. - t.ExcludeFromAll = true; - } else { + const std::string EXCLUDE_FROM_ALL("EXCLUDE_FROM_ALL"); + if (cmProp exclude = gt->GetProperty(EXCLUDE_FROM_ALL)) { + for (const std::string& config : configs) { + cmGeneratorExpressionInterpreter genexInterpreter(lg.get(), config, + gt.get()); + if (cmIsOn(genexInterpreter.Evaluate(*exclude, EXCLUDE_FROM_ALL))) { + // This target has been explicitly excluded. + t.ExcludedFromAllInConfigs.push_back(config); + } + } + + if (t.ExcludedFromAllInConfigs.empty()) { // This target has been explicitly un-excluded. The directory-level // rule for every directory between this and the root should depend // on the target-level rule for this target. @@ -78,3 +91,12 @@ cmGlobalCommonGenerator::ComputeDirectoryTargets() const return dirTargets; } + +bool cmGlobalCommonGenerator::IsExcludedFromAllInConfig( + const DirectoryTarget::Target& t, const std::string& config) +{ + if (this->IsMultiConfig()) { + return cm::contains(t.ExcludedFromAllInConfigs, config); + } + return !t.ExcludedFromAllInConfigs.empty(); +} diff --git a/Source/cmGlobalCommonGenerator.h b/Source/cmGlobalCommonGenerator.h index 7d16dac..f97b5f4 100644 --- a/Source/cmGlobalCommonGenerator.h +++ b/Source/cmGlobalCommonGenerator.h @@ -30,7 +30,7 @@ public: struct Target { cmGeneratorTarget const* GT = nullptr; - bool ExcludeFromAll = false; + std::vector<std::string> ExcludedFromAllInConfigs; }; std::vector<Target> Targets; struct Dir @@ -41,6 +41,8 @@ public: std::vector<Dir> Children; }; std::map<std::string, DirectoryTarget> ComputeDirectoryTargets() const; + bool IsExcludedFromAllInConfig(const DirectoryTarget::Target& t, + const std::string& config); }; #endif diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 49b73a8..d39fefa 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2173,13 +2173,38 @@ bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root, } bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root, - cmGeneratorTarget* target) const + const cmGeneratorTarget* target) const { if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { return true; } - if (cmProp exclude = target->GetProperty("EXCLUDE_FROM_ALL")) { - return cmIsOn(*exclude); + cmMakefile* mf = root->GetMakefile(); + const std::string EXCLUDE_FROM_ALL = "EXCLUDE_FROM_ALL"; + if (cmProp exclude = target->GetProperty(EXCLUDE_FROM_ALL)) { + // Expand the property value per configuration. + unsigned int trueCount = 0; + unsigned int falseCount = 0; + const std::vector<std::string>& configs = + mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (const std::string& config : configs) { + cmGeneratorExpressionInterpreter genexInterpreter(root, config, target); + if (cmIsOn(genexInterpreter.Evaluate(*exclude, EXCLUDE_FROM_ALL))) { + ++trueCount; + } else { + ++falseCount; + } + } + + // Check whether the genex expansion of the property agrees in all + // configurations. + if (trueCount && falseCount) { + std::ostringstream e; + e << "The EXCLUDED_FROM_ALL property of target \"" << target->GetName() + << "\" varies by configuration. This is not supported by the \"" + << root->GetGlobalGenerator()->GetName() << "\" generator."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } + return trueCount; } // This target is included in its directory. Check whether the // directory is excluded. diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 57c7808..c2c80c2 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -542,7 +542,8 @@ protected: bool IsExcluded(cmStateSnapshot const& root, cmStateSnapshot const& snp) const; bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen) const; - bool IsExcluded(cmLocalGenerator* root, cmGeneratorTarget* target) const; + bool IsExcluded(cmLocalGenerator* root, + const cmGeneratorTarget* target) const; virtual void InitializeProgressMarks() {} struct GlobalTargetInfo diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 2fcba9a..1664dd0 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -470,7 +470,7 @@ void cmGlobalGhsMultiGenerator::WriteAllTarget( if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { continue; } - if (!cmIsOn(t->GetProperty("EXCLUDE_FROM_ALL"))) { + if (!IsExcluded(t->GetLocalGenerator(), t)) { defaultTargets.push_back(t); } } diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 48eb405..786cde7 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -1357,7 +1357,7 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os) build.Outputs.front() = this->BuildAlias(buildDirAllTarget, config); configDeps.emplace_back(build.Outputs.front()); for (DirectoryTarget::Target const& t : dt.Targets) { - if (!t.ExcludeFromAll) { + if (!IsExcludedFromAllInConfig(t, config)) { this->AppendTargetOutputs(t.GT, build.ExplicitDeps, config); } } diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index c31983b..d45fc01 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -416,7 +416,7 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRule2( std::vector<std::string> depends; for (DirectoryTarget::Target const& t : dt.Targets) { // Add this to the list of depends rules in this directory. - if ((!check_all || !t.ExcludeFromAll) && + if ((!check_all || t.ExcludedFromAllInConfigs.empty()) && (!check_relink || t.GT->NeedRelinkBeforeInstall(lg->GetConfigName()))) { // The target may be from a different directory; use its local gen. @@ -846,7 +846,7 @@ void cmGlobalUnixMakefileGenerator3::InitializeProgressMarks() cmLocalGenerator* tlg = gt->GetLocalGenerator(); if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY || - gt->GetPropertyAsBool("EXCLUDE_FROM_ALL")) { + IsExcluded(lg.get(), gt.get())) { continue; } diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index b037a6d..422d475 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -207,6 +207,7 @@ add_RunCMake_test(DisallowedCommands) if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja") add_RunCMake_test(ExportCompileCommands) endif() +add_RunCMake_test(ExcludeFromAll) add_RunCMake_test(ExternalData) add_RunCMake_test(FeatureSummary) add_RunCMake_test(FPHSA) diff --git a/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt new file mode 100644 index 0000000..74b3ff8 --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExcludeFromAll/RunCMakeTest.cmake b/Tests/RunCMake/ExcludeFromAll/RunCMakeTest.cmake new file mode 100644 index 0000000..25201e4 --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/RunCMakeTest.cmake @@ -0,0 +1,26 @@ +include(RunCMake) + +function(run_single_config_test label config exclude_from_all_value expectation) + set(case single-config) + message("-- Starting ${case} test: ${label}") + set(full_case_name "${case}-build-${config}") + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${full_case_name}/") + run_cmake_with_options(${case} + -DCMAKE_BUILD_TYPE=${config} + -DTOOL_EXCLUDE_FROM_ALL=${exclude_from_all_value}) + set(RunCMake_TEST_NO_CLEAN 1) + include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config ${config}) +endfunction() + +run_single_config_test("explictly not excluded" Debug 0 "should_exist") +run_single_config_test("excluded" Debug 1 "should_not_exist") + +if(RunCMake_GENERATOR MATCHES "^(Xcode|Visual Studio)") + run_cmake(error-on-mixed-config) +else() + run_single_config_test("explicitly not excluded with genex" + Release $<CONFIG:Debug> "should_exist") + run_single_config_test("excluded with genex" + Debug $<CONFIG:Debug> "should_not_exist") +endif() diff --git a/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config-result.txt b/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config-stderr.txt b/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config-stderr.txt new file mode 100644 index 0000000..eee5278 --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config-stderr.txt @@ -0,0 +1,3 @@ +CMake Error in CMakeLists.txt: + The EXCLUDED_FROM_ALL property of target "release_only_tool" varies by + configuration. This is not supported by the "[^"]+" diff --git a/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config.cmake b/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config.cmake new file mode 100644 index 0000000..6c0ed1d --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/error-on-mixed-config.cmake @@ -0,0 +1,6 @@ +enable_language(C) + +set(CMAKE_CONFIGURATION_TYPES "Release;Debug" CACHE STRING "") + +add_executable(release_only_tool main.c) +set_property(TARGET release_only_tool PROPERTY EXCLUDE_FROM_ALL "$<NOT:$<CONFIG:Release>>") diff --git a/Tests/RunCMake/ExcludeFromAll/main.c b/Tests/RunCMake/ExcludeFromAll/main.c new file mode 100644 index 0000000..5047a34 --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/main.c @@ -0,0 +1,3 @@ +int main() +{ +} diff --git a/Tests/RunCMake/ExcludeFromAll/single-config-build-check.cmake b/Tests/RunCMake/ExcludeFromAll/single-config-build-check.cmake new file mode 100644 index 0000000..1c455f2 --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/single-config-build-check.cmake @@ -0,0 +1,17 @@ +if(expectation STREQUAL "should_not_exist") + set(should_exist FALSE) +elseif(expectation STREQUAL "should_exist") + set(should_exist TRUE) +else() + message(FATAL_ERROR "Encountered unknown expectation: ${expectation}") +endif() + +if(EXISTS "${TARGET_FILE_tool_${config}}") + if(NOT should_exist) + message(FATAL_ERROR "${TARGET_FILE_tool_${config}} should not exist.") + endif() +else() + if(should_exist) + message(FATAL_ERROR "${TARGET_FILE_tool_${config}} should exist.") + endif() +endif() diff --git a/Tests/RunCMake/ExcludeFromAll/single-config.cmake b/Tests/RunCMake/ExcludeFromAll/single-config.cmake new file mode 100644 index 0000000..71a9f06 --- /dev/null +++ b/Tests/RunCMake/ExcludeFromAll/single-config.cmake @@ -0,0 +1,11 @@ +enable_language(C) +add_executable(tool main.c) +set_property(TARGET tool PROPERTY EXCLUDE_FROM_ALL "${TOOL_EXCLUDE_FROM_ALL}") + +include(../NinjaMultiConfig/Common.cmake) +set(orig_CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES}) +if("${CMAKE_CONFIGURATION_TYPES}" STREQUAL "") + set(CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE}) +endif() +generate_output_files(tool) +set(CMAKE_CONFIGURATION_TYPES ${orig_CMAKE_CONFIGURATION_TYPES}) diff --git a/Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll-all-build-check.cmake b/Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll-all-build-check.cmake new file mode 100644 index 0000000..a4d2758 --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll-all-build-check.cmake @@ -0,0 +1,9 @@ +check_files("${RunCMake_TEST_BINARY_DIR}" + INCLUDE + ${TARGET_FILE_release_only_tool_Release} + ${TARGET_EXE_FILE_release_only_tool_Release} + + EXCLUDE + ${TARGET_FILE_release_only_tool_Debug} + ${TARGET_EXE_FILE_release_only_tool_Debug} + ) diff --git a/Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll.cmake b/Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll.cmake new file mode 100644 index 0000000..52f84ea --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll.cmake @@ -0,0 +1,12 @@ +enable_language(C) + +set(CMAKE_CONFIGURATION_TYPES "Release;Debug" CACHE STRING "") +set(CMAKE_DEFAULT_BUILD_TYPE "Release" CACHE STRING "") +set(CMAKE_CROSS_CONFIGS "all" CACHE STRING "") +set(CMAKE_DEFAULT_CONFIGS "all" CACHE STRING "") + +add_executable(release_only_tool main.c) +set_property(TARGET release_only_tool PROPERTY EXCLUDE_FROM_ALL "$<NOT:$<CONFIG:Release>>") + +include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) +generate_output_files(release_only_tool) diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index 76b488e..9249724 100644 --- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake @@ -274,6 +274,11 @@ run_ninja(Install default-install build.ninja install) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}/install") run_ninja(Install all-install build.ninja install:all) +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExcludeFromAll-build) +run_cmake_configure(ExcludeFromAll) +include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) +run_cmake_build(ExcludeFromAll all "" all:all) + # FIXME Get this working #set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AutoMocExecutable-build) #run_cmake_configure(AutoMocExecutable) |