install: Allow absolute EXPORT destination with relative targets (#15258)

When install(EXPORT) is given an absolute destination we cannot compute
the install prefix relative to the installed export file location.
Previously we disallowed installation of targets in such exports with a
relative destination, but did not enforce this for target property
values besides the location of the main target file.  This could lead to
broken installations when the EXPORT is installed to an absolute path
but usage requirements are specified relative to the install prefix.

Since an EXPORT installed to an absolute destination cannot be relocated
we can just hard-code the value of CMAKE_INSTALL_PREFIX as the base for
relative paths.  This will allow absolute install(EXPORT) destinations
to work with relative destinations for targets and usage requirements.

Extend the ExportImport test with a case covering this behavior.
This commit is contained in:
Brad King 2014-12-15 09:52:48 -05:00
parent 1b3ab3318d
commit dd089e08b5
10 changed files with 73 additions and 41 deletions

View File

@ -0,0 +1,9 @@
install-EXPORT-absolute-prefix
------------------------------
* The :command:`install(EXPORT)` command now works with an absolute
``DESTINATION`` even if targets in the export set are installed
with a destination or usage requirements specified relative to the
install prefix. The value of the :variable:`CMAKE_INSTALL_PREFIX`
variable is hard-coded into the installed export file as the base
for relative references.

View File

@ -69,13 +69,24 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
this->GenerateExpectedTargetsCode(os, expectedTargets); this->GenerateExpectedTargetsCode(os, expectedTargets);
} }
// Add code to compute the installation prefix relative to the // Set an _IMPORT_PREFIX variable for import location properties
// import file location. // to reference if they are relative to the install prefix.
std::string installPrefix =
this->IEGen->GetMakefile()->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
const char* installDest = this->IEGen->GetDestination(); const char* installDest = this->IEGen->GetDestination();
if(!cmSystemTools::FileIsFullPath(installDest)) if(cmSystemTools::FileIsFullPath(installDest))
{ {
std::string installPrefix = // The export file is being installed to an absolute path so the
this->IEGen->GetMakefile()->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); // package is not relocatable. Use the configured install prefix.
os <<
"# The installation prefix configured by this project.\n"
"set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
"\n";
}
else
{
// Add code to compute the installation prefix relative to the
// import file location.
std::string absDest = installPrefix + "/" + installDest; std::string absDest = installPrefix + "/" + installDest;
std::string absDestS = absDest + "/"; std::string absDestS = absDest + "/";
os << "# Compute the installation prefix relative to this file.\n" os << "# Compute the installation prefix relative to this file.\n"
@ -106,9 +117,6 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
dest = cmSystemTools::GetFilenamePath(dest); dest = cmSystemTools::GetFilenamePath(dest);
} }
os << "\n"; os << "\n";
// Import location properties may reference this variable.
this->ImportPrefix = "${_IMPORT_PREFIX}/";
} }
std::vector<std::string> missingTargets; std::vector<std::string> missingTargets;
@ -209,12 +217,9 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
<< "\n"; << "\n";
// Cleanup the import prefix variable. // Cleanup the import prefix variable.
if(!this->ImportPrefix.empty()) os << "# Cleanup temporary variables.\n"
{ << "set(_IMPORT_PREFIX)\n"
os << "# Cleanup temporary variables.\n" << "\n";
<< "set(_IMPORT_PREFIX)\n"
<< "\n";
}
this->GenerateImportedFileCheckLoop(os); this->GenerateImportedFileCheckLoop(os);
bool result = true; bool result = true;
@ -394,11 +399,7 @@ cmExportInstallFileGenerator
if(!cmSystemTools::FileIsFullPath(dest.c_str())) if(!cmSystemTools::FileIsFullPath(dest.c_str()))
{ {
// The target is installed relative to the installation prefix. // The target is installed relative to the installation prefix.
if(this->ImportPrefix.empty()) value = "${_IMPORT_PREFIX}/";
{
this->ComplainAboutImportPrefix(itgen);
}
value = this->ImportPrefix;
} }
value += dest; value += dest;
value += "/"; value += "/";
@ -508,24 +509,6 @@ cmExportInstallFileGenerator
return namespaces; return namespaces;
} }
//----------------------------------------------------------------------------
void
cmExportInstallFileGenerator
::ComplainAboutImportPrefix(cmInstallTargetGenerator* itgen)
{
const char* installDest = this->IEGen->GetDestination();
cmOStringStream e;
e << "install(EXPORT \""
<< this->IEGen->GetExportSet()->GetName()
<< "\") given absolute "
<< "DESTINATION \"" << installDest << "\" but the export "
<< "references an installation of target \""
<< itgen->GetTarget()->GetName() << "\" which has relative "
<< "DESTINATION \"" << itgen->GetDestination() << "\".";
cmSystemTools::Error(e.str().c_str());
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmExportInstallFileGenerator cmExportInstallFileGenerator

View File

@ -83,14 +83,10 @@ protected:
std::set<std::string>& importedLocations std::set<std::string>& importedLocations
); );
void ComplainAboutImportPrefix(cmInstallTargetGenerator* itgen);
std::string InstallNameDir(cmTarget* target, const std::string& config); std::string InstallNameDir(cmTarget* target, const std::string& config);
cmInstallExportGenerator* IEGen; cmInstallExportGenerator* IEGen;
std::string ImportPrefix;
// The import file generated for each configuration. // The import file generated for each configuration.
std::map<std::string, std::string> ConfigImportFiles; std::map<std::string, std::string> ConfigImportFiles;
}; };

