|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
#include "cmGlobalGhsMultiGenerator.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <functional>
|
|
|
|
#include <map>
|
|
|
|
#include <sstream>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <cm/memory>
|
|
|
|
#include <cm/string>
|
|
|
|
#include <cmext/algorithm>
|
|
|
|
#include <cmext/memory>
|
|
|
|
|
|
|
|
#include "cmCustomCommand.h"
|
|
|
|
#include "cmCustomCommandLines.h"
|
|
|
|
#include "cmGeneratedFileStream.h"
|
|
|
|
#include "cmGeneratorTarget.h"
|
|
|
|
#include "cmGhsMultiGpj.h"
|
|
|
|
#include "cmList.h"
|
|
|
|
#include "cmLocalGenerator.h"
|
|
|
|
#include "cmLocalGhsMultiGenerator.h"
|
|
|
|
#include "cmMakefile.h"
|
|
|
|
#include "cmMessageType.h"
|
|
|
|
#include "cmSourceFile.h"
|
|
|
|
#include "cmState.h"
|
|
|
|
#include "cmStateTypes.h"
|
|
|
|
#include "cmStringAlgorithms.h"
|
|
|
|
#include "cmSystemTools.h"
|
|
|
|
#include "cmTarget.h"
|
|
|
|
#include "cmValue.h"
|
|
|
|
#include "cmVersion.h"
|
|
|
|
#include "cmake.h"
|
|
|
|
|
|
|
|
const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj";
|
|
|
|
#ifdef __linux__
|
|
|
|
const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild";
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe";
|
|
|
|
#endif
|
|
|
|
const char* cmGlobalGhsMultiGenerator::CHECK_BUILD_SYSTEM_TARGET =
|
|
|
|
"RERUN_CMAKE";
|
|
|
|
|
|
|
|
cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm)
|
|
|
|
: cmGlobalGenerator(cm)
|
|
|
|
{
|
|
|
|
cm->GetState()->SetGhsMultiIDE(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default;
|
|
|
|
|
|
|
|
std::unique_ptr<cmLocalGenerator>
|
|
|
|
cmGlobalGhsMultiGenerator::CreateLocalGenerator(cmMakefile* mf)
|
|
|
|
{
|
|
|
|
return std::unique_ptr<cmLocalGenerator>(
|
|
|
|
cm::make_unique<cmLocalGhsMultiGenerator>(this, mf));
|
|
|
|
}
|
|
|
|
|
|
|
|
cmDocumentationEntry cmGlobalGhsMultiGenerator::GetDocumentation()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
GetActualName(),
|
|
|
|
"Generates Green Hills MULTI files (experimental, work-in-progress)."
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::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 cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts,
|
|
|
|
bool build, cmMakefile* mf)
|
|
|
|
{
|
|
|
|
/* In build mode nothing to be done.
|
|
|
|
* Toolset already determined and build tool absolute path is cached.
|
|
|
|
*/
|
|
|
|
if (build) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the absolute directory for the toolset */
|
|
|
|
std::string tsp;
|
|
|
|
this->GetToolset(mf, tsp, ts);
|
|
|
|
|
|
|
|
/* no toolset was found */
|
|
|
|
if (tsp.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the build tool to use */
|
|
|
|
std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") +
|
|
|
|
DEFAULT_BUILD_PROGRAM);
|
|
|
|
cmValue prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");
|
|
|
|
|
|
|
|
/* check if the toolset changed from last generate */
|
|
|
|
if (cmNonempty(prevTool) && !cmSystemTools::ComparePath(gbuild, *prevTool)) {
|
|
|
|
std::string const& e = cmStrCat(
|
|
|
|
"toolset build tool: ", gbuild, '\n',
|
|
|
|
"Does not match the previously used build tool: ", *prevTool, '\n',
|
|
|
|
"Either remove the CMakeCache.txt file and CMakeFiles "
|
|
|
|
"directory or choose a different binary directory.");
|
|
|
|
mf->IssueMessage(MessageType::FATAL_ERROR, e);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* store the toolset that is being used for this build */
|
|
|
|
mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild, "build program to use",
|
|
|
|
cmStateEnums::INTERNAL, true);
|
|
|
|
|
|
|
|
mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p,
|
|
|
|
cmMakefile* mf)
|
|
|
|
{
|
|
|
|
/* set primary target */
|
|
|
|
cmValue t = mf->GetDefinition("GHS_PRIMARY_TARGET");
|
|
|
|
if (cmIsOff(t)) {
|
|
|
|
/* Use the value from `-A` or use `arm` */
|
|
|
|
std::string arch = "arm";
|
|
|
|
if (!cmIsOff(p)) {
|
|
|
|
arch = p;
|
|
|
|
}
|
|
|
|
cmValue platform = mf->GetDefinition("GHS_TARGET_PLATFORM");
|
|
|
|
std::string tgt = cmStrCat(arch, '_', platform, ".tgt");
|
|
|
|
|
|
|
|
/* update the primary target name*/
|
|
|
|
mf->AddDefinition("GHS_PRIMARY_TARGET", tgt);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::EnableLanguage(
|
|
|
|
std::vector<std::string> const& l, cmMakefile* mf, bool optional)
|
|
|
|
{
|
|
|
|
mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI");
|
|
|
|
|
|
|
|
mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files
|
|
|
|
|
|
|
|
this->cmGlobalGenerator::EnableLanguage(l, mf, optional);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/)
|
|
|
|
{
|
|
|
|
// The GHS generator only knows how to lookup its build tool
|
|
|
|
// during generation of the project files, but this
|
|
|
|
// can only be done after the toolset is specified.
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsp,
|
|
|
|
const std::string& ts)
|
|
|
|
{
|
|
|
|
/* Determine tsp - full path of the toolset from ts (toolset hint via -T) */
|
|
|
|
|
|
|
|
std::string root = mf->GetSafeDefinition("GHS_TOOLSET_ROOT");
|
|
|
|
|
|
|
|
// Check if `-T` was set by user
|
|
|
|
if (ts.empty()) {
|
|
|
|
// Enter toolset search mode
|
|
|
|
std::vector<std::string> output;
|
|
|
|
|
|
|
|
// Make sure root exists...
|
|
|
|
if (!cmSystemTools::PathExists(root)) {
|
|
|
|
std::string msg =
|
|
|
|
"GHS_TOOLSET_ROOT directory \"" + root + "\" does not exist.";
|
|
|
|
mf->IssueMessage(MessageType::FATAL_ERROR, msg);
|
|
|
|
tsp = "";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a directory separator
|
|
|
|
if (root.back() != '/') {
|
|
|
|
root += "/";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get all compiler directories in toolset root
|
|
|
|
cmSystemTools::Glob(root, "comp_[^;]+", output);
|
|
|
|
|
|
|
|
if (output.empty()) {
|
|
|
|
// No compiler directories found
|
|
|
|
std::string msg =
|
|
|
|
"No GHS toolsets found in GHS_TOOLSET_ROOT \"" + root + "\".";
|
|
|
|
mf->IssueMessage(MessageType::FATAL_ERROR, msg);
|
|
|
|
tsp = "";
|
|
|
|
} else {
|
|
|
|
// Use latest? version
|
|
|
|
tsp = root + output.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Toolset was provided by user
|
|
|
|
std::string tryPath;
|
|
|
|
|
|
|
|
// NOTE: CollapseFullPath() will determine if user toolset was full path or
|
|
|
|
// or relative path.
|
|
|
|
tryPath = cmSystemTools::CollapseFullPath(ts, root);
|
|
|
|
if (!cmSystemTools::FileExists(tryPath)) {
|
|
|
|
std::string msg = "GHS toolset \"" + tryPath + "\" does not exist.";
|
|
|
|
mf->IssueMessage(MessageType::FATAL_ERROR, msg);
|
|
|
|
tsp = "";
|
|
|
|
} else {
|
|
|
|
tsp = tryPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout)
|
|
|
|
{
|
|
|
|
/* clang-format off */
|
|
|
|
fout << "#!gbuild\n"
|
|
|
|
"#\n"
|
|
|
|
"# CMAKE generated file: DO NOT EDIT!\n"
|
|
|
|
"# Generated by \"" << GetActualName() << "\""
|
|
|
|
" Generator, CMake Version " << cmVersion::GetMajorVersion() << '.'
|
|
|
|
<< cmVersion::GetMinorVersion() << "\n"
|
|
|
|
"#\n\n";
|
|
|
|
/* clang-format on */
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout)
|
|
|
|
{
|
|
|
|
fout << "Commands {\n"
|
|
|
|
" Custom_Rule_Command {\n"
|
|
|
|
" name = \"Custom Rule Command\"\n"
|
|
|
|
" exec = \""
|
|
|
|
#ifdef _WIN32
|
|
|
|
"cmd.exe"
|
|
|
|
#else
|
|
|
|
"/bin/sh"
|
|
|
|
#endif
|
|
|
|
"\"\n"
|
|
|
|
" options = {\"SpecialOptions\"}\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
|
|
|
|
"\n\n"
|
|
|
|
"FileTypes {\n"
|
|
|
|
" CmakeRule {\n"
|
|
|
|
" name = \"Custom Rule\"\n"
|
|
|
|
" action = \"&Run\"\n"
|
|
|
|
" extensions = {\""
|
|
|
|
#ifdef _WIN32
|
|
|
|
"bat"
|
|
|
|
#else
|
|
|
|
"sh"
|
|
|
|
#endif
|
|
|
|
"\"}\n"
|
|
|
|
" grepable = false\n"
|
|
|
|
" command = \"Custom Rule Command\"\n"
|
|
|
|
" commandLine = \"$COMMAND "
|
|
|
|
#ifdef _WIN32
|
|
|
|
"/c"
|
|
|
|
#endif
|
|
|
|
" $INPUTFILE\"\n"
|
|
|
|
" progress = \"Processing Custom Rule\"\n"
|
|
|
|
" promoteToFirstPass = true\n"
|
|
|
|
" outputType = \"None\"\n"
|
|
|
|
" color = \"#800080\"\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout)
|
|
|
|
{
|
|
|
|
fout << "FileTypes {\n"
|
|
|
|
" CmakeTarget {\n"
|
|
|
|
" name = \"Custom Target\"\n"
|
|
|
|
" action = \"&Execute\"\n"
|
|
|
|
" grepable = false\n"
|
|
|
|
" outputType = \"None\"\n"
|
|
|
|
" color = \"#800080\"\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout,
|
|
|
|
cmLocalGenerator* root)
|
|
|
|
{
|
|
|
|
this->WriteFileHeader(fout);
|
|
|
|
this->WriteMacros(fout, root);
|
|
|
|
this->WriteHighLevelDirectives(fout, root);
|
|
|
|
GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);
|
|
|
|
|
|
|
|
fout << "# Top Level Project File\n";
|
|
|
|
|
|
|
|
// Specify BSP option if supplied by user
|
|
|
|
// -- not all platforms require this entry in the project file
|
|
|
|
cmValue bspName = root->GetMakefile()->GetDefinition("GHS_BSP_NAME");
|
|
|
|
if (!cmIsOff(bspName)) {
|
|
|
|
fout << " -bsp " << *bspName << '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Specify OS DIR if supplied by user
|
|
|
|
// -- not all platforms require this entry in the project file
|
|
|
|
cmValue osDir = root->GetMakefile()->GetDefinition("GHS_OS_DIR");
|
|
|
|
if (!cmIsOff(osDir)) {
|
|
|
|
cmValue osDirOption =
|
|
|
|
root->GetMakefile()->GetDefinition("GHS_OS_DIR_OPTION");
|
|
|
|
fout << " ";
|
|
|
|
if (cmIsOff(osDirOption)) {
|
|
|
|
fout << "";
|
|
|
|
} else {
|
|
|
|
fout << *osDirOption;
|
|
|
|
}
|
|
|
|
fout << "\"" << osDir << "\"\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout,
|
|
|
|
bool filterPredefined)
|
|
|
|
{
|
|
|
|
std::set<std::string> predefinedTargets;
|
|
|
|
predefinedTargets.insert(this->GetInstallTargetName());
|
|
|
|
predefinedTargets.insert(this->GetAllTargetName());
|
|
|
|
predefinedTargets.insert(std::string(CHECK_BUILD_SYSTEM_TARGET));
|
|
|
|
|
|
|
|
// All known targets
|
|
|
|
for (cmGeneratorTarget const* target : this->ProjectTargets) {
|
|
|
|
if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
|
|
|
|
target->GetType() == cmStateEnums::MODULE_LIBRARY ||
|
|
|
|
target->GetType() == cmStateEnums::SHARED_LIBRARY ||
|
|
|
|
(target->GetType() == cmStateEnums::GLOBAL_TARGET &&
|
|
|
|
target->GetName() != this->GetInstallTargetName())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Check if the current target is a predefined CMake target */
|
|
|
|
bool predefinedTarget =
|
|
|
|
predefinedTargets.find(target->GetName()) != predefinedTargets.end();
|
|
|
|
if ((filterPredefined && predefinedTarget) ||
|
|
|
|
(!filterPredefined && !predefinedTarget)) {
|
|
|
|
fout << target->GetName() + ".tgt" + FILE_EXTENSION << " [Project]\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteProjectLine(
|
|
|
|
std::ostream& fout, cmGeneratorTarget const* target,
|
|
|
|
std::string& rootBinaryDir)
|
|
|
|
{
|
|
|
|
cmValue projFile = target->GetProperty("GENERATOR_FILE_NAME");
|
|
|
|
cmValue projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
|
|
|
|
/* If either value is not valid then this particular target is an
|
|
|
|
* unsupported target type and should be skipped.
|
|
|
|
*/
|
|
|
|
if (projFile && projType) {
|
|
|
|
std::string path = cmSystemTools::RelativePath(rootBinaryDir, *projFile);
|
|
|
|
|
|
|
|
fout << path;
|
|
|
|
fout << ' ' << *projType << '\n';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root)
|
|
|
|
{
|
|
|
|
std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
|
|
|
|
|
|
|
|
// All known targets
|
|
|
|
for (cmGeneratorTarget const* target : this->ProjectTargets) {
|
|
|
|
if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
|
|
|
|
target->GetType() == cmStateEnums::MODULE_LIBRARY ||
|
|
|
|
target->GetType() == cmStateEnums::SHARED_LIBRARY ||
|
|
|
|
(target->GetType() == cmStateEnums::GLOBAL_TARGET &&
|
|
|
|
target->GetName() != this->GetInstallTargetName())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create target build file
|
|
|
|
std::string name = cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION);
|
|
|
|
std::string fname = cmStrCat(rootBinaryDir, "/", name);
|
|
|
|
cmGeneratedFileStream fbld(fname);
|
|
|
|
fbld.SetCopyIfDifferent(true);
|
|
|
|
this->WriteFileHeader(fbld);
|
|
|
|
GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld);
|
|
|
|
std::vector<cmGeneratorTarget const*> build;
|
|
|
|
if (this->ComputeTargetBuildOrder(target, build)) {
|
|
|
|
cmSystemTools::Error(
|
|
|
|
cmStrCat("The inter-target dependency graph for target [",
|
|
|
|
target->GetName(), "] had a cycle.\n"));
|
|
|
|
} else {
|
|
|
|
for (auto& tgt : build) {
|
|
|
|
this->WriteProjectLine(fbld, tgt, rootBinaryDir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fbld.Close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::Generate()
|
|
|
|
{
|
|
|
|
std::string fname;
|
|
|
|
|
|
|
|
// first do the superclass method
|
|
|
|
this->cmGlobalGenerator::Generate();
|
|
|
|
|
|
|
|
// output top-level projects
|
|
|
|
for (auto& it : this->ProjectMap) {
|
|
|
|
this->OutputTopLevelProject(it.second[0], it.second);
|
|
|
|
}
|
|
|
|
|
|
|
|
// create custom rule BOD file
|
|
|
|
fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
|
|
|
|
"/CMakeFiles/custom_rule.bod";
|
|
|
|
cmGeneratedFileStream frule(fname);
|
|
|
|
frule.SetCopyIfDifferent(true);
|
|
|
|
this->WriteFileHeader(frule);
|
|
|
|
this->WriteCustomRuleBOD(frule);
|
|
|
|
frule.Close();
|
|
|
|
|
|
|
|
// create custom target BOD file
|
|
|
|
fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
|
|
|
|
"/CMakeFiles/custom_target.bod";
|
|
|
|
cmGeneratedFileStream ftarget(fname);
|
|
|
|
ftarget.SetCopyIfDifferent(true);
|
|
|
|
this->WriteFileHeader(ftarget);
|
|
|
|
this->WriteCustomTargetBOD(ftarget);
|
|
|
|
ftarget.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
|
|
|
|
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
|
|
|
|
{
|
|
|
|
std::string fname;
|
|
|
|
|
|
|
|
if (generators.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all targets under this root generator and the transitive
|
|
|
|
// closure of their dependencies.
|
|
|
|
TargetDependSet projectTargets;
|
|
|
|
TargetDependSet originalTargets;
|
|
|
|
this->GetTargetSets(projectTargets, originalTargets, root, generators);
|
|
|
|
OrderedTargetDependSet sortedProjectTargets(projectTargets, "");
|
|
|
|
this->ProjectTargets.clear();
|
|
|
|
for (cmGeneratorTarget const* t : sortedProjectTargets) {
|
|
|
|
/* save list of all targets in sorted order */
|
|
|
|
this->ProjectTargets.push_back(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Name top-level projects as filename.top.gpj to avoid name clashes
|
|
|
|
* with target projects. This avoid the issue where the project has
|
|
|
|
* the same name as the executable target.
|
|
|
|
*/
|
|
|
|
fname = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
|
|
|
|
root->GetProjectName(), ".top", FILE_EXTENSION);
|
|
|
|
|
|
|
|
cmGeneratedFileStream top(fname);
|
|
|
|
top.SetCopyIfDifferent(true);
|
|
|
|
this->WriteTopLevelProject(top, root);
|
|
|
|
this->WriteTargets(root);
|
|
|
|
this->WriteSubProjects(top, true);
|
|
|
|
this->WriteSubProjects(top, false);
|
|
|
|
top.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<cmGlobalGenerator::GeneratedMakeCommand>
|
|
|
|
cmGlobalGhsMultiGenerator::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;
|
|
|
|
|
|
|
|
makeCommand.Add(this->SelectMakeProgram(makeProgram));
|
|
|
|
|
|
|
|
if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
|
|
|
|
if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
|
|
|
|
makeCommand.Add("-parallel");
|
|
|
|
} else {
|
|
|
|
makeCommand.Add(std::string("-parallel=") + std::to_string(jobs));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* determine the top-project file in the project directory */
|
|
|
|
std::string proj = projectName + ".top" + FILE_EXTENSION;
|
|
|
|
std::vector<std::string> files;
|
|
|
|
cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
|
|
|
|
if (!files.empty()) {
|
|
|
|
/* use the real top-level project in the directory */
|
|
|
|
proj = files.at(0);
|
|
|
|
}
|
|
|
|
makeCommand.Add("-top", proj);
|
|
|
|
|
|
|
|
/* determine targets to build */
|
|
|
|
bool build_all = false;
|
|
|
|
if (!targetNames.empty()) {
|
|
|
|
for (const auto& tname : targetNames) {
|
|
|
|
if (!tname.empty()) {
|
|
|
|
if (tname == "clean") {
|
|
|
|
makeCommand.Add("-clean");
|
|
|
|
} else {
|
|
|
|
makeCommand.Add(tname + ".tgt.gpj");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
build_all = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
build_all = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (build_all) {
|
|
|
|
/* transform name to default build */;
|
|
|
|
std::string all = std::string(this->GetAllTargetName()) + ".tgt.gpj";
|
|
|
|
makeCommand.Add(all);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
makeCommand.Add("-commands");
|
|
|
|
}
|
|
|
|
makeCommand.Add(makeOptions.begin(), makeOptions.end());
|
|
|
|
|
|
|
|
return { std::move(makeCommand) };
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout,
|
|
|
|
cmLocalGenerator* root)
|
|
|
|
{
|
|
|
|
fout << "macro PROJ_NAME=" << root->GetProjectName() << '\n';
|
|
|
|
cmValue ghsGpjMacros = root->GetMakefile()->GetDefinition("GHS_GPJ_MACROS");
|
|
|
|
if (ghsGpjMacros) {
|
|
|
|
cmList expandedList{ *ghsGpjMacros };
|
|
|
|
for (std::string const& arg : expandedList) {
|
|
|
|
fout << "macro " << arg << '\n';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(
|
|
|
|
std::ostream& fout, cmLocalGenerator* root)
|
|
|
|
{
|
|
|
|
/* put primary target and customization files into project file */
|
|
|
|
cmValue const tgt = root->GetMakefile()->GetDefinition("GHS_PRIMARY_TARGET");
|
|
|
|
|
|
|
|
/* clang-format off */
|
|
|
|
fout << "primaryTarget=" << tgt << "\n"
|
|
|
|
"customization=" << root->GetBinaryDirectory()
|
|
|
|
<< "/CMakeFiles/custom_rule.bod\n"
|
|
|
|
"customization=" << root->GetBinaryDirectory()
|
|
|
|
<< "/CMakeFiles/custom_target.bod" << '\n';
|
|
|
|
/* clang-format on */
|
|
|
|
|
|
|
|
cmValue const customization =
|
|
|
|
root->GetMakefile()->GetDefinition("GHS_CUSTOMIZATION");
|
|
|
|
if (cmNonempty(customization)) {
|
|
|
|
fout << "customization="
|
|
|
|
<< cmGlobalGhsMultiGenerator::TrimQuotes(*customization) << '\n';
|
|
|
|
this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string str)
|
|
|
|
{
|
|
|
|
cm::erase(str, '"');
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmGlobalGhsMultiGenerator::TargetCompare::operator()(
|
|
|
|
cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
|
|
|
|
{
|
|
|
|
// Make sure a given named target is ordered first,
|
|
|
|
// e.g. to set ALL_BUILD as the default active project.
|
|
|
|
// When the empty string is named this is a no-op.
|
|
|
|
if (r->GetName() == this->First) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (l->GetName() == this->First) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return l->GetName() < r->GetName();
|
|
|
|
}
|
|
|
|
|
|
|
|
cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
|
|
|
|
TargetDependSet const& targets, std::string const& first)
|
|
|
|
: derived(TargetCompare(first))
|
|
|
|
{
|
|
|
|
this->insert(targets.begin(), targets.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
|
|
|
|
cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build)
|
|
|
|
{
|
|
|
|
std::vector<cmGeneratorTarget const*> t{ tgt };
|
|
|
|
return this->ComputeTargetBuildOrder(t, build);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
|
|
|
|
std::vector<cmGeneratorTarget const*>& tgt,
|
|
|
|
std::vector<cmGeneratorTarget const*>& build)
|
|
|
|
{
|
|
|
|
std::set<cmGeneratorTarget const*> temp;
|
|
|
|
std::set<cmGeneratorTarget const*> perm;
|
|
|
|
|
|
|
|
for (const auto* const ti : tgt) {
|
|
|
|
bool r = this->VisitTarget(temp, perm, build, ti);
|
|
|
|
if (r) {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmGlobalGhsMultiGenerator::VisitTarget(
|
|
|
|
std::set<cmGeneratorTarget const*>& temp,
|
|
|
|
std::set<cmGeneratorTarget const*>& perm,
|
|
|
|
std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti)
|
|
|
|
{
|
|
|
|
/* check if permanent mark is set*/
|
|
|
|
if (perm.find(ti) == perm.end()) {
|
|
|
|
/* set temporary mark; check if revisit*/
|
|
|
|
if (temp.insert(ti).second) {
|
|
|
|
/* sort targets lexicographically to ensure that nodes are always visited
|
|
|
|
* in the same order */
|
|
|
|
OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti),
|
|
|
|
"");
|
|
|
|
for (auto const& di : sortedTargets) {
|
|
|
|
if (this->VisitTarget(temp, perm, order, di)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* mark as complete; insert into beginning of list*/
|
|
|
|
perm.insert(ti);
|
|
|
|
order.push_back(ti);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* revisiting item - not a DAG */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/* already complete */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmGlobalGhsMultiGenerator::AddCheckTarget()
|
|
|
|
{
|
|
|
|
// Skip the target if no regeneration is to be done.
|
|
|
|
if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the generators.
|
|
|
|
std::vector<std::unique_ptr<cmLocalGenerator>> const& generators =
|
|
|
|
this->LocalGenerators;
|
|
|
|
auto& lg =
|
|
|
|
cm::static_reference_cast<cmLocalGhsMultiGenerator>(generators[0]);
|
|
|
|
|
|
|
|
// The name of the output file for the custom command.
|
|
|
|
this->StampFile = lg.GetBinaryDirectory() + std::string("/CMakeFiles/") +
|
|
|
|
CHECK_BUILD_SYSTEM_TARGET;
|
|
|
|
|
|
|
|
// Add a custom rule to re-run CMake if any input files changed.
|
|
|
|
{
|
|
|
|
// Collect the input files used to generate all targets in this
|
|
|
|
// project.
|
|
|
|
std::vector<std::string> listFiles;
|
|
|
|
for (const auto& gen : generators) {
|
|
|
|
cm::append(listFiles, gen->GetMakefile()->GetListFiles());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the cache file.
|
|
|
|
listFiles.emplace_back(cmStrCat(
|
|
|
|
this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeCache.txt"));
|
|
|
|
|
|
|
|
// Print not implemented warning.
|
|
|
|
if (this->GetCMakeInstance()->DoWriteGlobVerifyTarget()) {
|
|
|
|
std::ostringstream msg;
|
|
|
|
msg << "Any pre-check scripts, such as those generated for file(GLOB "
|
|
|
|
"CONFIGURE_DEPENDS), will not be run by gbuild.";
|
|
|
|
this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
|
|
|
|
msg.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort the list of input files and remove duplicates.
|
|
|
|
std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
|
|
|
|
auto newEnd = std::unique(listFiles.begin(), listFiles.end());
|
|
|
|
listFiles.erase(newEnd, listFiles.end());
|
|
|
|
|
|
|
|
// Create a rule to re-run CMake and create output file.
|
|
|
|
cmCustomCommandLines commandLines;
|
|
|
|
commandLines.emplace_back(
|
|
|
|
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "rm", "-f",
|
|
|
|
this->StampFile }));
|
|
|
|
std::string argS = cmStrCat("-S", lg.GetSourceDirectory());
|
|
|
|
std::string argB = cmStrCat("-B", lg.GetBinaryDirectory());
|
|
|
|
commandLines.emplace_back(
|
|
|
|
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), argS, argB }));
|
|
|
|
commandLines.emplace_back(cmMakeCommandLine(
|
|
|
|
{ cmSystemTools::GetCMakeCommand(), "-E", "touch", this->StampFile }));
|
|
|
|
|
|
|
|
/* Create the target(Exclude from ALL_BUILD).
|
|
|
|
*
|
|
|
|
* The build tool, currently, does not support rereading the project files
|
|
|
|
* if they get updated. So do not run this target as part of ALL_BUILD.
|
|
|
|
*/
|
|
|
|
auto cc = cm::make_unique<cmCustomCommand>();
|
|
|
|
cmTarget* tgt =
|
|
|
|
lg.AddUtilityCommand(CHECK_BUILD_SYSTEM_TARGET, true, std::move(cc));
|
|
|
|
auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg);
|
|
|
|
auto* gt = ptr.get();
|
|
|
|
lg.AddGeneratorTarget(std::move(ptr));
|
|
|
|
|
|
|
|
// Add the rule.
|
|
|
|
cc = cm::make_unique<cmCustomCommand>();
|
|
|
|
cc->SetOutputs(this->StampFile);
|
|
|
|
cc->SetDepends(listFiles);
|
|
|
|
cc->SetCommandLines(commandLines);
|
|
|
|
cc->SetComment("Checking Build System");
|
|
|
|
cc->SetEscapeOldStyle(false);
|
|
|
|
cc->SetStdPipesUTF8(true);
|
|
|
|
|
|
|
|
if (cmSourceFile* file =
|
|
|
|
lg.AddCustomCommandToOutput(std::move(cc), true)) {
|
|
|
|
gt->AddSource(file->ResolveFullPath());
|
|
|
|
} else {
|
|
|
|
cmSystemTools::Error("Error adding rule for " + this->StampFile);
|
|
|
|
}
|
|
|
|
// Organize in the "predefined targets" folder:
|
|
|
|
if (this->UseFolderProperty()) {
|
|
|
|
tgt->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::AddAllTarget()
|
|
|
|
{
|
|
|
|
// Add a special target that depends on ALL projects for easy build
|
|
|
|
// of one configuration only.
|
|
|
|
for (auto const& it : this->ProjectMap) {
|
|
|
|
std::vector<cmLocalGenerator*> const& gen = it.second;
|
|
|
|
// add the ALL_BUILD to the first local generator of each project
|
|
|
|
if (!gen.empty()) {
|
|
|
|
// Use no actual command lines so that the target itself is not
|
|
|
|
// considered always out of date.
|
|
|
|
auto cc = cm::make_unique<cmCustomCommand>();
|
|
|
|
cc->SetEscapeOldStyle(false);
|
|
|
|
cc->SetComment("Build all projects");
|
|
|
|
cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(),
|
|
|
|
true, std::move(cc));
|
|
|
|
|
|
|
|
gen[0]->AddGeneratorTarget(
|
|
|
|
cm::make_unique<cmGeneratorTarget>(allBuild, gen[0]));
|
|
|
|
|
|
|
|
// Organize in the "predefined targets" folder:
|
|
|
|
if (this->UseFolderProperty()) {
|
|
|
|
allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now make all targets depend on the ALL_BUILD target
|
|
|
|
for (cmLocalGenerator const* i : gen) {
|
|
|
|
for (const auto& tgt : i->GetGeneratorTargets()) {
|
|
|
|
// Skip global or imported targets
|
|
|
|
if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
|
|
|
|
tgt->IsImported()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Skip Exclude From All Targets
|
|
|
|
if (!this->IsExcluded(gen[0], tgt.get())) {
|
|
|
|
allBuild->AddUtility(tgt->GetName(), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmGlobalGhsMultiGenerator::AddExtraIDETargets()
|
|
|
|
{
|
|
|
|
// Add a special target that depends on ALL projects.
|
|
|
|
this->AddAllTarget();
|
|
|
|
|
|
|
|
/* Add Custom Target to check if CMake needs to be rerun.
|
|
|
|
*
|
|
|
|
* The build tool, currently, does not support rereading the project files
|
|
|
|
* if they get updated. So do not make the other targets dependent on this
|
|
|
|
* check.
|
|
|
|
*/
|
|
|
|
this->AddCheckTarget();
|
|
|
|
}
|