Commit a71caab4 authored by Marc Chevrier's avatar Marc Chevrier Committed by Craig Scott

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
parent 5ca130e2
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.
target_link_directories
-----------------------
Add link directories to a target.
::
target_link_directories(<target> [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [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 ``<target>`` must have been created by a command such as
:command:`add_executable` or :command:`add_library` and must not be an
:ref:`ALIAS target <Alias Targets>`.
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 ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the
:prop_tgt:`INTERFACE_LINK_DIRECTORIES` property of ``<target>``
(:ref:`IMPORTED targets <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 ``<target>`` 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``).
......@@ -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
......
......@@ -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
......
......@@ -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 <CMake Language Lists>` 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.
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:: ``$<TARGET_PROPERTY:foo,INTERFACE_LINK_DIRECTORIES>``
.. include:: INTERFACE_BUILD_PROPERTY.txt
LINK_DIRECTORIES
----------------
List of directories to use for the link step of shared library, module
and executable targets.
This property holds a :ref:`;-list <CMake Language Lists>` 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.
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.
......@@ -579,6 +579,8 @@ set(SRCS
cmTargetIncludeDirectoriesCommand.h
cmTargetLinkOptionsCommand.cxx
cmTargetLinkOptionsCommand.h
cmTargetLinkDirectoriesCommand.cxx
cmTargetLinkDirectoriesCommand.h
cmTargetLinkLibrariesCommand.cxx
cmTargetLinkLibrariesCommand.h
cmTargetPropCommandBase.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,
......
......@@ -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);
......
......@@ -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<std::string> 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<std::string> const& dirs = this->Target->GetLinkDirectories();
this->OldLinkDirMask.insert(dirs.begin(), dirs.end());
this->OldLinkDirMask.insert(directories.begin(), directories.end());
}
this->CMP0060Warn = this->Makefile->PolicyOptionalWarningEnabled(
......
......@@ -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);
......
......@@ -451,6 +451,37 @@ void cmExportFileGenerator::PopulateLinkDependsInterface(
}
}
void cmExportFileGenerator::PopulateLinkDirectoriesInterface(
cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties, std::vector<std::string>& 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,
......
......@@ -147,6 +147,10 @@ protected:
cmTargetExport* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
void PopulateLinkDirectoriesInterface(
cmTargetExport* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
void PopulateLinkDependsInterface(
cmTargetExport* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
......
......@@ -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);
......
......@@ -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) \
......
......@@ -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<std::string>& cmGeneratorTarget::GetLinkDirectories() const
{
return this->Target->GetLinkDirectories();
}
const std::set<std::string>& cmGeneratorTarget::GetUtilities() const
{
return this->Target->GetUtilities();
......@@ -3067,6 +3068,102 @@ void cmGeneratorTarget::GetStaticLibraryLinkOptions(
cmDeleteAll(entries);
}
namespace {
void processLinkDirectories(
cmGeneratorTarget const* tgt,
const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
std::vector<std::string>& directories,
std::unordered_set<std::string>& 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<std::string> 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<std::string>& result,
const std::string& config,
const std::string& language) const
{
std::unordered_set<std::string> uniqueDirectories;
cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr,
nullptr);
std::vector<std::string> 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<cmGeneratorTarget::TargetPropertyEntry*>
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,
......
......@@ -273,8 +273,6 @@ public:
cmListFileBacktrace GetBacktrace() const;
const std::vector<std::string>& GetLinkDirectories() const;
std::set<std::string> 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<std::string>& result,
const std::string& config,
const std::string& language) const;
void GetLinkDepends(std::vector<std::string>& result,
const std::string& config,
const std::string& language) const;
......@@ -825,6 +827,7 @@ private:
std::vector<TargetPropertyEntry*> CompileFeaturesEntries;
std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
std::vector<TargetPropertyEntry*> LinkOptionsEntries;
std::vector<TargetPropertyEntry*> LinkDirectoriesEntries;
std::vector<TargetPropertyEntry*> SourceEntries;
mutable std::set<std::string> 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;
......
......@@ -4,6 +4,7 @@
#include <sstream>
#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);
}
......@@ -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<std::string> 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<std::string> linkLibs;
......
......@@ -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<std::string> const& GetSystemIncludeDirectories() const
{
......
......@@ -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)) {
......
......@@ -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();