mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
cmCommonTargetGenerator: Improve launcher shell conversion
Encapsulate the shell escaping logic in `cmCommonTargetGenerator` and represent launchers via a vector of strings (as is done with custom commands, execute process commands, etc.) to make them easier to work with. This facilitates work in a future commit which will reuse this pathway. Issue: #27598, #27402
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <cm/filesystem>
|
#include <cm/filesystem>
|
||||||
#include <cm/string_view>
|
#include <cm/string_view>
|
||||||
|
#include <cmext/algorithm>
|
||||||
#include <cmext/string_view>
|
#include <cmext/string_view>
|
||||||
|
|
||||||
#include "cmComputeLinkInformation.h"
|
#include "cmComputeLinkInformation.h"
|
||||||
@@ -422,10 +423,10 @@ void cmCommonTargetGenerator::AppendOSXVerFlag(std::string& flags,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cmCommonTargetGenerator::GetCompilerLauncher(
|
std::vector<std::string> cmCommonTargetGenerator::GetCompilerLauncher(
|
||||||
std::string const& lang, std::string const& config)
|
std::string const& lang, std::string const& config)
|
||||||
{
|
{
|
||||||
std::string compilerLauncher;
|
std::vector<std::string> compilerLauncher;
|
||||||
if (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
|
if (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
|
||||||
lang == "HIP" || lang == "ISPC" || lang == "OBJC" || lang == "OBJCXX") {
|
lang == "HIP" || lang == "ISPC" || lang == "OBJC" || lang == "OBJCXX") {
|
||||||
std::string const propName = cmStrCat(lang, "_COMPILER_LAUNCHER");
|
std::string const propName = cmStrCat(lang, "_COMPILER_LAUNCHER");
|
||||||
@@ -434,14 +435,15 @@ std::string cmCommonTargetGenerator::GetCompilerLauncher(
|
|||||||
*cLauncherValue, this->GeneratorTarget->GetLocalGenerator(), config,
|
*cLauncherValue, this->GeneratorTarget->GetLocalGenerator(), config,
|
||||||
this->GeneratorTarget, nullptr, this->GeneratorTarget, lang);
|
this->GeneratorTarget, nullptr, this->GeneratorTarget, lang);
|
||||||
if (!evaluatedCLauncher.empty()) {
|
if (!evaluatedCLauncher.empty()) {
|
||||||
compilerLauncher = evaluatedCLauncher;
|
cm::append(compilerLauncher,
|
||||||
|
cmList{ evaluatedCLauncher, cmList::EmptyElements::Yes });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compilerLauncher;
|
return compilerLauncher;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
|
std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
|
||||||
cmSourceFile const& source, std::string& compilerLauncher,
|
cmSourceFile const& source, std::vector<std::string>& compilerLauncher,
|
||||||
std::string const& cmakeCmd, std::string const& config,
|
std::string const& cmakeCmd, std::string const& config,
|
||||||
std::function<std::string(std::string const&)> const& pathConverter)
|
std::function<std::string(std::string const&)> const& pathConverter)
|
||||||
{
|
{
|
||||||
@@ -488,11 +490,14 @@ std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
|
|||||||
std::string code_check = cmakeCmd + " -E __run_co_compile";
|
std::string code_check = cmakeCmd + " -E __run_co_compile";
|
||||||
if (!compilerLauncher.empty()) {
|
if (!compilerLauncher.empty()) {
|
||||||
// In __run_co_compile case the launcher command is supplied
|
// In __run_co_compile case the launcher command is supplied
|
||||||
// via --launcher=<maybe-list> and consumed
|
// via --launcher=<list> and consumed
|
||||||
|
for (std::string& arg : compilerLauncher) {
|
||||||
|
cmSystemTools::ReplaceString(arg, ";", "\\;");
|
||||||
|
}
|
||||||
code_check =
|
code_check =
|
||||||
cmStrCat(std::move(code_check), " --launcher=",
|
cmStrCat(std::move(code_check), " --launcher=",
|
||||||
this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
|
this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
|
||||||
compilerLauncher));
|
cmJoin(compilerLauncher, ";")));
|
||||||
compilerLauncher.clear();
|
compilerLauncher.clear();
|
||||||
}
|
}
|
||||||
if (cmNonempty(iwyu)) {
|
if (cmNonempty(iwyu)) {
|
||||||
@@ -639,32 +644,44 @@ std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cmCommonTargetGenerator::GetLinkerLauncher(
|
std::vector<std::string> cmCommonTargetGenerator::GetLinkerLauncher(
|
||||||
std::string const& config)
|
std::string const& config)
|
||||||
{
|
{
|
||||||
std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
|
std::vector<std::string> linkLauncher;
|
||||||
std::string propName = lang + "_LINKER_LAUNCHER";
|
std::string const lang = this->GeneratorTarget->GetLinkerLanguage(config);
|
||||||
|
std::string const propName = cmStrCat(lang, "_LINKER_LAUNCHER");
|
||||||
cmValue launcherProp = this->GeneratorTarget->GetProperty(propName);
|
cmValue launcherProp = this->GeneratorTarget->GetProperty(propName);
|
||||||
if (cmNonempty(launcherProp)) {
|
if (cmNonempty(launcherProp)) {
|
||||||
cm::GenEx::Context context(this->LocalCommonGenerator, config, lang);
|
cm::GenEx::Context context(this->LocalCommonGenerator, config, lang);
|
||||||
cmGeneratorExpressionDAGChecker dagChecker{ this->GeneratorTarget,
|
cmGeneratorExpressionDAGChecker dagChecker{ this->GeneratorTarget,
|
||||||
propName, nullptr, nullptr,
|
propName, nullptr, nullptr,
|
||||||
context };
|
context };
|
||||||
std::string evaluatedLinklauncher = cmGeneratorExpression::Evaluate(
|
|
||||||
|
std::string const evaluatedLinkLauncher = cmGeneratorExpression::Evaluate(
|
||||||
*launcherProp, context.LG, context.Config, this->GeneratorTarget,
|
*launcherProp, context.LG, context.Config, this->GeneratorTarget,
|
||||||
&dagChecker, this->GeneratorTarget, context.Language);
|
&dagChecker, this->GeneratorTarget, context.Language);
|
||||||
// Convert ;-delimited list to single string
|
if (!evaluatedLinkLauncher.empty()) {
|
||||||
cmList args{ evaluatedLinklauncher, cmList::EmptyElements::Yes };
|
cm::append(linkLauncher,
|
||||||
if (!args.empty()) {
|
cmList{ evaluatedLinkLauncher, cmList::EmptyElements::Yes });
|
||||||
args[0] = this->LocalCommonGenerator->ConvertToOutputFormat(
|
|
||||||
args[0], cmOutputConverter::SHELL);
|
|
||||||
for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
|
|
||||||
i = this->LocalCommonGenerator->EscapeForShell(i);
|
|
||||||
}
|
|
||||||
return cmJoin(args, " ");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::string();
|
return linkLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cmCommonTargetGenerator::ConvertLauncherToShell(
|
||||||
|
std::vector<std::string> const& launcher) const
|
||||||
|
{
|
||||||
|
if (launcher.empty()) {
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> args = launcher;
|
||||||
|
args[0] = this->LocalCommonGenerator->ConvertToOutputFormat(
|
||||||
|
args[0], cmOutputConverter::SHELL);
|
||||||
|
for (std::string& arg : cmMakeRange(args.begin() + 1, args.end())) {
|
||||||
|
arg = this->LocalCommonGenerator->EscapeForShell(arg);
|
||||||
|
}
|
||||||
|
return cmJoin(args, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmCommonTargetGenerator::HaveRequiredLanguages(
|
bool cmCommonTargetGenerator::HaveRequiredLanguages(
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ protected:
|
|||||||
std::string GetManifests(std::string const& config);
|
std::string GetManifests(std::string const& config);
|
||||||
std::string GetAIXExports(std::string const& config);
|
std::string GetAIXExports(std::string const& config);
|
||||||
std::string GenerateCodeCheckRules(
|
std::string GenerateCodeCheckRules(
|
||||||
cmSourceFile const& source, std::string& compilerLauncher,
|
cmSourceFile const& source, std::vector<std::string>& compilerLauncher,
|
||||||
std::string const& cmakeCmd, std::string const& config,
|
std::string const& cmakeCmd, std::string const& config,
|
||||||
std::function<std::string(std::string const&)> const& pathConverter);
|
std::function<std::string(std::string const&)> const& pathConverter);
|
||||||
|
|
||||||
std::string GetCompilerLauncher(std::string const& lang,
|
std::vector<std::string> GetCompilerLauncher(std::string const& lang,
|
||||||
std::string const& config);
|
std::string const& config);
|
||||||
|
|
||||||
struct LinkedTargetDirs
|
struct LinkedTargetDirs
|
||||||
{
|
{
|
||||||
@@ -85,7 +85,10 @@ protected:
|
|||||||
std::string const& config) const;
|
std::string const& config) const;
|
||||||
std::string ComputeTargetCompilePDB(std::string const& config) const;
|
std::string ComputeTargetCompilePDB(std::string const& config) const;
|
||||||
|
|
||||||
std::string GetLinkerLauncher(std::string const& config);
|
std::vector<std::string> GetLinkerLauncher(std::string const& config);
|
||||||
|
|
||||||
|
std::string ConvertLauncherToShell(
|
||||||
|
std::vector<std::string> const& launcher) const;
|
||||||
|
|
||||||
bool HaveRequiredLanguages(std::vector<cmSourceFile const*> const& sources,
|
bool HaveRequiredLanguages(std::vector<cmSourceFile const*> const& sources,
|
||||||
std::set<std::string>& languagesNeeded) const;
|
std::set<std::string>& languagesNeeded) const;
|
||||||
|
|||||||
@@ -822,11 +822,12 @@ void cmFastbuildNormalTargetGenerator::AddCompilerLaunchersForLanguages()
|
|||||||
this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", Config);
|
this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", Config);
|
||||||
// See if we need to use a compiler launcher like ccache or distcc
|
// See if we need to use a compiler launcher like ccache or distcc
|
||||||
for (std::string const& language : Languages) {
|
for (std::string const& language : Languages) {
|
||||||
std::string const compilerLauncher =
|
std::vector<std::string> expanded =
|
||||||
cmCommonTargetGenerator::GetCompilerLauncher(language, Config);
|
cmCommonTargetGenerator::GetCompilerLauncher(language, Config);
|
||||||
LogMessage("compilerLauncher: " + compilerLauncher);
|
LogMessage("compilerLauncher: " + cmJoin(expanded, ";"));
|
||||||
std::vector<std::string> expanded;
|
// FIXME(#27402): Empty arguments are not supported here.
|
||||||
cmExpandList(compilerLauncher, expanded);
|
expanded.erase(std::remove(expanded.begin(), expanded.end(), ""),
|
||||||
|
expanded.end());
|
||||||
|
|
||||||
if (!expanded.empty()) {
|
if (!expanded.empty()) {
|
||||||
std::string const exe = expanded[0];
|
std::string const exe = expanded[0];
|
||||||
@@ -844,14 +845,8 @@ void cmFastbuildNormalTargetGenerator::AddCompilerLaunchersForLanguages()
|
|||||||
}
|
}
|
||||||
void cmFastbuildNormalTargetGenerator::AddLinkerLauncher()
|
void cmFastbuildNormalTargetGenerator::AddLinkerLauncher()
|
||||||
{
|
{
|
||||||
std::string const linkerLauncher =
|
std::vector<std::string> args =
|
||||||
cmCommonTargetGenerator::GetLinkerLauncher(Config);
|
cmCommonTargetGenerator::GetLinkerLauncher(Config);
|
||||||
std::vector<std::string> args;
|
|
||||||
#ifdef _WIN32
|
|
||||||
cmSystemTools::ParseWindowsCommandLine(linkerLauncher.c_str(), args);
|
|
||||||
#else
|
|
||||||
cmSystemTools::ParseUnixCommandLine(linkerLauncher.c_str(), args);
|
|
||||||
#endif
|
|
||||||
if (!args.empty()) {
|
if (!args.empty()) {
|
||||||
std::string const exe = std::move(args[0]);
|
std::string const exe = std::move(args[0]);
|
||||||
args.erase(args.begin());
|
args.erase(args.begin());
|
||||||
@@ -1191,7 +1186,7 @@ std::string cmFastbuildNormalTargetGenerator::ComputeCodeCheckOptions(
|
|||||||
if (skipCodeCheck) {
|
if (skipCodeCheck) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
std::string compilerLauncher;
|
std::vector<std::string> compilerLauncher;
|
||||||
std::string staticCheckRule = this->GenerateCodeCheckRules(
|
std::string staticCheckRule = this->GenerateCodeCheckRules(
|
||||||
srcFile, compilerLauncher, "", Config, nullptr);
|
srcFile, compilerLauncher, "", Config, nullptr);
|
||||||
LogMessage(cmStrCat("CodeCheck: ", staticCheckRule));
|
LogMessage(cmStrCat("CodeCheck: ", staticCheckRule));
|
||||||
|
|||||||
@@ -592,8 +592,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
|||||||
vars.LinkFlags = linkFlags.c_str();
|
vars.LinkFlags = linkFlags.c_str();
|
||||||
vars.Manifests = manifests.c_str();
|
vars.Manifests = manifests.c_str();
|
||||||
|
|
||||||
std::string linkerLauncher =
|
std::string const linkerLauncher = this->ConvertLauncherToShell(
|
||||||
this->GetLinkerLauncher(this->GetConfigName());
|
this->GetLinkerLauncher(this->GetConfigName()));
|
||||||
if (cmNonempty(linkerLauncher)) {
|
if (cmNonempty(linkerLauncher)) {
|
||||||
vars.Launcher = linkerLauncher.c_str();
|
vars.Launcher = linkerLauncher.c_str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -876,8 +876,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
|
|||||||
|
|
||||||
vars.LanguageCompileFlags = langFlags.c_str();
|
vars.LanguageCompileFlags = langFlags.c_str();
|
||||||
|
|
||||||
std::string linkerLauncher =
|
std::string const linkerLauncher = this->ConvertLauncherToShell(
|
||||||
this->GetLinkerLauncher(this->GetConfigName());
|
this->GetLinkerLauncher(this->GetConfigName()));
|
||||||
if (cmNonempty(linkerLauncher)) {
|
if (cmNonempty(linkerLauncher)) {
|
||||||
vars.Launcher = linkerLauncher.c_str();
|
vars.Launcher = linkerLauncher.c_str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1106,7 +1106,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See if we need to use a compiler launcher like ccache or distcc
|
// See if we need to use a compiler launcher like ccache or distcc
|
||||||
std::string compilerLauncher;
|
std::vector<std::string> compilerLauncher;
|
||||||
if (!compileCommands.empty()) {
|
if (!compileCommands.empty()) {
|
||||||
compilerLauncher = GetCompilerLauncher(lang, config);
|
compilerLauncher = GetCompilerLauncher(lang, config);
|
||||||
}
|
}
|
||||||
@@ -1131,15 +1131,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
|
|||||||
// If compiler launcher was specified and not consumed above, it
|
// If compiler launcher was specified and not consumed above, it
|
||||||
// goes to the beginning of the command line.
|
// goes to the beginning of the command line.
|
||||||
if (!compileCommands.empty() && !compilerLauncher.empty()) {
|
if (!compileCommands.empty() && !compilerLauncher.empty()) {
|
||||||
cmList args{ compilerLauncher, cmList::EmptyElements::Yes };
|
compileCommands.front().insert(
|
||||||
if (!args.empty()) {
|
0, cmStrCat(this->ConvertLauncherToShell(compilerLauncher), ' '));
|
||||||
args[0] = this->LocalGenerator->ConvertToOutputFormat(
|
|
||||||
args[0], cmOutputConverter::SHELL);
|
|
||||||
for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
|
|
||||||
i = this->LocalGenerator->EscapeForShell(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compileCommands.front().insert(0, args.join(" ") + " ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string launcher;
|
std::string launcher;
|
||||||
|
|||||||
@@ -584,7 +584,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(
|
|||||||
vars.LanguageCompileFlags = langFlags.c_str();
|
vars.LanguageCompileFlags = langFlags.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string linkerLauncher = this->GetLinkerLauncher(config);
|
std::string const linkerLauncher =
|
||||||
|
this->ConvertLauncherToShell(this->GetLinkerLauncher(config));
|
||||||
if (cmNonempty(linkerLauncher)) {
|
if (cmNonempty(linkerLauncher)) {
|
||||||
vars.Launcher = linkerLauncher.c_str();
|
vars.Launcher = linkerLauncher.c_str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
#include "cmNinjaUtilityTargetGenerator.h"
|
#include "cmNinjaUtilityTargetGenerator.h"
|
||||||
#include "cmOutputConverter.h"
|
#include "cmOutputConverter.h"
|
||||||
#include "cmPolicies.h"
|
#include "cmPolicies.h"
|
||||||
#include "cmRange.h"
|
|
||||||
#include "cmRulePlaceholderExpander.h"
|
#include "cmRulePlaceholderExpander.h"
|
||||||
#include "cmSourceFile.h"
|
#include "cmSourceFile.h"
|
||||||
#include "cmSourceFileLocationKind.h"
|
#include "cmSourceFileLocationKind.h"
|
||||||
@@ -1598,15 +1597,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
|
|||||||
// If compiler launcher was specified and not consumed above, it
|
// If compiler launcher was specified and not consumed above, it
|
||||||
// goes to the beginning of the command line.
|
// goes to the beginning of the command line.
|
||||||
if (!compilerLauncher.empty()) {
|
if (!compilerLauncher.empty()) {
|
||||||
cmList args{ compilerLauncher, cmList::EmptyElements::Yes };
|
vars["LAUNCHER"] =
|
||||||
if (!args.empty()) {
|
cmStrCat(this->ConvertLauncherToShell(compilerLauncher), ' ');
|
||||||
args[0] = this->LocalGenerator->ConvertToOutputFormat(
|
|
||||||
args[0], cmOutputConverter::SHELL);
|
|
||||||
for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
|
|
||||||
i = this->LocalGenerator->EscapeForShell(i);
|
|
||||||
}
|
|
||||||
vars["LAUNCHER"] = args.join(" ") + " ";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->GetMakefile()->GetSafeDefinition(
|
if (this->GetMakefile()->GetSafeDefinition(
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
special-args launcher ok: compile
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
special-args launcher ok: compile
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Lint tools run the launcher through "cmake -E __run_co_compile --launcher=...".
|
||||||
|
set(CMAKE_C_CPPCHECK "${CMAKE_COMMAND};-E;true")
|
||||||
|
include(C-special-args.cmake)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
set(check "${CMAKE_CURRENT_LIST_DIR}/check-special-args.cmake")
|
||||||
|
set(CMAKE_C_COMPILER_LAUNCHER
|
||||||
|
"${CMAKE_COMMAND};-P;${check};--;compile;;semi\\;colon;with space")
|
||||||
|
include(C-common.cmake)
|
||||||
@@ -51,3 +51,10 @@ foreach(lang ${langs})
|
|||||||
run_compiler_launcher_env(${lang}-launch-env)
|
run_compiler_launcher_env(${lang}-launch-env)
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT RunCMake_GENERATOR MATCHES "FASTBuild")
|
||||||
|
# FIXME(#27402): FASTBuild builds its launcher command line without shell
|
||||||
|
# quoting, so it can't handle empty or special-character arguments.
|
||||||
|
run_compiler_launcher(C-special-args)
|
||||||
|
run_compiler_launcher(C-special-args-cppcheck)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Verifies that the launcher's own arguments arrive intact, then runs the
|
||||||
|
# launched command.
|
||||||
|
|
||||||
|
set(base 4) # CMAKE_ARGV0..3 are: cmake -P <script> --
|
||||||
|
set(num_args 4) # <phase> plus the three arguments below
|
||||||
|
|
||||||
|
set(expect_1 "")
|
||||||
|
set(expect_2 "semi;colon")
|
||||||
|
set(expect_3 "with space")
|
||||||
|
|
||||||
|
math(EXPR min_argc "${base} + ${num_args} + 1")
|
||||||
|
if(CMAKE_ARGC LESS min_argc)
|
||||||
|
message(FATAL_ERROR "too few arguments: ${CMAKE_ARGC}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(i RANGE 1 3)
|
||||||
|
math(EXPR idx "${base} + ${i}")
|
||||||
|
if(NOT "${CMAKE_ARGV${idx}}" STREQUAL "${expect_${i}}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"launcher argument ${i}: expected [${expect_${i}}] but got [${CMAKE_ARGV${idx}}]")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
message("special-args launcher ok: ${CMAKE_ARGV${base}}")
|
||||||
|
|
||||||
|
# Build and invoke the launched command via cmake_language(EVAL CODE) using
|
||||||
|
# bracket arguments, rather than a ";"-separated list. A list element ending
|
||||||
|
# in a backslash (e.g. MSVC's "/Fd<dir>\") would otherwise merge with the
|
||||||
|
# list's own separator when the list is later expanded, corrupting the
|
||||||
|
# command line.
|
||||||
|
set(cmd_code "execute_process(COMMAND")
|
||||||
|
math(EXPR first "${base} + ${num_args}")
|
||||||
|
math(EXPR last "${CMAKE_ARGC} - 1")
|
||||||
|
foreach(i RANGE ${first} ${last})
|
||||||
|
string(APPEND cmd_code " [==[${CMAKE_ARGV${i}}]==]")
|
||||||
|
endforeach()
|
||||||
|
string(APPEND cmd_code " RESULT_VARIABLE result)")
|
||||||
|
cmake_language(EVAL CODE "${cmd_code}")
|
||||||
|
|
||||||
|
if(NOT result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "launched command failed: ${result}")
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Launcher invocations cannot be reliably verified via stdout, so it records to
|
||||||
|
# a log file instead. Verify by checking its content.
|
||||||
|
set(log_file "${RunCMake_TEST_BINARY_DIR}/special-args.log")
|
||||||
|
if(NOT EXISTS "${log_file}")
|
||||||
|
set(RunCMake_TEST_FAILED "special-args.log was not created by the wrapper")
|
||||||
|
else()
|
||||||
|
file(READ "${log_file}" log_content)
|
||||||
|
if(NOT log_content MATCHES "link")
|
||||||
|
set(RunCMake_TEST_FAILED
|
||||||
|
"special-args.log does not show link launcher ran:\n${log_content}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
set(check "${CMAKE_CURRENT_LIST_DIR}/check-special-args.cmake")
|
||||||
|
set(CMAKE_C_LINKER_LAUNCHER
|
||||||
|
"${CMAKE_COMMAND};-P;${check};--;link;;semi\\;colon;with space")
|
||||||
|
include(C-common.cmake)
|
||||||
@@ -48,3 +48,9 @@ foreach(lang ${langs})
|
|||||||
run_linker_launcher_env(${lang}-launch-env)
|
run_linker_launcher_env(${lang}-launch-env)
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT RunCMake_GENERATOR MATCHES "FASTBuild")
|
||||||
|
# FIXME(#27402): FASTBuild builds its launcher command line without shell
|
||||||
|
# quoting, so it can't handle empty or special-character arguments.
|
||||||
|
run_linker_launcher(C-special-args)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Verifies that the launcher's own arguments arrive intact, then runs the
|
||||||
|
# launched command.
|
||||||
|
|
||||||
|
set(base 4) # CMAKE_ARGV0..3 are: cmake -P <script> --
|
||||||
|
set(num_args 4) # <phase> plus the three arguments below
|
||||||
|
|
||||||
|
set(expect_1 "")
|
||||||
|
set(expect_2 "semi;colon")
|
||||||
|
set(expect_3 "with space")
|
||||||
|
|
||||||
|
math(EXPR min_argc "${base} + ${num_args} + 1")
|
||||||
|
if(CMAKE_ARGC LESS min_argc)
|
||||||
|
message(FATAL_ERROR "too few arguments: ${CMAKE_ARGC}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(i RANGE 1 3)
|
||||||
|
math(EXPR idx "${base} + ${i}")
|
||||||
|
if(NOT "${CMAKE_ARGV${idx}}" STREQUAL "${expect_${i}}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"launcher argument ${i}: expected [${expect_${i}}] but got [${CMAKE_ARGV${idx}}]")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(APPEND "special-args.log" "${CMAKE_ARGV${base}}\n")
|
||||||
|
|
||||||
|
# Build and invoke the launched command via cmake_language(EVAL CODE) using
|
||||||
|
# bracket arguments, rather than a ";"-separated list. A list element ending
|
||||||
|
# in a backslash (e.g. MSVC's "/Fd<dir>\") would otherwise merge with the
|
||||||
|
# list's own separator when the list is later expanded, corrupting the
|
||||||
|
# command line.
|
||||||
|
set(cmd_code "execute_process(COMMAND")
|
||||||
|
math(EXPR first "${base} + ${num_args}")
|
||||||
|
math(EXPR last "${CMAKE_ARGC} - 1")
|
||||||
|
foreach(i RANGE ${first} ${last})
|
||||||
|
string(APPEND cmd_code " [==[${CMAKE_ARGV${i}}]==]")
|
||||||
|
endforeach()
|
||||||
|
string(APPEND cmd_code " RESULT_VARIABLE result)")
|
||||||
|
cmake_language(EVAL CODE "${cmd_code}")
|
||||||
|
|
||||||
|
if(NOT result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "launched command failed: ${result}")
|
||||||
|
endif()
|
||||||
Reference in New Issue
Block a user