Merge branch 'backport-3.14-fix-EXCLUDE_FROM_ALL-subdir-all' into release-3.14

Merge-request: !3863
diff --git a/Help/prop_dir/EXCLUDE_FROM_ALL.rst b/Help/prop_dir/EXCLUDE_FROM_ALL.rst
index 9d3192c..8e3cca0 100644
--- a/Help/prop_dir/EXCLUDE_FROM_ALL.rst
+++ b/Help/prop_dir/EXCLUDE_FROM_ALL.rst
@@ -1,15 +1,13 @@
 EXCLUDE_FROM_ALL
 ----------------
 
-Exclude the directory from the all target of its parent.
+Set this directory property to a true value on a subdirectory to exclude
+its targets from the "all" target of its ancestors.  If excluded, running
+e.g. ``make`` in the parent directory will not build targets the
+subdirectory by default.  This does not affect the "all" target of the
+subdirectory itself.  Running e.g. ``make`` inside the subdirectory will
+still build its targets.
 
-A property on a directory that indicates if its targets are excluded
-from the default build target.  If it is not, then with a Makefile for
-example typing make will cause the targets to be built.  The same
-concept applies to the default build of other generators.
-
-Targets inherit the :prop_tgt:`EXCLUDE_FROM_ALL` property from the directory
-that they are created in. When a directory is excluded, all of its targets will
-have :prop_tgt:`EXCLUDE_FROM_ALL` set to ``TRUE``. After creating such a target
-you can change its :prop_tgt:`EXCLUDE_FROM_ALL` property to ``FALSE``. This
-will cause the target to be included in the default build target.
+If the :prop_tgt:`EXCLUDE_FROM_ALL` target property is set on a target
+then its value determines whether the target is included in the "all"
+target of this directory and its ancestors.
diff --git a/Help/prop_tgt/EXCLUDE_FROM_ALL.rst b/Help/prop_tgt/EXCLUDE_FROM_ALL.rst
index 0eee297..3aa296d 100644
--- a/Help/prop_tgt/EXCLUDE_FROM_ALL.rst
+++ b/Help/prop_tgt/EXCLUDE_FROM_ALL.rst
@@ -1,12 +1,15 @@
 EXCLUDE_FROM_ALL
 ----------------
 
-Exclude the target from the all target.
+Set this target property to a true (or false) value to exclude (or include)
+the target from the "all" target of the containing directory and its
+ancestors.  If excluded, running e.g. ``make`` in the containing directory
+or its ancestors will not build the target by default.
 
-A property on a target that indicates if the target is excluded from
-the default build target.  If it is not, then with a Makefile for
-example typing make will cause this target to be built.  The same
-concept applies to the default build of other generators.
+If this target property is not set then the target will be included in
+the "all" target of the containing directory.  Furthermore, it will be
+included in the "all" target of its ancestor directories unless the
+:prop_dir:`EXCLUDE_FROM_ALL` directory property is set.
 
 With ``EXCLUDE_FROM_ALL`` set to false or not set at all, the target
 will be brought up to date as part of doing a ``make install`` or its
@@ -16,6 +19,3 @@
 be listed in an :command:`install(TARGETS)` command as long as the install
 components the target belongs to are not part of the set of components
 that anything tries to install.
-
-This property is enabled by default for targets that are created in
-directories that have :prop_dir:`EXCLUDE_FROM_ALL` set to ``TRUE``.
diff --git a/Help/release/3.14.rst b/Help/release/3.14.rst
index 229d8dc..8a9738c 100644
--- a/Help/release/3.14.rst
+++ b/Help/release/3.14.rst
@@ -428,3 +428,11 @@
   policy :policy:`CMP0088` ``NEW`` behavior accidentally interpreted
   a relative path to the ``.y`` input as relative to the build tree
   directory instead of the source tree directory.  This has been fixed.
+
+3.14.7
+------
+
+* In CMake 3.14.0 through 3.14.6, the :prop_dir:`EXCLUDE_FROM_ALL`
+  directory property was regressed from pre-3.14 behavior and caused
+  targets within the directory to be excluded even from its own "all".
+  This has been fixed.
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 386a3f7..8a3720f 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -2029,10 +2029,18 @@
   return this->IsExcluded(rootSnp, snp);
 }
 
