diff --git a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst index 0c7c70809b..59cd1048cd 100644 --- a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst +++ b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst @@ -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 ``_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. diff --git a/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst b/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst index c7f578ee42..889a2074e4 100644 --- a/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst +++ b/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst @@ -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 ``_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 ``_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 diff --git a/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst b/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst index 71e34cd4c7..b8e8d07ec3 100644 --- a/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst +++ b/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst @@ -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. diff --git a/Help/release/dev/always-create-all-verify-targets.rst b/Help/release/dev/always-create-all-verify-targets.rst new file mode 100644 index 0000000000..a634265094 --- /dev/null +++ b/Help/release/dev/always-create-all-verify-targets.rst @@ -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. diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 1b3b38528d..79a2aae522 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -5863,6 +5863,10 @@ bool cmGeneratorTarget::AddHeaderSetVerification() } cm::optional> defaultLanguage; + + // First, collect all verification stubs before creating the target, + // so we know whether to create an OBJECT library or not. + std::vector 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 _... 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 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("$<$:", 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(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 _... 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 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(verifyTarget, this->LocalGenerator)); + } return true; } diff --git a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake index 26fc8cb9b1..e9a5747825 100644 --- a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake @@ -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 +) diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyEmptyHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyEmptyHeaderSets.cmake new file mode 100644 index 0000000000..d1aeacacb8 --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyEmptyHeaderSets.cmake @@ -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) diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt deleted file mode 100644 index d197c913c2..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt +++ /dev/null @@ -1 +0,0 @@ -[^0] diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9debd..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-stderr.txt +++ /dev/null @@ -1 +0,0 @@ -.* diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt deleted file mode 100644 index d197c913c2..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt +++ /dev/null @@ -1 +0,0 @@ -[^0] diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9debd..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt +++ /dev/null @@ -1 +0,0 @@ -.* diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt deleted file mode 100644 index d197c913c2..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt +++ /dev/null @@ -1 +0,0 @@ -[^0] diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9debd..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt +++ /dev/null @@ -1 +0,0 @@ -.* diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt deleted file mode 100644 index d197c913c2..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt +++ /dev/null @@ -1 +0,0 @@ -[^0] diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9debd..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt +++ /dev/null @@ -1 +0,0 @@ -.* diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt deleted file mode 100644 index d197c913c2..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt +++ /dev/null @@ -1 +0,0 @@ -[^0] diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9debd..0000000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt +++ /dev/null @@ -1 +0,0 @@ -.*