From a71caab46b205c2b0367c2b11c12a9b55b09bcca Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Fri, 14 Sep 2018 17:48:20 +0200 Subject: LINK_DIRECTORIES: Add new properties and commands These new capabilities enable to manage link directories Two new properties: * target properties: LINK_DIRECTORIES and INTERFACE_LINK_DIRECTORIES One new command * target_link_directories(): to populate target properties Fixes: #17215 --- Help/command/link_directories.rst | 44 +++++++-- Help/command/target_link_directories.rst | 55 +++++++++++ Help/manual/cmake-commands.7.rst | 1 + Help/manual/cmake-properties.7.rst | 2 + Help/prop_dir/LINK_DIRECTORIES.rst | 15 ++- Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst | 9 ++ Help/prop_tgt/LINK_DIRECTORIES.rst | 18 ++++ Help/release/dev/LINK_DIRECTORIES.rst | 9 ++ Source/CMakeLists.txt | 2 + Source/cmCPluginAPI.cxx | 2 +- Source/cmCommands.cxx | 3 + Source/cmComputeLinkInformation.cxx | 11 +-- Source/cmExportBuildFileGenerator.cxx | 3 + Source/cmExportFileGenerator.cxx | 31 ++++++ Source/cmExportFileGenerator.h | 4 + Source/cmExportInstallFileGenerator.cxx | 2 + Source/cmGeneratorExpressionDAGChecker.h | 1 + Source/cmGeneratorTarget.cxx | 107 ++++++++++++++++++++- Source/cmGeneratorTarget.h | 8 +- Source/cmLinkDirectoriesCommand.cxx | 7 +- Source/cmMakefile.cxx | 31 +++--- Source/cmMakefile.h | 3 + Source/cmState.cxx | 5 + Source/cmStateDirectory.cxx | 53 ++++++++++ Source/cmStateDirectory.h | 8 ++ Source/cmStatePrivate.h | 4 + Source/cmStateSnapshot.cxx | 7 ++ Source/cmTarget.cxx | 79 ++++++++++++--- Source/cmTarget.h | 11 +-- Source/cmTargetLinkDirectoriesCommand.cxx | 61 ++++++++++++ Source/cmTargetLinkDirectoriesCommand.h | 41 ++++++++ .../target_link_directories/CMakeLists.txt | 40 ++++++++ .../target_link_directories/LinkDirectoriesLib.c | 7 ++ .../target_link_directories/subdir/CMakeLists.txt | 2 + Tests/CMakeLists.txt | 1 + Tests/ExportImport/Export/CMakeLists.txt | 12 +++ Tests/ExportImport/Import/A/CMakeLists.txt | 5 + Tests/LinkDirectory/External/CMakeLists.txt | 14 +++ .../set_property/LINK_DIRECTORIES-stdout.txt | 2 + Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake | 3 + Tests/RunCMake/set_property/RunCMakeTest.cmake | 1 + 41 files changed, 662 insertions(+), 62 deletions(-) create mode 100644 Help/command/target_link_directories.rst create mode 100644 Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst create mode 100644 Help/prop_tgt/LINK_DIRECTORIES.rst create mode 100644 Help/release/dev/LINK_DIRECTORIES.rst create mode 100644 Source/cmTargetLinkDirectoriesCommand.cxx create mode 100644 Source/cmTargetLinkDirectoriesCommand.h create mode 100644 Tests/CMakeCommands/target_link_directories/CMakeLists.txt create mode 100644 Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c create mode 100644 Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt create mode 100644 Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt create mode 100644 Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake diff --git a/Help/command/link_directories.rst b/Help/command/link_directories.rst index 5c64bc6..3efa8e5 100644 --- a/Help/command/link_directories.rst +++ b/Help/command/link_directories.rst @@ -1,19 +1,45 @@ link_directories ---------------- -Specify directories in which the linker will look for libraries. +Add directories in which the linker will look for libraries. :: - link_directories(directory1 directory2 ...) + link_directories(directory1 [directory2 ...]) -Specify the paths in which the linker should search for libraries. -The command will apply only to targets created after it is called. +Add the paths in which the linker should search for libraries. Relative paths given to this command are interpreted as relative to the current source directory, see :policy:`CMP0015`. -Note that this command is rarely necessary. Library locations -returned by :command:`find_package` and :command:`find_library` are -absolute paths. Pass these absolute library file paths directly to the -:command:`target_link_libraries` command. CMake will ensure the linker finds -them. +The directories are added to the :prop_dir:`LINK_DIRECTORIES` directory +property for the current ``CMakeLists.txt`` file, converting relative +paths to absolute as needed. +The command will apply only to targets created after it is called. + +Arguments to ``link_directories`` may use "generator expressions" with +the syntax "$<...>". See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. See the :manual:`cmake-buildsystem(7)` +manual for more on defining buildsystem properties. + +.. note:: + + This command is rarely necessary and should be avoided where there are + other choices. Prefer to pass full absolute paths to libraries where + possible, since this ensures the correct library will always be linked. + The :command:`find_library` command provides the full path, which can + generally be used directly in calls to :command:`target_link_libraries`. + Situations where a library search path may be needed include: + + - Project generators like Xcode where the user can switch target + architecture at build time, but a full path to a library cannot + be used because it only provides one architecture (i.e. it is not + a universal binary). + - Libraries may themselves have other private library dependencies + that expect to be found via ``RPATH`` mechanisms, but some linkers + are not able to fully decode those paths (e.g. due to the presence + of things like ``$ORIGIN``). + + If a library search path must be provided, prefer to localize the effect + where possible by using the :command:`target_link_directories` command + rather than ``link_directories()``. The target-specific command can also + control how the search directories propagate to other dependent targets. diff --git a/Help/command/target_link_directories.rst b/Help/command/target_link_directories.rst new file mode 100644 index 0000000..b46aac0 --- /dev/null +++ b/Help/command/target_link_directories.rst @@ -0,0 +1,55 @@ +target_link_directories +----------------------- + +Add link directories to a target. + +:: + + target_link_directories( [BEFORE] + [items1...] + [ [items2...] ...]) + +Specify the paths in which the linker should search for libraries when +linking a given target. Each item can be an absolute or relative path, +with the latter being interpreted as relative to the current source +directory. These items will be added to the link command. + +The named ```` must have been created by a command such as +:command:`add_executable` or :command:`add_library` and must not be an +:ref:`ALIAS target `. + +The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to +specify the scope of the items that follow them. ``PRIVATE`` and +``PUBLIC`` items will populate the :prop_tgt:`LINK_DIRECTORIES` property +of ````. ``PUBLIC`` and ``INTERFACE`` items will populate the +:prop_tgt:`INTERFACE_LINK_DIRECTORIES` property of ```` +(:ref:`IMPORTED targets ` only support ``INTERFACE`` items). +Each item specifies a link directory and will be converted to an absolute +path if necessary before adding it to the relevant property. Repeated +calls for the same ```` append items in the order called. + +If ``BEFORE`` is specified, the content will be prepended to the relevant +property instead of being appended. + +Arguments to ``target_link_directories`` may use "generator expressions" +with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. See the :manual:`cmake-buildsystem(7)` +manual for more on defining buildsystem properties. + +.. note:: + + This command is rarely necessary and should be avoided where there are + other choices. Prefer to pass full absolute paths to libraries where + possible, since this ensures the correct library will always be linked. + The :command:`find_library` command provides the full path, which can + generally be used directly in calls to :command:`target_link_libraries`. + Situations where a library search path may be needed include: + + - Project generators like Xcode where the user can switch target + architecture at build time, but a full path to a library cannot + be used because it only provides one architecture (i.e. it is not + a universal binary). + - Libraries may themselves have other private library dependencies + that expect to be found via ``RPATH`` mechanisms, but some linkers + are not able to fully decode those paths (e.g. due to the presence + of things like ``$ORIGIN``). diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst index 753647d..0cc5fca 100644 --- a/Help/manual/cmake-commands.7.rst +++ b/Help/manual/cmake-commands.7.rst @@ -111,6 +111,7 @@ These commands are available only in CMake projects. /command/target_compile_features /command/target_compile_options /command/target_include_directories + /command/target_link_directories /command/target_link_libraries /command/target_link_options /command/target_sources diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 8ccd7f6..5c3eb81 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -228,6 +228,7 @@ Properties on Targets /prop_tgt/INTERFACE_COMPILE_OPTIONS /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES /prop_tgt/INTERFACE_LINK_DEPENDS + /prop_tgt/INTERFACE_LINK_DIRECTORIES /prop_tgt/INTERFACE_LINK_LIBRARIES /prop_tgt/INTERFACE_LINK_OPTIONS /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE @@ -252,6 +253,7 @@ Properties on Targets /prop_tgt/LINK_DEPENDS_NO_SHARED /prop_tgt/LINK_DEPENDS /prop_tgt/LINKER_LANGUAGE + /prop_tgt/LINK_DIRECTORIES /prop_tgt/LINK_FLAGS_CONFIG /prop_tgt/LINK_FLAGS /prop_tgt/LINK_INTERFACE_LIBRARIES_CONFIG diff --git a/Help/prop_dir/LINK_DIRECTORIES.rst b/Help/prop_dir/LINK_DIRECTORIES.rst index fa37576..f9fb815 100644 --- a/Help/prop_dir/LINK_DIRECTORIES.rst +++ b/Help/prop_dir/LINK_DIRECTORIES.rst @@ -3,6 +3,15 @@ LINK_DIRECTORIES List of linker search directories. -This read-only property specifies the list of directories given so far -to the link_directories command. It is intended for debugging -purposes. +This property holds a :ref:`;-list ` of directories +and is typically populated using the :command:`link_directories` command. +It gets its initial value from its parent directory, if it has one. + +The directory property is used to initialize the :prop_tgt:`LINK_DIRECTORIES` +target property when a target is created. That target property is used +by the generators to set the library search directories for the linker. + +Contents of ``LINK_DIRECTORIES`` may use "generator expressions" with +the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. See the :manual:`cmake-buildsystem(7)` +manual for more on defining buildsystem properties. diff --git a/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst b/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst new file mode 100644 index 0000000..56a4ec0 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst @@ -0,0 +1,9 @@ +INTERFACE_LINK_DIRECTORIES +-------------------------- + +.. |property_name| replace:: link directories +.. |command_name| replace:: :command:`target_link_directories` +.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_LINK_DIRECTORIES`` +.. |PROPERTY_LINK| replace:: :prop_tgt:`LINK_DIRECTORIES` +.. |PROPERTY_GENEX| replace:: ``$`` +.. include:: INTERFACE_BUILD_PROPERTY.txt diff --git a/Help/prop_tgt/LINK_DIRECTORIES.rst b/Help/prop_tgt/LINK_DIRECTORIES.rst new file mode 100644 index 0000000..085a701 --- /dev/null +++ b/Help/prop_tgt/LINK_DIRECTORIES.rst @@ -0,0 +1,18 @@ +LINK_DIRECTORIES +---------------- + +List of directories to use for the link step of shared library, module +and executable targets. + +This property holds a :ref:`;-list ` of directories +specified so far for its target. Use the :command:`target_link_directories` +command to append more search directories. + +This property is initialized by the :prop_dir:`LINK_DIRECTORIES` directory +property when a target is created, and is used by the generators to set +the search directories for the linker. + +Contents of ``LINK_DIRECTORIES`` may use "generator expressions" with the +syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual +for available expressions. See the :manual:`cmake-buildsystem(7)` manual +for more on defining buildsystem properties. diff --git a/Help/release/dev/LINK_DIRECTORIES.rst b/Help/release/dev/LINK_DIRECTORIES.rst new file mode 100644 index 0000000..dc7d609 --- /dev/null +++ b/Help/release/dev/LINK_DIRECTORIES.rst @@ -0,0 +1,9 @@ +LINK_DIRECTORIES +---------------- + +* CMake gained new capabilities to manage link directories: + + * :prop_tgt:`LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DIRECTORIES` + target properties. + * :command:`target_link_directories` command to add link directories to + targets. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index bfddbc6..3cf6c8f 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -579,6 +579,8 @@ set(SRCS cmTargetIncludeDirectoriesCommand.h cmTargetLinkOptionsCommand.cxx cmTargetLinkOptionsCommand.h + cmTargetLinkDirectoriesCommand.cxx + cmTargetLinkDirectoriesCommand.h cmTargetLinkLibrariesCommand.cxx cmTargetLinkLibrariesCommand.h cmTargetPropCommandBase.cxx diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 3aa59d6..22ae340 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -171,7 +171,7 @@ void CCONV cmAddLinkDirectoryForTarget(void* arg, const char* tgt, " for directory ", d); return; } - t->AddLinkDirectory(d); + t->InsertLinkDirectory(d, mf->GetBacktrace()); } void CCONV cmAddExecutable(void* arg, const char* exename, int numSrcs, diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 15fbd40..873372f 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -101,6 +101,7 @@ # include "cmRemoveDefinitionsCommand.h" # include "cmSourceGroupCommand.h" # include "cmSubdirDependsCommand.h" +# include "cmTargetLinkDirectoriesCommand.h" # include "cmTargetLinkOptionsCommand.h" # include "cmUseMangledMesaCommand.h" # include "cmUtilitySourceCommand.h" @@ -278,6 +279,8 @@ void GetProjectCommands(cmState* state) state->AddBuiltinCommand("link_libraries", new cmLinkLibrariesCommand); state->AddBuiltinCommand("target_link_options", new cmTargetLinkOptionsCommand); + state->AddBuiltinCommand("target_link_directories", + new cmTargetLinkDirectoriesCommand); state->AddBuiltinCommand("load_cache", new cmLoadCacheCommand); state->AddBuiltinCommand("qt_wrap_cpp", new cmQTWrapCPPCommand); state->AddBuiltinCommand("qt_wrap_ui", new cmQTWrapUICommand); diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index a3e135f..0e48ca8 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -357,10 +357,10 @@ cmComputeLinkInformation::cmComputeLinkInformation( } // Add the search path entries requested by the user to path ordering. - this->OrderLinkerSearchPath->AddUserDirectories( - this->Target->GetLinkDirectories()); - this->OrderRuntimeSearchPath->AddUserDirectories( - this->Target->GetLinkDirectories()); + std::vector directories; + this->Target->GetLinkDirectories(directories, config, this->LinkLanguage); + this->OrderLinkerSearchPath->AddUserDirectories(directories); + this->OrderRuntimeSearchPath->AddUserDirectories(directories); // Set up the implicit link directories. this->LoadImplicitLinkInfo(); @@ -387,8 +387,7 @@ cmComputeLinkInformation::cmComputeLinkInformation( if (this->OldLinkDirMode) { // Construct a mask to not bother with this behavior for link // directories already specified by the user. - std::vector const& dirs = this->Target->GetLinkDirectories(); - this->OldLinkDirMask.insert(dirs.begin(), dirs.end()); + this->OldLinkDirMask.insert(directories.begin(), directories.end()); } this->CMP0060Warn = this->Makefile->PolicyOptionalWarningEnabled( diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 7f42035..024e641 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -98,6 +98,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte, cmGeneratorExpression::BuildInterface, properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte, + cmGeneratorExpression::BuildInterface, + properties, missingTargets); this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte, cmGeneratorExpression::BuildInterface, properties, missingTargets); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index d6573b8..4cf9dd7 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -451,6 +451,37 @@ void cmExportFileGenerator::PopulateLinkDependsInterface( } } +void cmExportFileGenerator::PopulateLinkDirectoriesInterface( + cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, std::vector& missingTargets) +{ + cmGeneratorTarget* gt = tei->Target; + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + const char* propName = "INTERFACE_LINK_DIRECTORIES"; + const char* input = gt->GetProperty(propName); + + if (!input) { + return; + } + + if (!*input) { + properties[propName].clear(); + return; + } + + std::string prepro = + cmGeneratorExpression::Preprocess(input, preprocessRule, true); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt, missingTargets); + + if (!checkInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; + } +} + void cmExportFileGenerator::PopulateInterfaceProperty( const std::string& propName, cmGeneratorTarget* target, cmGeneratorExpression::PreprocessContext preprocessRule, diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index 6ca2e07..41c6538 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -147,6 +147,10 @@ protected: cmTargetExport* target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties, std::vector& missingTargets); + void PopulateLinkDirectoriesInterface( + cmTargetExport* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, std::vector& missingTargets); void PopulateLinkDependsInterface( cmTargetExport* target, cmGeneratorExpression::PreprocessContext preprocessRule, diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index bfb7a05..e444087 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -106,6 +106,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt, cmGeneratorExpression::InstallInterface, properties, missingTargets); + this->PopulateLinkDirectoriesInterface( + te, cmGeneratorExpression::InstallInterface, properties, missingTargets); this->PopulateLinkDependsInterface( te, cmGeneratorExpression::InstallInterface, properties, missingTargets); diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 8b1697b..a5134c3 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -28,6 +28,7 @@ class cmGeneratorTarget; SELECT(F, EvaluatingSources, SOURCES) \ SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES) \ SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS) \ + SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES) \ SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS) #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \ diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index a58d3cb..012d77a 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -103,6 +103,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) , DebugCompileFeaturesDone(false) , DebugCompileDefinitionsDone(false) , DebugLinkOptionsDone(false) + , DebugLinkDirectoriesDone(false) , DebugSourcesDone(false) , LinkImplementationLanguageIsContextDependent(true) , UtilityItemsDone(false) @@ -133,6 +134,10 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) t->GetLinkOptionsBacktraces(), this->LinkOptionsEntries); + CreatePropertyGeneratorExpressions(t->GetLinkDirectoriesEntries(), + t->GetLinkDirectoriesBacktraces(), + this->LinkDirectoriesEntries); + CreatePropertyGeneratorExpressions(t->GetSourceEntries(), t->GetSourceBacktraces(), this->SourceEntries, true); @@ -150,6 +155,7 @@ cmGeneratorTarget::~cmGeneratorTarget() cmDeleteAll(this->CompileFeaturesEntries); cmDeleteAll(this->CompileDefinitionsEntries); cmDeleteAll(this->LinkOptionsEntries); + cmDeleteAll(this->LinkDirectoriesEntries); cmDeleteAll(this->SourceEntries); cmDeleteAll(this->LinkInformation); } @@ -1704,11 +1710,6 @@ cmListFileBacktrace cmGeneratorTarget::GetBacktrace() const return this->Target->GetBacktrace(); } -const std::vector& cmGeneratorTarget::GetLinkDirectories() const -{ - return this->Target->GetLinkDirectories(); -} - const std::set& cmGeneratorTarget::GetUtilities() const { return this->Target->GetUtilities(); @@ -3068,6 +3069,102 @@ void cmGeneratorTarget::GetStaticLibraryLinkOptions( } namespace { +void processLinkDirectories( + cmGeneratorTarget const* tgt, + const std::vector& entries, + std::vector& directories, + std::unordered_set& uniqueDirectories, + cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, + bool debugDirectories, std::string const& language) +{ + for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { + cmLinkImplItem const& item = entry->LinkImplItem; + std::string const& targetName = item.AsStr(); + + std::vector entryDirectories; + cmSystemTools::ExpandListArgument( + entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, + dagChecker, language), + entryDirectories); + + std::string usedDirectories; + for (std::string& entryDirectory : entryDirectories) { + if (!cmSystemTools::FileIsFullPath(entryDirectory)) { + std::ostringstream e; + if (!targetName.empty()) { + /* clang-format off */ + e << "Target \"" << targetName << "\" contains relative " + "path in its INTERFACE_LINK_DIRECTORIES:\n" + " \"" << entryDirectory << "\""; + /* clang-format on */ + tgt->GetLocalGenerator()->IssueMessage(cmake::FATAL_ERROR, e.str()); + return; + } + } + + // Sanitize the path the same way the link_directories command does + // in case projects set the LINK_DIRECTORIES property directly. + cmSystemTools::ConvertToUnixSlashes(entryDirectory); + if (uniqueDirectories.insert(entryDirectory).second) { + directories.push_back(entryDirectory); + if (debugDirectories) { + usedDirectories += " * " + entryDirectory + "\n"; + } + } + } + if (!usedDirectories.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + cmake::LOG, + std::string("Used link directories for target ") + tgt->GetName() + + ":\n" + usedDirectories, + entry->ge->GetBacktrace()); + } + } +} +} + +void cmGeneratorTarget::GetLinkDirectories(std::vector& result, + const std::string& config, + const std::string& language) const +{ + std::unordered_set uniqueDirectories; + + cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr, + nullptr); + + std::vector debugProperties; + const char* debugProp = + this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); + if (debugProp) { + cmSystemTools::ExpandListArgument(debugProp, debugProperties); + } + + bool debugDirectories = !this->DebugLinkDirectoriesDone && + std::find(debugProperties.begin(), debugProperties.end(), + "LINK_DIRECTORIES") != debugProperties.end(); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugLinkDirectoriesDone = true; + } + + processLinkDirectories(this, this->LinkDirectoriesEntries, result, + uniqueDirectories, &dagChecker, config, + debugDirectories, language); + + std::vector + linkInterfaceLinkDirectoriesEntries; + + AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", + linkInterfaceLinkDirectoriesEntries); + + processLinkDirectories(this, linkInterfaceLinkDirectoriesEntries, result, + uniqueDirectories, &dagChecker, config, + debugDirectories, language); + + cmDeleteAll(linkInterfaceLinkDirectoriesEntries); +} + +namespace { void processLinkDepends( cmGeneratorTarget const* tgt, const std::vector& entries, diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 9d8c9f5..bfd95ac 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -273,8 +273,6 @@ public: cmListFileBacktrace GetBacktrace() const; - const std::vector& GetLinkDirectories() const; - std::set const& GetUtilities() const; cmListFileBacktrace const* GetUtilityBacktrace(const std::string& u) const; @@ -435,6 +433,10 @@ public: const std::string& config, const std::string& language) const; + void GetLinkDirectories(std::vector& result, + const std::string& config, + const std::string& language) const; + void GetLinkDepends(std::vector& result, const std::string& config, const std::string& language) const; @@ -825,6 +827,7 @@ private: std::vector CompileFeaturesEntries; std::vector CompileDefinitionsEntries; std::vector LinkOptionsEntries; + std::vector LinkDirectoriesEntries; std::vector SourceEntries; mutable std::set LinkImplicitNullProperties; @@ -874,6 +877,7 @@ private: mutable bool DebugCompileFeaturesDone; mutable bool DebugCompileDefinitionsDone; mutable bool DebugLinkOptionsDone; + mutable bool DebugLinkDirectoriesDone; mutable bool DebugSourcesDone; mutable bool LinkImplementationLanguageIsContextDependent; mutable bool UtilityItemsDone; diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx index 1371c53..5c52c76 100644 --- a/Source/cmLinkDirectoriesCommand.cxx +++ b/Source/cmLinkDirectoriesCommand.cxx @@ -4,6 +4,7 @@ #include +#include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmPolicies.h" #include "cmSystemTools.h" @@ -29,7 +30,8 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir) { std::string unixPath = dir; cmSystemTools::ConvertToUnixSlashes(unixPath); - if (!cmSystemTools::FileIsFullPath(unixPath)) { + if (!cmSystemTools::FileIsFullPath(unixPath) && + !cmGeneratorExpression::StartsWithGeneratorExpression(unixPath)) { bool convertToAbsolute = false; std::ostringstream e; /* clang-format off */ @@ -41,6 +43,7 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir) case cmPolicies::WARN: e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0015); this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, e.str()); + break; case cmPolicies::OLD: // OLD behavior does not convert break; @@ -61,5 +64,5 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir) unixPath = tmp; } } - this->Makefile->AppendProperty("LINK_DIRECTORIES", unixPath.c_str()); + this->Makefile->AddLinkDirectory(unixPath); } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 354da4e..963bb44 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -243,6 +243,17 @@ cmBacktraceRange cmMakefile::GetLinkOptionsBacktraces() const return this->StateSnapshot.GetDirectory().GetLinkOptionsEntryBacktraces(); } +cmStringRange cmMakefile::GetLinkDirectoriesEntries() const +{ + return this->StateSnapshot.GetDirectory().GetLinkDirectoriesEntries(); +} + +cmBacktraceRange cmMakefile::GetLinkDirectoriesBacktraces() const +{ + return this->StateSnapshot.GetDirectory() + .GetLinkDirectoriesEntryBacktraces(); +} + cmListFileBacktrace cmMakefile::GetBacktrace() const { return this->Backtrace; @@ -1237,6 +1248,11 @@ void cmMakefile::AddLinkOption(std::string const& option) this->AppendProperty("LINK_OPTIONS", option.c_str()); } +void cmMakefile::AddLinkDirectory(std::string const& directory) +{ + this->AppendProperty("LINK_DIRECTORIES", directory.c_str()); +} + bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove) { // Create a regular expression to match valid definitions. @@ -1335,10 +1351,6 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent) // link libraries this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES")); - // link directories - this->SetProperty("LINK_DIRECTORIES", - parent->GetProperty("LINK_DIRECTORIES")); - // the initial project name this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName()); @@ -1872,17 +1884,6 @@ void cmMakefile::AddGlobalLinkInformation(cmTarget& target) return; default:; } - if (const char* linkDirsProp = this->GetProperty("LINK_DIRECTORIES")) { - std::vector linkDirs; - cmSystemTools::ExpandListArgument(linkDirsProp, linkDirs); - - for (std::string& linkDir : linkDirs) { - // Sanitize the path the same way the link_directories command does - // in case projects set the LINK_DIRECTORIES property directly. - cmSystemTools::ConvertToUnixSlashes(linkDir); - target.AddLinkDirectory(linkDir); - } - } if (const char* linkLibsProp = this->GetProperty("LINK_LIBRARIES")) { std::vector linkLibs; diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index bb01c0b..cde661b 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -182,6 +182,7 @@ public: void AddCompileDefinition(std::string const& definition); void AddCompileOption(std::string const& option); void AddLinkOption(std::string const& option); + void AddLinkDirectory(std::string const& directory); /** Create a new imported target with the name and type given. */ cmTarget* AddImportedTarget(const std::string& name, @@ -802,6 +803,8 @@ public: cmBacktraceRange GetCompileDefinitionsBacktraces() const; cmStringRange GetLinkOptionsEntries() const; cmBacktraceRange GetLinkOptionsBacktraces() const; + cmStringRange GetLinkDirectoriesEntries() const; + cmBacktraceRange GetLinkDirectoriesBacktraces() const; std::set const& GetSystemIncludeDirectories() const { diff --git a/Source/cmState.cxx b/Source/cmState.cxx index c6667f6..4b65cf1 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -284,6 +284,8 @@ cmStateSnapshot cmState::Reset() it->CompileOptionsBacktraces.clear(); it->LinkOptions.clear(); it->LinkOptionsBacktraces.clear(); + it->LinkDirectories.clear(); + it->LinkDirectoriesBacktraces.clear(); it->DirectoryEnd = pos; it->NormalTargetNames.clear(); it->Properties.clear(); @@ -660,6 +662,7 @@ cmStateSnapshot cmState::CreateBaseSnapshot() pos->CompileDefinitionsPosition = 0; pos->CompileOptionsPosition = 0; pos->LinkOptionsPosition = 0; + pos->LinkDirectoriesPosition = 0; pos->BuildSystemDirectory->DirectoryEnd = pos; pos->Policies = this->PolicyStack.Root(); pos->PolicyRoot = this->PolicyStack.Root(); @@ -813,6 +816,8 @@ cmStateSnapshot cmState::Pop(cmStateSnapshot const& originSnapshot) prevPos->BuildSystemDirectory->CompileOptions.size(); prevPos->LinkOptionsPosition = prevPos->BuildSystemDirectory->LinkOptions.size(); + prevPos->LinkDirectoriesPosition = + prevPos->BuildSystemDirectory->LinkDirectories.size(); prevPos->BuildSystemDirectory->DirectoryEnd = prevPos; if (!pos->Keep && this->SnapshotData.IsLast(pos)) { diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index 925b161..5160f51 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx @@ -396,6 +396,43 @@ void cmStateDirectory::ClearLinkOptions() this->Snapshot_.Position->LinkOptionsPosition); } +cmStringRange cmStateDirectory::GetLinkDirectoriesEntries() const +{ + return GetPropertyContent(this->DirectoryState->LinkDirectories, + this->Snapshot_.Position->LinkDirectoriesPosition); +} + +cmBacktraceRange cmStateDirectory::GetLinkDirectoriesEntryBacktraces() const +{ + return GetPropertyBacktraces( + this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition); +} + +void cmStateDirectory::AppendLinkDirectoriesEntry( + const std::string& vec, const cmListFileBacktrace& lfbt) +{ + AppendEntry(this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt); +} + +void cmStateDirectory::SetLinkDirectories(const std::string& vec, + const cmListFileBacktrace& lfbt) +{ + SetContent(this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt); +} + +void cmStateDirectory::ClearLinkDirectories() +{ + ClearContent(this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition); +} + void cmStateDirectory::SetProperty(const std::string& prop, const char* value, cmListFileBacktrace const& lfbt) { @@ -431,6 +468,14 @@ void cmStateDirectory::SetProperty(const std::string& prop, const char* value, this->SetLinkOptions(value, lfbt); return; } + if (prop == "LINK_DIRECTORIES") { + if (!value) { + this->ClearLinkDirectories(); + return; + } + this->SetLinkDirectories(value, lfbt); + return; + } this->DirectoryState->Properties.SetProperty(prop, value); } @@ -455,6 +500,10 @@ void cmStateDirectory::AppendProperty(const std::string& prop, this->AppendLinkOptionsEntry(value, lfbt); return; } + if (prop == "LINK_DIRECTORIES") { + this->AppendLinkDirectoriesEntry(value, lfbt); + return; + } this->DirectoryState->Properties.AppendProperty(prop, value, asString); } @@ -542,6 +591,10 @@ const char* cmStateDirectory::GetProperty(const std::string& prop, output = cmJoin(this->GetLinkOptionsEntries(), ";"); return output.c_str(); } + if (prop == "LINK_DIRECTORIES") { + output = cmJoin(this->GetLinkDirectoriesEntries(), ";"); + return output.c_str(); + } const char* retVal = this->DirectoryState->Properties.GetPropertyValue(prop); if (!retVal && chain) { diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h index 412664f..06345e2 100644 --- a/Source/cmStateDirectory.h +++ b/Source/cmStateDirectory.h @@ -65,6 +65,14 @@ public: void SetLinkOptions(std::string const& vec, cmListFileBacktrace const& lfbt); void ClearLinkOptions(); + cmStringRange GetLinkDirectoriesEntries() const; + cmBacktraceRange GetLinkDirectoriesEntryBacktraces() const; + void AppendLinkDirectoriesEntry(std::string const& vec, + cmListFileBacktrace const& lfbt); + void SetLinkDirectories(std::string const& vec, + cmListFileBacktrace const& lfbt); + void ClearLinkDirectories(); + void SetProperty(const std::string& prop, const char* value, cmListFileBacktrace const& lfbt); void AppendProperty(const std::string& prop, const char* value, diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h index 7177221..e76f2af 100644 --- a/Source/cmStatePrivate.h +++ b/Source/cmStatePrivate.h @@ -43,6 +43,7 @@ struct cmStateDetail::SnapshotDataType std::vector::size_type CompileDefinitionsPosition; std::vector::size_type CompileOptionsPosition; std::vector::size_type LinkOptionsPosition; + std::vector::size_type LinkDirectoriesPosition; }; struct cmStateDetail::PolicyStackEntry : public cmPolicies::PolicyMap @@ -88,6 +89,9 @@ struct cmStateDetail::BuildsystemDirectoryStateType std::vector LinkOptions; std::vector LinkOptionsBacktraces; + std::vector LinkDirectories; + std::vector LinkDirectoriesBacktraces; + std::vector NormalTargetNames; std::string ProjectName; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index 0379e7e..c2510f3 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -398,6 +398,13 @@ void cmStateSnapshot::InitializeFromParent() this->Position->BuildSystemDirectory->LinkOptionsBacktraces, this->Position->LinkOptionsPosition); + InitializeContentFromParent( + parent->BuildSystemDirectory->LinkDirectories, + this->Position->BuildSystemDirectory->LinkDirectories, + parent->BuildSystemDirectory->LinkDirectoriesBacktraces, + this->Position->BuildSystemDirectory->LinkDirectoriesBacktraces, + this->Position->LinkDirectoriesPosition); + const char* include_regex = parent->BuildSystemDirectory->Properties.GetPropertyValue( "INCLUDE_REGULAR_EXPRESSION"); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 6cf7c42..c5295f2 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -168,6 +168,8 @@ public: std::vector SourceBacktraces; std::vector LinkOptionsEntries; std::vector LinkOptionsBacktraces; + std::vector LinkDirectoriesEntries; + std::vector LinkDirectoriesBacktraces; std::vector LinkImplementationPropertyEntries; std::vector LinkImplementationPropertyBacktraces; }; @@ -391,6 +393,18 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->Internal->LinkOptionsBacktraces.insert( this->Internal->LinkOptionsBacktraces.end(), parentLinkOptionsBts.begin(), parentLinkOptionsBts.end()); + + const cmStringRange parentLinkDirectories = + this->Makefile->GetLinkDirectoriesEntries(); + const cmBacktraceRange parentLinkDirectoriesBts = + this->Makefile->GetLinkDirectoriesBacktraces(); + + this->Internal->LinkDirectoriesEntries.insert( + this->Internal->LinkDirectoriesEntries.end(), + parentLinkDirectories.begin(), parentLinkDirectories.end()); + this->Internal->LinkDirectoriesBacktraces.insert( + this->Internal->LinkDirectoriesBacktraces.end(), + parentLinkDirectoriesBts.begin(), parentLinkDirectoriesBts.end()); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && @@ -654,19 +668,6 @@ cmSourceFile* cmTarget::AddSource(const std::string& src) cmSourceFileLocationKind::Known); } -void cmTarget::AddLinkDirectory(const std::string& d) -{ - // Make sure we don't add unnecessary search directories. - if (this->LinkDirectoriesEmmitted.insert(d).second) { - this->LinkDirectories.push_back(d); - } -} - -const std::vector& cmTarget::GetLinkDirectories() const -{ - return this->LinkDirectories; -} - void cmTarget::ClearDependencyInformation(cmMakefile& mf) { std::string depname = this->GetName(); @@ -874,6 +875,16 @@ cmBacktraceRange cmTarget::GetLinkOptionsBacktraces() const return cmMakeRange(this->Internal->LinkOptionsBacktraces); } +cmStringRange cmTarget::GetLinkDirectoriesEntries() const +{ + return cmMakeRange(this->Internal->LinkDirectoriesEntries); +} + +cmBacktraceRange cmTarget::GetLinkDirectoriesBacktraces() const +{ + return cmMakeRange(this->Internal->LinkDirectoriesBacktraces); +} + cmStringRange cmTarget::GetLinkImplementationEntries() const { return cmMakeRange(this->Internal->LinkImplementationPropertyEntries); @@ -900,6 +911,7 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) MAKE_STATIC_PROP(IMPORTED_GLOBAL); MAKE_STATIC_PROP(INCLUDE_DIRECTORIES); MAKE_STATIC_PROP(LINK_OPTIONS); + MAKE_STATIC_PROP(LINK_DIRECTORIES); MAKE_STATIC_PROP(LINK_LIBRARIES); MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES); MAKE_STATIC_PROP(NAME); @@ -986,6 +998,14 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); this->Internal->LinkOptionsBacktraces.push_back(lfbt); } + } else if (prop == propLINK_DIRECTORIES) { + this->Internal->LinkDirectoriesEntries.clear(); + this->Internal->LinkDirectoriesBacktraces.clear(); + if (value) { + this->Internal->LinkDirectoriesEntries.push_back(value); + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + this->Internal->LinkDirectoriesBacktraces.push_back(lfbt); + } } else if (prop == propLINK_LIBRARIES) { this->Internal->LinkImplementationPropertyEntries.clear(); this->Internal->LinkImplementationPropertyBacktraces.clear(); @@ -1097,6 +1117,12 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value, cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); this->Internal->LinkOptionsBacktraces.push_back(lfbt); } + } else if (prop == "LINK_DIRECTORIES") { + if (value && *value) { + this->Internal->LinkDirectoriesEntries.push_back(value); + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + this->Internal->LinkDirectoriesBacktraces.push_back(lfbt); + } } else if (prop == "LINK_LIBRARIES") { if (value && *value) { cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); @@ -1194,6 +1220,21 @@ void cmTarget::InsertLinkOption(std::string const& entry, this->Internal->LinkOptionsBacktraces.insert(btPosition, bt); } +void cmTarget::InsertLinkDirectory(std::string const& entry, + cmListFileBacktrace const& bt, bool before) +{ + std::vector::iterator position = before + ? this->Internal->LinkDirectoriesEntries.begin() + : this->Internal->LinkDirectoriesEntries.end(); + + std::vector::iterator btPosition = before + ? this->Internal->LinkDirectoriesBacktraces.begin() + : this->Internal->LinkDirectoriesBacktraces.end(); + + this->Internal->LinkDirectoriesEntries.insert(position, entry); + this->Internal->LinkDirectoriesBacktraces.insert(btPosition, bt); +} + static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop, const char* value, cmMakefile* context, @@ -1314,6 +1355,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const MAKE_STATIC_PROP(COMPILE_OPTIONS); MAKE_STATIC_PROP(COMPILE_DEFINITIONS); MAKE_STATIC_PROP(LINK_OPTIONS); + MAKE_STATIC_PROP(LINK_DIRECTORIES); MAKE_STATIC_PROP(IMPORTED); MAKE_STATIC_PROP(IMPORTED_GLOBAL); MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES); @@ -1330,6 +1372,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const specialProps.insert(propCOMPILE_OPTIONS); specialProps.insert(propCOMPILE_DEFINITIONS); specialProps.insert(propLINK_OPTIONS); + specialProps.insert(propLINK_DIRECTORIES); specialProps.insert(propIMPORTED); specialProps.insert(propIMPORTED_GLOBAL); specialProps.insert(propMANUALLY_ADDED_DEPENDENCIES); @@ -1397,6 +1440,16 @@ const char* cmTarget::GetProperty(const std::string& prop) const output = cmJoin(this->Internal->LinkOptionsEntries, ";"); return output.c_str(); } + if (prop == propLINK_DIRECTORIES) { + if (this->Internal->LinkDirectoriesEntries.empty()) { + return nullptr; + } + + static std::string output; + output = cmJoin(this->Internal->LinkDirectoriesEntries, ";"); + + return output.c_str(); + } if (prop == propMANUALLY_ADDED_DEPENDENCIES) { if (this->Utilities.empty()) { return nullptr; diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 1f380df..694de1c 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -154,10 +154,6 @@ public: cmListFileContext const& lfc); void GetTllSignatureTraces(std::ostream& s, TLLSignature sig) const; - const std::vector& GetLinkDirectories() const; - - void AddLinkDirectory(const std::string& d); - /** * Set the path where this target should be installed. This is relative to * INSTALL_PREFIX @@ -247,6 +243,8 @@ public: cmListFileBacktrace const& bt); void InsertLinkOption(std::string const& entry, cmListFileBacktrace const& bt, bool before = false); + void InsertLinkDirectory(std::string const& entry, + cmListFileBacktrace const& bt, bool before = false); void AppendBuildInterfaceIncludes(); @@ -277,6 +275,9 @@ public: cmStringRange GetLinkOptionsEntries() const; cmBacktraceRange GetLinkOptionsBacktraces() const; + cmStringRange GetLinkDirectoriesEntries() const; + cmBacktraceRange GetLinkDirectoriesBacktraces() const; + cmStringRange GetLinkImplementationEntries() const; cmBacktraceRange GetLinkImplementationBacktraces() const; @@ -306,14 +307,12 @@ private: bool IsGeneratorProvided; cmPropertyMap Properties; std::set SystemIncludeDirectories; - std::set LinkDirectoriesEmmitted; std::set Utilities; std::map UtilityBacktraces; cmPolicies::PolicyMap PolicyMap; std::string Name; std::string InstallPath; std::string RuntimeInstallPath; - std::vector LinkDirectories; std::vector PreBuildCommands; std::vector PreLinkCommands; std::vector PostBuildCommands; diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx new file mode 100644 index 0000000..bca3e45 --- /dev/null +++ b/Source/cmTargetLinkDirectoriesCommand.cxx @@ -0,0 +1,61 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmTargetLinkDirectoriesCommand.h" + +#include + +#include "cmAlgorithms.h" +#include "cmGeneratorExpression.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmake.h" + +class cmExecutionStatus; + +bool cmTargetLinkDirectoriesCommand::InitialPass( + std::vector const& args, cmExecutionStatus&) +{ + return this->HandleArguments(args, "LINK_DIRECTORIES", PROCESS_BEFORE); +} + +void cmTargetLinkDirectoriesCommand::HandleMissingTarget( + const std::string& name) +{ + std::ostringstream e; + e << "Cannot specify link directories for target \"" << name + << "\" which is not built by this project."; + this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); +} + +std::string cmTargetLinkDirectoriesCommand::Join( + const std::vector& content) +{ + std::vector directories; + + for (const auto& dir : content) { + auto unixPath = dir; + cmSystemTools::ConvertToUnixSlashes(unixPath); + if (!cmSystemTools::FileIsFullPath(unixPath) && + !cmGeneratorExpression::StartsWithGeneratorExpression(unixPath)) { + auto tmp = this->Makefile->GetCurrentSourceDirectory(); + tmp += "/"; + tmp += unixPath; + unixPath = tmp; + } + directories.push_back(unixPath); + } + + return cmJoin(directories, ";"); +} + +bool cmTargetLinkDirectoriesCommand::HandleDirectContent( + cmTarget* tgt, const std::vector& content, bool prepend, bool) +{ + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + + tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend); + + return true; // Successfully handled. +} diff --git a/Source/cmTargetLinkDirectoriesCommand.h b/Source/cmTargetLinkDirectoriesCommand.h new file mode 100644 index 0000000..52c75a0 --- /dev/null +++ b/Source/cmTargetLinkDirectoriesCommand.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 cmTargetLinkDirectoriesCommand_h +#define cmTargetLinkDirectoriesCommand_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include + +#include "cmTargetPropCommandBase.h" + +class cmCommand; +class cmExecutionStatus; +class cmTarget; + +class cmTargetLinkDirectoriesCommand : public cmTargetPropCommandBase +{ +public: + /** + * This is a virtual constructor for the command. + */ + cmCommand* Clone() override { return new cmTargetLinkDirectoriesCommand; } + + /** + * This is called when the command is first encountered in + * the CMakeLists.txt file. + */ + bool InitialPass(std::vector const& args, + cmExecutionStatus& status) override; + +private: + void HandleMissingTarget(const std::string& name) override; + + std::string Join(const std::vector& content) override; + bool HandleDirectContent(cmTarget* tgt, + const std::vector& content, + bool prepend, bool system) override; +}; + +#endif diff --git a/Tests/CMakeCommands/target_link_directories/CMakeLists.txt b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt new file mode 100644 index 0000000..bc7b9b2 --- /dev/null +++ b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt @@ -0,0 +1,40 @@ + +cmake_minimum_required(VERSION 3.12) + +project(target_link_directories LANGUAGES C) + +add_library(target_link_directories SHARED LinkDirectoriesLib.c) +# Test no items +target_link_directories(target_link_directories PRIVATE) + +add_library(target_link_directories_2 SHARED EXCLUDE_FROM_ALL LinkDirectoriesLib.c) +target_link_directories(target_link_directories_2 PRIVATE /private/dir INTERFACE /interface/dir) +get_target_property(result target_link_directories_2 LINK_DIRECTORIES) +if (NOT result MATCHES "/private/dir") + message(SEND_ERROR "${result} target_link_directories not populated the LINK_DIRECTORIES target property") +endif() +get_target_property(result target_link_directories_2 INTERFACE_LINK_DIRECTORIES) +if (NOT result MATCHES "/interface/dir") + message(SEND_ERROR "target_link_directories not populated the INTERFACE_LINK_DIRECTORIES target property of shared library") +endif() + +add_library(target_link_directories_3 STATIC EXCLUDE_FROM_ALL LinkDirectoriesLib.c) +target_link_directories(target_link_directories_3 INTERFACE /interface/dir) +get_target_property(result target_link_directories_3 INTERFACE_LINK_DIRECTORIES) +if (NOT result MATCHES "/interface/dir") + message(SEND_ERROR "target_link_directories not populated the INTERFACE_LINK_DIRECTORIES target property of static library") +endif() + +add_library(target_link_directories_4 SHARED EXCLUDE_FROM_ALL LinkDirectoriesLib.c) +target_link_directories(target_link_directories_4 PRIVATE relative/dir) +get_target_property(result target_link_directories_4 LINK_DIRECTORIES) +if (NOT result MATCHES "${CMAKE_CURRENT_SOURCE_DIR}/relative/dir") + message(SEND_ERROR "target_link_directories not populated the LINK_DIRECTORIES with relative path") +endif() + +add_subdirectory(subdir) +target_link_directories(target_link_directories_5 PRIVATE relative/dir) +get_target_property(result target_link_directories_5 LINK_DIRECTORIES) +if (NOT result MATCHES "${CMAKE_CURRENT_SOURCE_DIR}/relative/dir") + message(SEND_ERROR "target_link_directories not populated the LINK_DIRECTORIES with relative path") +endif() diff --git a/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c b/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c new file mode 100644 index 0000000..9bbd24c --- /dev/null +++ b/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c @@ -0,0 +1,7 @@ +#if defined(_WIN32) +__declspec(dllexport) +#endif + int flags_lib(void) +{ + return 0; +} diff --git a/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt b/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt new file mode 100644 index 0000000..7e7ad2a --- /dev/null +++ b/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_library(target_link_directories_5 SHARED EXCLUDE_FROM_ALL ../LinkDirectoriesLib.c) diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index fb44077..4753aac 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2823,6 +2823,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release ADD_TEST_MACRO(CMakeCommands.add_compile_definitions add_compile_definitions) ADD_TEST_MACRO(CMakeCommands.add_compile_options add_compile_options) ADD_TEST_MACRO(CMakeCommands.target_link_libraries target_link_libraries) + ADD_TEST_MACRO(CMakeCommands.target_link_directories) ADD_TEST_MACRO(CMakeCommands.target_include_directories target_include_directories) ADD_TEST_MACRO(CMakeCommands.target_compile_definitions target_compile_definitions) ADD_TEST_MACRO(CMakeCommands.target_compile_options target_compile_options) diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index cb048be..c6b7dbc 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -619,6 +619,18 @@ export(TARGETS testLinkOptions NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake) #------------------------------------------------------------------------------ +# test export of INTERFACE_LINK_DIRECTORIES +add_library(testLinkDirectories INTERFACE) +target_link_directories(testLinkDirectories INTERFACE + $ + $) + +install(TARGETS testLinkDirectories + EXPORT RequiredExp DESTINATION lib) +export(TARGETS testLinkDirectories NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake) + + +#------------------------------------------------------------------------------ # test export of INTERFACE_LINK_DEPENDS if(CMAKE_GENERATOR MATCHES "Make|Ninja") add_library(testLinkDepends INTERFACE) diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 8791a19..67fcc02 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -490,6 +490,11 @@ checkForProperty(bld_testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG") checkForProperty(Req::testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG") #--------------------------------------------------------------------------------- +# check that imported libraries have the expected INTERFACE_LINK_DIRECTORIES property +checkForProperty(bld_testLinkDirectories "INTERFACE_LINK_DIRECTORIES" "/interface/build") +checkForProperty(Req::testLinkDirectories "INTERFACE_LINK_DIRECTORIES" "${CMAKE_INSTALL_PREFIX}/interface/install") + +#--------------------------------------------------------------------------------- # check that imported libraries have the expected INTERFACE_LINK_DEPENDS property if(CMAKE_GENERATOR MATCHES "Make|Ninja") checkForProperty(bld_testLinkDepends "INTERFACE_LINK_DEPENDS" "BUILD_LINK_DEPENDS") diff --git a/Tests/LinkDirectory/External/CMakeLists.txt b/Tests/LinkDirectory/External/CMakeLists.txt index f7c840f..d2a1f9f 100644 --- a/Tests/LinkDirectory/External/CMakeLists.txt +++ b/Tests/LinkDirectory/External/CMakeLists.txt @@ -1,6 +1,20 @@ cmake_minimum_required(VERSION 2.8) project(LinkDirectoryExternal C) + +add_executable(myexe2 myexe.c) +set_property(TARGET myexe2 PROPERTY OUTPUT_NAME LinkDirectory2) +target_link_directories(myexe2 PRIVATE lib "${CMAKE_CURRENT_SOURCE_DIR}/../lib") +target_link_libraries(myexe2 PRIVATE mylibA mylibB) + +add_library (mylibs INTERFACE) +target_link_directories(mylibs INTERFACE lib "${CMAKE_CURRENT_SOURCE_DIR}/../lib") +target_link_libraries(mylibs INTERFACE mylibA mylibB) +add_executable(myexe3 myexe.c) +set_property(TARGET myexe3 PROPERTY OUTPUT_NAME LinkDirectory3) +target_link_libraries(myexe3 PRIVATE mylibs) + + # Test CMP0015 OLD behavior: -L../lib cmake_policy(SET CMP0015 OLD) link_directories(../lib) diff --git a/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt b/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt new file mode 100644 index 0000000..580c373 --- /dev/null +++ b/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt @@ -0,0 +1,2 @@ +-- Target LINK_DIRECTORIES is 'a;b;c;d;;e' +-- Directory LINK_DIRECTORIES is 'a;b;c;d;;e' diff --git a/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake b/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake new file mode 100644 index 0000000..8529ef5 --- /dev/null +++ b/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake @@ -0,0 +1,3 @@ +include(Common.cmake) +test_target_property(LINK_DIRECTORIES) +test_directory_property(LINK_DIRECTORIES) diff --git a/Tests/RunCMake/set_property/RunCMakeTest.cmake b/Tests/RunCMake/set_property/RunCMakeTest.cmake index 77da703..8d4614c 100644 --- a/Tests/RunCMake/set_property/RunCMakeTest.cmake +++ b/Tests/RunCMake/set_property/RunCMakeTest.cmake @@ -6,6 +6,7 @@ run_cmake(COMPILE_OPTIONS) run_cmake(IMPORTED_GLOBAL) run_cmake(INCLUDE_DIRECTORIES) run_cmake(LINK_OPTIONS) +run_cmake(LINK_DIRECTORIES) run_cmake(LINK_LIBRARIES) run_cmake(SOURCES) run_cmake(TYPE) -- cgit v0.12