mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-11 16:32:14 -06:00
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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
3
Tests/RunCMake/ExcludeFromAll/CMakeLists.txt
Normal file
3
Tests/RunCMake/ExcludeFromAll/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
26
Tests/RunCMake/ExcludeFromAll/RunCMakeTest.cmake
Normal file
26
Tests/RunCMake/ExcludeFromAll/RunCMakeTest.cmake
Normal file
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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 "[^"]+"
|
||||
@@ -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>>")
|
||||
3
Tests/RunCMake/ExcludeFromAll/main.c
Normal file
3
Tests/RunCMake/ExcludeFromAll/main.c
Normal file
@@ -0,0 +1,3 @@
|
||||
int main()
|
||||
{
|
||||
}
|
||||
@@ -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()
|
||||
11
Tests/RunCMake/ExcludeFromAll/single-config.cmake
Normal file
11
Tests/RunCMake/ExcludeFromAll/single-config.cmake
Normal file
@@ -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})
|
||||
@@ -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}
|
||||
)
|
||||
12
Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll.cmake
Normal file
12
Tests/RunCMake/NinjaMultiConfig/ExcludeFromAll.cmake
Normal file
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user