View File

@ -508,3 +508,18 @@ export(TARGETS testExe2 testLib4 testLib5 testLib6 testExe3 testExe2lib
) )
add_subdirectory(Interface) add_subdirectory(Interface)
#-----------------------------------------------------------------------------
# Install export with absolute destination but relative pieces.
add_library(testLibAbs1 STATIC testLibAbs1.c)
target_include_directories(testLibAbs1 INTERFACE
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/abs/1a;include/abs/1b>"
)
install(
TARGETS testLibAbs1
EXPORT expAbs
ARCHIVE DESTINATION lib
INCLUDES DESTINATION include/abs
)
install(DIRECTORY include/abs DESTINATION include)
install(EXPORT expAbs NAMESPACE expAbs_ DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/expAbs)

View File

@ -0,0 +1 @@
#define testLibAbs1a

View File

@ -0,0 +1 @@
#define testLibAbs1b

View File

@ -0,0 +1 @@
extern int testLibAbs1(void);

View File

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

View File

@ -95,6 +95,16 @@ foreach(c DEBUG RELWITHDEBINFO)
set_property(TARGET imp_testExe1b PROPERTY COMPILE_DEFINITIONS_${c} EXE_DBG) set_property(TARGET imp_testExe1b PROPERTY COMPILE_DEFINITIONS_${c} EXE_DBG)
endforeach() endforeach()
#-----------------------------------------------------------------------------
include(${CMAKE_INSTALL_PREFIX}/lib/expAbs/expAbs.cmake)
add_executable(imp_testExeAbs1
imp_testExeAbs1.c
)
target_link_libraries(imp_testExeAbs1
expAbs_testLibAbs1
)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# Create a custom target to generate a header for the libraries below. # Create a custom target to generate a header for the libraries below.
# Drive the header generation through an indirect chain of imported # Drive the header generation through an indirect chain of imported

View File

@ -0,0 +1,15 @@
#include "testLibAbs1.h"
#include "testLibAbs1a.h"
#include "testLibAbs1b.h"
#ifndef testLibAbs1a
# error "testLibAbs1a not defined"
#endif
#ifndef testLibAbs1b
# error "testLibAbs1b not defined"
#endif
int main()
{
return 0
+ testLibAbs1()
;
}