Browse Source

Merge topic 'EXCLUDE_FROM_ALL-genex'

2cdaf43d96 Allow generator expressions in the EXCLUDE_FROM_ALL target property

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5021
Brad King 5 years ago
parent
commit
d45f0d719e

+ 7 - 0
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.

+ 27 - 5
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();
+}

+ 3 - 1
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

+ 28 - 3
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.

+ 2 - 1
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

+ 1 - 1
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);
     }
   }

+ 1 - 1
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);
         }
       }

+ 2 - 2
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;
       }
 

+ 1 - 0
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)

+ 3 - 0
Tests/RunCMake/ExcludeFromAll/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.3)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 26 - 0
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()

+ 1 - 0
Tests/RunCMake/ExcludeFromAll/error-on-mixed-config-result.txt

@@ -0,0 +1 @@
+1

+ 3 - 0
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 "[^"]+"

+ 6 - 0
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>>")

+ 3 - 0
Tests/RunCMake/ExcludeFromAll/main.c

@@ -0,0 +1,3 @@
+int main()
+{
+}

+ 17 - 0
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()

+ 11 - 0
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})

+ 9 - 0
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}
+  )

+ 12 - 0
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)

+ 5 - 0
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)