diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index b4e6e8124..cff539f11 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -277,27 +277,31 @@ static bool checkInterfaceDirs(const std::string &prepro, //---------------------------------------------------------------------------- void cmExportFileGenerator::PopulateIncludeDirectoriesInterface( - cmTarget *target, + cmTargetExport *tei, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap &properties, std::vector &missingTargets) { + cmTarget *target = tei->Target; assert(preprocessRule == cmGeneratorExpression::InstallInterface); const char *propName = "INTERFACE_INCLUDE_DIRECTORIES"; const char *input = target->GetProperty(propName); - if (!input) + if (!input && tei->InterfaceIncludeDirectories.empty()) { return; } - if (!*input) + if (!*input && tei->InterfaceIncludeDirectories.empty()) { // Set to empty properties[propName] = ""; return; } - std::string prepro = cmGeneratorExpression::Preprocess(input, + std::string includes = (input?input:""); + const char* sep = input ? ";" : ""; + includes += sep + tei->InterfaceIncludeDirectories; + std::string prepro = cmGeneratorExpression::Preprocess(includes, preprocessRule); if (!prepro.empty()) { diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index a624ba6c4..9628b9681 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -15,6 +15,8 @@ #include "cmCommand.h" #include "cmGeneratorExpression.h" +class cmTargetExport; + /** \class cmExportFileGenerator * \brief Generate a file exporting targets from a build or install tree. * @@ -114,7 +116,7 @@ protected: void GenerateInterfaceProperties(cmTarget *target, std::ostream& os, const ImportPropertyMap &properties); void PopulateIncludeDirectoriesInterface( - cmTarget *target, + cmTargetExport *target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap &properties, std::vector &missingTargets); diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index ce7afc5a9..c97d4ff7c 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -39,7 +39,7 @@ std::string cmExportInstallFileGenerator::GetConfigImportFileGlob() //---------------------------------------------------------------------------- bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) { - std::vector allTargets; + std::vector allTargets; { std::string expectedTargets; std::string sep; @@ -49,10 +49,10 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) { expectedTargets += sep + this->Namespace + (*tei)->Target->GetExportName(); sep = " "; - cmTargetExport const* te = *tei; + cmTargetExport * te = *tei; if(this->ExportedTargets.insert(te->Target).second) { - allTargets.push_back(te->Target); + allTargets.push_back(te); } else { @@ -115,16 +115,16 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) bool require2_8_12 = false; // Create all the imported targets. - for(std::vector::const_iterator + for(std::vector::const_iterator tei = allTargets.begin(); tei != allTargets.end(); ++tei) { - cmTarget* te = *tei; + cmTarget* te = (*tei)->Target; this->GenerateImportTargetCode(os, te); ImportPropertyMap properties; - this->PopulateIncludeDirectoriesInterface(te, + this->PopulateIncludeDirectoriesInterface(*tei, cmGeneratorExpression::InstallInterface, properties, missingTargets); this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index d3a803724..4649eda14 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -219,6 +219,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) cmCAStringVector runtimeArgVector (&argHelper,"RUNTIME",&group); cmCAStringVector frameworkArgVector (&argHelper,"FRAMEWORK",&group); cmCAStringVector bundleArgVector (&argHelper,"BUNDLE",&group); + cmCAStringVector includesArgVector (&argHelper,"INCLUDES",&group); cmCAStringVector privateHeaderArgVector(&argHelper,"PRIVATE_HEADER",&group); cmCAStringVector publicHeaderArgVector (&argHelper,"PUBLIC_HEADER",&group); cmCAStringVector resourceArgVector (&argHelper,"RESOURCE",&group); @@ -247,6 +248,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) cmInstallCommandArguments privateHeaderArgs(this->DefaultComponentName); cmInstallCommandArguments publicHeaderArgs(this->DefaultComponentName); cmInstallCommandArguments resourceArgs(this->DefaultComponentName); + cmInstallCommandIncludesArgument includesArgs; // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. @@ -258,6 +260,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) privateHeaderArgs.Parse(&privateHeaderArgVector.GetVector(), &unknownArgs); publicHeaderArgs.Parse (&publicHeaderArgVector.GetVector(), &unknownArgs); resourceArgs.Parse (&resourceArgVector.GetVector(), &unknownArgs); + includesArgs.Parse (&includesArgVector.GetVector(), &unknownArgs); if(!unknownArgs.empty()) { @@ -292,6 +295,13 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) return false; } + if(!includesArgs.GetIncludeDirs().empty() && exports.GetString().empty()) + { + this->SetError("TARGETS given INCLUDES DESTINATION, but EXPORT set " + "not specified."); + return false; + } + // Enforce argument rules too complex to specify for the // general-purpose parser. if(archiveArgs.GetNamelinkOnly() || @@ -747,6 +757,20 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) te->RuntimeGenerator = runtimeGenerator; this->Makefile->GetLocalGenerator()->GetGlobalGenerator() ->GetExportSets()[exports.GetString()]->AddTargetExport(te); + + std::vector dirs = includesArgs.GetIncludeDirs(); + if(!dirs.empty()) + { + std::string dirString; + const char *sep = ""; + for (std::vector::const_iterator it = dirs.begin(); + it != dirs.end(); ++it) + { + te->InterfaceIncludeDirectories += sep; + te->InterfaceIncludeDirectories += *it; + sep = ";"; + } + } } } diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h index 39acd235e..65095011f 100644 --- a/Source/cmInstallCommand.h +++ b/Source/cmInstallCommand.h @@ -102,6 +102,7 @@ public: " [[ARCHIVE|LIBRARY|RUNTIME|FRAMEWORK|BUNDLE|\n" " PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]\n" " [DESTINATION ]\n" + " [INCLUDES DESTINATION [ ...]]\n" " [PERMISSIONS permissions...]\n" " [CONFIGURATIONS [Debug|Release|...]]\n" " [COMPONENT ]\n" @@ -130,6 +131,10 @@ public: "all target types. If only one is given then only targets of that " "type will be installed (which can be used to install just a DLL or " "just an import library)." + "The INCLUDES DESTINATION specifies a list of directories which will " + "be added to the INTERFACE_INCLUDE_DIRECTORIES of the when " + "exported by install(EXPORT). If a relative path is specified, it is " + "treated as relative to the $." "\n" "The PRIVATE_HEADER, PUBLIC_HEADER, and RESOURCE arguments cause " "subsequent properties to be applied to installing a FRAMEWORK " diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index 8e48f08e6..91ea861a4 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx @@ -203,3 +203,37 @@ bool cmInstallCommandArguments::CheckPermissions( // This is not a valid permission. return false; } + +cmInstallCommandIncludesArgument::cmInstallCommandIncludesArgument() +{ + +} + +const std::vector& +cmInstallCommandIncludesArgument::GetIncludeDirs() const +{ + return this->IncludeDirs; +} + +void cmInstallCommandIncludesArgument::Parse( + const std::vector* args, + std::vector*) +{ + if(args->empty()) + { + return; + } + std::vector::const_iterator it = args->begin(); + ++it; + for ( ; it != args->end(); ++it) + { + std::string dir = *it; + if (!cmSystemTools::FileIsFullPath(it->c_str()) + && cmGeneratorExpression::Find(*it) == std::string::npos) + { + dir = "$/" + dir; + } + cmSystemTools::ConvertToUnixSlashes(dir); + this->IncludeDirs.push_back(dir); + } +} diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 01f7d5679..90347e668 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -65,4 +65,17 @@ class cmInstallCommandArguments bool CheckPermissions(); }; +class cmInstallCommandIncludesArgument +{ + public: + cmInstallCommandIncludesArgument(); + void Parse(const std::vector* args, + std::vector* unconsumedArgs); + + const std::vector& GetIncludeDirs() const; + + private: + std::vector IncludeDirs; +}; + #endif diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h index c9d87fb7e..76658887d 100644 --- a/Source/cmTargetExport.h +++ b/Source/cmTargetExport.h @@ -12,6 +12,8 @@ #ifndef cmTargetExport_h #define cmTargetExport_h +#include "cmStandardIncludes.h" + class cmTarget; class cmInstallTargetGenerator; class cmInstallFilesGenerator; @@ -33,6 +35,7 @@ public: cmInstallTargetGenerator* FrameworkGenerator; cmInstallTargetGenerator* BundleGenerator; cmInstallFilesGenerator* HeaderGenerator; + std::string InterfaceIncludeDirectories; ///@} }; diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 370dffecf..737ec432b 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -270,9 +270,26 @@ install(TARGETS testLibRequired testLibIncludeRequired5 testLibIncludeRequired6 testSharedLibRequired - EXPORT RequiredExp DESTINATION lib ) + EXPORT RequiredExp DESTINATION lib + INCLUDES DESTINATION + installIncludesTest + $/installIncludesTest2) install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredTargets.cmake DESTINATION lib/cmake/testLibRequired) +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/installIncludesTest") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/installIncludesTest/installIncludesTest.h" "// No content\n") + +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/installIncludesTest2") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/installIncludesTest2/installIncludesTest2.h" "// No content\n") +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/installIncludesTest/installIncludesTest.h" + DESTINATION installIncludesTest +) +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/installIncludesTest2/installIncludesTest2.h" + DESTINATION installIncludesTest2 +) + install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib ) install(EXPORT DependsExp FILE testLibDependsTargets.cmake DESTINATION lib/cmake/testLibDepends) diff --git a/Tests/ExportImport/Import/A/deps_iface.c b/Tests/ExportImport/Import/A/deps_iface.c index e73ca26bf..d9dc3319c 100644 --- a/Tests/ExportImport/Import/A/deps_iface.c +++ b/Tests/ExportImport/Import/A/deps_iface.c @@ -3,6 +3,9 @@ #include "testLibIncludeRequired2.h" #include "testLibIncludeRequired6.h" +#include "installIncludesTest.h" +#include "installIncludesTest2.h" + #ifndef testLibRequired_IFACE_DEFINE #error Expected testLibRequired_IFACE_DEFINE #endif diff --git a/Tests/RunCMake/include_directories/RunCMakeTest.cmake b/Tests/RunCMake/include_directories/RunCMakeTest.cmake index 520dd448d..e582960fb 100644 --- a/Tests/RunCMake/include_directories/RunCMakeTest.cmake +++ b/Tests/RunCMake/include_directories/RunCMakeTest.cmake @@ -9,3 +9,4 @@ run_cmake(RelativePathInInterface) run_cmake(ImportedTarget) run_cmake(RelativePathInGenex) run_cmake(CMP0021) +run_cmake(TargetInterfaceIncludes) diff --git a/Tests/RunCMake/include_directories/TargetInterfaceIncludes-result.txt b/Tests/RunCMake/include_directories/TargetInterfaceIncludes-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/include_directories/TargetInterfaceIncludes-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/include_directories/TargetInterfaceIncludes-stderr.txt b/Tests/RunCMake/include_directories/TargetInterfaceIncludes-stderr.txt new file mode 100644 index 000000000..d1539275d --- /dev/null +++ b/Tests/RunCMake/include_directories/TargetInterfaceIncludes-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at TargetInterfaceIncludes.cmake:3 \(install\): + install TARGETS given INCLUDES DESTINATION, but EXPORT set not specified. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/include_directories/TargetInterfaceIncludes.cmake b/Tests/RunCMake/include_directories/TargetInterfaceIncludes.cmake new file mode 100644 index 000000000..92f31fc64 --- /dev/null +++ b/Tests/RunCMake/include_directories/TargetInterfaceIncludes.cmake @@ -0,0 +1,4 @@ + +add_library(empty empty.cpp) +install(TARGETS empty DESTINATION lib INCLUDES DESTINATION include) +# install(EXPORT )