浏览代码

cmGeneratorTarget: Factor options-like usage requirements into own source

Brad King 1 年之前
父节点
当前提交
cdd7112a5d
共有 4 个文件被更改,包括 659 次插入624 次删除
  1. 1 0
      Source/CMakeLists.txt
  2. 0 624
      Source/cmGeneratorTarget.cxx
  3. 657 0
      Source/cmGeneratorTarget_Options.cxx
  4. 1 0
      bootstrap

+ 1 - 0
Source/CMakeLists.txt

@@ -287,6 +287,7 @@ add_library(
   cmGeneratorTarget.h
   cmGeneratorTarget_IncludeDirectories.cxx
   cmGeneratorTarget_LinkDirectories.cxx
+  cmGeneratorTarget_Options.cxx
   cmGeneratorTarget_Sources.cxx
   cmGeneratorTarget_TargetPropertyEntry.cxx
   cmLinkItemGraphVisitor.cxx

+ 0 - 624
Source/cmGeneratorTarget.cxx

@@ -27,7 +27,6 @@
 #include "cmComputeLinkInformation.h"
 #include "cmCryptoHash.h"
 #include "cmCxxModuleUsageEffects.h"
-#include "cmEvaluatedTargetProperty.h"
 #include "cmExperimental.h"
 #include "cmFileSet.h"
 #include "cmFileTimes.h"
@@ -2770,372 +2769,6 @@ std::string cmGeneratorTarget::GetClangTidyExportFixesDirectory(
   return cmSystemTools::CollapseFullPath(path);
 }
 
-enum class OptionsParse
-{
-  None,
-  Shell
-};
-
-namespace {
-const auto DL_BEGIN = "<DEVICE_LINK>"_s;
-const auto DL_END = "</DEVICE_LINK>"_s;
-
-void processOptions(cmGeneratorTarget const* tgt,
-                    EvaluatedTargetPropertyEntries const& entries,
-                    std::vector<BT<std::string>>& options,
-                    std::unordered_set<std::string>& uniqueOptions,
-                    bool debugOptions, const char* logName, OptionsParse parse,
-                    bool processDeviceOptions = false)
-{
-  bool splitOption = !processDeviceOptions;
-  for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) {
-    std::string usedOptions;
-    for (std::string const& opt : entry.Values) {
-      if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) {
-        options.emplace_back(opt, entry.Backtrace);
-        splitOption = opt == DL_BEGIN;
-        continue;
-      }
-
-      if (uniqueOptions.insert(opt).second) {
-        if (parse == OptionsParse::Shell &&
-            cmHasLiteralPrefix(opt, "SHELL:")) {
-          if (splitOption) {
-            std::vector<std::string> tmp;
-            cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp);
-            for (std::string& o : tmp) {
-              options.emplace_back(std::move(o), entry.Backtrace);
-            }
-          } else {
-            options.emplace_back(std::string(opt.c_str() + 6),
-                                 entry.Backtrace);
-          }
-        } else {
-          options.emplace_back(opt, entry.Backtrace);
-        }
-        if (debugOptions) {
-          usedOptions += " * " + opt + "\n";
-        }
-      }
-    }
-    if (!usedOptions.empty()) {
-      tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
-        MessageType::LOG,
-        std::string("Used ") + logName + std::string(" for target ") +
-          tgt->GetName() + ":\n" + usedOptions,
-        entry.Backtrace);
-    }
-  }
-}
-
-std::vector<BT<std::string>> wrapOptions(
-  std::vector<std::string>& options, const cmListFileBacktrace& bt,
-  const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep,
-  bool concatFlagAndArgs)
-{
-  std::vector<BT<std::string>> result;
-
-  if (options.empty()) {
-    return result;
-  }
-
-  if (wrapperFlag.empty()) {
-    // nothing specified, insert elements as is
-    result.reserve(options.size());
-    for (std::string& o : options) {
-      result.emplace_back(std::move(o), bt);
-    }
-    return result;
-  }
-
-  for (std::vector<std::string>::size_type index = 0; index < options.size();
-       index++) {
-    if (cmHasLiteralPrefix(options[index], "LINKER:")) {
-      // LINKER wrapper specified, insert elements as is
-      result.emplace_back(std::move(options[index]), bt);
-      continue;
-    }
-    if (cmHasLiteralPrefix(options[index], "-Wl,")) {
-      // replace option by LINKER wrapper
-      result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt);
-      continue;
-    }
-    if (cmHasLiteralPrefix(options[index], "-Xlinker=")) {
-      // replace option by LINKER wrapper
-      result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt);
-      continue;
-    }
-    if (options[index] == "-Xlinker") {
-      // replace option by LINKER wrapper
-      if (index + 1 < options.size()) {
-        result.emplace_back("LINKER:" + options[++index], bt);
-      } else {
-        result.emplace_back(std::move(options[index]), bt);
-      }
-      continue;
-    }
-
-    // collect all options which must be transformed
-    std::vector<std::string> opts;
-    while (index < options.size()) {
-      if (!cmHasLiteralPrefix(options[index], "LINKER:") &&
-          !cmHasLiteralPrefix(options[index], "-Wl,") &&
-          !cmHasLiteralPrefix(options[index], "-Xlinker")) {
-        opts.emplace_back(std::move(options[index++]));
-      } else {
-        --index;
-        break;
-      }
-    }
-    if (opts.empty()) {
-      continue;
-    }
-
-    if (!wrapperSep.empty()) {
-      if (concatFlagAndArgs) {
-        // insert flag elements except last one
-        for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) {
-          result.emplace_back(*i, bt);
-        }
-        // concatenate last flag element and all list values
-        // in one option
-        result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt);
-      } else {
-        for (std::string const& i : wrapperFlag) {
-          result.emplace_back(i, bt);
-        }
-        // concatenate all list values in one option
-        result.emplace_back(cmJoin(opts, wrapperSep), bt);
-      }
-    } else {
-      // prefix each element of list with wrapper
-      if (concatFlagAndArgs) {
-        std::transform(opts.begin(), opts.end(), opts.begin(),
-                       [&wrapperFlag](std::string const& o) -> std::string {
-                         return wrapperFlag.back() + o;
-                       });
-      }
-      for (std::string& o : opts) {
-        for (auto i = wrapperFlag.begin(),
-                  e = concatFlagAndArgs ? wrapperFlag.end() - 1
-                                        : wrapperFlag.end();
-             i != e; ++i) {
-          result.emplace_back(*i, bt);
-        }
-        result.emplace_back(std::move(o), bt);
-      }
-    }
-  }
-  return result;
-}
-}
-
-void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
-                                          const std::string& config,
-                                          const std::string& language) const
-{
-  std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language);
-  result.reserve(tmp.size());
-  for (BT<std::string>& v : tmp) {
-    result.emplace_back(std::move(v.Value));
-  }
-}
-
-std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
-  std::string const& config, std::string const& language) const
-{
-  ConfigAndLanguage cacheKey(config, language);
-  {
-    auto it = this->CompileOptionsCache.find(cacheKey);
-    if (it != this->CompileOptionsCache.end()) {
-      return it->second;
-    }
-  }
-  std::vector<BT<std::string>> result;
-  std::unordered_set<std::string> uniqueOptions;
-
-  cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr,
-                                             nullptr, this->LocalGenerator);
-
-  cmList debugProperties{ this->Makefile->GetDefinition(
-    "CMAKE_DEBUG_TARGET_PROPERTIES") };
-  bool debugOptions = !this->DebugCompileOptionsDone &&
-    cm::contains(debugProperties, "COMPILE_OPTIONS");
-
-  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
-    this->DebugCompileOptionsDone = true;
-  }
-
-  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
-    this, config, language, &dagChecker, this->CompileOptionsEntries);
-
-  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language,
-                      &dagChecker, entries, IncludeRuntimeInterface::Yes);
-
-  processOptions(this, entries, result, uniqueOptions, debugOptions,
-                 "compile options", OptionsParse::Shell);
-
-  CompileOptionsCache.emplace(cacheKey, result);
-  return result;
-}
-
-void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
-                                           const std::string& config) const
-{
-  std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config);
-  result.reserve(tmp.size());
-  for (BT<std::string>& v : tmp) {
-    result.emplace_back(std::move(v.Value));
-  }
-}
-
-std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures(
-  std::string const& config) const
-{
-  std::vector<BT<std::string>> result;
-  std::unordered_set<std::string> uniqueFeatures;
-
-  cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr,
-                                             nullptr, this->LocalGenerator);
-
-  cmList debugProperties{ this->Makefile->GetDefinition(
-    "CMAKE_DEBUG_TARGET_PROPERTIES") };
-  bool debugFeatures = !this->DebugCompileFeaturesDone &&
-    cm::contains(debugProperties, "COMPILE_FEATURES");
-
-  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
-    this->DebugCompileFeaturesDone = true;
-  }
-
-  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
-    this, config, std::string(), &dagChecker, this->CompileFeaturesEntries);
-
-  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES",
-                      std::string(), &dagChecker, entries,
-                      IncludeRuntimeInterface::Yes);
-
-  processOptions(this, entries, result, uniqueFeatures, debugFeatures,
-                 "compile features", OptionsParse::None);
-
-  return result;
-}
-
-void cmGeneratorTarget::GetCompileDefinitions(
-  std::vector<std::string>& result, const std::string& config,
-  const std::string& language) const
-{
-  std::vector<BT<std::string>> tmp =
-    this->GetCompileDefinitions(config, language);
-  result.reserve(tmp.size());
-  for (BT<std::string>& v : tmp) {
-    result.emplace_back(std::move(v.Value));
-  }
-}
-
-std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
-  std::string const& config, std::string const& language) const
-{
-  ConfigAndLanguage cacheKey(config, language);
-  {
-    auto it = this->CompileDefinitionsCache.find(cacheKey);
-    if (it != this->CompileDefinitionsCache.end()) {
-      return it->second;
-    }
-  }
-  std::vector<BT<std::string>> list;
-  std::unordered_set<std::string> uniqueOptions;
-
-  cmGeneratorExpressionDAGChecker dagChecker(
-    this, "COMPILE_DEFINITIONS", nullptr, nullptr, this->LocalGenerator);
-
-  cmList debugProperties{ this->Makefile->GetDefinition(
-    "CMAKE_DEBUG_TARGET_PROPERTIES") };
-  bool debugDefines = !this->DebugCompileDefinitionsDone &&
-    cm::contains(debugProperties, "COMPILE_DEFINITIONS");
-
-  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
-    this->DebugCompileDefinitionsDone = true;
-  }
-
-  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
-    this, config, language, &dagChecker, this->CompileDefinitionsEntries);
-
-  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language,
-                      &dagChecker, entries, IncludeRuntimeInterface::Yes);
-
-  if (!config.empty()) {
-    std::string configPropName =
-      "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config);
-    cmValue configProp = this->GetProperty(configPropName);
-    if (configProp) {
-      switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) {
-        case cmPolicies::WARN: {
-          this->LocalGenerator->IssueMessage(
-            MessageType::AUTHOR_WARNING,
-            cmPolicies::GetPolicyWarning(cmPolicies::CMP0043));
-          CM_FALLTHROUGH;
-        }
-        case cmPolicies::OLD: {
-          std::unique_ptr<TargetPropertyEntry> entry =
-            TargetPropertyEntry::Create(
-              *this->LocalGenerator->GetCMakeInstance(), *configProp);
-          entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
-            this, config, language, &dagChecker, *entry));
-        } break;
-        case cmPolicies::NEW:
-        case cmPolicies::REQUIRED_ALWAYS:
-        case cmPolicies::REQUIRED_IF_USED:
-          break;
-      }
-    }
-  }
-
-  processOptions(this, entries, list, uniqueOptions, debugDefines,
-                 "compile definitions", OptionsParse::None);
-
-  this->CompileDefinitionsCache.emplace(cacheKey, list);
-  return list;
-}
-
-std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
-  const std::string& config, const std::string& language) const
-{
-  ConfigAndLanguage cacheKey(config, language);
-  {
-    auto it = this->PrecompileHeadersCache.find(cacheKey);
-    if (it != this->PrecompileHeadersCache.end()) {
-      return it->second;
-    }
-  }
-  std::unordered_set<std::string> uniqueOptions;
-
-  cmGeneratorExpressionDAGChecker dagChecker(
-    this, "PRECOMPILE_HEADERS", nullptr, nullptr, this->LocalGenerator);
-
-  cmList debugProperties{ this->Makefile->GetDefinition(
-    "CMAKE_DEBUG_TARGET_PROPERTIES") };
-  bool debugDefines = !this->DebugPrecompileHeadersDone &&
-    std::find(debugProperties.begin(), debugProperties.end(),
-              "PRECOMPILE_HEADERS") != debugProperties.end();
-
-  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
-    this->DebugPrecompileHeadersDone = true;
-  }
-
-  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
-    this, config, language, &dagChecker, this->PrecompileHeadersEntries);
-
-  AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language,
-                      &dagChecker, entries, IncludeRuntimeInterface::Yes);
-
-  std::vector<BT<std::string>> list;
-  processOptions(this, entries, list, uniqueOptions, debugDefines,
-                 "precompile headers", OptionsParse::None);
-
-  this->PrecompileHeadersCache.emplace(cacheKey, list);
-  return list;
-}
-
 std::vector<std::string> cmGeneratorTarget::GetPchArchs(
   std::string const& config, std::string const& lang) const
 {
@@ -3481,263 +3114,6 @@ bool cmGeneratorTarget::IsSourceFilePartOfUnityBatch(
     this->UnityBatchedSourceFiles.end();
 }
 
-void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
-                                       const std::string& config,
-                                       const std::string& language) const
-{
-  if (this->IsDeviceLink() &&
-      this->GetPolicyStatusCMP0105() != cmPolicies::NEW) {
-    // link options are not propagated to the device link step
-    return;
-  }
-
-  std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language);
-  result.reserve(tmp.size());
-  for (BT<std::string>& v : tmp) {
-    result.emplace_back(std::move(v.Value));
-  }
-}
-
-std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
-  std::string const& config, std::string const& language) const
-{
-  ConfigAndLanguage cacheKey(
-    config, cmStrCat(language, this->IsDeviceLink() ? "-device" : ""));
-  {
-    auto it = this->LinkOptionsCache.find(cacheKey);
-    if (it != this->LinkOptionsCache.end()) {
-      return it->second;
-    }
-  }
-  std::vector<BT<std::string>> result;
-  std::unordered_set<std::string> uniqueOptions;
-
-  cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr,
-                                             nullptr, this->LocalGenerator);
-
-  cmList debugProperties{ this->Makefile->GetDefinition(
-    "CMAKE_DEBUG_TARGET_PROPERTIES") };
-  bool debugOptions = !this->DebugLinkOptionsDone &&
-    cm::contains(debugProperties, "LINK_OPTIONS");
-
-  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
-    this->DebugLinkOptionsDone = true;
-  }
-
-  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
-    this, config, language, &dagChecker, this->LinkOptionsEntries);
-
-  AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language,
-                      &dagChecker, entries, IncludeRuntimeInterface::Yes,
-                      this->GetPolicyStatusCMP0099() == cmPolicies::NEW
-                        ? UseTo::Link
-                        : UseTo::Compile);
-
-  processOptions(this, entries, result, uniqueOptions, debugOptions,
-                 "link options", OptionsParse::Shell, this->IsDeviceLink());
-
-  if (this->IsDeviceLink()) {
-    // wrap host link options
-    const std::string wrapper(this->Makefile->GetSafeDefinition(
-      "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG"));
-    cmList wrapperFlag{ wrapper };
-    const std::string wrapperSep(this->Makefile->GetSafeDefinition(
-      "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP"));
-    bool concatFlagAndArgs = true;
-    if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
-      concatFlagAndArgs = false;
-      wrapperFlag.pop_back();
-    }
-
-    auto it = result.begin();
-    while (it != result.end()) {
-      if (it->Value == DL_BEGIN) {
-        // device link options, no treatment
-        it = result.erase(it);
-        it = std::find_if(it, result.end(), [](const BT<std::string>& item) {
-          return item.Value == DL_END;
-        });
-        if (it != result.end()) {
-          it = result.erase(it);
-        }
-      } else {
-        // host link options must be wrapped
-        std::vector<std::string> options;
-        cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options);
-        auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag,
-                                       wrapperSep, concatFlagAndArgs);
-        it = result.erase(it);
-        // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect
-        // C++11 standard: 'std::vector::insert()' do not returns an iterator,
-        // so need to recompute the iterator after insertion.
-        if (it == result.end()) {
-          cm::append(result, hostOptions);
-          it = result.end();
-        } else {
-          auto index = it - result.begin();
-          result.insert(it, hostOptions.begin(), hostOptions.end());
-          it = result.begin() + index + hostOptions.size();
-        }
-      }
-    }
-  }
-
-  // Last step: replace "LINKER:" prefixed elements by
-  // actual linker wrapper
-  result = this->ResolveLinkerWrapper(result, language);
-
-  this->LinkOptionsCache.emplace(cacheKey, result);
-  return result;
-}
-
-std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
-  std::vector<BT<std::string>>& result, const std::string& language,
-  bool joinItems) const
-{
-  // replace "LINKER:" prefixed elements by actual linker wrapper
-  const std::string wrapper(this->Makefile->GetSafeDefinition(
-    "CMAKE_" + language +
-    (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG"
-                          : "_LINKER_WRAPPER_FLAG")));
-  cmList wrapperFlag{ wrapper };
-  const std::string wrapperSep(this->Makefile->GetSafeDefinition(
-    "CMAKE_" + language +
-    (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP"
-                          : "_LINKER_WRAPPER_FLAG_SEP")));
-  bool concatFlagAndArgs = true;
-  if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
-    concatFlagAndArgs = false;
-    wrapperFlag.pop_back();
-  }
-
-  const std::string LINKER{ "LINKER:" };
-  const std::string SHELL{ "SHELL:" };
-  const std::string LINKER_SHELL = LINKER + SHELL;
-
-  std::vector<BT<std::string>>::iterator entry;
-  while ((entry = std::find_if(result.begin(), result.end(),
-                               [&LINKER](BT<std::string> const& item) -> bool {
-                                 return item.Value.compare(0, LINKER.length(),
-                                                           LINKER) == 0;
-                               })) != result.end()) {
-    std::string value = std::move(entry->Value);
-    cmListFileBacktrace bt = std::move(entry->Backtrace);
-    entry = result.erase(entry);
-
-    std::vector<std::string> linkerOptions;
-    if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) {
-      cmSystemTools::ParseUnixCommandLine(
-        value.c_str() + LINKER_SHELL.length(), linkerOptions);
-    } else {
-      linkerOptions = cmTokenize(value.substr(LINKER.length()), ",");
-    }
-
-    if (linkerOptions.empty() ||
-        (linkerOptions.size() == 1 && linkerOptions.front().empty())) {
-      continue;
-    }
-
-    // for now, raise an error if prefix SHELL: is part of arguments
-    if (std::find_if(linkerOptions.begin(), linkerOptions.end(),
-                     [&SHELL](const std::string& item) -> bool {
-                       return item.find(SHELL) != std::string::npos;
-                     }) != linkerOptions.end()) {
-      this->LocalGenerator->GetCMakeInstance()->IssueMessage(
-        MessageType::FATAL_ERROR,
-        "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.",
-        this->GetBacktrace());
-      return result;
-    }
-
-    std::vector<BT<std::string>> options = wrapOptions(
-      linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
-    if (joinItems) {
-      result.insert(entry,
-                    cmJoin(cmRange<decltype(options.cbegin())>(
-                             options.cbegin(), options.cend()),
-                           " "_s));
-    } else {
-      result.insert(entry, options.begin(), options.end());
-    }
-  }
-  return result;
-}
-
-void cmGeneratorTarget::GetStaticLibraryLinkOptions(
-  std::vector<std::string>& result, const std::string& config,
-  const std::string& language) const
-{
-  std::vector<BT<std::string>> tmp =
-    this->GetStaticLibraryLinkOptions(config, language);
-  result.reserve(tmp.size());
-  for (BT<std::string>& v : tmp) {
-    result.emplace_back(std::move(v.Value));
-  }
-}
-
-std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions(
-  std::string const& config, std::string const& language) const
-{
-  std::vector<BT<std::string>> result;
-  std::unordered_set<std::string> uniqueOptions;
-
-  cmGeneratorExpressionDAGChecker dagChecker(
-    this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, this->LocalGenerator);
-
-  EvaluatedTargetPropertyEntries entries;
-  if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
-    std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create(
-      *this->LocalGenerator->GetCMakeInstance(), *linkOptions);
-    entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
-      this, config, language, &dagChecker, *entry));
-  }
-  processOptions(this, entries, result, uniqueOptions, false,
-                 "static library link options", OptionsParse::Shell);
-
-  return result;
-}
-
-void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
-                                       const std::string& config,
-                                       const std::string& language) const
-{
-  std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language);
-  result.reserve(tmp.size());
-  for (BT<std::string>& v : tmp) {
-    result.emplace_back(std::move(v.Value));
-  }
-}
-
-std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
-  std::string const& config, std::string const& language) const
-{
-  std::vector<BT<std::string>> result;
-  std::unordered_set<std::string> uniqueOptions;
-  cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr,
-                                             nullptr, this->LocalGenerator);
-
-  EvaluatedTargetPropertyEntries entries;
-  if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
-    cmList depends{ *linkDepends };
-    for (const auto& depend : depends) {
-      std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create(
-        *this->LocalGenerator->GetCMakeInstance(), depend);
-      entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
-        this, config, language, &dagChecker, *entry));
-    }
-  }
-  AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language,
-                      &dagChecker, entries, IncludeRuntimeInterface::Yes,
-                      this->GetPolicyStatusCMP0099() == cmPolicies::NEW
-                        ? UseTo::Link
-                        : UseTo::Compile);
-
-  processOptions(this, entries, result, uniqueOptions, false, "link depends",
-                 OptionsParse::None);
-
-  return result;
-}
-
 void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
 {
   if (this->IsImported()) {

+ 657 - 0
Source/cmGeneratorTarget_Options.cxx

@@ -0,0 +1,657 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+/* clang-format off */
+#include "cmGeneratorTarget.h"
+/* clang-format on */
+
+#include "cmConfigure.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+#include <cmext/algorithm>
+#include <cmext/string_view>
+
+#include "cmEvaluatedTargetProperty.h"
+#include "cmGeneratorExpressionDAGChecker.h"
+#include "cmGlobalGenerator.h"
+#include "cmList.h"
+#include "cmListFileCache.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmPolicies.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmValue.h"
+#include "cmake.h"
+
+enum class OptionsParse
+{
+  None,
+  Shell
+};
+
+namespace {
+const auto DL_BEGIN = "<DEVICE_LINK>"_s;
+const auto DL_END = "</DEVICE_LINK>"_s;
+
+void processOptions(cmGeneratorTarget const* tgt,
+                    EvaluatedTargetPropertyEntries const& entries,
+                    std::vector<BT<std::string>>& options,
+                    std::unordered_set<std::string>& uniqueOptions,
+                    bool debugOptions, const char* logName, OptionsParse parse,
+                    bool processDeviceOptions = false)
+{
+  bool splitOption = !processDeviceOptions;
+  for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) {
+    std::string usedOptions;
+    for (std::string const& opt : entry.Values) {
+      if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) {
+        options.emplace_back(opt, entry.Backtrace);
+        splitOption = opt == DL_BEGIN;
+        continue;
+      }
+
+      if (uniqueOptions.insert(opt).second) {
+        if (parse == OptionsParse::Shell &&
+            cmHasLiteralPrefix(opt, "SHELL:")) {
+          if (splitOption) {
+            std::vector<std::string> tmp;
+            cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp);
+            for (std::string& o : tmp) {
+              options.emplace_back(std::move(o), entry.Backtrace);
+            }
+          } else {
+            options.emplace_back(std::string(opt.c_str() + 6),
+                                 entry.Backtrace);
+          }
+        } else {
+          options.emplace_back(opt, entry.Backtrace);
+        }
+        if (debugOptions) {
+          usedOptions += " * " + opt + "\n";
+        }
+      }
+    }
+    if (!usedOptions.empty()) {
+      tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
+        MessageType::LOG,
+        std::string("Used ") + logName + std::string(" for target ") +
+          tgt->GetName() + ":\n" + usedOptions,
+        entry.Backtrace);
+    }
+  }
+}
+
+std::vector<BT<std::string>> wrapOptions(
+  std::vector<std::string>& options, const cmListFileBacktrace& bt,
+  const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep,
+  bool concatFlagAndArgs)
+{
+  std::vector<BT<std::string>> result;
+
+  if (options.empty()) {
+    return result;
+  }
+
+  if (wrapperFlag.empty()) {
+    // nothing specified, insert elements as is
+    result.reserve(options.size());
+    for (std::string& o : options) {
+      result.emplace_back(std::move(o), bt);
+    }
+    return result;
+  }
+
+  for (std::vector<std::string>::size_type index = 0; index < options.size();
+       index++) {
+    if (cmHasLiteralPrefix(options[index], "LINKER:")) {
+      // LINKER wrapper specified, insert elements as is
+      result.emplace_back(std::move(options[index]), bt);
+      continue;
+    }
+    if (cmHasLiteralPrefix(options[index], "-Wl,")) {
+      // replace option by LINKER wrapper
+      result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt);
+      continue;
+    }
+    if (cmHasLiteralPrefix(options[index], "-Xlinker=")) {
+      // replace option by LINKER wrapper
+      result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt);
+      continue;
+    }
+    if (options[index] == "-Xlinker") {
+      // replace option by LINKER wrapper
+      if (index + 1 < options.size()) {
+        result.emplace_back("LINKER:" + options[++index], bt);
+      } else {
+        result.emplace_back(std::move(options[index]), bt);
+      }
+      continue;
+    }
+
+    // collect all options which must be transformed
+    std::vector<std::string> opts;
+    while (index < options.size()) {
+      if (!cmHasLiteralPrefix(options[index], "LINKER:") &&
+          !cmHasLiteralPrefix(options[index], "-Wl,") &&
+          !cmHasLiteralPrefix(options[index], "-Xlinker")) {
+        opts.emplace_back(std::move(options[index++]));
+      } else {
+        --index;
+        break;
+      }
+    }
+    if (opts.empty()) {
+      continue;
+    }
+
+    if (!wrapperSep.empty()) {
+      if (concatFlagAndArgs) {
+        // insert flag elements except last one
+        for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) {
+          result.emplace_back(*i, bt);
+        }
+        // concatenate last flag element and all list values
+        // in one option
+        result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt);
+      } else {
+        for (std::string const& i : wrapperFlag) {
+          result.emplace_back(i, bt);
+        }
+        // concatenate all list values in one option
+        result.emplace_back(cmJoin(opts, wrapperSep), bt);
+      }
+    } else {
+      // prefix each element of list with wrapper
+      if (concatFlagAndArgs) {
+        std::transform(opts.begin(), opts.end(), opts.begin(),
+                       [&wrapperFlag](std::string const& o) -> std::string {
+                         return wrapperFlag.back() + o;
+                       });
+      }
+      for (std::string& o : opts) {
+        for (auto i = wrapperFlag.begin(),
+                  e = concatFlagAndArgs ? wrapperFlag.end() - 1
+                                        : wrapperFlag.end();
+             i != e; ++i) {
+          result.emplace_back(*i, bt);
+        }
+        result.emplace_back(std::move(o), bt);
+      }
+    }
+  }
+  return result;
+}
+}
+
+void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
+                                          const std::string& config,
+                                          const std::string& language) const
+{
+  std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language);
+  result.reserve(tmp.size());
+  for (BT<std::string>& v : tmp) {
+    result.emplace_back(std::move(v.Value));
+  }
+}
+
+std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
+  std::string const& config, std::string const& language) const
+{
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->CompileOptionsCache.find(cacheKey);
+    if (it != this->CompileOptionsCache.end()) {
+      return it->second;
+    }
+  }
+  std::vector<BT<std::string>> result;
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr,
+                                             nullptr, this->LocalGenerator);
+
+  cmList debugProperties{ this->Makefile->GetDefinition(
+    "CMAKE_DEBUG_TARGET_PROPERTIES") };
+  bool debugOptions = !this->DebugCompileOptionsDone &&
+    cm::contains(debugProperties, "COMPILE_OPTIONS");
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugCompileOptionsDone = true;
+  }
+
+  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
+    this, config, language, &dagChecker, this->CompileOptionsEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language,
+                      &dagChecker, entries, IncludeRuntimeInterface::Yes);
+
+  processOptions(this, entries, result, uniqueOptions, debugOptions,
+                 "compile options", OptionsParse::Shell);
+
+  CompileOptionsCache.emplace(cacheKey, result);
+  return result;
+}
+
+void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
+                                           const std::string& config) const
+{
+  std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config);
+  result.reserve(tmp.size());
+  for (BT<std::string>& v : tmp) {
+    result.emplace_back(std::move(v.Value));
+  }
+}
+
+std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures(
+  std::string const& config) const
+{
+  std::vector<BT<std::string>> result;
+  std::unordered_set<std::string> uniqueFeatures;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr,
+                                             nullptr, this->LocalGenerator);
+
+  cmList debugProperties{ this->Makefile->GetDefinition(
+    "CMAKE_DEBUG_TARGET_PROPERTIES") };
+  bool debugFeatures = !this->DebugCompileFeaturesDone &&
+    cm::contains(debugProperties, "COMPILE_FEATURES");
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugCompileFeaturesDone = true;
+  }
+
+  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
+    this, config, std::string(), &dagChecker, this->CompileFeaturesEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES",
+                      std::string(), &dagChecker, entries,
+                      IncludeRuntimeInterface::Yes);
+
+  processOptions(this, entries, result, uniqueFeatures, debugFeatures,
+                 "compile features", OptionsParse::None);
+
+  return result;
+}
+
+void cmGeneratorTarget::GetCompileDefinitions(
+  std::vector<std::string>& result, const std::string& config,
+  const std::string& language) const
+{
+  std::vector<BT<std::string>> tmp =
+    this->GetCompileDefinitions(config, language);
+  result.reserve(tmp.size());
+  for (BT<std::string>& v : tmp) {
+    result.emplace_back(std::move(v.Value));
+  }
+}
+
+std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
+  std::string const& config, std::string const& language) const
+{
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->CompileDefinitionsCache.find(cacheKey);
+    if (it != this->CompileDefinitionsCache.end()) {
+      return it->second;
+    }
+  }
+  std::vector<BT<std::string>> list;
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(
+    this, "COMPILE_DEFINITIONS", nullptr, nullptr, this->LocalGenerator);
+
+  cmList debugProperties{ this->Makefile->GetDefinition(
+    "CMAKE_DEBUG_TARGET_PROPERTIES") };
+  bool debugDefines = !this->DebugCompileDefinitionsDone &&
+    cm::contains(debugProperties, "COMPILE_DEFINITIONS");
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugCompileDefinitionsDone = true;
+  }
+
+  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
+    this, config, language, &dagChecker, this->CompileDefinitionsEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language,
+                      &dagChecker, entries, IncludeRuntimeInterface::Yes);
+
+  if (!config.empty()) {
+    std::string configPropName =
+      "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config);
+    cmValue configProp = this->GetProperty(configPropName);
+    if (configProp) {
+      switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) {
+        case cmPolicies::WARN: {
+          this->LocalGenerator->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmPolicies::GetPolicyWarning(cmPolicies::CMP0043));
+          CM_FALLTHROUGH;
+        }
+        case cmPolicies::OLD: {
+          std::unique_ptr<TargetPropertyEntry> entry =
+            TargetPropertyEntry::Create(
+              *this->LocalGenerator->GetCMakeInstance(), *configProp);
+          entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
+            this, config, language, &dagChecker, *entry));
+        } break;
+        case cmPolicies::NEW:
+        case cmPolicies::REQUIRED_ALWAYS:
+        case cmPolicies::REQUIRED_IF_USED:
+          break;
+      }
+    }
+  }
+
+  processOptions(this, entries, list, uniqueOptions, debugDefines,
+                 "compile definitions", OptionsParse::None);
+
+  this->CompileDefinitionsCache.emplace(cacheKey, list);
+  return list;
+}
+
+std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
+  const std::string& config, const std::string& language) const
+{
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->PrecompileHeadersCache.find(cacheKey);
+    if (it != this->PrecompileHeadersCache.end()) {
+      return it->second;
+    }
+  }
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(
+    this, "PRECOMPILE_HEADERS", nullptr, nullptr, this->LocalGenerator);
+
+  cmList debugProperties{ this->Makefile->GetDefinition(
+    "CMAKE_DEBUG_TARGET_PROPERTIES") };
+  bool debugDefines = !this->DebugPrecompileHeadersDone &&
+    std::find(debugProperties.begin(), debugProperties.end(),
+              "PRECOMPILE_HEADERS") != debugProperties.end();
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugPrecompileHeadersDone = true;
+  }
+
+  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
+    this, config, language, &dagChecker, this->PrecompileHeadersEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language,
+                      &dagChecker, entries, IncludeRuntimeInterface::Yes);
+
+  std::vector<BT<std::string>> list;
+  processOptions(this, entries, list, uniqueOptions, debugDefines,
+                 "precompile headers", OptionsParse::None);
+
+  this->PrecompileHeadersCache.emplace(cacheKey, list);
+  return list;
+}
+
+void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
+                                       const std::string& config,
+                                       const std::string& language) const
+{
+  if (this->IsDeviceLink() &&
+      this->GetPolicyStatusCMP0105() != cmPolicies::NEW) {
+    // link options are not propagated to the device link step
+    return;
+  }
+
+  std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language);
+  result.reserve(tmp.size());
+  for (BT<std::string>& v : tmp) {
+    result.emplace_back(std::move(v.Value));
+  }
+}
+
+std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
+  std::string const& config, std::string const& language) const
+{
+  ConfigAndLanguage cacheKey(
+    config, cmStrCat(language, this->IsDeviceLink() ? "-device" : ""));
+  {
+    auto it = this->LinkOptionsCache.find(cacheKey);
+    if (it != this->LinkOptionsCache.end()) {
+      return it->second;
+    }
+  }
+  std::vector<BT<std::string>> result;
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr,
+                                             nullptr, this->LocalGenerator);
+
+  cmList debugProperties{ this->Makefile->GetDefinition(
+    "CMAKE_DEBUG_TARGET_PROPERTIES") };
+  bool debugOptions = !this->DebugLinkOptionsDone &&
+    cm::contains(debugProperties, "LINK_OPTIONS");
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugLinkOptionsDone = true;
+  }
+
+  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
+    this, config, language, &dagChecker, this->LinkOptionsEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language,
+                      &dagChecker, entries, IncludeRuntimeInterface::Yes,
+                      this->GetPolicyStatusCMP0099() == cmPolicies::NEW
+                        ? UseTo::Link
+                        : UseTo::Compile);
+
+  processOptions(this, entries, result, uniqueOptions, debugOptions,
+                 "link options", OptionsParse::Shell, this->IsDeviceLink());
+
+  if (this->IsDeviceLink()) {
+    // wrap host link options
+    const std::string wrapper(this->Makefile->GetSafeDefinition(
+      "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG"));
+    cmList wrapperFlag{ wrapper };
+    const std::string wrapperSep(this->Makefile->GetSafeDefinition(
+      "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP"));
+    bool concatFlagAndArgs = true;
+    if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
+      concatFlagAndArgs = false;
+      wrapperFlag.pop_back();
+    }
+
+    auto it = result.begin();
+    while (it != result.end()) {
+      if (it->Value == DL_BEGIN) {
+        // device link options, no treatment
+        it = result.erase(it);
+        it = std::find_if(it, result.end(), [](const BT<std::string>& item) {
+          return item.Value == DL_END;
+        });
+        if (it != result.end()) {
+          it = result.erase(it);
+        }
+      } else {
+        // host link options must be wrapped
+        std::vector<std::string> options;
+        cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options);
+        auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag,
+                                       wrapperSep, concatFlagAndArgs);
+        it = result.erase(it);
+        // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect
+        // C++11 standard: 'std::vector::insert()' do not returns an iterator,
+        // so need to recompute the iterator after insertion.
+        if (it == result.end()) {
+          cm::append(result, hostOptions);
+          it = result.end();
+        } else {
+          auto index = it - result.begin();
+          result.insert(it, hostOptions.begin(), hostOptions.end());
+          it = result.begin() + index + hostOptions.size();
+        }
+      }
+    }
+  }
+
+  // Last step: replace "LINKER:" prefixed elements by
+  // actual linker wrapper
+  result = this->ResolveLinkerWrapper(result, language);
+
+  this->LinkOptionsCache.emplace(cacheKey, result);
+  return result;
+}
+
+std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
+  std::vector<BT<std::string>>& result, const std::string& language,
+  bool joinItems) const
+{
+  // replace "LINKER:" prefixed elements by actual linker wrapper
+  const std::string wrapper(this->Makefile->GetSafeDefinition(
+    "CMAKE_" + language +
+    (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG"
+                          : "_LINKER_WRAPPER_FLAG")));
+  cmList wrapperFlag{ wrapper };
+  const std::string wrapperSep(this->Makefile->GetSafeDefinition(
+    "CMAKE_" + language +
+    (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP"
+                          : "_LINKER_WRAPPER_FLAG_SEP")));
+  bool concatFlagAndArgs = true;
+  if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
+    concatFlagAndArgs = false;
+    wrapperFlag.pop_back();
+  }
+
+  const std::string LINKER{ "LINKER:" };
+  const std::string SHELL{ "SHELL:" };
+  const std::string LINKER_SHELL = LINKER + SHELL;
+
+  std::vector<BT<std::string>>::iterator entry;
+  while ((entry = std::find_if(result.begin(), result.end(),
+                               [&LINKER](BT<std::string> const& item) -> bool {
+                                 return item.Value.compare(0, LINKER.length(),
+                                                           LINKER) == 0;
+                               })) != result.end()) {
+    std::string value = std::move(entry->Value);
+    cmListFileBacktrace bt = std::move(entry->Backtrace);
+    entry = result.erase(entry);
+
+    std::vector<std::string> linkerOptions;
+    if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) {
+      cmSystemTools::ParseUnixCommandLine(
+        value.c_str() + LINKER_SHELL.length(), linkerOptions);
+    } else {
+      linkerOptions = cmTokenize(value.substr(LINKER.length()), ",");
+    }
+
+    if (linkerOptions.empty() ||
+        (linkerOptions.size() == 1 && linkerOptions.front().empty())) {
+      continue;
+    }
+
+    // for now, raise an error if prefix SHELL: is part of arguments
+    if (std::find_if(linkerOptions.begin(), linkerOptions.end(),
+                     [&SHELL](const std::string& item) -> bool {
+                       return item.find(SHELL) != std::string::npos;
+                     }) != linkerOptions.end()) {
+      this->LocalGenerator->GetCMakeInstance()->IssueMessage(
+        MessageType::FATAL_ERROR,
+        "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.",
+        this->GetBacktrace());
+      return result;
+    }
+
+    std::vector<BT<std::string>> options = wrapOptions(
+      linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
+    if (joinItems) {
+      result.insert(entry,
+                    cmJoin(cmRange<decltype(options.cbegin())>(
+                             options.cbegin(), options.cend()),
+                           " "_s));
+    } else {
+      result.insert(entry, options.begin(), options.end());
+    }
+  }
+  return result;
+}
+
+void cmGeneratorTarget::GetStaticLibraryLinkOptions(
+  std::vector<std::string>& result, const std::string& config,
+  const std::string& language) const
+{
+  std::vector<BT<std::string>> tmp =
+    this->GetStaticLibraryLinkOptions(config, language);
+  result.reserve(tmp.size());
+  for (BT<std::string>& v : tmp) {
+    result.emplace_back(std::move(v.Value));
+  }
+}
+
+std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions(
+  std::string const& config, std::string const& language) const
+{
+  std::vector<BT<std::string>> result;
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(
+    this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, this->LocalGenerator);
+
+  EvaluatedTargetPropertyEntries entries;
+  if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
+    std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create(
+      *this->LocalGenerator->GetCMakeInstance(), *linkOptions);
+    entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
+      this, config, language, &dagChecker, *entry));
+  }
+  processOptions(this, entries, result, uniqueOptions, false,
+                 "static library link options", OptionsParse::Shell);
+
+  return result;
+}
+
+void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
+                                       const std::string& config,
+                                       const std::string& language) const
+{
+  std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language);
+  result.reserve(tmp.size());
+  for (BT<std::string>& v : tmp) {
+    result.emplace_back(std::move(v.Value));
+  }
+}
+
+std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
+  std::string const& config, std::string const& language) const
+{
+  std::vector<BT<std::string>> result;
+  std::unordered_set<std::string> uniqueOptions;
+  cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr,
+                                             nullptr, this->LocalGenerator);
+
+  EvaluatedTargetPropertyEntries entries;
+  if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
+    cmList depends{ *linkDepends };
+    for (const auto& depend : depends) {
+      std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create(
+        *this->LocalGenerator->GetCMakeInstance(), depend);
+      entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
+        this, config, language, &dagChecker, *entry));
+    }
+  }
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language,
+                      &dagChecker, entries, IncludeRuntimeInterface::Yes,
+                      this->GetPolicyStatusCMP0099() == cmPolicies::NEW
+                        ? UseTo::Link
+                        : UseTo::Compile);
+
+  processOptions(this, entries, result, uniqueOptions, false, "link depends",
+                 OptionsParse::None);
+
+  return result;
+}

+ 1 - 0
bootstrap

@@ -383,6 +383,7 @@ CMAKE_CXX_SOURCES="\
   cmGeneratorTarget \
   cmGeneratorTarget_IncludeDirectories \
   cmGeneratorTarget_LinkDirectories \
+  cmGeneratorTarget_Options \
   cmGeneratorTarget_Sources \
   cmGeneratorTarget_TargetPropertyEntry \
   cmGetCMakePropertyCommand \