blob: 9f361f61824cd407ff223f6edc9cfb80b2f678bb [file] [log] [blame]
/* 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 "cmDocumentationEntry.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmGhsMultiGpj.h"
#include "cmLocalGenerator.h"
#include "cmLocalGhsMultiGenerator.h"
#include "cmMakefile.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmVersion.h"
#include "cmake.h"
#include <algorithm>
#include <map>
#include <ostream>
#include <string.h>
#include <utility>
const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj";
#ifdef __linux__
const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild";
const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "/usr/ghs";
#elif defined(_WIN32)
const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe";
const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "C:/ghs";
#endif
cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm)
: cmGlobalGenerator(cm)
{
cm->GetState()->SetGhsMultiIDE(true);
}
cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default;
cmLocalGenerator* cmGlobalGhsMultiGenerator::CreateLocalGenerator(
cmMakefile* mf)
{
return new cmLocalGhsMultiGenerator(this, mf);
}
void cmGlobalGhsMultiGenerator::GetDocumentation(cmDocumentationEntry& entry)
{
entry.Name = GetActualName();
entry.Brief =
"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;
dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
dir += "/";
dir += gt->LocalGenerator->GetTargetDirectory(gt);
dir += "/";
gt->ObjectDirectory = dir;
}
bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts,
cmMakefile* mf)
{
std::string tsp; /* toolset path */
this->GetToolset(mf, tsp, ts);
/* no toolset was found */
if (tsp.empty()) {
return false;
}
if (ts.empty()) {
std::string message;
message =
"Green Hills MULTI: -T <toolset> not specified; defaulting to \"";
message += tsp;
message += "\"";
cmSystemTools::Message(message);
/* store the full toolset for later use
* -- already done if -T<toolset> was specified
*/
mf->AddCacheDefinition("CMAKE_GENERATOR_TOOLSET", tsp.c_str(),
"Location of generator toolset.",
cmStateEnums::INTERNAL);
}
/* set the build tool to use */
std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") +
DEFAULT_BUILD_PROGRAM);
const char* prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");
/* check if the toolset changed from last generate */
if (prevTool != nullptr && (gbuild != prevTool)) {
std::string message = "toolset build tool: ";
message += gbuild;
message += "\nDoes not match the previously used build tool: ";
message += prevTool;
message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
"directory or choose a different binary directory.";
cmSystemTools::Error(message);
return false;
}
/* store the toolset that is being used for this build */
mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild.c_str(),
"build program to use", cmStateEnums::INTERNAL, true);
mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp.c_str());
return true;
}
bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p,
cmMakefile* mf)
{
std::string arch;
if (p.empty()) {
cmSystemTools::Message(
"Green Hills MULTI: -A <arch> not specified; defaulting to \"arm\"");
arch = "arm";
/* store the platform name for later use
* -- already done if -A<arch> was specified
*/
mf->AddCacheDefinition("CMAKE_GENERATOR_PLATFORM", arch.c_str(),
"Name of generator platform.",
cmStateEnums::INTERNAL);
} else {
arch = p;
}
/* check if OS location has been updated by platform scripts */
std::string platform = mf->GetSafeDefinition("GHS_TARGET_PLATFORM");
std::string osdir = mf->GetSafeDefinition("GHS_OS_DIR");
if (cmSystemTools::IsOff(osdir.c_str()) &&
platform.find("integrity") != std::string::npos) {
if (!this->CMakeInstance->GetIsInTryCompile()) {
/* required OS location is not found */
std::string m =
"Green Hills MULTI: GHS_OS_DIR not specified; No OS found in \"";
m += mf->GetSafeDefinition("GHS_OS_ROOT");
m += "\"";
cmSystemTools::Message(m);
}
osdir = "GHS_OS_DIR-NOT-SPECIFIED";
} else if (!this->CMakeInstance->GetIsInTryCompile() &&
cmSystemTools::IsOff(this->OsDir) &&
!cmSystemTools::IsOff(osdir)) {
/* OS location was updated by auto-selection */
std::string m = "Green Hills MULTI: GHS_OS_DIR not specified; found \"";
m += osdir;
m += "\"";
cmSystemTools::Message(m);
}
this->OsDir = osdir;
// Determine GHS_BSP_NAME
std::string bspName = mf->GetSafeDefinition("GHS_BSP_NAME");
if (cmSystemTools::IsOff(bspName.c_str()) &&
platform.find("integrity") != std::string::npos) {
bspName = "sim" + arch;
/* write back the calculate name for next time */
mf->AddCacheDefinition("GHS_BSP_NAME", bspName.c_str(),
"Name of GHS target platform.",
cmStateEnums::STRING, true);
std::string m =
"Green Hills MULTI: GHS_BSP_NAME not specified; defaulting to \"";
m += bspName;
m += "\"";
cmSystemTools::Message(m);
}
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
const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM");
if (!tgtPlatform) {
cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not "
"specified; defaulting to \"integrity\"");
tgtPlatform = "integrity";
}
/* store the platform name for later use */
mf->AddCacheDefinition("GHS_TARGET_PLATFORM", tgtPlatform,
"Name of GHS target platform.", cmStateEnums::STRING);
/* store original OS location */
this->OsDir = mf->GetSafeDefinition("GHS_OS_DIR");
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& tsd,
const std::string& ts)
{
const char* ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT");
if (!ghsRoot || ghsRoot[0] == '\0') {
ghsRoot = DEFAULT_TOOLSET_ROOT;
}
tsd = ghsRoot;
if (ts.empty()) {
std::vector<std::string> output;
// Use latest? version
if (tsd.back() != '/') {
tsd += "/";
}
cmSystemTools::Glob(tsd, "comp_[^;]+", output);
if (output.empty()) {
std::string msg =
"No GHS toolsets found in GHS_TOOLSET_ROOT \"" + tsd + "\".";
cmSystemTools::Error(msg);
tsd = "";
} else {
tsd += output.back();
}
} else {
std::string tryPath;
tryPath = cmSystemTools::CollapseFullPath(ts, tsd);
if (!cmSystemTools::FileExists(tryPath)) {
std::string msg = "GHS toolset \"" + tryPath + "\" not found.";
cmSystemTools::Error(msg);
tsd = "";
} else {
tsd = tryPath;
}
}
}
void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout)
{
fout << "#!gbuild" << std::endl;
fout << "#" << std::endl
<< "# CMAKE generated file: DO NOT EDIT!" << std::endl
<< "# Generated by \"" << GetActualName() << "\""
<< " Generator, CMake Version " << cmVersion::GetMajorVersion() << "."
<< cmVersion::GetMinorVersion() << std::endl
<< "#" << std::endl
<< std::endl;
}
void cmGlobalGhsMultiGenerator::WriteTopLevelProject(
std::ostream& fout, cmLocalGenerator* root,
std::vector<cmLocalGenerator*>& generators)
{
WriteFileHeader(fout);
this->WriteMacros(fout);
this->WriteHighLevelDirectives(fout);
GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);
fout << "# Top Level Project File" << std::endl;
// Specify BSP option if supplied by user
const char* bspName =
this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME");
if (!cmSystemTools::IsOff(bspName)) {
fout << " -bsp " << bspName << std::endl;
}
// Specify OS DIR if supplied by user
// -- not all platforms require this entry in the project file
if (!cmSystemTools::IsOff(this->OsDir.c_str())) {
const char* osDirOption =
this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION");
std::replace(this->OsDir.begin(), this->OsDir.end(), '\\', '/');
fout << " ";
if (cmSystemTools::IsOff(osDirOption)) {
fout << "";
} else {
fout << osDirOption;
}
fout << "\"" << this->OsDir << "\"" << std::endl;
}
WriteSubProjects(fout, root, generators);
}
void cmGlobalGhsMultiGenerator::WriteSubProjects(
std::ostream& fout, cmLocalGenerator* root,
std::vector<cmLocalGenerator*>& generators)
{
// 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 orderedProjectTargets(projectTargets, "");
// write out all the sub-projects
std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
for (cmGeneratorTarget const* target : orderedProjectTargets) {
if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
continue;
}
const char* projName = target->GetProperty("GENERATOR_FILE_NAME");
const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
if (projName && projType) {
cmLocalGenerator* lg = target->GetLocalGenerator();
std::string dir = lg->GetCurrentBinaryDirectory();
dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir);
if (dir == ".") {
dir.clear();
} else {
if (dir.back() != '/') {
dir += "/";
}
}
if (cmSystemTools::IsOn(target->GetProperty("EXCLUDE_FROM_ALL"))) {
fout << "{comment} ";
}
std::string projFile = dir + projName + FILE_EXTENSION;
fout << projFile;
fout << " " << projType << std::endl;
if (cmSystemTools::IsOn(target->GetProperty("GHS_REFERENCE_PROJECT"))) {
// create reference project
std::string fname = dir;
fname += target->GetName();
fname += "REF";
fname += FILE_EXTENSION;
cmGeneratedFileStream fref(fname);
fref.SetCopyIfDifferent(true);
this->WriteFileHeader(fref);
GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fref);
fref << " :reference=" << projFile << std::endl;
fref.Close();
}
}
}
}
void cmGlobalGhsMultiGenerator::Generate()
{
// first do the superclass method
this->cmGlobalGenerator::Generate();
// output top-level projects
for (auto& it : this->ProjectMap) {
this->OutputTopLevelProject(it.second[0], it.second);
}
}
void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
{
if (generators.empty()) {
return;
}
/* 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.
*/
std::string fname = root->GetCurrentBinaryDirectory();
fname += "/";
fname += root->GetProjectName();
fname += ".top";
fname += FILE_EXTENSION;
cmGeneratedFileStream fout(fname);
fout.SetCopyIfDifferent(true);
this->WriteTopLevelProject(fout, root, generators);
fout.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*/, bool /*fast*/, int jobs, bool /*verbose*/,
std::vector<std::string> const& makeOptions)
{
GeneratedMakeCommand makeCommand = {};
std::string gbuild;
if (const char* gbuildCached =
this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM")) {
gbuild = gbuildCached;
}
makeCommand.Add(this->SelectMakeProgram(makeProgram, gbuild));
if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
makeCommand.Add("-parallel");
if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
makeCommand.Add(std::to_string(jobs));
}
}
makeCommand.Add(makeOptions.begin(), makeOptions.end());
/* determine which top-project file to use */
std::string proj = projectName + ".top" + FILE_EXTENSION;
std::vector<std::string> files;
cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
if (!files.empty()) {
auto p = std::find(files.begin(), files.end(), proj);
if (p == files.end()) {
proj = files.at(0);
}
}
makeCommand.Add("-top", proj);
if (!targetNames.empty()) {
if (std::find(targetNames.begin(), targetNames.end(), "clean") !=
targetNames.end()) {
makeCommand.Add("-clean");
} else {
for (const auto& tname : targetNames) {
if (!tname.empty()) {
if (tname.compare(tname.size() - 4, 4, ".gpj") == 0) {
makeCommand.Add(tname);
} else {
makeCommand.Add(tname + ".gpj");
}
}
}
}
}
return { makeCommand };
}
void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout)
{
char const* ghsGpjMacros =
this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS");
if (nullptr != ghsGpjMacros) {
std::vector<std::string> expandedList;
cmSystemTools::ExpandListArgument(std::string(ghsGpjMacros), expandedList);
for (std::string const& arg : expandedList) {
fout << "macro " << arg << std::endl;
}
}
}
void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout)
{
/* set primary target */
std::string tgt;
const char* t =
this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET");
if (t) {
tgt = t;
this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET");
} else {
const char* a =
this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM");
const char* p =
this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM");
tgt = (a ? a : "");
tgt += "_";
tgt += (p ? p : "");
tgt += ".tgt";
}
fout << "primaryTarget=" << tgt << std::endl;
char const* const customization =
this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION");
if (nullptr != customization && strlen(customization) > 0) {
fout << "customization=" << trimQuotes(customization) << std::endl;
this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION");
}
}
std::string cmGlobalGhsMultiGenerator::trimQuotes(std::string const& str)
{
std::string result;
result.reserve(str.size());
for (const char* ch = str.c_str(); *ch != '\0'; ++ch) {
if (*ch != '"') {
result += *ch;
}
}
return result;
}
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());
}