-bool cmGlobalGenerator::IsExcluded(cmGeneratorTarget* target) const
+bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root,
+                                   cmGeneratorTarget* target) const
 {
-  return target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
-    target->GetPropertyAsBool("EXCLUDE_FROM_ALL");
+  if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+    return true;
+  }
+  if (const char* exclude = target->GetProperty("EXCLUDE_FROM_ALL")) {
+    return cmSystemTools::IsOn(exclude);
+  }
+  // This target is included in its directory.  Check whether the
+  // directory is excluded.
+  return this->IsExcluded(root, target->GetLocalGenerator());
 }
 
 void cmGlobalGenerator::GetEnabledLanguages(
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index ac01326..0e40610 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -522,7 +522,7 @@
   bool IsExcluded(cmStateSnapshot const& root,
                   cmStateSnapshot const& snp) const;
   bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen) const;
-  bool IsExcluded(cmGeneratorTarget* target) const;
+  bool IsExcluded(cmLocalGenerator* root, cmGeneratorTarget* target) const;
   virtual void InitializeProgressMarks() {}
 
   struct GlobalTargetInfo
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index c619e67..226b73d 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -329,9 +329,9 @@
     return LocalGenerators;
   }
 
-  bool IsExcluded(cmGeneratorTarget* target)
+  bool IsExcluded(cmLocalGenerator* root, cmGeneratorTarget* target)
   {
-    return cmGlobalGenerator::IsExcluded(target);
+    return cmGlobalGenerator::IsExcluded(root, target);
   }
 
   int GetRuleCmdLength(const std::string& name) { return RuleCmdLength[name]; }
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index dac6ea6..f1a128a 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -713,7 +713,7 @@
                         localName, depends, commands, true);
 
       // add the all/all dependency
-      if (!this->IsExcluded(gtarget)) {
+      if (!this->IsExcluded(this->LocalGenerators[0], gtarget)) {
         depends.clear();
         depends.push_back(localName);
         commands.clear();
@@ -777,7 +777,7 @@
                           "Pre-install relink rule for target.", localName,
                           depends, commands, true);
 
-        if (!this->IsExcluded(gtarget)) {
+        if (!this->IsExcluded(this->LocalGenerators[0], gtarget)) {
           depends.clear();
           depends.push_back(localName);
           commands.clear();
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index f3ed76b..cc5a880 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -209,7 +209,7 @@
               tgt->IsImported()) {
             continue;
           }
-          if (!this->IsExcluded(tgt)) {
+          if (!this->IsExcluded(gen[0], tgt)) {
             allBuild->AddUtility(tgt->GetName());
           }
         }
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 57de60e..dc63ce6 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -567,7 +567,7 @@
           false, "", false, cmMakefile::AcceptObjectLibraryCommands);
       }
 
