mirror of
https://github.com/Kitware/CMake.git
synced 2025-06-09 15:38:15 +08:00

By specifying CODEGEN as an argument to add_custom_command the custom command will be added to a codegen build target. The intent is to provide a convenient way for users to get their generated files without having to build the whole project. This can be helpful for code analysis tools which can be useful for IDEs and CI.
1005 lines
34 KiB
C++
1005 lines
34 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmGlobalUnixMakefileGenerator3.h"
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
#include <cm/memory>
|
|
#include <cmext/algorithm>
|
|
#include <cmext/memory>
|
|
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmLocalUnixMakefileGenerator3.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMakefileTargetGenerator.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmState.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
#include "cmTargetDepend.h"
|
|
#include "cmValue.h"
|
|
#include "cmake.h"
|
|
|
|
cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3(cmake* cm)
|
|
: cmGlobalCommonGenerator(cm)
|
|
{
|
|
// This type of makefile always requires unix style paths
|
|
this->ForceUnixPaths = true;
|
|
this->FindMakeProgramFile = "CMakeUnixFindMake.cmake";
|
|
this->ToolSupportsColor = true;
|
|
|
|
#if defined(_WIN32) || defined(__VMS)
|
|
this->UseLinkScript = false;
|
|
#else
|
|
this->UseLinkScript = true;
|
|
#endif
|
|
|
|
this->IncludeDirective = "include";
|
|
this->LineContinueDirective = "\\\n";
|
|
this->DefineWindowsNULL = false;
|
|
this->PassMakeflags = false;
|
|
this->UnixCD = true;
|
|
}
|
|
|
|
cmGlobalUnixMakefileGenerator3::~cmGlobalUnixMakefileGenerator3() = default;
|
|
|
|
void cmGlobalUnixMakefileGenerator3::EnableLanguage(
|
|
std::vector<std::string> const& languages, cmMakefile* mf, bool optional)
|
|
{
|
|
this->cmGlobalGenerator::EnableLanguage(languages, mf, optional);
|
|
for (std::string const& language : languages) {
|
|
if (language == "NONE") {
|
|
continue;
|
|
}
|
|
this->ResolveLanguageCompiler(language, mf, optional);
|
|
}
|
|
}
|
|
|
|
//! Create a local generator appropriate to this Global Generator
|
|
std::unique_ptr<cmLocalGenerator>
|
|
cmGlobalUnixMakefileGenerator3::CreateLocalGenerator(cmMakefile* mf)
|
|
{
|
|
return std::unique_ptr<cmLocalGenerator>(
|
|
cm::make_unique<cmLocalUnixMakefileGenerator3>(this, mf));
|
|
}
|
|
|
|
cmDocumentationEntry cmGlobalUnixMakefileGenerator3::GetDocumentation()
|
|
{
|
|
return { cmGlobalUnixMakefileGenerator3::GetActualName(),
|
|
"Generates standard UNIX makefiles." };
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::ComputeTargetObjectDirectory(
|
|
cmGeneratorTarget* gt) const
|
|
{
|
|
// Compute full path to object file directory for this target.
|
|
std::string dir =
|
|
cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/',
|
|
gt->LocalGenerator->GetTargetDirectory(gt), '/');
|
|
gt->ObjectDirectory = dir;
|
|
}
|
|
|
|
bool cmGlobalUnixMakefileGenerator3::CanEscapeOctothorpe() const
|
|
{
|
|
// Make tools that use UNIX-style '/' paths also support '\' escaping.
|
|
return this->ForceUnixPaths;
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::Configure()
|
|
{
|
|
// Initialize CMAKE_EDIT_COMMAND cache entry.
|
|
this->GetEditCacheCommand();
|
|
|
|
this->cmGlobalGenerator::Configure();
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::Generate()
|
|
{
|
|
this->ClangTidyExportFixesDirs.clear();
|
|
this->ClangTidyExportFixesFiles.clear();
|
|
|
|
// first do superclass method
|
|
this->cmGlobalGenerator::Generate();
|
|
|
|
// initialize progress
|
|
unsigned long total = 0;
|
|
for (auto const& pmi : this->ProgressMap) {
|
|
total += pmi.second.NumberOfActions;
|
|
}
|
|
|
|
// write each target's progress.make this loop is done twice. Basically the
|
|
// Generate pass counts all the actions, the first loop below determines
|
|
// how many actions have progress updates for each target and writes to
|
|
// correct variable values for everything except the all targets. The
|
|
// second loop actually writes out correct values for the all targets as
|
|
// well. This is because the all targets require more information that is
|
|
// computed in the first loop.
|
|
unsigned long current = 0;
|
|
for (auto& pmi : this->ProgressMap) {
|
|
pmi.second.WriteProgressVariables(total, current);
|
|
}
|
|
for (const auto& lg : this->LocalGenerators) {
|
|
std::string markFileName =
|
|
cmStrCat(lg->GetCurrentBinaryDirectory(), "/CMakeFiles/progress.marks");
|
|
cmGeneratedFileStream markFile(markFileName);
|
|
markFile << this->CountProgressMarksInAll(*lg) << "\n";
|
|
}
|
|
|
|
// write the main makefile
|
|
this->WriteMainMakefile2();
|
|
this->WriteMainCMakefile();
|
|
|
|
if (this->CommandDatabase) {
|
|
*this->CommandDatabase << "\n]";
|
|
this->CommandDatabase.reset();
|
|
}
|
|
|
|
this->RemoveUnknownClangTidyExportFixesFiles();
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::AddCXXCompileCommand(
|
|
const std::string& sourceFile, const std::string& workingDirectory,
|
|
const std::string& compileCommand, const std::string& objPath)
|
|
{
|
|
if (!this->CommandDatabase) {
|
|
std::string commandDatabaseName =
|
|
this->GetCMakeInstance()->GetHomeOutputDirectory() +
|
|
"/compile_commands.json";
|
|
this->CommandDatabase =
|
|
cm::make_unique<cmGeneratedFileStream>(commandDatabaseName);
|
|
*this->CommandDatabase << "[\n";
|
|
} else {
|
|
*this->CommandDatabase << ",\n";
|
|
}
|
|
*this->CommandDatabase << "{\n"
|
|
<< R"( "directory": ")"
|
|
<< cmGlobalGenerator::EscapeJSON(workingDirectory)
|
|
<< "\",\n"
|
|
<< R"( "command": ")"
|
|
<< cmGlobalGenerator::EscapeJSON(compileCommand)
|
|
<< "\",\n"
|
|
<< R"( "file": ")"
|
|
<< cmGlobalGenerator::EscapeJSON(sourceFile)
|
|
<< "\",\n"
|
|
<< R"( "output": ")"
|
|
<< cmGlobalGenerator::EscapeJSON(objPath) << "\"\n}";
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2()
|
|
{
|
|
// Open the output file. This should not be copy-if-different
|
|
// because the check-build-system step compares the makefile time to
|
|
// see if the build system must be regenerated.
|
|
std::string makefileName =
|
|
cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
|
|
"/CMakeFiles/Makefile2");
|
|
cmGeneratedFileStream makefileStream(makefileName, false,
|
|
this->GetMakefileEncoding());
|
|
if (!makefileStream) {
|
|
return;
|
|
}
|
|
|
|
// The global dependency graph is expressed via the root local generator.
|
|
auto& rootLG = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
|
|
this->LocalGenerators[0]);
|
|
|
|
// Write the do not edit header.
|
|
rootLG.WriteDisclaimer(makefileStream);
|
|
|
|
// Write the main entry point target. This must be the VERY first
|
|
// target so that make with no arguments will run it.
|
|
// Just depend on the all target to drive the build.
|
|
std::vector<std::string> depends;
|
|
std::vector<std::string> no_commands;
|
|
depends.emplace_back("all");
|
|
|
|
// Write the rule.
|
|
rootLG.WriteMakeRule(makefileStream,
|
|
"Default target executed when no arguments are "
|
|
"given to make.",
|
|
"default_target", depends, no_commands, true);
|
|
|
|
depends.clear();
|
|
|
|
// The all and preinstall rules might never have any dependencies
|
|
// added to them.
|
|
if (!this->EmptyRuleHackDepends.empty()) {
|
|
depends.push_back(this->EmptyRuleHackDepends);
|
|
}
|
|
|
|
// Write out the "special" stuff
|
|
rootLG.WriteSpecialTargetsTop(makefileStream);
|
|
|
|
// Write the directory level rules.
|
|
for (auto const& it : this->ComputeDirectoryTargets()) {
|
|
this->WriteDirectoryRules2(makefileStream, rootLG, it.second);
|
|
}
|
|
|
|
// Write the target convenience rules
|
|
for (const auto& localGen : this->LocalGenerators) {
|
|
this->WriteConvenienceRules2(
|
|
makefileStream, rootLG,
|
|
cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(localGen));
|
|
}
|
|
|
|
// Write special bottom targets
|
|
rootLG.WriteSpecialTargetsBottom(makefileStream);
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile()
|
|
{
|
|
if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
|
|
return;
|
|
}
|
|
|
|
// Open the output file. This should not be copy-if-different
|
|
// because the check-build-system step compares the makefile time to
|
|
// see if the build system must be regenerated.
|
|
std::string cmakefileName =
|
|
cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
|
|
"/CMakeFiles/Makefile.cmake");
|
|
cmGeneratedFileStream cmakefileStream(cmakefileName);
|
|
if (!cmakefileStream) {
|
|
return;
|
|
}
|
|
|
|
std::string makefileName =
|
|
cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), "/Makefile");
|
|
|
|
{
|
|
// get a local generator for some useful methods
|
|
auto& lg = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
|
|
this->LocalGenerators[0]);
|
|
|
|
// Write the do not edit header.
|
|
lg.WriteDisclaimer(cmakefileStream);
|
|
}
|
|
|
|
// Save the generator name
|
|
cmakefileStream << "# The generator used is:\n"
|
|
<< "set(CMAKE_DEPENDS_GENERATOR \"" << this->GetName()
|
|
<< "\")\n\n";
|
|
|
|
// for each cmMakefile get its list of dependencies
|
|
std::vector<std::string> lfiles;
|
|
for (const auto& localGen : this->LocalGenerators) {
|
|
// Get the list of files contributing to this generation step.
|
|
cm::append(lfiles, localGen->GetMakefile()->GetListFiles());
|
|
}
|
|
|
|
cmake* cm = this->GetCMakeInstance();
|
|
if (cm->DoWriteGlobVerifyTarget()) {
|
|
lfiles.push_back(cm->GetGlobVerifyScript());
|
|
lfiles.push_back(cm->GetGlobVerifyStamp());
|
|
}
|
|
|
|
// Sort the list and remove duplicates.
|
|
std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>());
|
|
#if !defined(__VMS) // The Compaq STL on VMS crashes, so accept duplicates.
|
|
auto new_end = std::unique(lfiles.begin(), lfiles.end());
|
|
lfiles.erase(new_end, lfiles.end());
|
|
#endif
|
|
|
|
{
|
|
// reset lg to the first makefile
|
|
const auto& lg = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
|
|
this->LocalGenerators[0]);
|
|
|
|
// Save the list to the cmake file.
|
|
cmakefileStream
|
|
<< "# The top level Makefile was generated from the following files:\n"
|
|
<< "set(CMAKE_MAKEFILE_DEPENDS\n"
|
|
<< " \"CMakeCache.txt\"\n";
|
|
for (std::string const& f : lfiles) {
|
|
cmakefileStream << " \"" << lg.MaybeRelativeToCurBinDir(f) << "\"\n";
|
|
}
|
|
cmakefileStream << " )\n\n";
|
|
|
|
// Build the path to the cache check file.
|
|
std::string check =
|
|
cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
|
|
"/CMakeFiles/cmake.check_cache");
|
|
|
|
// Set the corresponding makefile in the cmake file.
|
|
cmakefileStream << "# The corresponding makefile is:\n"
|
|
<< "set(CMAKE_MAKEFILE_OUTPUTS\n"
|
|
<< " \"" << lg.MaybeRelativeToCurBinDir(makefileName)
|
|
<< "\"\n"
|
|
<< " \"" << lg.MaybeRelativeToCurBinDir(check) << "\"\n";
|
|
cmakefileStream << " )\n\n";
|
|
|
|
// CMake must rerun if a byproduct is missing.
|
|
cmakefileStream << "# Byproducts of CMake generate step:\n"
|
|
<< "set(CMAKE_MAKEFILE_PRODUCTS\n";
|
|
|
|
// add in any byproducts and all the directory information files
|
|
std::string tmpStr;
|
|
for (const auto& localGen : this->LocalGenerators) {
|
|
for (std::string const& outfile :
|
|
localGen->GetMakefile()->GetOutputFiles()) {
|
|
cmakefileStream << " \"" << lg.MaybeRelativeToTopBinDir(outfile)
|
|
<< "\"\n";
|
|
}
|
|
tmpStr = cmStrCat(localGen->GetCurrentBinaryDirectory(),
|
|
"/CMakeFiles/CMakeDirectoryInformation.cmake");
|
|
cmakefileStream << " \"" << localGen->MaybeRelativeToTopBinDir(tmpStr)
|
|
<< "\"\n";
|
|
}
|
|
cmakefileStream << " )\n\n";
|
|
}
|
|
|
|
this->WriteMainCMakefileLanguageRules(cmakefileStream,
|
|
this->LocalGenerators);
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteMainCMakefileLanguageRules(
|
|
cmGeneratedFileStream& cmakefileStream,
|
|
std::vector<std::unique_ptr<cmLocalGenerator>>& lGenerators)
|
|
{
|
|
// now list all the target info files
|
|
cmakefileStream << "# Dependency information for all targets:\n";
|
|
cmakefileStream << "set(CMAKE_DEPEND_INFO_FILES\n";
|
|
for (const auto& lGenerator : lGenerators) {
|
|
const auto& lg =
|
|
cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(lGenerator);
|
|
// for all of out targets
|
|
for (const auto& tgt : lg.GetGeneratorTargets()) {
|
|
if (tgt->IsInBuildSystem() &&
|
|
tgt->GetType() != cmStateEnums::GLOBAL_TARGET) {
|
|
std::string tname = cmStrCat(lg.GetRelativeTargetDirectory(tgt.get()),
|
|
"/DependInfo.cmake");
|
|
cmSystemTools::ConvertToUnixSlashes(tname);
|
|
cmakefileStream << " \"" << tname << "\"\n";
|
|
}
|
|
}
|
|
}
|
|
cmakefileStream << " )\n";
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteDirectoryRule2(
|
|
std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3& rootLG,
|
|
DirectoryTarget const& dt, const char* pass, bool check_all,
|
|
bool check_relink, std::vector<std::string> const& commands)
|
|
{
|
|
auto* lg = static_cast<cmLocalUnixMakefileGenerator3*>(dt.LG);
|
|
std::string makeTarget =
|
|
cmStrCat(lg->GetCurrentBinaryDirectory(), '/', pass);
|
|
|
|
// The directory-level rule should depend on the target-level rules
|
|
// for all targets in the directory.
|
|
std::vector<std::string> depends;
|
|
for (DirectoryTarget::Target const& t : dt.Targets) {
|
|
// Add this to the list of depends rules in this directory.
|
|
if ((!check_all || t.ExcludedFromAllInConfigs.empty()) &&
|
|
(!check_relink ||
|
|
t.GT->NeedRelinkBeforeInstall(lg->GetConfigName()))) {
|
|
// The target may be from a different directory; use its local gen.
|
|
auto const* tlg = static_cast<cmLocalUnixMakefileGenerator3 const*>(
|
|
t.GT->GetLocalGenerator());
|
|
std::string tname =
|
|
cmStrCat(tlg->GetRelativeTargetDirectory(t.GT), '/', pass);
|
|
depends.push_back(std::move(tname));
|
|
}
|
|
}
|
|
|
|
// The directory-level rule should depend on the directory-level
|
|
// rules of the subdirectories.
|
|
for (DirectoryTarget::Dir const& d : dt.Children) {
|
|
if (check_all && d.ExcludeFromAll) {
|
|
continue;
|
|
}
|
|
std::string subdir = cmStrCat(d.Path, '/', pass);
|
|
depends.push_back(std::move(subdir));
|
|
}
|
|
|
|
// Work-around for makes that drop rules that have no dependencies
|
|
// or commands.
|
|
if (depends.empty() && !this->EmptyRuleHackDepends.empty()) {
|
|
depends.push_back(this->EmptyRuleHackDepends);
|
|
}
|
|
|
|
// Write the rule.
|
|
std::string doc;
|
|
if (lg->IsRootMakefile()) {
|
|
doc = cmStrCat("The main recursive \"", pass, "\" target.");
|
|
} else {
|
|
doc = cmStrCat("Recursive \"", pass, "\" directory target.");
|
|
}
|
|
|
|
rootLG.WriteMakeRule(ruleFileStream, doc.c_str(), makeTarget, depends,
|
|
commands, true);
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2(
|
|
std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3& rootLG,
|
|
DirectoryTarget const& dt)
|
|
{
|
|
auto* lg = static_cast<cmLocalUnixMakefileGenerator3*>(dt.LG);
|
|
// Begin the directory-level rules section.
|
|
{
|
|
std::string dir = cmSystemTools::ConvertToOutputPath(
|
|
rootLG.MaybeRelativeToTopBinDir(lg->GetCurrentBinaryDirectory()));
|
|
rootLG.WriteDivider(ruleFileStream);
|
|
if (lg->IsRootMakefile()) {
|
|
ruleFileStream << "# Directory level rules for the build root directory";
|
|
} else {
|
|
ruleFileStream << "# Directory level rules for directory " << dir;
|
|
}
|
|
ruleFileStream << "\n\n";
|
|
}
|
|
|
|
// Write directory-level rules for "all".
|
|
this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "all", true, false);
|
|
|
|
// Write directory-level rules for "codegen".
|
|
this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "codegen", true,
|
|
false);
|
|
|
|
// Write directory-level rules for "preinstall".
|
|
this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "preinstall", true,
|
|
true);
|
|
|
|
// Write directory-level rules for "clean".
|
|
{
|
|
std::vector<std::string> cmds;
|
|
lg->AppendDirectoryCleanCommand(cmds);
|
|
this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "clean", false,
|
|
false, cmds);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
std::string ConvertToMakefilePathForUnix(std::string const& path)
|
|
{
|
|
std::string result;
|
|
result.reserve(path.size());
|
|
for (char c : path) {
|
|
switch (c) {
|
|
case '=':
|
|
// We provide 'EQUALS = =' to encode '=' in a non-assignment case.
|
|
result.append("$(EQUALS)");
|
|
break;
|
|
case '$':
|
|
result.append("$$");
|
|
break;
|
|
case '\\':
|
|
case ' ':
|
|
case '#':
|
|
result.push_back('\\');
|
|
CM_FALLTHROUGH;
|
|
default:
|
|
result.push_back(c);
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
std::string ConvertToMakefilePathForWindows(std::string const& path)
|
|
{
|
|
bool const quote = path.find_first_of(" #") != std::string::npos;
|
|
std::string result;
|
|
result.reserve(path.size() + (quote ? 2 : 0));
|
|
if (quote) {
|
|
result.push_back('"');
|
|
}
|
|
for (char c : path) {
|
|
switch (c) {
|
|
case '=':
|
|
// We provide 'EQUALS = =' to encode '=' in a non-assignment case.
|
|
result.append("$(EQUALS)");
|
|
break;
|
|
case '$':
|
|
result.append("$$");
|
|
break;
|
|
case '/':
|
|
result.push_back('\\');
|
|
break;
|
|
default:
|
|
result.push_back(c);
|
|
break;
|
|
}
|
|
}
|
|
if (quote) {
|
|
result.push_back('"');
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::string cmGlobalUnixMakefileGenerator3::ConvertToMakefilePath(
|
|
std::string const& path) const
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if (!this->ForceUnixPaths) {
|
|
return ConvertToMakefilePathForWindows(path);
|
|
}
|
|
#endif
|
|
return ConvertToMakefilePathForUnix(path);
|
|
}
|
|
|
|
std::vector<cmGlobalGenerator::GeneratedMakeCommand>
|
|
cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
|
|
const std::string& makeProgram, const std::string& /*projectName*/,
|
|
const std::string& /*projectDir*/,
|
|
std::vector<std::string> const& targetNames, const std::string& /*config*/,
|
|
int jobs, bool verbose, const cmBuildOptions& buildOptions,
|
|
std::vector<std::string> const& makeOptions)
|
|
{
|
|
GeneratedMakeCommand makeCommand;
|
|
|
|
// Make it possible to set verbosity also from command line
|
|
if (verbose) {
|
|
makeCommand.Add(cmSystemTools::GetCMakeCommand());
|
|
makeCommand.Add("-E");
|
|
makeCommand.Add("env");
|
|
makeCommand.Add("VERBOSE=1");
|
|
}
|
|
makeCommand.Add(this->SelectMakeProgram(makeProgram));
|
|
|
|
// Explicitly tell the make tool to use the Makefile written by
|
|
// cmLocalUnixMakefileGenerator3::WriteLocalMakefile
|
|
makeCommand.Add("-f");
|
|
makeCommand.Add("Makefile");
|
|
|
|
if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
|
|
if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
|
|
makeCommand.Add("-j");
|
|
} else {
|
|
makeCommand.Add("-j" + std::to_string(jobs));
|
|
}
|
|
}
|
|
|
|
makeCommand.Add(makeOptions.begin(), makeOptions.end());
|
|
for (auto tname : targetNames) {
|
|
if (!tname.empty()) {
|
|
if (buildOptions.Fast) {
|
|
tname += "/fast";
|
|
}
|
|
cmSystemTools::ConvertToOutputSlashes(tname);
|
|
makeCommand.Add(std::move(tname));
|
|
}
|
|
}
|
|
return { std::move(makeCommand) };
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules(
|
|
std::ostream& ruleFileStream, std::set<std::string>& emitted)
|
|
{
|
|
std::vector<std::string> depends;
|
|
std::vector<std::string> commands;
|
|
|
|
bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
|
|
if (regenerate) {
|
|
depends.emplace_back("cmake_check_build_system");
|
|
}
|
|
|
|
// write the target convenience rules
|
|
for (const auto& localGen : this->LocalGenerators) {
|
|
auto& lg =
|
|
cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(localGen);
|
|
// for each target Generate the rule files for each target.
|
|
for (const auto& gtarget : lg.GetGeneratorTargets()) {
|
|
// Don't emit the same rule twice (e.g. two targets with the same
|
|
// simple name)
|
|
std::string name = gtarget->GetName();
|
|
if (!name.empty() && emitted.insert(name).second &&
|
|
// Handle user targets here. Global targets are handled in
|
|
// the local generator on a per-directory basis.
|
|
(gtarget->IsInBuildSystem() &&
|
|
gtarget->GetType() != cmStateEnums::GLOBAL_TARGET)) {
|
|
// Add a rule to build the target by name.
|
|
lg.WriteDivider(ruleFileStream);
|
|
ruleFileStream << "# Target rules for targets named " << name
|
|
<< "\n\n";
|
|
|
|
// Write the rule.
|
|
commands.clear();
|
|
std::string tmp = "CMakeFiles/Makefile2";
|
|
commands.push_back(lg.GetRecursiveMakeCall(tmp, name));
|
|
depends.clear();
|
|
if (regenerate) {
|
|
depends.emplace_back("cmake_check_build_system");
|
|
}
|
|
lg.WriteMakeRule(ruleFileStream, "Build rule for target.", name,
|
|
depends, commands, true);
|
|
|
|
// Add a fast rule to build the target
|
|
std::string localName = lg.GetRelativeTargetDirectory(gtarget.get());
|
|
std::string makefileName;
|
|
makefileName = cmStrCat(localName, "/build.make");
|
|
depends.clear();
|
|
commands.clear();
|
|
std::string makeTargetName = cmStrCat(localName, "/build");
|
|
localName = cmStrCat(name, "/fast");
|
|
commands.push_back(
|
|
lg.GetRecursiveMakeCall(makefileName, makeTargetName));
|
|
lg.WriteMakeRule(ruleFileStream, "fast build rule for target.",
|
|
localName, depends, commands, true);
|
|
|
|
// Add a local name for the rule to relink the target before
|
|
// installation.
|
|
if (gtarget->NeedRelinkBeforeInstall(lg.GetConfigName())) {
|
|
makeTargetName = cmStrCat(
|
|
lg.GetRelativeTargetDirectory(gtarget.get()), "/preinstall");
|
|
localName = cmStrCat(name, "/preinstall");
|
|
depends.clear();
|
|
commands.clear();
|
|
commands.push_back(
|
|
lg.GetRecursiveMakeCall(makefileName, makeTargetName));
|
|
lg.WriteMakeRule(ruleFileStream,
|
|
"Manual pre-install relink rule for target.",
|
|
localName, depends, commands, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2(
|
|
std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3& rootLG,
|
|
cmLocalUnixMakefileGenerator3& lg)
|
|
{
|
|
std::vector<std::string> depends;
|
|
std::vector<std::string> commands;
|
|
std::string localName;
|
|
std::string makeTargetName;
|
|
|
|
bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
|
|
if (regenerate) {
|
|
depends.emplace_back("cmake_check_build_system");
|
|
}
|
|
|
|
// for each target Generate the rule files for each target.
|
|
for (const auto& gtarget : lg.GetGeneratorTargets()) {
|
|
std::string name = gtarget->GetName();
|
|
if (!name.empty() &&
|
|
(gtarget->IsInBuildSystem() &&
|
|
gtarget->GetType() != cmStateEnums::GLOBAL_TARGET)) {
|
|
std::string makefileName;
|
|
// Add a rule to build the target by name.
|
|
localName = lg.GetRelativeTargetDirectory(gtarget.get());
|
|
makefileName = cmStrCat(localName, "/build.make");
|
|
|
|
lg.WriteDivider(ruleFileStream);
|
|
ruleFileStream << "# Target rules for target " << localName << "\n\n";
|
|
|
|
commands.clear();
|
|
makeTargetName = cmStrCat(localName, "/depend");
|
|
commands.push_back(
|
|
lg.GetRecursiveMakeCall(makefileName, makeTargetName));
|
|
|
|
makeTargetName = cmStrCat(localName, "/build");
|
|
commands.push_back(
|
|
lg.GetRecursiveMakeCall(makefileName, makeTargetName));
|
|
|
|
// Write the rule.
|
|
localName += "/all";
|
|
depends.clear();
|
|
|
|
cmLocalUnixMakefileGenerator3::EchoProgress progress;
|
|
progress.Dir = cmStrCat(lg.GetBinaryDirectory(), "/CMakeFiles");
|
|
{
|
|
std::ostringstream progressArg;
|
|
const char* sep = "";
|
|
for (unsigned long progFile : this->ProgressMap[gtarget.get()].Marks) {
|
|
progressArg << sep << progFile;
|
|
sep = ",";
|
|
}
|
|
progress.Arg = progressArg.str();
|
|
}
|
|
|
|
bool targetMessages = true;
|
|
if (cmValue tgtMsg =
|
|
this->GetCMakeInstance()->GetState()->GetGlobalProperty(
|
|
"TARGET_MESSAGES")) {
|
|
targetMessages = tgtMsg.IsOn();
|
|
}
|
|
|
|
if (targetMessages) {
|
|
lg.AppendEcho(commands, "Built target " + name,
|
|
cmLocalUnixMakefileGenerator3::EchoNormal, &progress);
|
|
}
|
|
|
|
this->AppendGlobalTargetDepends(depends, gtarget.get());
|
|
rootLG.WriteMakeRule(ruleFileStream, "All Build rule for target.",
|
|
localName, depends, commands, true);
|
|
|
|
// Write the rule.
|
|
commands.clear();
|
|
|
|
{
|
|
// TODO: Convert the total progress count to a make variable.
|
|
std::ostringstream progCmd;
|
|
progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start ";
|
|
// # in target
|
|
progCmd << lg.ConvertToOutputFormat(progress.Dir,
|
|
cmOutputConverter::SHELL);
|
|
//
|
|
std::set<cmGeneratorTarget const*> emitted;
|
|
progCmd << " "
|
|
<< this->CountProgressMarksInTarget(gtarget.get(), emitted);
|
|
commands.push_back(progCmd.str());
|
|
}
|
|
std::string tmp = "CMakeFiles/Makefile2";
|
|
commands.push_back(lg.GetRecursiveMakeCall(tmp, localName));
|
|
{
|
|
std::ostringstream progCmd;
|
|
progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start "; // # 0
|
|
progCmd << lg.ConvertToOutputFormat(progress.Dir,
|
|
cmOutputConverter::SHELL);
|
|
progCmd << " 0";
|
|
commands.push_back(progCmd.str());
|
|
}
|
|
depends.clear();
|
|
if (regenerate) {
|
|
depends.emplace_back("cmake_check_build_system");
|
|
}
|
|
localName =
|
|
cmStrCat(lg.GetRelativeTargetDirectory(gtarget.get()), "/rule");
|
|
rootLG.WriteMakeRule(ruleFileStream,
|
|
"Build rule for subdir invocation for target.",
|
|
localName, depends, commands, true);
|
|
|
|
// Add a target with the canonical name (no prefix, suffix or path).
|
|
commands.clear();
|
|
depends.clear();
|
|
depends.push_back(localName);
|
|
rootLG.WriteMakeRule(ruleFileStream, "Convenience name for target.",
|
|
name, depends, commands, true);
|
|
|
|
// Add rules to prepare the target for installation.
|
|
if (gtarget->NeedRelinkBeforeInstall(lg.GetConfigName())) {
|
|
localName = cmStrCat(lg.GetRelativeTargetDirectory(gtarget.get()),
|
|
"/preinstall");
|
|
depends.clear();
|
|
commands.clear();
|
|
commands.push_back(lg.GetRecursiveMakeCall(makefileName, localName));
|
|
rootLG.WriteMakeRule(ruleFileStream,
|
|
"Pre-install relink rule for target.", localName,
|
|
depends, commands, true);
|
|
}
|
|
|
|
// add the codegen rule
|
|
localName = lg.GetRelativeTargetDirectory(gtarget.get());
|
|
depends.clear();
|
|
commands.clear();
|
|
makeTargetName = cmStrCat(localName, "/codegen");
|
|
commands.push_back(
|
|
lg.GetRecursiveMakeCall(makefileName, makeTargetName));
|
|
this->AppendCodegenTargetDepends(depends, gtarget.get());
|
|
rootLG.WriteMakeRule(ruleFileStream, "codegen rule for target.",
|
|
makeTargetName, depends, commands, true);
|
|
|
|
// add the clean rule
|
|
localName = lg.GetRelativeTargetDirectory(gtarget.get());
|
|
makeTargetName = cmStrCat(localName, "/clean");
|
|
depends.clear();
|
|
commands.clear();
|
|
commands.push_back(
|
|
lg.GetRecursiveMakeCall(makefileName, makeTargetName));
|
|
rootLG.WriteMakeRule(ruleFileStream, "clean rule for target.",
|
|
makeTargetName, depends, commands, true);
|
|
commands.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build a map that contains the set of targets used by each local
|
|
// generator directory level.
|
|
void cmGlobalUnixMakefileGenerator3::InitializeProgressMarks()
|
|
{
|
|
this->DirectoryTargetsMap.clear();
|
|
// Loop over all targets in all local generators.
|
|
for (const auto& lg : this->LocalGenerators) {
|
|
for (const auto& gt : lg->GetGeneratorTargets()) {
|
|
cmLocalGenerator* tlg = gt->GetLocalGenerator();
|
|
|
|
if (!gt->IsInBuildSystem() || this->IsExcluded(lg.get(), gt.get())) {
|
|
continue;
|
|
}
|
|
|
|
cmStateSnapshot csnp = lg->GetStateSnapshot();
|
|
cmStateSnapshot tsnp = tlg->GetStateSnapshot();
|
|
|
|
// Consider the directory containing the target and all its
|
|
// parents until something excludes the target.
|
|
for (; csnp.IsValid() && !this->IsExcluded(csnp, tsnp);
|
|
csnp = csnp.GetBuildsystemDirectoryParent()) {
|
|
// This local generator includes the target.
|
|
std::set<cmGeneratorTarget const*>& targetSet =
|
|
this->DirectoryTargetsMap[csnp];
|
|
targetSet.insert(gt.get());
|
|
|
|
// Add dependencies of the included target. An excluded
|
|
// target may still be included if it is a dependency of a
|
|
// non-excluded target.
|
|
for (cmTargetDepend const& tgtdep :
|
|
this->GetTargetDirectDepends(gt.get())) {
|
|
targetSet.insert(tgtdep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t cmGlobalUnixMakefileGenerator3::CountProgressMarksInTarget(
|
|
cmGeneratorTarget const* target, std::set<cmGeneratorTarget const*>& emitted)
|
|
{
|
|
size_t count = 0;
|
|
if (emitted.insert(target).second) {
|
|
count = this->ProgressMap[target].Marks.size();
|
|
for (cmTargetDepend const& depend : this->GetTargetDirectDepends(target)) {
|
|
if (!depend->IsInBuildSystem()) {
|
|
continue;
|
|
}
|
|
count += this->CountProgressMarksInTarget(depend, emitted);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
size_t cmGlobalUnixMakefileGenerator3::CountProgressMarksInAll(
|
|
const cmLocalGenerator& lg)
|
|
{
|
|
size_t count = 0;
|
|
std::set<cmGeneratorTarget const*> emitted;
|
|
for (cmGeneratorTarget const* target :
|
|
this->DirectoryTargetsMap[lg.GetStateSnapshot()]) {
|
|
count += this->CountProgressMarksInTarget(target, emitted);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::RecordTargetProgress(
|
|
cmMakefileTargetGenerator* tg)
|
|
{
|
|
TargetProgress& tp = this->ProgressMap[tg->GetGeneratorTarget()];
|
|
tp.NumberOfActions = tg->GetNumberOfProgressActions();
|
|
tp.VariableFile = tg->GetProgressFileNameFull();
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::TargetProgress::WriteProgressVariables(
|
|
unsigned long total, unsigned long& current)
|
|
{
|
|
cmGeneratedFileStream fout(this->VariableFile);
|
|
for (unsigned long i = 1; i <= this->NumberOfActions; ++i) {
|
|
fout << "CMAKE_PROGRESS_" << i << " = ";
|
|
if (total <= 100) {
|
|
unsigned long num = i + current;
|
|
fout << num;
|
|
this->Marks.push_back(num);
|
|
} else if (((i + current) * 100) / total >
|
|
((i - 1 + current) * 100) / total) {
|
|
unsigned long num = ((i + current) * 100) / total;
|
|
fout << num;
|
|
this->Marks.push_back(num);
|
|
}
|
|
fout << "\n";
|
|
}
|
|
fout << "\n";
|
|
current += this->NumberOfActions;
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::AppendGlobalTargetDepends(
|
|
std::vector<std::string>& depends, cmGeneratorTarget* target)
|
|
{
|
|
for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) {
|
|
// Create the target-level dependency.
|
|
cmGeneratorTarget const* dep = i;
|
|
if (!dep->IsInBuildSystem()) {
|
|
continue;
|
|
}
|
|
cmLocalUnixMakefileGenerator3* lg3 =
|
|
static_cast<cmLocalUnixMakefileGenerator3*>(dep->GetLocalGenerator());
|
|
std::string tgtName = cmStrCat(
|
|
lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep)),
|
|
"/all");
|
|
depends.push_back(tgtName);
|
|
}
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::AppendCodegenTargetDepends(
|
|
std::vector<std::string>& depends, cmGeneratorTarget* target)
|
|
{
|
|
const std::set<std::string>& codegen_depends =
|
|
target->Target->GetCodegenDeps();
|
|
|
|
for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) {
|
|
// Create the target-level dependency.
|
|
cmGeneratorTarget const* dep = i;
|
|
if (!dep->IsInBuildSystem()) {
|
|
continue;
|
|
}
|
|
if (codegen_depends.find(dep->GetName()) != codegen_depends.end()) {
|
|
cmLocalUnixMakefileGenerator3* lg3 =
|
|
static_cast<cmLocalUnixMakefileGenerator3*>(dep->GetLocalGenerator());
|
|
std::string tgtName = cmStrCat(
|
|
lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep)),
|
|
"/all");
|
|
depends.push_back(tgtName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmGlobalUnixMakefileGenerator3::WriteHelpRule(
|
|
std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3* lg)
|
|
{
|
|
// add the help target
|
|
std::string path;
|
|
std::vector<std::string> no_depends;
|
|
std::vector<std::string> commands;
|
|
lg->AppendEcho(commands,
|
|
"The following are some of the valid targets "
|
|
"for this Makefile:");
|
|
lg->AppendEcho(commands, "... all (the default if no target is provided)");
|
|
lg->AppendEcho(commands, "... clean");
|
|
if (!this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
|
|
lg->AppendEcho(commands, "... depend");
|
|
}
|
|
|
|
// Keep track of targets already listed.
|
|
std::set<std::string> emittedTargets;
|
|
std::set<std::string> utility_targets;
|
|
std::set<std::string> globals_targets;
|
|
std::set<std::string> project_targets;
|
|
|
|
// for each local generator
|
|
for (const auto& localGen : this->LocalGenerators) {
|
|
const auto& lg2 =
|
|
cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(localGen);
|
|
// for the passed in makefile or if this is the top Makefile wripte out
|
|
// the targets
|
|
if (&lg2 == lg || lg->IsRootMakefile()) {
|
|
// for each target Generate the rule files for each target.
|
|
for (const auto& target : lg2.GetGeneratorTargets()) {
|
|
cmStateEnums::TargetType type = target->GetType();
|
|
if ((type == cmStateEnums::EXECUTABLE) ||
|
|
(type == cmStateEnums::STATIC_LIBRARY) ||
|
|
(type == cmStateEnums::SHARED_LIBRARY) ||
|
|
(type == cmStateEnums::MODULE_LIBRARY) ||
|
|
(type == cmStateEnums::OBJECT_LIBRARY) ||
|
|
(type == cmStateEnums::INTERFACE_LIBRARY &&
|
|
target->IsInBuildSystem())) {
|
|
project_targets.insert(target->GetName());
|
|
} else if (type == cmStateEnums::GLOBAL_TARGET) {
|
|
globals_targets.insert(target->GetName());
|
|
} else if (type == cmStateEnums::UTILITY) {
|
|
utility_targets.insert(target->GetName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (std::string const& name : globals_targets) {
|
|
path = cmStrCat("... ", name);
|
|
lg->AppendEcho(commands, path);
|
|
}
|
|
for (std::string const& name : utility_targets) {
|
|
path = cmStrCat("... ", name);
|
|
lg->AppendEcho(commands, path);
|
|
}
|
|
for (std::string const& name : project_targets) {
|
|
path = cmStrCat("... ", name);
|
|
lg->AppendEcho(commands, path);
|
|
}
|
|
|
|
for (std::string const& o : lg->GetLocalHelp()) {
|
|
path = cmStrCat("... ", o);
|
|
lg->AppendEcho(commands, path);
|
|
}
|
|
lg->WriteMakeRule(ruleFileStream, "Help Target", "help", no_depends,
|
|
commands, true);
|
|
ruleFileStream << "\n\n";
|
|
}
|