Browse Source

add_subdirectory: Run subdirectory install rules in correct order

Before this change, install rules created by add_subdirectory()
would be executed after all of the top-level install rules, even
if they were declared before the top-level rules. This change
adds a new policy, CMP0082, which interleaves the add_subdirectory()
install rules with the other install rules so they are run in the
correct order.
Kyle Edwards 7 years ago
parent
commit
fc8955e889

+ 8 - 0
Help/manual/cmake-policies.7.rst

@@ -51,6 +51,14 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
 to determine whether to report an error on use of deprecated macros or
 to determine whether to report an error on use of deprecated macros or
 functions.
 functions.
 
 
+Policies Introduced by CMake 3.14
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0082: Install rules from add_subdirectory() are interleaved with those in caller. </policy/CMP0082>
+
 Policies Introduced by CMake 3.13
 Policies Introduced by CMake 3.13
 =================================
 =================================
 
 

+ 24 - 0
Help/policy/CMP0082.rst

@@ -0,0 +1,24 @@
+CMP0082
+-------
+
+Install rules from :command:`add_subdirectory` calls are interleaved with
+those in caller.
+
+CMake 3.13 and lower ran the install rules from :command:`add_subdirectory`
+after all other install rules, even if :command:`add_subdirectory` was called
+before the other install rules.  CMake 3.14 and later interleaves these
+:command:`add_subdirectory` install rules with the others so that they are
+run in the order they are declared.
+
+The ``OLD`` behavior for this policy is to run the install rules from
+:command:`add_subdirectory` after the other install rules.  The ``NEW``
+behavior for this policy is to run all install rules in the order they are
+declared.
+
+This policy was introduced in CMake version 3.14.  Unlike most policies,
+CMake version |release| does *not* warn by default when this policy
+is not set and simply uses OLD behavior.  See documentation of the
+:variable:`CMAKE_POLICY_WARNING_CMP0082 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
+variable to control the warning.
+
+.. include:: DEPRECATED.txt

+ 5 - 0
Help/release/dev/install-subdirectory-order.rst

@@ -0,0 +1,5 @@
+install-subdirectory-order
+--------------------------
+
+* Install rules under :command:`add_subdirectory` now interleave with those in
+  the calling directory. See policy :policy:`CMP0082` for details.

+ 2 - 0
Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst

@@ -19,6 +19,8 @@ warn by default:
   policy :policy:`CMP0066`.
   policy :policy:`CMP0066`.
 * ``CMAKE_POLICY_WARNING_CMP0067`` controls the warning for
 * ``CMAKE_POLICY_WARNING_CMP0067`` controls the warning for
   policy :policy:`CMP0067`.
   policy :policy:`CMP0067`.
+* ``CMAKE_POLICY_WARNING_CMP0082`` controls the warning for
+  policy :policy:`CMP0082`.
 
 
 This variable should not be set by a project in CMake code.  Project
 This variable should not be set by a project in CMake code.  Project
 developers running CMake may set this variable in their cache to
 developers running CMake may set this variable in their cache to

+ 2 - 0
Source/CMakeLists.txt

@@ -260,6 +260,8 @@ set(SRCS
   cmInstallFilesGenerator.cxx
   cmInstallFilesGenerator.cxx
   cmInstallScriptGenerator.h
   cmInstallScriptGenerator.h
   cmInstallScriptGenerator.cxx
   cmInstallScriptGenerator.cxx
+  cmInstallSubdirectoryGenerator.h
+  cmInstallSubdirectoryGenerator.cxx
   cmInstallTargetGenerator.h
   cmInstallTargetGenerator.h
   cmInstallTargetGenerator.cxx
   cmInstallTargetGenerator.cxx
   cmInstallDirectoryGenerator.h
   cmInstallDirectoryGenerator.h

+ 13 - 0
Source/cmInstallGenerator.cxx

@@ -22,6 +22,19 @@ cmInstallGenerator::~cmInstallGenerator()
 {
 {
 }
 }
 
 
+bool cmInstallGenerator::HaveInstall()
+{
+  return true;
+}
+
+void cmInstallGenerator::CheckCMP0082(bool& haveSubdirectoryInstall,
+                                      bool& haveInstallAfterSubdirectory)
+{
+  if (haveSubdirectoryInstall) {
+    haveInstallAfterSubdirectory = true;
+  }
+}
+
 void cmInstallGenerator::AddInstallRule(
 void cmInstallGenerator::AddInstallRule(
   std::ostream& os, std::string const& dest, cmInstallType type,
   std::ostream& os, std::string const& dest, cmInstallType type,
   std::vector<std::string> const& files, bool optional /* = false */,
   std::vector<std::string> const& files, bool optional /* = false */,

+ 4 - 0
Source/cmInstallGenerator.h

@@ -38,6 +38,10 @@ public:
                      bool exclude_from_all);
                      bool exclude_from_all);
   ~cmInstallGenerator() override;
   ~cmInstallGenerator() override;
 
 
+  virtual bool HaveInstall();
+  virtual void CheckCMP0082(bool& haveSubdirectoryInstall,
+                            bool& haveInstallAfterSubdirectory);
+
   void AddInstallRule(
   void AddInstallRule(
     std::ostream& os, std::string const& dest, cmInstallType type,
     std::ostream& os, std::string const& dest, cmInstallType type,
     std::vector<std::string> const& files, bool optional = false,
     std::vector<std::string> const& files, bool optional = false,

+ 2 - 2
Source/cmInstallScriptGenerator.cxx

@@ -37,9 +37,9 @@ void cmInstallScriptGenerator::AddScriptInstallRule(std::ostream& os,
                                                     std::string const& script)
                                                     std::string const& script)
 {
 {
   if (this->Code) {
   if (this->Code) {
-    os << indent.Next() << script << "\n";
+    os << indent << script << "\n";
   } else {
   } else {
-    os << indent.Next() << "include(\"" << script << "\")\n";
+    os << indent << "include(\"" << script << "\")\n";
   }
   }
 }
 }
 
 

+ 77 - 0
Source/cmInstallSubdirectoryGenerator.cxx

@@ -0,0 +1,77 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmInstallSubdirectoryGenerator.h"
+
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmPolicies.h"
+#include "cmScriptGenerator.h"
+#include "cmSystemTools.h"
+
+#include <sstream>
+#include <vector>
+
+cmInstallSubdirectoryGenerator::cmInstallSubdirectoryGenerator(
+  cmMakefile* makefile, const char* binaryDirectory, bool excludeFromAll)
+  : cmInstallGenerator(nullptr, std::vector<std::string>(), nullptr,
+                       MessageDefault, excludeFromAll)
+  , Makefile(makefile)
+  , BinaryDirectory(binaryDirectory)
+{
+}
+
+cmInstallSubdirectoryGenerator::~cmInstallSubdirectoryGenerator()
+{
+}
+
+bool cmInstallSubdirectoryGenerator::HaveInstall()
+{
+  for (auto generator : this->Makefile->GetInstallGenerators()) {
+    if (generator->HaveInstall()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void cmInstallSubdirectoryGenerator::CheckCMP0082(
+  bool& haveSubdirectoryInstall, bool& /*unused*/)
+{
+  if (this->HaveInstall()) {
+    haveSubdirectoryInstall = true;
+  }
+}
+
+void cmInstallSubdirectoryGenerator::Compute(cmLocalGenerator* lg)
+{
+  this->LocalGenerator = lg;
+}
+
+void cmInstallSubdirectoryGenerator::GenerateScript(std::ostream& os)
+{
+  if (!this->ExcludeFromAll) {
+    cmPolicies::PolicyStatus status =
+      this->LocalGenerator->GetPolicyStatus(cmPolicies::CMP0082);
+    switch (status) {
+      case cmPolicies::WARN:
+      case cmPolicies::OLD:
+        // OLD behavior is handled in cmLocalGenerator::GenerateInstallRules()
+        break;
+
+      case cmPolicies::NEW:
+      case cmPolicies::REQUIRED_IF_USED:
+      case cmPolicies::REQUIRED_ALWAYS: {
+        Indent indent;
+        std::string odir = this->BinaryDirectory;
+        cmSystemTools::ConvertToUnixSlashes(odir);
+        os << indent << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n"
+           << indent.Next()
+           << "# Include the install script for the subdirectory.\n"
+           << indent.Next() << "include(\"" << odir
+           << "/cmake_install.cmake\")\n"
+           << indent << "endif()\n\n";
+      } break;
+    }
+  }
+}

+ 41 - 0
Source/cmInstallSubdirectoryGenerator.h

@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmInstallSubdirectoryGenerator_h
+#define cmInstallSubdirectoryGenerator_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmInstallGenerator.h"
+
+#include <iosfwd>
+#include <string>
+
+class cmLocalGenerator;
+class cmMakefile;
+
+/** \class cmInstallSubdirectoryGenerator
+ * \brief Generate target installation rules.
+ */
+class cmInstallSubdirectoryGenerator : public cmInstallGenerator
+{
+public:
+  cmInstallSubdirectoryGenerator(cmMakefile* makefile,
+                                 const char* binaryDirectory,
+                                 bool excludeFromAll);
+  ~cmInstallSubdirectoryGenerator() override;
+
+  bool HaveInstall() override;
+  void CheckCMP0082(bool& haveSubdirectoryInstall,
+                    bool& haveInstallAfterSubdirectory) override;
+
+  void Compute(cmLocalGenerator* lg) override;
+
+protected:
+  void GenerateScript(std::ostream& os) override;
+
+  cmMakefile* Makefile;
+  std::string BinaryDirectory;
+  cmLocalGenerator* LocalGenerator;
+};
+
+#endif

+ 47 - 16
Source/cmLocalGenerator.cxx

@@ -517,31 +517,62 @@ void cmLocalGenerator::GenerateInstallRules()
   }
   }
 
 
   // Ask each install generator to write its code.
   // Ask each install generator to write its code.
+  cmPolicies::PolicyStatus status = this->GetPolicyStatus(cmPolicies::CMP0082);
   std::vector<cmInstallGenerator*> const& installers =
   std::vector<cmInstallGenerator*> const& installers =
     this->Makefile->GetInstallGenerators();
     this->Makefile->GetInstallGenerators();
-  for (cmInstallGenerator* installer : installers) {
-    installer->Generate(fout, config, configurationTypes);
+  bool haveSubdirectoryInstall = false;
+  bool haveInstallAfterSubdirectory = false;
+  if (status == cmPolicies::WARN) {
+    for (cmInstallGenerator* installer : installers) {
+      installer->CheckCMP0082(haveSubdirectoryInstall,
+                              haveInstallAfterSubdirectory);
+      installer->Generate(fout, config, configurationTypes);
+    }
+  } else {
+    for (cmInstallGenerator* installer : installers) {
+      installer->Generate(fout, config, configurationTypes);
+    }
   }
   }
 
 
   // Write rules from old-style specification stored in targets.
   // Write rules from old-style specification stored in targets.
   this->GenerateTargetInstallRules(fout, config, configurationTypes);
   this->GenerateTargetInstallRules(fout, config, configurationTypes);
 
 
   // Include install scripts from subdirectories.
   // Include install scripts from subdirectories.
-  std::vector<cmStateSnapshot> children =
-    this->Makefile->GetStateSnapshot().GetChildren();
-  if (!children.empty()) {
-    fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n";
-    fout << "  # Include the install script for each subdirectory.\n";
-    for (cmStateSnapshot const& c : children) {
-      if (!c.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
-        std::string odir = c.GetDirectory().GetCurrentBinary();
-        cmSystemTools::ConvertToUnixSlashes(odir);
-        fout << "  include(\"" << odir << "/cmake_install.cmake\")"
-             << std::endl;
+  switch (status) {
+    case cmPolicies::WARN:
+      if (haveInstallAfterSubdirectory &&
+          this->Makefile->PolicyOptionalWarningEnabled(
+            "CMAKE_POLICY_WARNING_CMP0082")) {
+        std::ostringstream e;
+        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n";
+        this->IssueMessage(cmake::AUTHOR_WARNING, e.str());
       }
       }
-    }
-    fout << "\n";
-    fout << "endif()\n\n";
+      CM_FALLTHROUGH;
+    case cmPolicies::OLD: {
+      std::vector<cmStateSnapshot> children =
+        this->Makefile->GetStateSnapshot().GetChildren();
+      if (!children.empty()) {
+        fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n";
+        fout << "  # Include the install script for each subdirectory.\n";
+        for (cmStateSnapshot const& c : children) {
+          if (!c.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
+            std::string odir = c.GetDirectory().GetCurrentBinary();
+            cmSystemTools::ConvertToUnixSlashes(odir);
+            fout << "  include(\"" << odir << "/cmake_install.cmake\")"
+                 << std::endl;
+          }
+        }
+        fout << "\n";
+        fout << "endif()\n\n";
+      }
+    } break;
+
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::REQUIRED_ALWAYS:
+    case cmPolicies::NEW:
+      // NEW behavior is handled in
+      // cmInstallSubdirectoryGenerator::GenerateScript()
+      break;
   }
   }
 
 
   // Record the install manifest.
   // Record the install manifest.

+ 4 - 0
Source/cmMakefile.cxx

@@ -28,6 +28,7 @@
 #include "cmGeneratorExpressionEvaluationFile.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallGenerator.h" // IWYU pragma: keep
 #include "cmInstallGenerator.h" // IWYU pragma: keep
+#include "cmInstallSubdirectoryGenerator.h"
 #include "cmListFileCache.h"
 #include "cmListFileCache.h"
 #include "cmSourceFile.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
 #include "cmSourceFileLocation.h"
@@ -1669,6 +1670,9 @@ void cmMakefile::AddSubDirectory(const std::string& srcPath,
   } else {
   } else {
     this->UnConfiguredDirectories.push_back(subMf);
     this->UnConfiguredDirectories.push_back(subMf);
   }
   }
+
+  this->AddInstallGenerator(new cmInstallSubdirectoryGenerator(
+    subMf, binPath.c_str(), excludeFromAll));
 }
 }
 
 
 const std::string& cmMakefile::GetCurrentSourceDirectory() const
 const std::string& cmMakefile::GetCurrentSourceDirectory() const

+ 5 - 1
Source/cmPolicies.h

@@ -240,7 +240,11 @@ class cmMakefile;
          cmPolicies::WARN)                                                    \
          cmPolicies::WARN)                                                    \
   SELECT(POLICY, CMP0081,                                                     \
   SELECT(POLICY, CMP0081,                                                     \
          "Relative paths not allowed in LINK_DIRECTORIES target property.",   \
          "Relative paths not allowed in LINK_DIRECTORIES target property.",   \
-         3, 13, 0, cmPolicies::WARN)
+         3, 13, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0082,                                                     \
+         "Install rules from add_subdirectory() are interleaved with those "  \
+         "in caller.",                                                        \
+         3, 14, 0, cmPolicies::WARN)
 
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 1 - 0
bootstrap

@@ -348,6 +348,7 @@ CMAKE_CXX_SOURCES="\
   cmInstallFilesGenerator \
   cmInstallFilesGenerator \
   cmInstallGenerator \
   cmInstallGenerator \
   cmInstallScriptGenerator \
   cmInstallScriptGenerator \
+  cmInstallSubdirectoryGenerator \
   cmInstallTargetGenerator \
   cmInstallTargetGenerator \
   cmInstallTargetsCommand \
   cmInstallTargetsCommand \
   cmInstalledFile \
   cmInstalledFile \