Browse Source

install: Allow generator expressions in TARGETS DESTINATION (#14317)

This will allow per-config destinations for targets in EXPORT sets.
Using multiple install(TARGETS) with separate CONFIGURATIONS is
rejected as a target appearing more than once in an export set.
Now instead one can write

 install(TARGETS foo EXPORT exp DESTINATION lib/$<CONFIG>)

to get a single logical membership of the target in the export set
while still having a per-config destination.
Brad King 10 years ago
parent
commit
f30022eb07

+ 4 - 0
Help/command/install.rst

@@ -159,6 +159,10 @@ file itself, call ``install(EXPORT)``, documented below.
 Installing a target with the :prop_tgt:`EXCLUDE_FROM_ALL` target property
 set to ``TRUE`` has undefined behavior.
 
+The install destination given to the target install ``DESTINATION`` may
+use "generator expressions" with the syntax ``$<...>``.  See the
+:manual:`cmake-generator-expressions(7)` manual for available expressions.
+
 Installing Files
 ^^^^^^^^^^^^^^^^
 

+ 5 - 0
Help/release/dev/install-DESTINATION-genex.rst

@@ -0,0 +1,5 @@
+install-DESTINATION-genex
+-------------------------
+
+* The :command:`install(TARGETS)` command learned to support
+  generator expressions in the ``DESTINATION`` value.

+ 1 - 1
Source/cmExportInstallFileGenerator.cxx

@@ -398,7 +398,7 @@ cmExportInstallFileGenerator
   cmTarget* target = itgen->GetTarget();
 
   // Construct the installed location of the target.
-  std::string dest = itgen->GetDestination();
+  std::string dest = itgen->GetDestination(config);
   std::string value;
   if(!cmSystemTools::FileIsFullPath(dest.c_str()))
     {

+ 12 - 2
Source/cmInstallTargetGenerator.cxx

@@ -12,6 +12,7 @@
 #include "cmInstallTargetGenerator.h"
 
 #include "cmComputeLinkInformation.h"
+#include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
@@ -78,7 +79,7 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os,
     fromDirConfig += "/";
     }
   std::string toDir =
-    this->ConvertToAbsoluteDestination(this->Destination);
+    this->ConvertToAbsoluteDestination(this->GetDestination(config));
   toDir += "/";
 
   // Compute the list of files to install for this target.
@@ -323,7 +324,7 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os,
   const char* no_dir_permissions = 0;
   const char* no_rename = 0;
   bool optional = this->Optional || this->ImportLibrary;
-  this->AddInstallRule(os, this->Destination,
+  this->AddInstallRule(os, this->GetDestination(config),
                        type, filesFrom, optional,
                        this->FilePermissions.c_str(), no_dir_permissions,
                        no_rename, literal_args.c_str(),
@@ -334,6 +335,15 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os,
                  &cmInstallTargetGenerator::PostReplacementTweaks);
 }
 
+//----------------------------------------------------------------------------
+std::string
+cmInstallTargetGenerator::GetDestination(std::string const& config) const
+{
+  cmGeneratorExpression ge;
+  return ge.Parse(this->Destination)
+    ->Evaluate(this->Target->GetMakefile(), config);
+}
+
 //----------------------------------------------------------------------------
 std::string
 cmInstallTargetGenerator::GetInstallFilename(const std::string& config) const

+ 1 - 2
Source/cmInstallTargetGenerator.h

@@ -60,8 +60,7 @@ public:
   cmTarget* GetTarget() const { return this->Target; }
   bool IsImportLibrary() const { return this->ImportLibrary; }
 
-  std::string const& GetDestination() const
-    { return this->Destination; }
+  std::string GetDestination(std::string const& config) const;
 
 protected:
   virtual void GenerateScript(std::ostream& os);

+ 9 - 3
Tests/ExportImport/Export/CMakeLists.txt

@@ -68,6 +68,11 @@ add_library(testLib5 SHARED testLib5.c)
 
 add_library(testLib6 STATIC testLib6.cxx testLib6c.c)
 
+add_library(testLibPerConfigDest STATIC testLibPerConfigDest.c)
+install(TARGETS testLibPerConfigDest EXPORT exp
+  DESTINATION lib/$<$<BOOL:$<CONFIG>>:$<CONFIG>>$<$<NOT:$<BOOL:$<CONFIG>>>:NoConfig>
+  )
+
 # Work-around: Visual Studio 6 does not support per-target object files.
 set(VS6)
 if("${CMAKE_GENERATOR}" MATCHES "Visual Studio 6")
@@ -446,9 +451,9 @@ install(
   cmp0022NEW cmp0022OLD
   systemlib
   EXPORT exp
-  RUNTIME DESTINATION bin
-  LIBRARY DESTINATION lib NAMELINK_SKIP
-  ARCHIVE DESTINATION lib
+  RUNTIME DESTINATION $<1:bin>
+  LIBRARY DESTINATION $<1:lib> NAMELINK_SKIP
+  ARCHIVE DESTINATION $<1:lib>
   FRAMEWORK DESTINATION Frameworks
   BUNDLE DESTINATION Applications
   )
@@ -503,6 +508,7 @@ export(TARGETS testExe1 testLib1 testLib2 testLib3
 export(TARGETS testExe2 testLib4 testLib5 testLib6 testExe3 testExe2lib
   testLib4lib testLib4libdbg testLib4libopt
   testLibCycleA testLibCycleB
+  testLibPerConfigDest
   NAMESPACE bld_
   APPEND FILE ExportBuildTree.cmake
   )

+ 1 - 0
Tests/ExportImport/Export/testLibPerConfigDest.c

@@ -0,0 +1 @@
+int testLibPerConfigDest(void) { return 0; }

+ 2 - 0
Tests/ExportImport/Import/A/CMakeLists.txt

@@ -34,6 +34,7 @@ target_link_libraries(imp_testExe1
   exp_testLib5
   exp_testLib6
   exp_testLibCycleA
+  exp_testLibPerConfigDest
   )
 
 # Try building a plugin to an executable imported from the install tree.
@@ -66,6 +67,7 @@ target_link_libraries(imp_testExe1b
   bld_testLib5
   bld_testLib6
   bld_testLibCycleA
+  bld_testLibPerConfigDest
   )
 
 add_custom_target(check_testLib1_genex ALL

+ 2 - 0
Tests/ExportImport/Import/A/imp_testExe1.c

@@ -7,6 +7,7 @@ extern int testLib4lib();
 extern int testLib5();
 extern int testLib6();
 extern int testLibCycleA1();
+extern int testLibPerConfigDest();
 
 /* Switch a symbol between debug and optimized builds to make sure the
    proper library is found from the testLib4 link interface.  */
@@ -21,5 +22,6 @@ int main()
 {
   return (testLib2() + generated_by_testExe1() + testLib3() + testLib4()
           + testLib5() + testLib6() + testLibCycleA1()
+          + testLibPerConfigDest()
           + generated_by_testExe3() + testLib4lib() + testLib4libcfg());
 }

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

@@ -6,3 +6,4 @@ run_cmake(DIRECTORY-message-lazy)
 run_cmake(SkipInstallRulesWarning)
 run_cmake(SkipInstallRulesNoWarning1)
 run_cmake(SkipInstallRulesNoWarning2)
+run_cmake(TARGETS-DESTINATION-bad)

+ 1 - 0
Tests/RunCMake/install/TARGETS-DESTINATION-bad-result.txt

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

+ 6 - 0
Tests/RunCMake/install/TARGETS-DESTINATION-bad-stderr.txt

@@ -0,0 +1,6 @@
+CMake Error:
+  Error evaluating generator expression:
+
+    \$<NOTAGENEX>
+
+  Expression did not evaluate to a known generator expression

+ 3 - 0
Tests/RunCMake/install/TARGETS-DESTINATION-bad.cmake

@@ -0,0 +1,3 @@
+enable_language(C)
+add_library(empty empty.c)
+install(TARGETS empty DESTINATION $<NOTAGENEX>)

+ 0 - 0
Tests/RunCMake/install/empty.c