Browse Source

Merge topic 'install-defaults'

fdcd559a8e Help: Add documentation and release notes for install
9fc20a4f3e install: Add sane set of defaults for DESTINATION and file type parameters

Acked-by: Kitware Robot <[email protected]>
Acked-by: Alex Turbov <[email protected]>
Acked-by: Ruslan Baratov <[email protected]>
Merge-request: !2558
Brad King 7 years ago
parent
commit
17e98e00c4
38 changed files with 953 additions and 83 deletions
  1. 109 4
      Help/command/install.rst
  2. 12 0
      Help/release/dev/install-defaults.rst
  3. 297 79
      Source/cmInstallCommand.cxx
  4. 22 0
      Source/cmInstallCommand.h
  5. 6 0
      Source/cmInstallCommandArguments.cxx
  6. 2 0
      Source/cmInstallCommandArguments.h
  7. 1 0
      Tests/RunCMake/install/DIRECTORY-DESTINATION-TYPE-result.txt
  8. 5 0
      Tests/RunCMake/install/DIRECTORY-DESTINATION-TYPE-stderr.txt
  9. 1 0
      Tests/RunCMake/install/DIRECTORY-DESTINATION-TYPE.cmake
  10. 42 0
      Tests/RunCMake/install/DIRECTORY-TYPE-Cache-all-check.cmake
  11. 13 0
      Tests/RunCMake/install/DIRECTORY-TYPE-Cache.cmake
  12. 24 0
      Tests/RunCMake/install/DIRECTORY-TYPE-CacheDependent-all-check.cmake
  13. 7 0
      Tests/RunCMake/install/DIRECTORY-TYPE-CacheDependent.cmake
  14. 42 0
      Tests/RunCMake/install/DIRECTORY-TYPE-all-check.cmake
  15. 13 0
      Tests/RunCMake/install/DIRECTORY-TYPE.cmake
  16. 1 0
      Tests/RunCMake/install/FILES-DESTINATION-TYPE-result.txt
  17. 5 0
      Tests/RunCMake/install/FILES-DESTINATION-TYPE-stderr.txt
  18. 1 0
      Tests/RunCMake/install/FILES-DESTINATION-TYPE.cmake
  19. 29 0
      Tests/RunCMake/install/FILES-TYPE-Cache-all-check.cmake
  20. 13 0
      Tests/RunCMake/install/FILES-TYPE-Cache.cmake
  21. 17 0
      Tests/RunCMake/install/FILES-TYPE-CacheDependent-all-check.cmake
  22. 7 0
      Tests/RunCMake/install/FILES-TYPE-CacheDependent.cmake
  23. 29 0
      Tests/RunCMake/install/FILES-TYPE-all-check.cmake
  24. 13 0
      Tests/RunCMake/install/FILES-TYPE.cmake
  25. 46 0
      Tests/RunCMake/install/RunCMakeTest.cmake
  26. 1 0
      Tests/RunCMake/install/TARGETS-Apple-Defaults-result.txt
  27. 12 0
      Tests/RunCMake/install/TARGETS-Apple-Defaults-stderr.txt
  28. 8 0
      Tests/RunCMake/install/TARGETS-Apple-Defaults.cmake
  29. 49 0
      Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake
  30. 19 0
      Tests/RunCMake/install/TARGETS-Defaults-Cache.cmake
  31. 49 0
      Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake
  32. 19 0
      Tests/RunCMake/install/TARGETS-Defaults.cmake
  33. 7 0
      Tests/RunCMake/install/obj3.c
  34. 6 0
      Tests/RunCMake/install/obj3.h
  35. 7 0
      Tests/RunCMake/install/obj4.c
  36. 6 0
      Tests/RunCMake/install/obj4.h
  37. 7 0
      Tests/RunCMake/install/obj5.c
  38. 6 0
      Tests/RunCMake/install/obj5.h

+ 109 - 4
Help/command/install.rst

@@ -9,8 +9,8 @@ Synopsis
 .. parsed-literal::
 .. parsed-literal::
 
 
   install(`TARGETS`_ <target>... [...])
   install(`TARGETS`_ <target>... [...])
-  install({`FILES`_ | `PROGRAMS`_} <file>... DESTINATION <dir> [...])
-  install(`DIRECTORY`_ <dir>... DESTINATION <dir> [...])
+  install({`FILES`_ | `PROGRAMS`_} <file>... [DESTINATION <dir>] [...])
+  install(`DIRECTORY`_ <dir>... [DESTINATION <dir>] [...])
   install(`SCRIPT`_ <file> [...])
   install(`SCRIPT`_ <file> [...])
   install(`CODE`_ <code> [...])
   install(`CODE`_ <code> [...])
   install(`EXPORT`_ <export-name> DESTINATION <dir> [...])
   install(`EXPORT`_ <export-name> DESTINATION <dir> [...])
@@ -172,6 +172,29 @@ installation properties apply to all target types. If only one is given then
 only targets of that type will be installed (which can be used to install
 only targets of that type will be installed (which can be used to install
 just a DLL or just an import library.)
 just a DLL or just an import library.)
 
 
+For some target types, the ``DESTINATION`` argument is optional. If no
+``DESTINATION`` argument is specified for these target types, the destination
+will default to either the appropriate variable from :module:`GNUInstallDirs`
+(if it is defined) or a built-in default (if the variable is not defined.) These
+defaults are outlined below:
+
+================== =============================== ======================
+   Target Type         GNUInstallDirs Variable        Built-In Default
+================== =============================== ======================
+``RUNTIME``        ``${CMAKE_INSTALL_BINDIR}``     ``bin``
+``LIBRARY``        ``${CMAKE_INSTALL_LIBDIR}``     ``lib``
+``ARCHIVE``        ``${CMAKE_INSTALL_LIBDIR}``     ``lib``
+``PRIVATE_HEADER`` ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
+``PUBLIC_HEADER``  ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
+================== =============================== ======================
+
+To make your package compliant with distribution filesystem layout policies, it
+is not recommended that you specify a ``DESTINATION`` for a target unless it
+must be installed in a nonstandard location. That way, package maintainers can
+control the install destination by setting the appropriate cache variables. In
+any case, it is recommended that you use the :module:`GNUInstallDirs` variables
+in your ``DESTINATION`` arguments whenever possible.
+
 In addition to the common options listed above, each target can accept
 In addition to the common options listed above, each target can accept
 the following additional arguments:
 the following additional arguments:
 
 
@@ -307,7 +330,7 @@ Installing Files
 
 
 .. code-block:: cmake
 .. code-block:: cmake
 
 
