Ninja: Command line length dependent response file usage.
From the code it seems like the Ninja generator should be able to decide for each rule individually to use response files depending on whether the command line length limit is hit.
Linker rules are handled in cmNinjaNormalTargetGenerator::WriteLinkStatement()
. Line 1000 calculates the command line rest length:
if (!lang_supports_response || !this->ForceResponseFile()) {
commandLineLengthLimit =
static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
globalGen.GetRuleCmdLength(this->LanguageLinkerRule());
}
However, GetRuleCmdLength()
will always return zero at this point because the rule has not yet been added to the GlobalGenerator using cmGlobalNinjaGenerator::AddRule()
. This happens later as part of cmNinjaNormalTargetGenerator::WriteLinkRule()
in Line 1028.
Nevertheless, the decision logic in cmGlobalNinjaGenerator::WriteBuild()
in Line 213 seems to rely on the real command line limit anyway.
if (cmdLineLimit < 0 ||
(cmdLineLimit > 0 &&
(arguments.size() + buildstr.size() + assignments.size() + 1000) >
static_cast<size_t>(cmdLineLimit))) {
variable_assignments.str(std::string());
cmGlobalNinjaGenerator::WriteVariable(variable_assignments, "RSP_FILE",
rspfile, "", 1);
assignments += variable_assignments.str();
useResponseFile = true;
}
The build statements are handled in cmNinjaTargetGenerator::WriteObjectBuildStatement()
. From Line 916 it becomes apparent that the response file usage for compile rules is only supported if forced via CMAKE_NINJA_FORCE_RESPONSE_FILE
.
int const commandLineLengthLimit =
((lang_supports_response && this->ForceResponseFile())) ? -1 : 0;
I actually replaced this with
int commandLineLengthLimit = 0;
if (lang_supports_response) {
commandLineLengthLimit = (this->ForceResponseFile()) ?
-1 : static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit());
}
which seems to work as expected.
However, the generated rules need also to make use of the $RSP_FILE
variable using rspfile
and rspfile_content
. This happens in cmNinjaTargetGenerator::WriteCompileRule()
. In Line 469 the earlier decision logic is present again:
if (lang_supports_response && this->ForceResponseFile()) {
std::string const responseFlagVar =
"CMAKE_" + lang + "_RESPONSE_FILE_FLAG";
responseFlag = this->Makefile->GetSafeDefinition(responseFlagVar);
if (responseFlag.empty()) {
responseFlag = "@";
}
}
The compile rules are generated before the build statements. However, we do not know the command line length until the build statements are generated. This suggests to change the generation order, i.e. generate the build statements before the compile rules.
Any suggestion on how to fix this?