mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-05 07:10:26 +00:00
Merge topic 'always-create-all-verify-targets'
d6ecae0600VERIFY_*_HEADER_SETS: Create verification targets even when empty2f96373389cmGeneratorTarget: refactor header set verification to collect then create Acked-by: Kitware Robot <kwrobot@kitware.com> Tested-by: buildbot <buildbot@kitware.com> Merge-request: !11807
This commit is contained in:
@@ -10,13 +10,20 @@ VERIFY_INTERFACE_HEADER_SETS
|
||||
Used to verify that all headers in a target's ``PUBLIC`` and ``INTERFACE``
|
||||
header sets can be included on their own.
|
||||
|
||||
.. versionchanged:: 4.4
|
||||
Previously, the verification target was only created when the target had
|
||||
at least one matching header. Now it is always created when the property
|
||||
is enabled.
|
||||
|
||||
When this property is set to true, and the target is an object library, static
|
||||
library, shared library, interface library, or executable (subject to policy
|
||||
:policy:`CMP0209`) and the target has one or more ``PUBLIC`` or ``INTERFACE``
|
||||
header sets, an object library target named
|
||||
:policy:`CMP0209`), an object library target named
|
||||
``<target_name>_verify_interface_header_sets`` is created. This verification
|
||||
target has one source file per header in the ``PUBLIC`` and ``INTERFACE``
|
||||
header sets. Each source file only includes its associated header file.
|
||||
target has one source file per header in the target's ``PUBLIC`` and
|
||||
``INTERFACE`` header sets. Each source file only includes its associated
|
||||
header file. If the target has no matching header sets, a utility target is
|
||||
created instead so that the target name always exists for build system
|
||||
dependencies.
|
||||
The verification target links against the original target to get all of its
|
||||
usage requirements.
|
||||
|
||||
|
||||
@@ -10,13 +10,19 @@ VERIFY_PRIVATE_HEADER_SETS
|
||||
Used to verify that all headers in a target's ``PUBLIC`` and ``PRIVATE``
|
||||
header sets can be included on their own.
|
||||
|
||||
.. versionchanged:: 4.4
|
||||
Previously, the verification target was only created when the target had
|
||||
at least one matching header. Now it is always created when the property
|
||||
is enabled.
|
||||
|
||||
When this property is set to true, and the target is an object library, static
|
||||
library, shared library, module library, interface library, or executable, and
|
||||
the target has one or more ``PUBLIC`` or ``PRIVATE`` header sets, an object
|
||||
library target named ``<target_name>_verify_private_header_sets`` is created.
|
||||
This verification target has one source file per header in the ``PUBLIC`` and
|
||||
``PRIVATE`` header sets. Each source file only includes its associated header
|
||||
file.
|
||||
library, shared library, module library, interface library, or executable, an
|
||||
object library target named ``<target_name>_verify_private_header_sets`` is
|
||||
created. This verification target has one source file per header in the
|
||||
target's ``PUBLIC`` and ``PRIVATE`` header sets. Each source file only
|
||||
includes its associated header file. If the target has no matching header
|
||||
sets, a utility target is created instead so that the target name always
|
||||
exists for build system dependencies.
|
||||
|
||||
Properties affecting compilation are copied from the original target to the
|
||||
verification target so that the headers will be interpreted the same way by
|
||||
|
||||
@@ -26,11 +26,10 @@ conditions. The compiler flags used for private and interface contexts can be
|
||||
different, leading to the compiler interpreting the contents of the header
|
||||
differently.
|
||||
|
||||
If any |xxx| file set verification targets are created, a top-level target
|
||||
called |THIS_ALL_TARGET| is created which depends on all |xxx| verification
|
||||
targets. Another target called ``all_verify_header_sets`` is also created
|
||||
which depends on |THIS_ALL_TARGET|, and on |COMPLEMENTARY_ALL_TARGET| if it
|
||||
exists (see |COMPLEMENTARY_PROPERTY|).
|
||||
A top-level target called |THIS_ALL_TARGET| is created which depends on all
|
||||
|xxx| verification targets. Another target called ``all_verify_header_sets``
|
||||
is also created which depends on |THIS_ALL_TARGET|, and on
|
||||
|COMPLEMENTARY_ALL_TARGET| if it exists (see |COMPLEMENTARY_PROPERTY|).
|
||||
|
||||
This property is initialized by the value of the |INIT_VARIABLE| variable if
|
||||
it is set when a target is created.
|
||||
|
||||
9
Help/release/dev/always-create-all-verify-targets.rst
Normal file
9
Help/release/dev/always-create-all-verify-targets.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
always-create-all-verify-targets
|
||||
---------------------------------
|
||||
|
||||
* The :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` and
|
||||
:prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` target properties now always
|
||||
create the per-target verification target and the aggregate
|
||||
``all_verify_interface_header_sets``, ``all_verify_private_header_sets``,
|
||||
and ``all_verify_header_sets`` targets when the property is enabled,
|
||||
even if the target has no matching header sets.
|
||||
@@ -5863,6 +5863,10 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
|
||||
}
|
||||
|
||||
cm::optional<cm::optional<std::string>> defaultLanguage;
|
||||
|
||||
// First, collect all verification stubs before creating the target,
|
||||
// so we know whether to create an OBJECT library or not.
|
||||
std::vector<std::string> stubSources;
|
||||
for (auto const* fileSet : fileSets) {
|
||||
auto const& dirCges = fileSet->CompileDirectoryEntries();
|
||||
auto const& fileCges = fileSet->CompileFileEntries();
|
||||
@@ -5907,93 +5911,10 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
|
||||
}
|
||||
std::string filename = *filenameOpt;
|
||||
|
||||
if (!verifyTarget) {
|
||||
{
|
||||
cmMakefile::PolicyPushPop polScope(this->Makefile);
|
||||
this->Makefile->SetPolicy(cmPolicies::CMP0119,
|
||||
cmPolicies::NEW);
|
||||
verifyTarget = this->Makefile->AddLibrary(
|
||||
verifyTargetName, cmStateEnums::OBJECT_LIBRARY, {}, true);
|
||||
}
|
||||
|
||||
if (isInterface) {
|
||||
// Link to the original target so that we pick up its
|
||||
// interface compile options just like a consumer would.
|
||||
// This also ensures any generated headers in the original
|
||||
// target will be created.
|
||||
verifyTarget->AddLinkLibrary(
|
||||
*this->Makefile, this->GetName(),
|
||||
cmTargetLinkLibraryType::GENERAL_LibraryType);
|
||||
} else {
|
||||
// For private file sets, we need to simulate compiling the
|
||||
// same way as the original target. That includes linking to
|
||||
// the same things so we pick up the same transitive
|
||||
// properties. For the <LANG>_... properties, we don't care if
|
||||
// we set them for languages this target won't eventually use.
|
||||
// Copy language-standard properties for all supported
|
||||
// languages. We don't care if we set properties for languages
|
||||
// this target won't eventually use.
|
||||
static std::array<std::string, 19> const propertiesToCopy{ {
|
||||
"COMPILE_DEFINITIONS", "COMPILE_FEATURES",
|
||||
"COMPILE_FLAGS", "COMPILE_OPTIONS",
|
||||
"DEFINE_SYMBOL", "INCLUDE_DIRECTORIES",
|
||||
"LINK_LIBRARIES", "C_STANDARD",
|
||||
"C_STANDARD_REQUIRED", "C_EXTENSIONS",
|
||||
"CXX_STANDARD", "CXX_STANDARD_REQUIRED",
|
||||
"CXX_EXTENSIONS", "OBJC_STANDARD",
|
||||
"OBJC_STANDARD_REQUIRED", "OBJC_EXTENSIONS",
|
||||
"OBJCXX_STANDARD", "OBJCXX_STANDARD_REQUIRED",
|
||||
"OBJCXX_EXTENSIONS",
|
||||
} };
|
||||
for (std::string const& prop : propertiesToCopy) {
|
||||
cmValue propValue = this->Target->GetProperty(prop);
|
||||
if (propValue.IsSet()) {
|
||||
verifyTarget->SetProperty(prop, propValue);
|
||||
}
|
||||
}
|
||||
// The original target might have generated headers. Since
|
||||
// we only link to the original target for compilation,
|
||||
// there's nothing to force such generation to happen yet.
|
||||
// Our verify target must depend on the original target to
|
||||
// ensure such generated files will be created.
|
||||
verifyTarget->AddUtility(this->GetName(), false,
|
||||
this->Makefile);
|
||||
verifyTarget->AddCodegenDependency(this->GetName());
|
||||
}
|
||||
|
||||
verifyTarget->SetProperty("AUTOMOC", "OFF");
|
||||
verifyTarget->SetProperty("AUTORCC", "OFF");
|
||||
verifyTarget->SetProperty("AUTOUIC", "OFF");
|
||||
verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON");
|
||||
verifyTarget->SetProperty("UNITY_BUILD", "OFF");
|
||||
verifyTarget->SetProperty("CXX_SCAN_FOR_MODULES", "OFF");
|
||||
|
||||
if (isInterface) {
|
||||
verifyTarget->FinalizeTargetConfiguration(
|
||||
this->Makefile->GetCompileDefinitionsEntries());
|
||||
} else {
|
||||
// Private verification only needs to add the directory scope
|
||||
// definitions here
|
||||
for (auto const& def :
|
||||
this->Makefile->GetCompileDefinitionsEntries()) {
|
||||
verifyTarget->InsertCompileDefinition(def);
|
||||
}
|
||||
}
|
||||
|
||||
if (!allVerifyTarget) {
|
||||
allVerifyTarget =
|
||||
this->GlobalGenerator->GetMakefiles()
|
||||
.front()
|
||||
->AddNewUtilityTarget(allVerifyTargetName, true);
|
||||
}
|
||||
|
||||
allVerifyTarget->AddUtility(verifyTargetName, false);
|
||||
}
|
||||
|
||||
if (fileCgesContextSensitive) {
|
||||
filename = cmStrCat("$<$<CONFIG:", config, ">:", filename, '>');
|
||||
}
|
||||
verifyTarget->AddSource(filename);
|
||||
stubSources.emplace_back(std::move(filename));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6004,13 +5925,99 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
|
||||
}
|
||||
}
|
||||
|
||||
if (verifyTarget) {
|
||||
this->LocalGenerator->AddGeneratorTarget(
|
||||
cm::make_unique<cmGeneratorTarget>(verifyTarget,
|
||||
this->LocalGenerator));
|
||||
}
|
||||
}
|
||||
if (stubSources.empty()) {
|
||||
// No headers to verify. Create a utility target so the target
|
||||
// name always exists (e.g. for build system dependencies) without
|
||||
// needing a placeholder source. This avoids warnings from tools
|
||||
// like Xcode's libtool about empty static libraries.
|
||||
verifyTarget =
|
||||
this->Makefile->AddNewUtilityTarget(verifyTargetName, true);
|
||||
} else {
|
||||
// Create an OBJECT library to compile the verification stubs.
|
||||
{
|
||||
cmMakefile::PolicyPushPop polScope(this->Makefile);
|
||||
this->Makefile->SetPolicy(cmPolicies::CMP0119, cmPolicies::NEW);
|
||||
verifyTarget = this->Makefile->AddLibrary(
|
||||
verifyTargetName, cmStateEnums::OBJECT_LIBRARY, {}, true);
|
||||
}
|
||||
|
||||
if (isInterface) {
|
||||
// Link to the original target so that we pick up its
|
||||
// interface compile options just like a consumer would.
|
||||
// This also ensures any generated headers in the original
|
||||
// target will be created.
|
||||
verifyTarget->AddLinkLibrary(
|
||||
*this->Makefile, this->GetName(),
|
||||
cmTargetLinkLibraryType::GENERAL_LibraryType);
|
||||
} else {
|
||||
// For private file sets, we need to simulate compiling the
|
||||
// same way as the original target. That includes linking to
|
||||
// the same things so we pick up the same transitive
|
||||
// properties. For the <LANG>_... properties, we don't care if
|
||||
// we set them for languages this target won't eventually use.
|
||||
// Copy language-standard properties for all supported
|
||||
// languages. We don't care if we set properties for languages
|
||||
// this target won't eventually use.
|
||||
static std::array<std::string, 19> const propertiesToCopy{ {
|
||||
"COMPILE_DEFINITIONS", "COMPILE_FEATURES",
|
||||
"COMPILE_FLAGS", "COMPILE_OPTIONS",
|
||||
"DEFINE_SYMBOL", "INCLUDE_DIRECTORIES",
|
||||
"LINK_LIBRARIES", "C_STANDARD",
|
||||
"C_STANDARD_REQUIRED", "C_EXTENSIONS",
|
||||
"CXX_STANDARD", "CXX_STANDARD_REQUIRED",
|
||||
"CXX_EXTENSIONS", "OBJC_STANDARD",
|
||||
"OBJC_STANDARD_REQUIRED", "OBJC_EXTENSIONS",
|
||||
"OBJCXX_STANDARD", "OBJCXX_STANDARD_REQUIRED",
|
||||
"OBJCXX_EXTENSIONS",
|
||||
} };
|
||||
for (std::string const& prop : propertiesToCopy) {
|
||||
cmValue propValue = this->Target->GetProperty(prop);
|
||||
if (propValue.IsSet()) {
|
||||
verifyTarget->SetProperty(prop, propValue);
|
||||
}
|
||||
}
|
||||
// The original target might have generated headers. Since
|
||||
// we only link to the original target for compilation,
|
||||
// there's nothing to force such generation to happen yet.
|
||||
// Our verify target must depend on the original target to
|
||||
// ensure such generated files will be created.
|
||||
verifyTarget->AddUtility(this->GetName(), false, this->Makefile);
|
||||
verifyTarget->AddCodegenDependency(this->GetName());
|
||||
}
|
||||
|
||||
verifyTarget->SetProperty("AUTOMOC", "OFF");
|
||||
verifyTarget->SetProperty("AUTORCC", "OFF");
|
||||
verifyTarget->SetProperty("AUTOUIC", "OFF");
|
||||
verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON");
|
||||
verifyTarget->SetProperty("UNITY_BUILD", "OFF");
|
||||
verifyTarget->SetProperty("CXX_SCAN_FOR_MODULES", "OFF");
|
||||
|
||||
if (isInterface) {
|
||||
verifyTarget->FinalizeTargetConfiguration(
|
||||
this->Makefile->GetCompileDefinitionsEntries());
|
||||
} else {
|
||||
// Private verification only needs to add the directory scope
|
||||
// definitions here
|
||||
for (auto const& def :
|
||||
this->Makefile->GetCompileDefinitionsEntries()) {
|
||||
verifyTarget->InsertCompileDefinition(def);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& source : stubSources) {
|
||||
verifyTarget->AddSource(source);
|
||||
}
|
||||
}
|
||||
if (!allVerifyTarget) {
|
||||
allVerifyTarget =
|
||||
this->GlobalGenerator->GetMakefiles().front()->AddNewUtilityTarget(
|
||||
allVerifyTargetName, true);
|
||||
}
|
||||
allVerifyTarget->AddUtility(verifyTargetName, false);
|
||||
|
||||
this->LocalGenerator->AddGeneratorTarget(
|
||||
cm::make_unique<cmGeneratorTarget>(verifyTarget, this->LocalGenerator));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,3 +125,21 @@ run_cmake_build(AllVerifyPrivateHeaderSets private all)
|
||||
set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_PRIVATE_HEADER_SETS=ON)
|
||||
run_cmake(VerifyPrivateHeaderSetsNonexistent)
|
||||
unset(RunCMake_TEST_OPTIONS)
|
||||
|
||||
# Test that verify targets are always created even with no headers
|
||||
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
|
||||
endif()
|
||||
run_cmake(VerifyEmptyHeaderSets)
|
||||
unset(RunCMake_TEST_OPTIONS)
|
||||
|
||||
run_cmake_build(VerifyEmptyHeaderSets interface empty_iface)
|
||||
run_cmake_build(VerifyEmptyHeaderSets private empty_priv)
|
||||
run_cmake_build(VerifyEmptyHeaderSets interface all)
|
||||
run_cmake_build(VerifyEmptyHeaderSets private all)
|
||||
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VerifyEmptyHeaderSets-build)
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
run_cmake_command(VerifyEmptyHeaderSets-all_verify_header_sets-Debug-build
|
||||
${CMAKE_COMMAND} --build . --config Debug --target all_verify_header_sets
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
enable_language(C)
|
||||
|
||||
# Target with VERIFY_INTERFACE_HEADER_SETS ON but no interface file sets
|
||||
add_library(empty_iface STATIC lib.c)
|
||||
set_property(TARGET empty_iface PROPERTY VERIFY_INTERFACE_HEADER_SETS ON)
|
||||
|
||||
# Target with VERIFY_PRIVATE_HEADER_SETS ON but no private file sets
|
||||
add_library(empty_priv STATIC lib.c)
|
||||
set_property(TARGET empty_priv PROPERTY VERIFY_PRIVATE_HEADER_SETS ON)
|
||||
@@ -1 +0,0 @@
|
||||
[^0]
|
||||
@@ -1 +0,0 @@
|
||||
.*
|
||||
@@ -1 +0,0 @@
|
||||
[^0]
|
||||
@@ -1 +0,0 @@
|
||||
.*
|
||||
@@ -1 +0,0 @@
|
||||
[^0]
|
||||
@@ -1 +0,0 @@
|
||||
.*
|
||||
@@ -1 +0,0 @@
|
||||
[^0]
|
||||
@@ -1 +0,0 @@
|
||||
.*
|
||||
@@ -1 +0,0 @@
|
||||
[^0]
|
||||
Reference in New Issue
Block a user