-  install(<FILES|PROGRAMS> files... DESTINATION <dir>
+  install(<FILES|PROGRAMS> files... [DESTINATION <dir> | TYPE <type>]
           [PERMISSIONS permissions...]
           [PERMISSIONS permissions...]
           [CONFIGURATIONS [Debug|Release|...]]
           [CONFIGURATIONS [Debug|Release|...]]
           [COMPONENT <component>]
           [COMPONENT <component>]
@@ -331,6 +354,47 @@ The list of ``files...`` given to ``FILES`` or ``PROGRAMS`` may use
 However, if any item begins in a generator expression it must evaluate
 However, if any item begins in a generator expression it must evaluate
 to a full path.
 to a full path.
 
 
+Instead of specifying ``DESTINATION``, you may specify a generic file type
+via the ``TYPE`` argument as listed below. If a type is selected and no
+destination is specified, the destination will default to either the
+appropriate variable from :module:`GNUInstallDirs` (if it is defined) or a
+built-in default (if the variable is not defined.) These defaults are outlined
+below:
+
+======================= ================================== =========================
+   ``TYPE`` Argument         GNUInstallDirs Variable           Built-In Default
+======================= ================================== =========================
+``BIN``                 ``${CMAKE_INSTALL_BINDIR}``        ``bin``
+``SBIN``                ``${CMAKE_INSTALL_SBINDIR}``       ``sbin``
+``LIB``                 ``${CMAKE_INSTALL_LIBDIR}``        ``lib``
+``INCLUDE``             ``${CMAKE_INSTALL_INCLUDEDIR}``    ``include``
+``SYSCONF``             ``${CMAKE_INSTALL_SYSCONFDIR}``    ``etc``
+``SHAREDSTATE``         ``${CMAKE_INSTALL_SHARESTATEDIR}`` ``com``
+``LOCALSTATE``          ``${CMAKE_INSTALL_LOCALSTATEDIR}`` ``var``
+``RUNSTATE``            ``${CMAKE_INSTALL_RUNSTATEDIR}``   ``<LOCALSTATE dir>/run``
+``DATA``                ``${CMAKE_INSTALL_DATADIR}``       ``<DATAROOT dir>``
+``INFO``                ``${CMAKE_INSTALL_INFODIR}``       ``<DATAROOT dir>/info``
+``LOCALE``              ``${CMAKE_INSTALL_LOCALEDIR}``     ``<DATAROOT dir>/locale``
+``MAN``                 ``${CMAKE_INSTALL_MANDIR}``        ``<DATAROOT dir>/man``
+``DOC``                 ``${CMAKE_INSTALL_DOCDIR}``        ``<DATAROOT dir>/doc``
+======================= ================================== =========================
+
+It is an error to use ``TYPE`` and ``DESTINATION`` arguments together.
+
+Note that some of the types' built-in defaults use the ``DATAROOT`` directory as
+a prefix. The ``DATAROOT`` prefix is calculated similarly to the types, with
+``CMAKE_INSTALL_DATAROOTDIR`` as the variable and ``share`` as the built-in
+default. You cannot use ``DATAROOT`` as a ``TYPE`` parameter; please use
+``DATA`` instead.
+
+To make your package compliant with distribution filesystem layout policies, it
+is recommended that you specify one of the above generic file types, rather than
+a ``DESTINATION`` argument, unless the files must be installed in a nonstandard
+location. That way, package maintainers can control the install destination by
+setting the appropriate cache variables. In any case, it is recommended that you
+use the :module:`GNUInstallDirs` variables in your ``DESTINATION`` arguments
+whenever possible.
+
 The install destination given to the files install ``DESTINATION`` may
 The install destination given to the files install ``DESTINATION`` may
 use "generator expressions" with the syntax ``$<...>``.  See the
 use "generator expressions" with the syntax ``$<...>``.  See the
 :manual:`cmake-generator-expressions(7)` manual for available expressions.
 :manual:`cmake-generator-expressions(7)` manual for available expressions.
@@ -342,7 +406,7 @@ Installing Directories
 
 
 .. code-block:: cmake
 .. code-block:: cmake
 
 
-  install(DIRECTORY dirs... DESTINATION <dir>
+  install(DIRECTORY dirs... [DESTINATION <dir> | TYPE <type>]
           [FILE_PERMISSIONS permissions...]
           [FILE_PERMISSIONS permissions...]
           [DIRECTORY_PERMISSIONS permissions...]
           [DIRECTORY_PERMISSIONS permissions...]
           [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
           [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
@@ -413,6 +477,47 @@ will install the ``icons`` directory to ``share/myproj/icons`` and the
 file permissions, the scripts will be given specific permissions, and any
 file permissions, the scripts will be given specific permissions, and any
 ``CVS`` directories will be excluded.
 ``CVS`` directories will be excluded.
 
 
+Instead of specifying ``DESTINATION``, you may specify a generic file type
+via the ``TYPE`` argument as listed below. If a type is selected and no
+destination is specified, the destination will default to either the
+appropriate variable from :module:`GNUInstallDirs` (if it is defined) or a
+built-in default (if the variable is not defined.) These defaults are outlined
+below:
+
+======================= ================================== =========================
+   ``TYPE`` Argument         GNUInstallDirs Variable           Built-In Default
+======================= ================================== =========================
+``BIN``                 ``${CMAKE_INSTALL_BINDIR}``        ``bin``
+``SBIN``                ``${CMAKE_INSTALL_SBINDIR}``       ``sbin``
+``LIB``                 ``${CMAKE_INSTALL_LIBDIR}``        ``lib``
+``INCLUDE``             ``${CMAKE_INSTALL_INCLUDEDIR}``    ``include``
+``SYSCONF``             ``${CMAKE_INSTALL_SYSCONFDIR}``    ``etc``
+``SHAREDSTATE``         ``${CMAKE_INSTALL_SHARESTATEDIR}`` ``com``
+``LOCALSTATE``          ``${CMAKE_INSTALL_LOCALSTATEDIR}`` ``var``
+``RUNSTATE``            ``${CMAKE_INSTALL_RUNSTATEDIR}``   ``<LOCALSTATE dir>/run``
+``DATA``                ``${CMAKE_INSTALL_DATADIR}``       ``<DATAROOT dir>``
+``INFO``                ``${CMAKE_INSTALL_INFODIR}``       ``<DATAROOT dir>/info``
+``LOCALE``              ``${CMAKE_INSTALL_LOCALEDIR}``     ``<DATAROOT dir>/locale``
+``MAN``                 ``${CMAKE_INSTALL_MANDIR}``        ``<DATAROOT dir>/man``
+``DOC``                 ``${CMAKE_INSTALL_DOCDIR}``        ``<DATAROOT dir>/doc``
+======================= ================================== =========================
+
+It is an error to use ``TYPE`` and ``DESTINATION`` arguments together.
+
+Note that some of the types' built-in defaults use the ``DATAROOT`` directory as
+a prefix. The ``DATAROOT`` prefix is calculated similarly to the types, with
+``CMAKE_INSTALL_DATAROOTDIR`` as the variable and ``share`` as the built-in
+default. You cannot use ``DATAROOT`` as a ``TYPE`` parameter; please use
+``DATA`` instead.
+
+To make your package compliant with distribution filesystem layout policies, it
+is recommended that you specify one of the above generic file types, rather than
+a ``DESTINATION`` argument, unless the files must be installed in a nonstandard
+location. That way, package maintainers can control the install destination by
+setting the appropriate cache variables. In any case, it is recommended that you
+use the :module:`GNUInstallDirs` variables in your ``DESTINATION`` arguments
+whenever possible.
+
 The list of ``dirs...`` given to ``DIRECTORY`` and the install destination
 The list of ``dirs...`` given to ``DIRECTORY`` and the install destination
 given to the directory install ``DESTINATION`` may use "generator expressions"
 given to the directory install ``DESTINATION`` may use "generator expressions"
 with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
 with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`

+ 12 - 0
Help/release/dev/install-defaults.rst

@@ -0,0 +1,12 @@
+install-defaults
+----------------
+
+* The ``TARGETS`` variant of the :command:`install` command learned how to
+  install to an appropriate default directory for a given target type, based
+  on variables from the :module:`GNUInstallDirs` module and built-in defaults,
+  in lieu of a ``DESTINATION`` argument.
+* The ``FILES`` and ``DIRECTORY`` variants of the :command:`install` command
+  learned a new set of parameters for installing files as a file type, setting
+  the destination based on the appropriate variables from
+  :module:`GNUInstallDirs` and built-in defaults, in lieu of a ``DESTINATION``
+  argument.

+ 297 - 79
Source/cmInstallCommand.cxx

@@ -3,6 +3,7 @@
 #include "cmInstallCommand.h"
 #include "cmInstallCommand.h"
 
 
 #include "cmsys/Glob.hxx"
 #include "cmsys/Glob.hxx"
+#include <set>
 #include <sstream>
 #include <sstream>
 #include <stddef.h>
 #include <stddef.h>
 #include <utility>
 #include <utility>
@@ -33,8 +34,8 @@ class cmExecutionStatus;
 
 
 static cmInstallTargetGenerator* CreateInstallTargetGenerator(
 static cmInstallTargetGenerator* CreateInstallTargetGenerator(
   cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
   cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
-  cmListFileBacktrace const& backtrace, bool forceOpt = false,
-  bool namelink = false)
+  cmListFileBacktrace const& backtrace, const std::string& destination,
+  bool forceOpt = false, bool namelink = false)
 {
 {
   cmInstallGenerator::MessageLevel message =
   cmInstallGenerator::MessageLevel message =
     cmInstallGenerator::SelectMessageLevel(target.GetMakefile());
     cmInstallGenerator::SelectMessageLevel(target.GetMakefile());
@@ -42,25 +43,49 @@ static cmInstallTargetGenerator* CreateInstallTargetGenerator(
   const char* component = namelink ? args.GetNamelinkComponent().c_str()
   const char* component = namelink ? args.GetNamelinkComponent().c_str()
                                    : args.GetComponent().c_str();
                                    : args.GetComponent().c_str();
   return new cmInstallTargetGenerator(
   return new cmInstallTargetGenerator(
-    target.GetName(), args.GetDestination().c_str(), impLib,
+    target.GetName(), destination.c_str(), impLib,
     args.GetPermissions().c_str(), args.GetConfigurations(), component,
     args.GetPermissions().c_str(), args.GetConfigurations(), component,
     message, args.GetExcludeFromAll(), args.GetOptional() || forceOpt,
     message, args.GetExcludeFromAll(), args.GetOptional() || forceOpt,
     backtrace);
     backtrace);
 }
 }
 
 
+static cmInstallTargetGenerator* CreateInstallTargetGenerator(
+  cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
+  cmListFileBacktrace const& backtrace, bool forceOpt = false,
+  bool namelink = false)
+{
+  return CreateInstallTargetGenerator(target, args, impLib, backtrace,
+                                      args.GetDestination(), forceOpt,
+                                      namelink);
+}
+
 static cmInstallFilesGenerator* CreateInstallFilesGenerator(
 static cmInstallFilesGenerator* CreateInstallFilesGenerator(
   cmMakefile* mf, const std::vector<std::string>& absFiles,
   cmMakefile* mf, const std::vector<std::string>& absFiles,
-  const cmInstallCommandArguments& args, bool programs)
+  const cmInstallCommandArguments& args, bool programs,
+  const std::string& destination)
 {
 {
   cmInstallGenerator::MessageLevel message =
   cmInstallGenerator::MessageLevel message =
     cmInstallGenerator::SelectMessageLevel(mf);
     cmInstallGenerator::SelectMessageLevel(mf);
   return new cmInstallFilesGenerator(
   return new cmInstallFilesGenerator(
-    absFiles, args.GetDestination().c_str(), programs,
-    args.GetPermissions().c_str(), args.GetConfigurations(),
-    args.GetComponent().c_str(), message, args.GetExcludeFromAll(),
-    args.GetRename().c_str(), args.GetOptional());
+    absFiles, destination.c_str(), programs, args.GetPermissions().c_str(),
+    args.GetConfigurations(), args.GetComponent().c_str(), message,
+    args.GetExcludeFromAll(), args.GetRename().c_str(), args.GetOptional());
 }
 }
 
 
+static cmInstallFilesGenerator* CreateInstallFilesGenerator(
+  cmMakefile* mf, const std::vector<std::string>& absFiles,
+  const cmInstallCommandArguments& args, bool programs)
+{
+  return CreateInstallFilesGenerator(mf, absFiles, args, programs,
+                                     args.GetDestination());
+}
+
+static const std::set<std::string> allowedTypes{
+  "BIN",         "SBIN",       "LIB",      "INCLUDE", "SYSCONF",
+  "SHAREDSTATE", "LOCALSTATE", "RUNSTATE", "DATA",    "INFO",
+  "LOCALE",      "MAN",        "DOC",
+};
+
 // cmInstallCommand
 // cmInstallCommand
 bool cmInstallCommand::InitialPass(std::vector<std::string> const& args,
 bool cmInstallCommand::InitialPass(std::vector<std::string> const& args,
                                    cmExecutionStatus&)
                                    cmExecutionStatus&)
@@ -335,6 +360,17 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
                    "At most one of these two options may be specified.");
                    "At most one of these two options may be specified.");
     return false;
     return false;
   }
   }
+  if (!genericArgs.GetType().empty() || !archiveArgs.GetType().empty() ||
+      !libraryArgs.GetType().empty() || !runtimeArgs.GetType().empty() ||
+      !objectArgs.GetType().empty() || !frameworkArgs.GetType().empty() ||
+      !bundleArgs.GetType().empty() || !privateHeaderArgs.GetType().empty() ||
+      !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty()) {
+    std::ostringstream e;
+    e << "TARGETS given TYPE option. The TYPE option may only be specified in "
+         " install(FILES) and install(DIRECTORIES).";
+    this->SetError(e.str());
+    return false;
+  }
 
 
   // Select the mode for installing symlinks to versioned shared libraries.
   // Select the mode for installing symlinks to versioned shared libraries.
   cmInstallTargetGenerator::NamelinkModeType namelinkMode =
   cmInstallTargetGenerator::NamelinkModeType namelinkMode =
@@ -447,8 +483,12 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
               target, runtimeArgs, false, this->Makefile->GetBacktrace());
               target, runtimeArgs, false, this->Makefile->GetBacktrace());
           }
           }
           if ((archiveGenerator == nullptr) && (runtimeGenerator == nullptr)) {
           if ((archiveGenerator == nullptr) && (runtimeGenerator == nullptr)) {
-            this->SetError("Library TARGETS given no DESTINATION!");
-            return false;
+            archiveGenerator = CreateInstallTargetGenerator(
+              target, archiveArgs, true, this->Makefile->GetBacktrace(),
+              this->GetArchiveDestination(nullptr));
+            runtimeGenerator = CreateInstallTargetGenerator(
+              target, runtimeArgs, false, this->Makefile->GetBacktrace(),
+              this->GetRuntimeDestination(nullptr));
           }
           }
         } else {
         } else {
           // This is a non-DLL platform.
           // This is a non-DLL platform.
@@ -474,30 +514,22 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
             }
             }
           } else {
           } else {
             // The shared library uses the LIBRARY properties.
             // The shared library uses the LIBRARY properties.
-            if (!libraryArgs.GetDestination().empty()) {
-              if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
-                libraryGenerator = CreateInstallTargetGenerator(
-                  target, libraryArgs, false, this->Makefile->GetBacktrace());
-                libraryGenerator->SetNamelinkMode(
-                  cmInstallTargetGenerator::NamelinkModeSkip);
-              }
-              if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
-                namelinkGenerator = CreateInstallTargetGenerator(
-                  target, libraryArgs, false, this->Makefile->GetBacktrace(),
-                  false, true);
-                namelinkGenerator->SetNamelinkMode(
-                  cmInstallTargetGenerator::NamelinkModeOnly);
-              }
-              namelinkOnly =
-                (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
-            } else {
-              std::ostringstream e;
-              e << "TARGETS given no LIBRARY DESTINATION for shared library "
-                   "target \""
-                << target.GetName() << "\".";
-              this->SetError(e.str());
-              return false;
+            if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
+              libraryGenerator = CreateInstallTargetGenerator(
+                target, libraryArgs, false, this->Makefile->GetBacktrace(),
+                this->GetLibraryDestination(&libraryArgs));
+              libraryGenerator->SetNamelinkMode(
+                cmInstallTargetGenerator::NamelinkModeSkip);
+            }
+            if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
+              namelinkGenerator = CreateInstallTargetGenerator(
+                target, libraryArgs, false, this->Makefile->GetBacktrace(),
+                this->GetLibraryDestination(&libraryArgs), false, true);
+              namelinkGenerator->SetNamelinkMode(
+                cmInstallTargetGenerator::NamelinkModeOnly);
             }
             }
+            namelinkOnly =
+              (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
           }
           }
         }
         }
       } break;
       } break;
@@ -524,17 +556,9 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
           }
           }
         } else {
         } else {
           // Static libraries use ARCHIVE properties.
           // Static libraries use ARCHIVE properties.
-          if (!archiveArgs.GetDestination().empty()) {
-            archiveGenerator = CreateInstallTargetGenerator(
-              target, archiveArgs, false, this->Makefile->GetBacktrace());
-          } else {
-            std::ostringstream e;
-            e << "TARGETS given no ARCHIVE DESTINATION for static library "
-                 "target \""
-              << target.GetName() << "\".";
-            this->SetError(e.str());
-            return false;
-          }
+          archiveGenerator = CreateInstallTargetGenerator(
+            target, archiveArgs, false, this->Makefile->GetBacktrace(),
+            this->GetArchiveDestination(&archiveArgs));
         }
         }
       } break;
       } break;
       case cmStateEnums::MODULE_LIBRARY: {
       case cmStateEnums::MODULE_LIBRARY: {
@@ -602,17 +626,9 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
           }
           }
         } else {
         } else {
           // Executables use the RUNTIME properties.
           // Executables use the RUNTIME properties.
-          if (!runtimeArgs.GetDestination().empty()) {
-            runtimeGenerator = CreateInstallTargetGenerator(
-              target, runtimeArgs, false, this->Makefile->GetBacktrace());
-          } else {
-            std::ostringstream e;
-            e << "TARGETS given no RUNTIME DESTINATION for executable "
-                 "target \""
-              << target.GetName() << "\".";
-            this->SetError(e.str());
-            return false;
-          }
+          runtimeGenerator = CreateInstallTargetGenerator(
+            target, runtimeArgs, false, this->Makefile->GetBacktrace(),
+            this->GetRuntimeDestination(&runtimeArgs));
         }
         }
 
 
         // On DLL platforms an executable may also have an import
         // On DLL platforms an executable may also have an import
@@ -659,15 +675,9 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
         }
         }
 
 
         // Create the files install generator.
         // Create the files install generator.
-        if (!privateHeaderArgs.GetDestination().empty()) {
-          privateHeaderGenerator = CreateInstallFilesGenerator(
-            this->Makefile, absFiles, privateHeaderArgs, false);
-        } else {
-          std::ostringstream e;
-          e << "INSTALL TARGETS - target " << target.GetName() << " has "
-            << "PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION.";
-          cmSystemTools::Message(e.str().c_str(), "Warning");
-        }
+        privateHeaderGenerator = CreateInstallFilesGenerator(
+          this->Makefile, absFiles, privateHeaderArgs, false,
+          this->GetIncludeDestination(&privateHeaderArgs));
       }
       }
 
 
       files = target.GetProperty("PUBLIC_HEADER");
       files = target.GetProperty("PUBLIC_HEADER");
@@ -680,15 +690,9 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
         }
         }
 
 
         // Create the files install generator.
         // Create the files install generator.
-        if (!publicHeaderArgs.GetDestination().empty()) {
-          publicHeaderGenerator = CreateInstallFilesGenerator(
-            this->Makefile, absFiles, publicHeaderArgs, false);
-        } else {
-          std::ostringstream e;
-          e << "INSTALL TARGETS - target " << target.GetName() << " has "
-            << "PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION.";
-          cmSystemTools::Message(e.str().c_str(), "Warning");
-        }
+        publicHeaderGenerator = CreateInstallFilesGenerator(
+          this->Makefile, absFiles, publicHeaderArgs, false,
+          this->GetIncludeDestination(&publicHeaderArgs));
       }
       }
 
 
       files = target.GetProperty("RESOURCE");
       files = target.GetProperty("RESOURCE");
@@ -824,6 +828,14 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args)
     return false;
     return false;
   }
   }
 
 
+  std::string type = ica.GetType();
+  if (!type.empty() && allowedTypes.count(type) == 0) {
+    std::ostringstream e;
+    e << args[0] << " given non-type \"" << type << "\" with TYPE argument.";
+    this->SetError(e.str());
+    return false;
+  }
+
   const std::vector<std::string>& filesVector = files.GetVector();
   const std::vector<std::string>& filesVector = files.GetVector();
 
 
   // Check if there is something to do.
   // Check if there is something to do.
@@ -886,7 +898,17 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args)
     return false;
     return false;
   }
   }
 
 
-  if (ica.GetDestination().empty()) {
+  if (!type.empty() && !ica.GetDestination().empty()) {
+    std::ostringstream e;
+    e << args[0]
+      << " given both TYPE and DESTINATION arguments. You may only specify "
+         "one.";
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string destination = this->GetDestinationForType(&ica, type);
+  if (destination.empty()) {
     // A destination is required.
     // A destination is required.
     std::ostringstream e;
     std::ostringstream e;
     e << args[0] << " given no DESTINATION!";
     e << args[0] << " given no DESTINATION!";
@@ -895,8 +917,8 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args)
   }
   }
 
 
   // Create the files install generator.
   // Create the files install generator.
-  this->Makefile->AddInstallGenerator(
-    CreateInstallFilesGenerator(this->Makefile, absFiles, ica, programs));
+  this->Makefile->AddInstallGenerator(CreateInstallFilesGenerator(
+    this->Makefile, absFiles, ica, programs, destination));
 
 
   // Tell the global generator about any installation component names
   // Tell the global generator about any installation component names
   // specified.
   // specified.
@@ -920,7 +942,8 @@ bool cmInstallCommand::HandleDirectoryMode(
     DoingPermsDir,
     DoingPermsDir,
     DoingPermsMatch,
     DoingPermsMatch,
     DoingConfigurations,
     DoingConfigurations,
-    DoingComponent
+    DoingComponent,
+    DoingType
   };
   };
   Doing doing = DoingDirs;
   Doing doing = DoingDirs;
   bool in_match_mode = false;
   bool in_match_mode = false;
@@ -934,6 +957,7 @@ bool cmInstallCommand::HandleDirectoryMode(
   std::vector<std::string> configurations;
   std::vector<std::string> configurations;
   std::string component = this->DefaultComponentName;
   std::string component = this->DefaultComponentName;
   std::string literal_args;
   std::string literal_args;
+  std::string type;
   for (unsigned int i = 1; i < args.size(); ++i) {
   for (unsigned int i = 1; i < args.size(); ++i) {
     if (args[i] == "DESTINATION") {
     if (args[i] == "DESTINATION") {
       if (in_match_mode) {
       if (in_match_mode) {
@@ -946,6 +970,17 @@ bool cmInstallCommand::HandleDirectoryMode(
 
 
       // Switch to setting the destination property.
       // Switch to setting the destination property.
       doing = DoingDestination;
       doing = DoingDestination;
+    } else if (args[i] == "TYPE") {
+      if (in_match_mode) {
+        std::ostringstream e;
+        e << args[0] << " does not allow \"" << args[i]
+          << "\" after PATTERN or REGEX.";
+        this->SetError(e.str());
+        return false;
+      }
+
+      // Switch to setting the type.
+      doing = DoingType;
     } else if (args[i] == "OPTIONAL") {
     } else if (args[i] == "OPTIONAL") {
       if (in_match_mode) {
       if (in_match_mode) {
         std::ostringstream e;
         std::ostringstream e;
@@ -1106,6 +1141,17 @@ bool cmInstallCommand::HandleDirectoryMode(
     } else if (doing == DoingDestination) {
     } else if (doing == DoingDestination) {
       destination = args[i].c_str();
       destination = args[i].c_str();
       doing = DoingNone;
       doing = DoingNone;
+    } else if (doing == DoingType) {
+      if (allowedTypes.count(args[i]) == 0) {
+        std::ostringstream e;
+        e << args[0] << " given non-type \"" << args[i]
+          << "\" with TYPE argument.";
+        this->SetError(e.str());
+        return false;
+      }
+
+      type = args[i];
+      doing = DoingNone;
     } else if (doing == DoingPattern) {
     } else if (doing == DoingPattern) {
       // Convert the pattern to a regular expression.  Require a
       // Convert the pattern to a regular expression.  Require a
       // leading slash and trailing end-of-string in the matched
       // leading slash and trailing end-of-string in the matched
@@ -1179,10 +1225,22 @@ bool cmInstallCommand::HandleDirectoryMode(
   if (dirs.empty()) {
   if (dirs.empty()) {
     return true;
     return true;
   }
   }
+  std::string destinationStr;
   if (!destination) {
   if (!destination) {
-    // A destination is required.
+    if (type.empty()) {
+      // A destination is required.
+      std::ostringstream e;
+      e << args[0] << " given no DESTINATION!";
+      this->SetError(e.str());
+      return false;
+    }
+    destinationStr = this->GetDestinationForType(nullptr, type);
+    destination = destinationStr.c_str();
+  } else if (!type.empty()) {
     std::ostringstream e;
     std::ostringstream e;
-    e << args[0] << " given no DESTINATION!";
+    e << args[0]
+      << " given both TYPE and DESTINATION arguments. You may only specify "
+         "one.";
     this->SetError(e.str());
     this->SetError(e.str());
     return false;
     return false;
   }
   }
@@ -1456,3 +1514,163 @@ bool cmInstallCommand::CheckCMP0006(bool& failure)
   }
   }
   return false;
   return false;
 }
 }
+
+std::string cmInstallCommand::GetDestination(
+  const cmInstallCommandArguments* args, const std::string& varName,
+  const std::string& guess)
+{
+  if (args && !args->GetDestination().empty()) {
+    return args->GetDestination();
+  }
+  std::string val = this->Makefile->GetSafeDefinition(varName);
+  if (!val.empty()) {
+    return val;
+  }
+  return guess;
+}
+
+std::string cmInstallCommand::GetRuntimeDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_BINDIR", "bin");
+}
+
+std::string cmInstallCommand::GetSbinDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_SBINDIR", "sbin");
+}
+
+std::string cmInstallCommand::GetArchiveDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib");
+}
+
+std::string cmInstallCommand::GetLibraryDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib");
+}
+
+std::string cmInstallCommand::GetIncludeDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_INCLUDEDIR", "include");
+}
+
+std::string cmInstallCommand::GetSysconfDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_SYSCONFDIR", "etc");
+}
+
+std::string cmInstallCommand::GetSharedStateDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_SHAREDSTATEDIR", "com");
+}
+
+std::string cmInstallCommand::GetLocalStateDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_LOCALSTATEDIR", "var");
+}
+
+std::string cmInstallCommand::GetRunStateDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_RUNSTATEDIR",
+                              this->GetLocalStateDestination(nullptr) +
+                                "/run");
+}
+
+std::string cmInstallCommand::GetDataRootDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_DATAROOTDIR", "share");
+}
+
+std::string cmInstallCommand::GetDataDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_DATADIR",
+                              this->GetDataRootDestination(nullptr));
+}
+
+std::string cmInstallCommand::GetInfoDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_INFODIR",
+                              this->GetDataRootDestination(nullptr) + "/info");
+}
+
+std::string cmInstallCommand::GetLocaleDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_LOCALEDIR",
+                              this->GetDataRootDestination(nullptr) +
+                                "/locale");
+}
+
+std::string cmInstallCommand::GetManDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_MANDIR",
+                              this->GetDataRootDestination(nullptr) + "/man");
+}
+
+std::string cmInstallCommand::GetDocDestination(
+  const cmInstallCommandArguments* args)
+{
+  return this->GetDestination(args, "CMAKE_INSTALL_DOCDIR",
+                              this->GetDataRootDestination(nullptr) + "/doc");
+}
+
+std::string cmInstallCommand::GetDestinationForType(
+  const cmInstallCommandArguments* args, const std::string& type)
+{
+  if (args && !args->GetDestination().empty()) {
+    return args->GetDestination();
+  }
+  if (type == "BIN") {
+    return this->GetRuntimeDestination(nullptr);
+  }
+  if (type == "SBIN") {
+    return this->GetSbinDestination(nullptr);
+  }
+  if (type == "SYSCONF") {
+    return this->GetSysconfDestination(nullptr);
+  }
+  if (type == "SHAREDSTATE") {
+    return this->GetSharedStateDestination(nullptr);
+  }
+  if (type == "LOCALSTATE") {
+    return this->GetLocalStateDestination(nullptr);
+  }
+  if (type == "RUNSTATE") {
+    return this->GetRunStateDestination(nullptr);
+  }
+  if (type == "LIB") {
+    return this->GetLibraryDestination(nullptr);
+  }
+  if (type == "INCLUDE") {
+    return this->GetIncludeDestination(nullptr);
+  }
+  if (type == "DATA") {
+    return this->GetDataDestination(nullptr);
+  }
+  if (type == "INFO") {
+    return this->GetInfoDestination(nullptr);
+  }
+  if (type == "LOCALE") {
+    return this->GetLocaleDestination(nullptr);
+  }
+  if (type == "MAN") {
+    return this->GetManDestination(nullptr);
+  }
+  if (type == "DOC") {
+    return this->GetDocDestination(nullptr);
+  }
+  return "";
+}

+ 22 - 0
Source/cmInstallCommand.h

@@ -11,6 +11,7 @@
 #include "cmCommand.h"
 #include "cmCommand.h"
 
 
 class cmExecutionStatus;
 class cmExecutionStatus;
+class cmInstallCommandArguments;
 
 
 /** \class cmInstallCommand
 /** \class cmInstallCommand
  * \brief Specifies where to install some files
  * \brief Specifies where to install some files
@@ -45,6 +46,27 @@ private:
                          std::vector<std::string>& absFiles);
                          std::vector<std::string>& absFiles);
   bool CheckCMP0006(bool& failure);
   bool CheckCMP0006(bool& failure);
 
 
+  std::string GetDestination(const cmInstallCommandArguments* args,
+                             const std::string& varName,
+                             const std::string& guess);
+  std::string GetRuntimeDestination(const cmInstallCommandArguments* args);
+  std::string GetSbinDestination(const cmInstallCommandArguments* args);
+  std::string GetArchiveDestination(const cmInstallCommandArguments* args);
+  std::string GetLibraryDestination(const cmInstallCommandArguments* args);
+  std::string GetIncludeDestination(const cmInstallCommandArguments* args);
+  std::string GetSysconfDestination(const cmInstallCommandArguments* args);
+  std::string GetSharedStateDestination(const cmInstallCommandArguments* args);
+  std::string GetLocalStateDestination(const cmInstallCommandArguments* args);
+  std::string GetRunStateDestination(const cmInstallCommandArguments* args);
+  std::string GetDataRootDestination(const cmInstallCommandArguments* args);
+  std::string GetDataDestination(const cmInstallCommandArguments* args);
+  std::string GetInfoDestination(const cmInstallCommandArguments* args);
+  std::string GetLocaleDestination(const cmInstallCommandArguments* args);
+  std::string GetManDestination(const cmInstallCommandArguments* args);
+  std::string GetDocDestination(const cmInstallCommandArguments* args);
+  std::string GetDestinationForType(const cmInstallCommandArguments* args,
+                                    const std::string& type);
+
   std::string DefaultComponentName;
   std::string DefaultComponentName;
 };
 };
 
 

+ 6 - 0
Source/cmInstallCommandArguments.cxx

@@ -29,6 +29,7 @@ cmInstallCommandArguments::cmInstallCommandArguments(
   , Optional(&Parser, "OPTIONAL", &ArgumentGroup)
   , Optional(&Parser, "OPTIONAL", &ArgumentGroup)
   , NamelinkOnly(&Parser, "NAMELINK_ONLY", &ArgumentGroup)
   , NamelinkOnly(&Parser, "NAMELINK_ONLY", &ArgumentGroup)
   , NamelinkSkip(&Parser, "NAMELINK_SKIP", &ArgumentGroup)
   , NamelinkSkip(&Parser, "NAMELINK_SKIP", &ArgumentGroup)
+  , Type(&Parser, "TYPE", &ArgumentGroup)
   , GenericArguments(nullptr)
   , GenericArguments(nullptr)
   , DefaultComponentName(defaultComponent)
   , DefaultComponentName(defaultComponent)
 {
 {
@@ -145,6 +146,11 @@ bool cmInstallCommandArguments::HasNamelinkComponent() const
   return false;
   return false;
 }
 }
 
 
+const std::string& cmInstallCommandArguments::GetType() const
+{
+  return this->Type.GetString();
+}
+
 const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations()
 const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations()
   const
   const
 {
 {

+ 2 - 0
Source/cmInstallCommandArguments.h

@@ -35,6 +35,7 @@ public:
   bool GetNamelinkOnly() const;
   bool GetNamelinkOnly() const;
   bool GetNamelinkSkip() const;
   bool GetNamelinkSkip() const;
   bool HasNamelinkComponent() const;
   bool HasNamelinkComponent() const;
+  const std::string& GetType() const;
 
 
   // once HandleDirectoryMode() is also switched to using
   // once HandleDirectoryMode() is also switched to using
   // cmInstallCommandArguments then these two functions can become non-static
   // cmInstallCommandArguments then these two functions can become non-static
@@ -55,6 +56,7 @@ private:
   cmCAEnabler Optional;
   cmCAEnabler Optional;
   cmCAEnabler NamelinkOnly;
   cmCAEnabler NamelinkOnly;
   cmCAEnabler NamelinkSkip;
   cmCAEnabler NamelinkSkip;
+  cmCAString Type;
 
 
   std::string DestinationString;
   std::string DestinationString;
   std::string PermissionsString;
   std::string PermissionsString;

+ 1 - 0
Tests/RunCMake/install/DIRECTORY-DESTINATION-TYPE-result.txt

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

+ 5 - 0
Tests/RunCMake/install/DIRECTORY-DESTINATION-TYPE-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at DIRECTORY-DESTINATION-TYPE\.cmake:[0-9]+ \(install\):
+  install DIRECTORY given both TYPE and DESTINATION arguments\.  You may only
+  specify one\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 1 - 0
Tests/RunCMake/install/DIRECTORY-DESTINATION-TYPE.cmake

@@ -0,0 +1 @@
+install(DIRECTORY dir TYPE BIN DESTINATION mybin)

+ 42 - 0
Tests/RunCMake/install/DIRECTORY-TYPE-Cache-all-check.cmake

@@ -0,0 +1,42 @@
+set(_check_files
+  [[mybin]]
+  [[mybin/dir]]
+  [[mybin/dir/empty\.txt]]
+  [[mycom]]
+  [[mycom/dir]]
+  [[mycom/dir/empty\.txt]]
+  [[mydoc]]
+  [[mydoc/dir]]
+  [[mydoc/dir/empty\.txt]]
+  [[myetc]]
+  [[myetc/dir]]
+  [[myetc/dir/empty\.txt]]
+  [[myinclude]]
+  [[myinclude/dir]]
+  [[myinclude/dir/empty\.txt]]
+  [[myinfo]]
+  [[myinfo/dir]]
+  [[myinfo/dir/empty\.txt]]
+  [[mylib]]
+  [[mylib/dir]]
+  [[mylib/dir/empty\.txt]]
+  [[mylocale]]
+  [[mylocale/dir]]
+  [[mylocale/dir/empty\.txt]]
+  [[myman]]
+  [[myman/dir]]
+  [[myman/dir/empty\.txt]]
+  [[myrun]]
+  [[myrun/dir]]
+  [[myrun/dir/empty\.txt]]
+  [[mysbin]]
+  [[mysbin/dir]]
+  [[mysbin/dir/empty\.txt]]
+  [[myshare]]
+  [[myshare/dir]]
+  [[myshare/dir/empty\.txt]]
+  [[myvar]]
+  [[myvar/dir]]
+  [[myvar/dir/empty\.txt]]
+  )
+check_installed("^${_check_files}$")

+ 13 - 0
Tests/RunCMake/install/DIRECTORY-TYPE-Cache.cmake

@@ -0,0 +1,13 @@
+install(DIRECTORY dir TYPE BIN)
+install(DIRECTORY dir TYPE SBIN)
+install(DIRECTORY dir TYPE LIB)
+install(DIRECTORY dir TYPE INCLUDE)
+install(DIRECTORY dir TYPE SYSCONF)
+install(DIRECTORY dir TYPE SHAREDSTATE)
+install(DIRECTORY dir TYPE LOCALSTATE)
+install(DIRECTORY dir TYPE RUNSTATE)
+install(DIRECTORY dir TYPE DATA)
+install(DIRECTORY dir TYPE INFO)
+install(DIRECTORY dir TYPE LOCALE)
+install(DIRECTORY dir TYPE MAN)
+install(DIRECTORY dir TYPE DOC)

+ 24 - 0
Tests/RunCMake/install/DIRECTORY-TYPE-CacheDependent-all-check.cmake

@@ -0,0 +1,24 @@
+set(_check_files
+  [[myshare]]
+  [[myshare/dir]]
+  [[myshare/dir/empty\.txt]]
+  [[myshare/doc]]
+  [[myshare/doc/dir]]
+  [[myshare/doc/dir/empty\.txt]]
+  [[myshare/info]]
+  [[myshare/info/dir]]
+  [[myshare/info/dir/empty\.txt]]
+  [[myshare/locale]]
+  [[myshare/locale/dir]]
+  [[myshare/locale/dir/empty\.txt]]
+  [[myshare/man]]
+  [[myshare/man/dir]]
+  [[myshare/man/dir/empty.txt]]
+  [[myvar]]
+  [[myvar/dir]]
+  [[myvar/dir/empty\.txt]]
+  [[myvar/run]]
+  [[myvar/run/dir]]
+  [[myvar/run/dir/empty\.txt]]
+  )
+check_installed("^${_check_files}$")

+ 7 - 0
Tests/RunCMake/install/DIRECTORY-TYPE-CacheDependent.cmake

@@ -0,0 +1,7 @@
+install(DIRECTORY dir TYPE LOCALSTATE)
+install(DIRECTORY dir TYPE RUNSTATE)
+install(DIRECTORY dir TYPE DATA)
+install(DIRECTORY dir TYPE INFO)
+install(DIRECTORY dir TYPE LOCALE)
+install(DIRECTORY dir TYPE MAN)
+install(DIRECTORY dir TYPE DOC)

+ 42 - 0
Tests/RunCMake/install/DIRECTORY-TYPE-all-check.cmake

@@ -0,0 +1,42 @@
+set(_check_files
+  [[bin]]
+  [[bin/dir]]
+  [[bin/dir/empty\.txt]]
+  [[com]]
+  [[com/dir]]
+  [[com/dir/empty\.txt]]
+  [[etc]]
+  [[etc/dir]]
+  [[etc/dir/empty\.txt]]
+  [[include]]
+  [[include/dir]]
+  [[include/dir/empty\.txt]]
+  [[lib]]
+  [[lib/dir]]
+  [[lib/dir/empty\.txt]]
+  [[sbin]]
+  [[sbin/dir]]
+  [[sbin/dir/empty\.txt]]
+  [[share]]
+  [[share/dir]]
+  [[share/dir/empty\.txt]]
+  [[share/doc]]
+  [[share/doc/dir]]
+  [[share/doc/dir/empty\.txt]]
+  [[share/info]]
+  [[share/info/dir]]
+  [[share/info/dir/empty\.txt]]
+  [[share/locale]]
+  [[share/locale/dir]]
+  [[share/locale/dir/empty\.txt]]
+  [[share/man]]
+  [[share/man/dir]]
+  [[share/man/dir/empty\.txt]]
+  [[var]]
+  [[var/dir]]
+  [[var/dir/empty\.txt]]
+  [[var/run]]
+  [[var/run/dir]]
+  [[var/run/dir/empty\.txt]]
+  )
+check_installed("^${_check_files}$")

+ 13 - 0
Tests/RunCMake/install/DIRECTORY-TYPE.cmake

@@ -0,0 +1,13 @@
+install(DIRECTORY dir TYPE BIN)
+install(DIRECTORY dir TYPE SBIN)
+install(DIRECTORY dir TYPE LIB)
+install(DIRECTORY dir TYPE INCLUDE)
+install(DIRECTORY dir TYPE SYSCONF)
+install(DIRECTORY dir TYPE SHAREDSTATE)
+install(DIRECTORY dir TYPE LOCALSTATE)
+install(DIRECTORY dir TYPE RUNSTATE)
+install(DIRECTORY dir TYPE DATA)
+install(DIRECTORY dir TYPE INFO)
+install(DIRECTORY dir TYPE LOCALE)
+install(DIRECTORY dir TYPE MAN)
+install(DIRECTORY dir TYPE DOC)

+ 1 - 0
Tests/RunCMake/install/FILES-DESTINATION-TYPE-result.txt

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

+ 5 - 0
Tests/RunCMake/install/FILES-DESTINATION-TYPE-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at FILES-DESTINATION-TYPE\.cmake:[0-9]+ \(install\):
+  install FILES given both TYPE and DESTINATION arguments\.  You may only
+  specify one\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 1 - 0
Tests/RunCMake/install/FILES-DESTINATION-TYPE.cmake

@@ -0,0 +1 @@
+install(FILES main.c TYPE BIN DESTINATION mybin)

+ 29 - 0
Tests/RunCMake/install/FILES-TYPE-Cache-all-check.cmake

@@ -0,0 +1,29 @@
+set(_check_files
+  [[mybin]]
+  [[mybin/main\.c]]
+  [[mycom]]
+  [[mycom/main\.c]]
+  [[mydoc]]
+  [[mydoc/main\.c]]
+  [[myetc]]
+  [[myetc/main\.c]]
+  [[myinclude]]
+  [[myinclude/main\.c]]
+  [[myinfo]]
+  [[myinfo/main\.c]]
+  [[mylib]]
+  [[mylib/main\.c]]
+  [[mylocale]]
+  [[mylocale/main\.c]]
+  [[myman]]
+  [[myman/main\.c]]
+  [[myrun]]
+  [[myrun/main\.c]]
+  [[mysbin]]
+  [[mysbin/main\.c]]
+  [[myshare]]
+  [[myshare/main\.c]]
+  [[myvar]]
+  [[myvar/main\.c]]
+  )
+check_installed("^${_check_files}$")

+ 13 - 0
Tests/RunCMake/install/FILES-TYPE-Cache.cmake

@@ -0,0 +1,13 @@
+install(FILES main.c TYPE BIN)
+install(FILES main.c TYPE SBIN)
+install(FILES main.c TYPE LIB)
+install(FILES main.c TYPE INCLUDE)
+install(FILES main.c TYPE SYSCONF)
+install(FILES main.c TYPE SHAREDSTATE)
+install(FILES main.c TYPE LOCALSTATE)
+install(FILES main.c TYPE RUNSTATE)
+install(FILES main.c TYPE DATA)
+install(FILES main.c TYPE INFO)
+install(FILES main.c TYPE LOCALE)
+install(FILES main.c TYPE MAN)
+install(FILES main.c TYPE DOC)

+ 17 - 0
Tests/RunCMake/install/FILES-TYPE-CacheDependent-all-check.cmake

@@ -0,0 +1,17 @@
+set(_check_files
+  [[myshare]]
+  [[myshare/doc]]
+  [[myshare/doc/main\.c]]
+  [[myshare/info]]
+  [[myshare/info/main\.c]]
+  [[myshare/locale]]
+  [[myshare/locale/main\.c]]
+  [[myshare/main\.c]]
+  [[myshare/man]]
+  [[myshare/man/main\.c]]
+  [[myvar]]
+  [[myvar/main\.c]]
+  [[myvar/run]]
+  [[myvar/run/main\.c]]
+  )
+check_installed("^${_check_files}$")

+ 7 - 0
Tests/RunCMake/install/FILES-TYPE-CacheDependent.cmake

@@ -0,0 +1,7 @@
+install(FILES main.c TYPE LOCALSTATE)
+install(FILES main.c TYPE RUNSTATE)
+install(FILES main.c TYPE DATA)
+install(FILES main.c TYPE INFO)
+install(FILES main.c TYPE LOCALE)
+install(FILES main.c TYPE MAN)
+install(FILES main.c TYPE DOC)

+ 29 - 0
Tests/RunCMake/install/FILES-TYPE-all-check.cmake

@@ -0,0 +1,29 @@
+set(_check_files
+  [[bin]]
+  [[bin/main\.c]]
+  [[com]]
+  [[com/main\.c]]
+  [[etc]]
+  [[etc/main\.c]]
+  [[include]]
+  [[include/main\.c]]
+  [[lib]]
+  [[lib/main\.c]]
+  [[sbin]]
+  [[sbin/main\.c]]
+  [[share]]
+  [[share/doc]]
+  [[share/doc/main\.c]]
+  [[share/info]]
+  [[share/info/main\.c]]
+  [[share/locale]]
+  [[share/locale/main\.c]]
+  [[share/main\.c]]
+  [[share/man]]
+  [[share/man/main\.c]]
+  [[var]]
+  [[var/main\.c]]
+  [[var/run]]
+  [[var/run/main\.c]]
+  )
+check_installed("^${_check_files}$")

+ 13 - 0
Tests/RunCMake/install/FILES-TYPE.cmake

@@ -0,0 +1,13 @@
+install(FILES main.c TYPE BIN)
+install(FILES main.c TYPE SBIN)
+install(FILES main.c TYPE LIB)
+install(FILES main.c TYPE INCLUDE)
+install(FILES main.c TYPE SYSCONF)
+install(FILES main.c TYPE SHAREDSTATE)
+install(FILES main.c TYPE LOCALSTATE)
+install(FILES main.c TYPE RUNSTATE)
+install(FILES main.c TYPE DATA)
+install(FILES main.c TYPE INFO)
+install(FILES main.c TYPE LOCALE)
+install(FILES main.c TYPE MAN)
+install(FILES main.c TYPE DOC)

+ 46 - 0
Tests/RunCMake/install/RunCMakeTest.cmake

@@ -65,6 +65,12 @@ run_cmake(CMP0062-NEW)
 run_cmake(CMP0062-WARN)
 run_cmake(CMP0062-WARN)
 run_cmake(TARGETS-NAMELINK_COMPONENT-bad-all)
 run_cmake(TARGETS-NAMELINK_COMPONENT-bad-all)
 run_cmake(TARGETS-NAMELINK_COMPONENT-bad-exc)
 run_cmake(TARGETS-NAMELINK_COMPONENT-bad-exc)
+run_cmake(FILES-DESTINATION-TYPE)
+run_cmake(DIRECTORY-DESTINATION-TYPE)
+
+if(APPLE)
+  run_cmake(TARGETS-Apple-Defaults)
+endif()
 
 
 if(NOT RunCMake_GENERATOR STREQUAL "Xcode" OR NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
 if(NOT RunCMake_GENERATOR STREQUAL "Xcode" OR NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
   run_install_test(FILES-TARGET_OBJECTS)
   run_install_test(FILES-TARGET_OBJECTS)
@@ -74,6 +80,46 @@ run_install_test(TARGETS-InstallFromSubDir)
 run_install_test(TARGETS-OPTIONAL)
 run_install_test(TARGETS-OPTIONAL)
 run_install_test(FILES-OPTIONAL)
 run_install_test(FILES-OPTIONAL)
 run_install_test(DIRECTORY-OPTIONAL)
 run_install_test(DIRECTORY-OPTIONAL)
+run_install_test(TARGETS-Defaults)
+
+set(RunCMake_TEST_OPTIONS
+  "-DCMAKE_INSTALL_BINDIR:PATH=mybin"
+  "-DCMAKE_INSTALL_LIBDIR:PATH=mylib"
+  "-DCMAKE_INSTALL_INCLUDEDIR:PATH=myinclude"
+  )
+run_install_test(TARGETS-Defaults-Cache)
+unset(RunCMake_TEST_OPTIONS)
+
+run_install_test(FILES-TYPE)
+run_install_test(DIRECTORY-TYPE)
+
+set(RunCMake_TEST_OPTIONS
+  "-DCMAKE_INSTALL_BINDIR:PATH=mybin"
+  "-DCMAKE_INSTALL_SBINDIR:PATH=mysbin"
+  "-DCMAKE_INSTALL_LIBEXECDIR:PATH=mylibexec"
+  "-DCMAKE_INSTALL_LIBDIR:PATH=mylib"
+  "-DCMAKE_INSTALL_INCLUDEDIR:PATH=myinclude"
+  "-DCMAKE_INSTALL_SYSCONFDIR:PATH=myetc"
+  "-DCMAKE_INSTALL_SHAREDSTATEDIR:PATH=mycom"
+  "-DCMAKE_INSTALL_LOCALSTATEDIR:PATH=myvar"
+  "-DCMAKE_INSTALL_RUNSTATEDIR:PATH=myrun"
+  "-DCMAKE_INSTALL_DATADIR:PATH=myshare"
+  "-DCMAKE_INSTALL_INFODIR:PATH=myinfo"
+  "-DCMAKE_INSTALL_LOCALEDIR:PATH=mylocale"
+  "-DCMAKE_INSTALL_MANDIR:PATH=myman"
+  "-DCMAKE_INSTALL_DOCDIR:PATH=mydoc"
+  )
+run_install_test(FILES-TYPE-Cache)
+run_install_test(DIRECTORY-TYPE-Cache)
+unset(RunCMake_TEST_OPTIONS)
+
+set(RunCMake_TEST_OPTIONS
+  "-DCMAKE_INSTALL_LOCALSTATEDIR:PATH=myvar"
+  "-DCMAKE_INSTALL_DATAROOTDIR:PATH=myshare"
+  )
+run_install_test(FILES-TYPE-CacheDependent)
+run_install_test(DIRECTORY-TYPE-CacheDependent)
+unset(RunCMake_TEST_OPTIONS)
 
 
 set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug")
 set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug")
 run_install_test(TARGETS-OUTPUT_NAME)
 run_install_test(TARGETS-OUTPUT_NAME)

+ 1 - 0
Tests/RunCMake/install/TARGETS-Apple-Defaults-result.txt

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

+ 12 - 0
Tests/RunCMake/install/TARGETS-Apple-Defaults-stderr.txt

@@ -0,0 +1,12 @@
+^CMake Error at TARGETS-Apple-Defaults\.cmake:[0-9]+ \(install\):
+  install TARGETS given no BUNDLE DESTINATION for MACOSX_BUNDLE executable
+  target "exe"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+
+
+CMake Error at TARGETS-Apple-Defaults\.cmake:[0-9]+ \(install\):
+  install TARGETS given no FRAMEWORK DESTINATION for shared library FRAMEWORK
+  target "lib1"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 8 - 0
Tests/RunCMake/install/TARGETS-Apple-Defaults.cmake

@@ -0,0 +1,8 @@
+enable_language(C)
+
+add_executable(exe MACOSX_BUNDLE main.c)
+add_library(lib1 SHARED obj1.c)
+set_property(TARGET lib1 PROPERTY FRAMEWORK ON)
+
+install(TARGETS exe)
+install(TARGETS lib1)

+ 49 - 0
Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake

@@ -0,0 +1,49 @@
+if(WIN32)
+  set(_check_files
+    [[lib3]]
+    [[lib3/(lib)?lib3\.(dll\.a|lib)]]
+    [[lib4]]
+    [[lib4/(lib)?lib4\.dll]]
+    [[mybin]]
+    [[mybin/exe\.exe]]
+    [[mybin/(lib)?lib1\.dll]]
+    [[myinclude]]
+    [[myinclude/obj4\.h]]
+    [[myinclude/obj5\.h]]
+    [[mylib]]
+    [[mylib/(lib)?lib1\.(dll\.a|lib)]]
+    [[mylib/(lib)?lib2\.(a|lib)]]
+    )
+elseif(CYGWIN)
+  set(_check_files
+    [[lib3]]
+    [[lib3/liblib3\.dll\.a]]
+    [[lib4]]
+    [[lib4/cyglib4\.dll]]
+    [[mybin]]
+    [[mybin/exe\.exe]]
+    [[mybin/cyglib1\.dll]]
+    [[myinclude]]
+    [[myinclude/obj4\.h]]
+    [[myinclude/obj5\.h]]
+    [[mylib]]
+    [[mylib/liblib1\.dll\.a]]
+    [[mylib/liblib2\.a]]
+    )
+else()
+  set(_check_files
+    [[lib3]]
+    [[lib3/liblib3\.(dylib|so)]]
+    [[lib4]]
+    [[lib4/liblib4\.(dylib|so)]]
+    [[mybin]]
+    [[mybin/exe]]
+    [[myinclude]]
+    [[myinclude/obj4\.h]]
+    [[myinclude/obj5\.h]]
+    [[mylib]]
+    [[mylib/liblib1\.(dylib|so)]]
+    [[mylib/liblib2\.a]]
+    )
+endif()
+check_installed("^${_check_files}$")

+ 19 - 0
Tests/RunCMake/install/TARGETS-Defaults-Cache.cmake

@@ -0,0 +1,19 @@
+enable_language(C)
+
+add_executable(exe main.c)
+add_library(lib1 SHARED obj1.c)
+add_library(lib2 STATIC obj3.c)
+add_library(lib3 SHARED obj4.c)
+set_property(TARGET lib3 PROPERTY PRIVATE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj4.h)
+add_library(lib4 SHARED obj5.c)
+set_property(TARGET lib4 PROPERTY PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj5.h)
+
+install(TARGETS exe lib1 lib2)
+install(TARGETS lib3
+  LIBRARY DESTINATION lib3
+  ARCHIVE DESTINATION lib3
+  )
+install(TARGETS lib4
+  LIBRARY DESTINATION lib4
+  RUNTIME DESTINATION lib4
+  )

+ 49 - 0
Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake

@@ -0,0 +1,49 @@
+if(WIN32)
+  set(_check_files
+    [[bin]]
+    [[bin/exe\.exe]]
+    [[bin/(lib)?lib1\.dll]]
+    [[include]]
+    [[include/obj4\.h]]
+    [[include/obj5\.h]]
+    [[lib]]
+    [[lib/(lib)?lib1\.(dll\.a|lib)]]
+    [[lib/(lib)?lib2\.(a|lib)]]
+    [[lib3]]
+    [[lib3/(lib)?lib3\.(dll\.a|lib)]]
+    [[lib4]]
+    [[lib4/(lib)?lib4\.dll]]
+    )
+elseif(CYGWIN)
+  set(_check_files
+    [[bin]]
+    [[bin/exe\.exe]]
+    [[bin/cyglib1\.dll]]
+    [[include]]
+    [[include/obj4\.h]]
+    [[include/obj5\.h]]
+    [[lib]]
+    [[lib/liblib1\.dll\.a]]
+    [[lib/liblib2\.a]]
+    [[lib3]]
+    [[lib3/liblib3\.dll\.a]]
+    [[lib4]]
+    [[lib4/cyglib4\.dll]]
+    )
+else()
+  set(_check_files
+    [[bin]]
+    [[bin/exe]]
+    [[include]]
+    [[include/obj4\.h]]
+    [[include/obj5\.h]]
+    [[lib]]
+    [[lib/liblib1\.(dylib|so)]]
+    [[lib/liblib2\.a]]
+    [[lib3]]
+    [[lib3/liblib3\.(dylib|so)]]
+    [[lib4]]
+    [[lib4/liblib4\.(dylib|so)]]
+    )
+endif()
+check_installed("^${_check_files}$")

+ 19 - 0
Tests/RunCMake/install/TARGETS-Defaults.cmake

@@ -0,0 +1,19 @@
+enable_language(C)
+
+add_executable(exe main.c)
+add_library(lib1 SHARED obj1.c)
+add_library(lib2 STATIC obj3.c)
+add_library(lib3 SHARED obj4.c)
+set_property(TARGET lib3 PROPERTY PRIVATE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj4.h)
+add_library(lib4 SHARED obj5.c)
+set_property(TARGET lib4 PROPERTY PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj5.h)
+
+install(TARGETS exe lib1 lib2)
+install(TARGETS lib3
+  LIBRARY DESTINATION lib3
+  ARCHIVE DESTINATION lib3
+  )
+install(TARGETS lib4
+  LIBRARY DESTINATION lib4
+  RUNTIME DESTINATION lib4
+  )

+ 7 - 0
Tests/RunCMake/install/obj3.c

@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  int obj3(void)
+{
+  return 0;
+}

+ 6 - 0
Tests/RunCMake/install/obj3.h

@@ -0,0 +1,6 @@
+#ifndef OBJ3_H
+#define OBJ3_H
+
+int obj3(void);
+
+#endif /* OBJ3_H */

+ 7 - 0
Tests/RunCMake/install/obj4.c

@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  int obj4(void)
+{
+  return 0;
+}

+ 6 - 0
Tests/RunCMake/install/obj4.h

@@ -0,0 +1,6 @@
+#ifndef OBJ4_H
+#define OBJ4_H
+
+int obj4(void);
+
+#endif /* OBJ4_H */

+ 7 - 0
Tests/RunCMake/install/obj5.c

@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  int obj5(void)
+{
+  return 0;
+}

+ 6 - 0
Tests/RunCMake/install/obj5.h

@@ -0,0 +1,6 @@
+#ifndef OBJ5_H
+#define OBJ5_H
+
+int obj5(void);
+
+#endif /* OBJ5_H */