-      if (!this->IsExcluded(target)) {
+      if (!this->IsExcluded(gens[0], target)) {
         allbuild->AddUtility(target->GetName());
       }
     }
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index c0afc25..69656a2 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -88,7 +88,9 @@
     if (tg) {
       tg->Generate();
       // Add the target to "all" if required.
-      if (!this->GetGlobalNinjaGenerator()->IsExcluded(target)) {
+      if (!this->GetGlobalNinjaGenerator()->IsExcluded(
+            this->GetGlobalNinjaGenerator()->GetLocalGenerators()[0],
+            target)) {
         this->GetGlobalNinjaGenerator()->AddDependencyToAll(target);
       }
       delete tg;
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 7e33bda..2735122 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -1151,7 +1151,7 @@
   // Create a target instance for this utility.
   cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName);
   target->SetIsGeneratorProvided(origin == TargetOrigin::Generator);
-  if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
+  if (excludeFromAll) {
     target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
   }
   if (!comment) {
@@ -1689,7 +1689,7 @@
   cmMakefile* subMf = new cmMakefile(this->GlobalGenerator, newSnapshot);
   this->GetGlobalGenerator()->AddMakefile(subMf);
 
-  if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
+  if (excludeFromAll) {
     subMf->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
   }
 
@@ -1985,9 +1985,7 @@
   // over changes in CMakeLists.txt, making the information stale and
   // hence useless.
   target->ClearDependencyInformation(*this);
-  if (excludeFromAll ||
-      (type != cmStateEnums::INTERFACE_LIBRARY &&
-       this->GetPropertyAsBool("EXCLUDE_FROM_ALL"))) {
+  if (excludeFromAll) {
     target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
   }
   target->AddSources(srcs);
@@ -2000,7 +1998,7 @@
                                     bool excludeFromAll)
 {
   cmTarget* target = this->AddNewTarget(cmStateEnums::EXECUTABLE, exeName);
-  if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
+  if (excludeFromAll) {
     target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
   }
   target->AddSources(srcs);
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake
index ce71677..644da60 100644
--- a/Tests/RunCMake/RunCMake.cmake
+++ b/Tests/RunCMake/RunCMake.cmake
@@ -82,9 +82,12 @@
     set(maybe_input_file "")
   endif()
   if(RunCMake_TEST_COMMAND)
+    if(NOT RunCMake_TEST_COMMAND_WORKING_DIRECTORY)
+      set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+    endif()
     execute_process(
       COMMAND ${RunCMake_TEST_COMMAND}
-      WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}"
+      WORKING_DIRECTORY "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}"
       OUTPUT_VARIABLE actual_stdout
       ERROR_VARIABLE ${actual_stderr_var}
       RESULT_VARIABLE actual_result
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll-build-sub-stderr.txt b/Tests/RunCMake/add_subdirectory/ExcludeFromAll-build-sub-stderr.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll-build-sub-stderr.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake
index f686005..ff676a6 100644
--- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake
@@ -4,3 +4,11 @@
 
 add_executable(main main.cpp)
 target_link_libraries(main PRIVATE foo)
+
+file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
+set(main_exe \"$<TARGET_FILE:main>\")
+set(foo_lib \"$<TARGET_FILE:foo>\")
+set(bar_lib \"$<TARGET_FILE:bar>\")
+set(zot_lib \"$<TARGET_FILE:zot>\")
+set(subinc_lib \"$<TARGET_FILE:subinc>\")
+")
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt
index 9e6462b..790da54 100644
--- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt
@@ -1,20 +1,11 @@
-add_library(bar STATIC bar.cpp)
+project(ExcludeFromAllSub NONE)
+
+add_library(bar STATIC EXCLUDE_FROM_ALL bar.cpp)
+
+add_library(zot STATIC zot.cpp)
 
 add_library(foo STATIC foo.cpp)
-
-add_library(baz STATIC foo.cpp)
-set_target_properties(baz PROPERTIES EXCLUDE_FROM_ALL OFF)
-
-file(GENERATE
-  OUTPUT "${CMAKE_BINARY_DIR}/main.txt"
-  CONTENT "$<TARGET_FILE_NAME:main>")
-
-file(GENERATE
-  OUTPUT "${CMAKE_BINARY_DIR}/bar.txt"
-  CONTENT "$<TARGET_FILE_NAME:bar>")
-
-file(GENERATE
-  OUTPUT "${CMAKE_BINARY_DIR}/baz.txt"
-  CONTENT "$<TARGET_FILE_NAME:baz>")
-
 target_include_directories(foo PUBLIC .)
+
+add_library(subinc STATIC subinc.cpp)
+set_target_properties(subinc PROPERTIES EXCLUDE_FROM_ALL OFF)
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake
new file mode 100644
index 0000000..297ad1e
--- /dev/null
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake
@@ -0,0 +1,32 @@
+if(EXISTS ${RunCMake_TEST_BINARY_DIR}/check-debug.cmake)
+  include(${RunCMake_TEST_BINARY_DIR}/check-debug.cmake)
+  if(RunCMake_TEST_FAILED)
+    return()
+  endif()
+
+  foreach(file
+      "${foo_lib}"
+      "${subinc_lib}"
+      "${zot_lib}"
+      )
+    if(NOT EXISTS "${file}")
+      set(RunCMake_TEST_FAILED
+        "Artifact should exist but is missing:\n  ${file}")
+      return()
+    endif()
+  endforeach()
+  foreach(file
+      "${main_exe}"
+      "${bar_lib}"
+      )
+    if(EXISTS "${file}")
+      set(RunCMake_TEST_FAILED
+        "Artifact should be missing but exists:\n  ${file}")
+      return()
+    endif()
+  endforeach()
+else()
+  set(RunCMake_TEST_FAILED "
+ '${RunCMake_TEST_BINARY_DIR}/check-debug.cmake' missing
+")
+endif()
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake
index 14ec482..433c032 100644
--- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake
@@ -1,44 +1,35 @@
-# Use globbing to check if exes / libs were built because determining
-# exactly where these files will live inside a CMake -P script is
-# pretty challenging.
-
-file(READ "${RunCMake_TEST_BINARY_DIR}/main.txt" main_exe)
-file(READ "${RunCMake_TEST_BINARY_DIR}/bar.txt" bar_lib)
-file(READ "${RunCMake_TEST_BINARY_DIR}/baz.txt" baz_lib)
-
-set(found_main FALSE)
-file(GLOB_RECURSE files
-  LIST_DIRECTORIES FALSE
-  RELATIVE "${RunCMake_TEST_BINARY_DIR}"
-  "${RunCMake_TEST_BINARY_DIR}/*")
-foreach (file IN LISTS files)
-  if (file MATCHES "${main_exe}")
-    set(found_main TRUE)
+if(EXISTS ${RunCMake_TEST_BINARY_DIR}/check-debug.cmake)
+  include(${RunCMake_TEST_BINARY_DIR}/check-debug.cmake)
+  if(RunCMake_TEST_FAILED)
+    return()
   endif()
-endforeach()
-if (NOT found_main)
-  set(RunCMake_TEST_FAILED "'main' missing from ${RunCMake_TEST_BINARY_DIR}")
-endif()
 
-set(found_bar FALSE)
-set(found_baz FALSE)
-file(GLOB_RECURSE files
-  LIST_DIRECTORIES FALSE
-  RELATIVE "${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll"
-  "${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll/*")
-foreach (file IN LISTS files)
-  if (file MATCHES "${bar_lib}")
-    set(found_bar TRUE)
-  endif()
-  if (file MATCHES "${baz_lib}")
-    set(found_baz TRUE)
-  endif()
-endforeach()
-if (found_bar)
-  set(RunCMake_TEST_FAILED
-    "'bar' was not excluded from ${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll")
-endif()
-if (NOT found_baz)
-  set(RunCMake_TEST_FAILED
-    "'baz' missing from ${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll")
+  foreach(file
+      "${foo_lib}"
+      "${subinc_lib}"
+      "${main_exe}"
+      )
+    if(EXISTS "${file}")
+      # Remove for next step of test.
+      file(REMOVE "${file}")
+    else()
+      set(RunCMake_TEST_FAILED
+        "Artifact should exist but is missing:\n  ${file}")
+      return()
+    endif()
+  endforeach()
+  foreach(file
+      "${zot_lib}"
+      "${bar_lib}"
+      )
+    if(EXISTS "${file}")
+      set(RunCMake_TEST_FAILED
+        "Artifact should be missing but exists:\n  ${file}")
+      return()
+    endif()
+  endforeach()
+else()
+  set(RunCMake_TEST_FAILED "
+ '${RunCMake_TEST_BINARY_DIR}/check-debug.cmake' missing
+")
 endif()
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/subinc.cpp b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/subinc.cpp
new file mode 100644
index 0000000..e9faacd
--- /dev/null
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/subinc.cpp
@@ -0,0 +1,4 @@
+int subinc()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/zot.cpp b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/zot.cpp
new file mode 100644
index 0000000..ba7e966
--- /dev/null
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/zot.cpp
@@ -0,0 +1,4 @@
+int zot()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake
index 781e483..951e03c 100644
--- a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake
+++ b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake
@@ -27,14 +27,34 @@
 run_cmake_install(CMP0082-NEW -DCMP0082_VALUE=NEW)
 
 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExcludeFromAll-build)
-set(RunCMake_TEST_NO_CLEAN 1)
-
-file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
-file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
-
+if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+endif()
 run_cmake(ExcludeFromAll)
+set(RunCMake_TEST_NO_CLEAN 1)
 set(RunCMake-check-file ExcludeFromAll/check.cmake)
-run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build .)
-
-unset(RunCMake_TEST_BINARY_DIR)
+run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build . --config Debug)
+if(RunCMake_GENERATOR STREQUAL "Ninja")
+  if(WIN32)
+    set(slash [[\]])
+  else()
+    set(slash [[/]])
+  endif()
+  set(RunCMake-check-file ExcludeFromAll/check-sub.cmake)
+  run_cmake_command(ExcludeFromAll-build-sub ${CMAKE_COMMAND} --build . --target "ExcludeFromAll${slash}all")
+elseif(RunCMake_GENERATOR MATCHES "Make")
+  set(RunCMake-check-file ExcludeFromAll/check-sub.cmake)
+  set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_BINARY_DIR}/ExcludeFromAll-build/ExcludeFromAll)
+  run_cmake_command(ExcludeFromAll-build-sub "${RunCMake_MAKE_PROGRAM}")
+elseif(RunCMake_GENERATOR MATCHES "^Visual Studio [1-9][0-9]")
+  set(RunCMake-check-file ExcludeFromAll/check-sub.cmake)
+  run_cmake_command(ExcludeFromAll-build-sub ${CMAKE_COMMAND} --build ExcludeFromAll --config Debug)
+elseif(RunCMake_GENERATOR STREQUAL "Xcode")
+  set(RunCMake-check-file ExcludeFromAll/check-sub.cmake)
+  set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_BINARY_DIR}/ExcludeFromAll-build/ExcludeFromAll)
+  run_cmake_command(ExcludeFromAll-build-sub xcodebuild -configuration Debug)
+endif()
+unset(RunCMake-check-file)
 unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_OPTIONS)
+unset(RunCMake_TEST_BINARY_DIR)