
Most calls to `MaybeConvertToRelativePath` use one of our common work directories (e.g. top of the build tree) as the local path. Add helpers for each of the common cases to simplify and clarify call sites.
778 lines
27 KiB
C++
778 lines
27 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmGhsMultiTargetGenerator.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "cmCustomCommand.h"
|
|
#include "cmCustomCommandGenerator.h"
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGhsMultiGenerator.h"
|
|
#include "cmLinkLineComputer.h" // IWYU pragma: keep
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmLocalGhsMultiGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmProperty.h"
|
|
#include "cmSourceFile.h"
|
|
#include "cmSourceFileLocation.h"
|
|
#include "cmSourceGroup.h"
|
|
#include "cmStateDirectory.h"
|
|
#include "cmStateSnapshot.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
|
|
cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target)
|
|
: GeneratorTarget(target)
|
|
, LocalGenerator(
|
|
static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator()))
|
|
, Makefile(target->Target->GetMakefile())
|
|
, Name(target->GetName())
|
|
#ifdef _WIN32
|
|
, CmdWindowsShell(true)
|
|
#else
|
|
, CmdWindowsShell(false)
|
|
#endif
|
|
{
|
|
// Store the configuration name that is being used
|
|
if (cmProp config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) {
|
|
// Use the build type given by the user.
|
|
this->ConfigName = *config;
|
|
} else {
|
|
// No configuration type given.
|
|
this->ConfigName.clear();
|
|
}
|
|
}
|
|
|
|
cmGhsMultiTargetGenerator::~cmGhsMultiTargetGenerator() = default;
|
|
|
|
void cmGhsMultiTargetGenerator::Generate()
|
|
{
|
|
// Determine type of target for this project
|
|
switch (this->GeneratorTarget->GetType()) {
|
|
case cmStateEnums::EXECUTABLE: {
|
|
// Get the name of the executable to generate.
|
|
this->TargetNameReal =
|
|
this->GeneratorTarget->GetExecutableNames(this->ConfigName).Real;
|
|
if (this->cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()) {
|
|
this->TagType = GhsMultiGpj::INTERGRITY_APPLICATION;
|
|
} else {
|
|
this->TagType = GhsMultiGpj::PROGRAM;
|
|
}
|
|
break;
|
|
}
|
|
case cmStateEnums::STATIC_LIBRARY: {
|
|
this->TargetNameReal =
|
|
this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real;
|
|
this->TagType = GhsMultiGpj::LIBRARY;
|
|
break;
|
|
}
|
|
case cmStateEnums::SHARED_LIBRARY: {
|
|
std::string msg =
|
|
cmStrCat("add_library(<name> SHARED ...) not supported: ", this->Name);
|
|
cmSystemTools::Message(msg);
|
|
return;
|
|
}
|
|
case cmStateEnums::OBJECT_LIBRARY: {
|
|
this->TargetNameReal =
|
|
this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real;
|
|
this->TagType = GhsMultiGpj::SUBPROJECT;
|
|
break;
|
|
}
|
|
case cmStateEnums::MODULE_LIBRARY: {
|
|
std::string msg =
|
|
cmStrCat("add_library(<name> MODULE ...) not supported: ", this->Name);
|
|
cmSystemTools::Message(msg);
|
|
return;
|
|
}
|
|
case cmStateEnums::UTILITY: {
|
|
this->TargetNameReal = this->GeneratorTarget->GetName();
|
|
this->TagType = GhsMultiGpj::CUSTOM_TARGET;
|
|
break;
|
|
}
|
|
case cmStateEnums::GLOBAL_TARGET: {
|
|
this->TargetNameReal = this->GeneratorTarget->GetName();
|
|
if (this->TargetNameReal ==
|
|
this->GetGlobalGenerator()->GetInstallTargetName()) {
|
|
this->TagType = GhsMultiGpj::CUSTOM_TARGET;
|
|
} else {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// Tell the global generator the name of the project file
|
|
this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME",
|
|
this->Name);
|
|
this->GeneratorTarget->Target->SetProperty(
|
|
"GENERATOR_FILE_NAME_EXT", GhsMultiGpj::GetGpjTag(this->TagType));
|
|
|
|
this->GenerateTarget();
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::GenerateTarget()
|
|
{
|
|
// Open the target file in copy-if-different mode.
|
|
std::string fproj =
|
|
cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
|
|
this->Name, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
|
|
cmGeneratedFileStream fout(fproj);
|
|
fout.SetCopyIfDifferent(true);
|
|
|
|
this->GetGlobalGenerator()->WriteFileHeader(fout);
|
|
GhsMultiGpj::WriteGpjTag(this->TagType, fout);
|
|
|
|
if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
|
|
const std::string language(
|
|
this->GeneratorTarget->GetLinkerLanguage(this->ConfigName));
|
|
this->WriteTargetSpecifics(fout, this->ConfigName);
|
|
this->SetCompilerFlags(this->ConfigName, language);
|
|
this->WriteCompilerFlags(fout, this->ConfigName, language);
|
|
this->WriteCompilerDefinitions(fout, this->ConfigName, language);
|
|
this->WriteIncludes(fout, this->ConfigName, language);
|
|
this->WriteTargetLinkLine(fout, this->ConfigName);
|
|
this->WriteBuildEvents(fout);
|
|
}
|
|
this->WriteSources(fout);
|
|
fout.Close();
|
|
}
|
|
|
|
cmGlobalGhsMultiGenerator* cmGhsMultiTargetGenerator::GetGlobalGenerator()
|
|
const
|
|
{
|
|
return static_cast<cmGlobalGhsMultiGenerator*>(
|
|
this->LocalGenerator->GetGlobalGenerator());
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteTargetSpecifics(std::ostream& fout,
|
|
const std::string& config)
|
|
{
|
|
std::string outpath;
|
|
|
|
if (this->TagType != GhsMultiGpj::SUBPROJECT) {
|
|
// set target binary file destination
|
|
outpath = this->GeneratorTarget->GetDirectory(config);
|
|
outpath = this->LocalGenerator->MaybeRelativeToCurBinDir(outpath);
|
|
/* clang-format off */
|
|
fout << " :binDirRelative=\"" << outpath << "\"\n"
|
|
" -o \"" << this->TargetNameReal << "\"\n";
|
|
/* clang-format on */
|
|
}
|
|
|
|
// set target object file destination
|
|
outpath = this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
|
|
fout << " :outputDirRelative=\"" << outpath << "\"\n";
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config,
|
|
const std::string& language)
|
|
{
|
|
auto i = this->FlagsByLanguage.find(language);
|
|
if (i == this->FlagsByLanguage.end()) {
|
|
std::string flags;
|
|
this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget,
|
|
language, config);
|
|
this->LocalGenerator->AddCMP0018Flags(flags, this->GeneratorTarget,
|
|
language, config);
|
|
this->LocalGenerator->AddVisibilityPresetFlags(
|
|
flags, this->GeneratorTarget, language);
|
|
|
|
// Append old-style preprocessor definition flags.
|
|
if (this->Makefile->GetDefineFlags() != " ") {
|
|
this->LocalGenerator->AppendFlags(flags,
|
|
this->Makefile->GetDefineFlags());
|
|
}
|
|
|
|
// Add target-specific flags.
|
|
this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget,
|
|
language, config);
|
|
|
|
std::map<std::string, std::string>::value_type entry(language, flags);
|
|
i = this->FlagsByLanguage.insert(entry).first;
|
|
}
|
|
}
|
|
|
|
std::string cmGhsMultiTargetGenerator::GetDefines(const std::string& language,
|
|
std::string const& config)
|
|
{
|
|
auto i = this->DefinesByLanguage.find(language);
|
|
if (i == this->DefinesByLanguage.end()) {
|
|
std::set<std::string> defines;
|
|
// Add preprocessor definitions for this target and configuration.
|
|
this->LocalGenerator->GetTargetDefines(this->GeneratorTarget, config,
|
|
language, defines);
|
|
|
|
std::string definesString;
|
|
this->LocalGenerator->JoinDefines(defines, definesString, language);
|
|
|
|
std::map<std::string, std::string>::value_type entry(language,
|
|
definesString);
|
|
i = this->DefinesByLanguage.insert(entry).first;
|
|
}
|
|
return i->second;
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteCompilerFlags(std::ostream& fout,
|
|
std::string const&,
|
|
const std::string& language)
|
|
{
|
|
auto flagsByLangI = this->FlagsByLanguage.find(language);
|
|
if (flagsByLangI != this->FlagsByLanguage.end()) {
|
|
if (!flagsByLangI->second.empty()) {
|
|
std::vector<std::string> ghsCompFlags =
|
|
cmSystemTools::ParseArguments(flagsByLangI->second);
|
|
for (const std::string& f : ghsCompFlags) {
|
|
fout << " " << f << '\n';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteCompilerDefinitions(
|
|
std::ostream& fout, const std::string& config, const std::string& language)
|
|
{
|
|
std::vector<std::string> compileDefinitions;
|
|
this->GeneratorTarget->GetCompileDefinitions(compileDefinitions, config,
|
|
language);
|
|
for (std::string const& compileDefinition : compileDefinitions) {
|
|
fout << " -D" << compileDefinition << '\n';
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteIncludes(std::ostream& fout,
|
|
const std::string& config,
|
|
const std::string& language)
|
|
{
|
|
std::vector<std::string> includes;
|
|
this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
|
|
language, config);
|
|
|
|
for (std::string const& include : includes) {
|
|
fout << " -I\"" << include << "\"\n";
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout,
|
|
std::string const& config)
|
|
{
|
|
if (this->TagType == GhsMultiGpj::INTERGRITY_APPLICATION) {
|
|
return;
|
|
}
|
|
|
|
std::string linkLibraries;
|
|
std::string flags;
|
|
std::string linkFlags;
|
|
std::string frameworkPath;
|
|
std::string linkPath;
|
|
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer =
|
|
this->GetGlobalGenerator()->CreateLinkLineComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory());
|
|
|
|
this->LocalGenerator->GetTargetFlags(
|
|
linkLineComputer.get(), config, linkLibraries, flags, linkFlags,
|
|
frameworkPath, linkPath, this->GeneratorTarget);
|
|
|
|
// write out link options
|
|
std::vector<std::string> lopts = cmSystemTools::ParseArguments(linkFlags);
|
|
for (const std::string& l : lopts) {
|
|
fout << " " << l << '\n';
|
|
}
|
|
|
|
// write out link search paths
|
|
// must be quoted for paths that contain spaces
|
|
std::vector<std::string> lpath = cmSystemTools::ParseArguments(linkPath);
|
|
for (const std::string& l : lpath) {
|
|
fout << " -L\"" << l << "\"\n";
|
|
}
|
|
|
|
// write out link libs
|
|
// must be quoted for filepaths that contains spaces
|
|
std::string cbd = this->LocalGenerator->GetCurrentBinaryDirectory();
|
|
|
|
std::vector<std::string> llibs =
|
|
cmSystemTools::ParseArguments(linkLibraries);
|
|
for (const std::string& l : llibs) {
|
|
if (l.compare(0, 2, "-l") == 0) {
|
|
fout << " \"" << l << "\"\n";
|
|
} else {
|
|
std::string rl = cmSystemTools::CollapseFullPath(l, cbd);
|
|
fout << " -l\"" << rl << "\"\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout)
|
|
{
|
|
this->WriteBuildEventsHelper(
|
|
fout, this->GeneratorTarget->GetPreBuildCommands(),
|
|
std::string("prebuild"), std::string("preexecShell"));
|
|
|
|
if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
|
|
this->WriteBuildEventsHelper(
|
|
fout, this->GeneratorTarget->GetPreLinkCommands(),
|
|
std::string("prelink"), std::string("preexecShell"));
|
|
}
|
|
|
|
this->WriteBuildEventsHelper(
|
|
fout, this->GeneratorTarget->GetPostBuildCommands(),
|
|
std::string("postbuild"), std::string("postexecShell"));
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteBuildEventsHelper(
|
|
std::ostream& fout, const std::vector<cmCustomCommand>& ccv,
|
|
std::string const& name, std::string const& cmd)
|
|
{
|
|
int cmdcount = 0;
|
|
|
|
for (cmCustomCommand const& cc : ccv) {
|
|
cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator);
|
|
// Open the filestream for this custom command
|
|
std::string fname =
|
|
cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
|
|
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
|
|
'/', this->Name, '_', name, cmdcount++,
|
|
this->CmdWindowsShell ? ".bat" : ".sh");
|
|
cmGeneratedFileStream f(fname);
|
|
f.SetCopyIfDifferent(true);
|
|
this->WriteCustomCommandsHelper(f, ccg);
|
|
f.Close();
|
|
if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
|
|
fout << " :" << cmd << "=\"" << fname << "\"\n";
|
|
} else {
|
|
fout << fname << "\n :outputName=\"" << fname << ".rule\"\n";
|
|
}
|
|
for (const auto& byp : ccg.GetByproducts()) {
|
|
fout << " :extraOutputFile=\"" << byp << "\"\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper(
|
|
std::ostream& fout, cmCustomCommandGenerator const& ccg)
|
|
{
|
|
std::vector<std::string> cmdLines;
|
|
|
|
// if the command specified a working directory use it.
|
|
std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory();
|
|
std::string workingDir = ccg.GetWorkingDirectory();
|
|
if (!workingDir.empty()) {
|
|
dir = workingDir;
|
|
}
|
|
|
|
// Line to check for error between commands.
|
|
#ifdef _WIN32
|
|
std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%";
|
|
#else
|
|
std::string check_error = "if [[ $? -ne 0 ]]; then exit 1; fi";
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
cmdLines.push_back("@echo off");
|
|
#endif
|
|
// Echo the custom command's comment text.
|
|
const char* comment = ccg.GetComment();
|
|
if (comment && *comment) {
|
|
std::string echocmd = cmStrCat("echo ", comment);
|
|
cmdLines.push_back(std::move(echocmd));
|
|
}
|
|
|
|
// Switch to working directory
|
|
std::string cdCmd;
|
|
#ifdef _WIN32
|
|
std::string cdStr = "cd /D ";
|
|
#else
|
|
std::string cdStr = "cd ";
|
|
#endif
|
|
cdCmd = cdStr +
|
|
this->LocalGenerator->ConvertToOutputFormat(dir, cmOutputConverter::SHELL);
|
|
cmdLines.push_back(std::move(cdCmd));
|
|
|
|
for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
|
|
// Build the command line in a single string.
|
|
std::string cmd = ccg.GetCommand(c);
|
|
if (!cmd.empty()) {
|
|
// Use "call " before any invocations of .bat or .cmd files
|
|
// invoked as custom commands in the WindowsShell.
|
|
//
|
|
bool useCall = false;
|
|
|
|
if (this->CmdWindowsShell) {
|
|
std::string suffix;
|
|
if (cmd.size() > 4) {
|
|
suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
|
|
if (suffix == ".bat" || suffix == ".cmd") {
|
|
useCall = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
cmSystemTools::ReplaceString(cmd, "/./", "/");
|
|
// Convert the command to a relative path only if the current
|
|
// working directory will be the start-output directory.
|
|
bool had_slash = cmd.find('/') != std::string::npos;
|
|
if (workingDir.empty()) {
|
|
cmd = this->LocalGenerator->MaybeRelativeToCurBinDir(cmd);
|
|
}
|
|
bool has_slash = cmd.find('/') != std::string::npos;
|
|
if (had_slash && !has_slash) {
|
|
// This command was specified as a path to a file in the
|
|
// current directory. Add a leading "./" so it can run
|
|
// without the current directory being in the search path.
|
|
cmd = cmStrCat("./", cmd);
|
|
}
|
|
cmd = this->LocalGenerator->ConvertToOutputFormat(
|
|
cmd, cmOutputConverter::SHELL);
|
|
if (useCall) {
|
|
cmd = cmStrCat("call ", cmd);
|
|
}
|
|
ccg.AppendArguments(c, cmd);
|
|
cmdLines.push_back(std::move(cmd));
|
|
}
|
|
}
|
|
|
|
// push back the custom commands
|
|
for (auto const& c : cmdLines) {
|
|
fout << c << '\n' << check_error << '\n';
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteSourceProperty(
|
|
std::ostream& fout, const cmSourceFile* sf, std::string const& propName,
|
|
std::string const& propFlag)
|
|
{
|
|
cmProp prop = sf->GetProperty(propName);
|
|
if (prop) {
|
|
std::vector<std::string> list = cmExpandedList(*prop);
|
|
for (const std::string& p : list) {
|
|
fout << " " << propFlag << p << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj)
|
|
{
|
|
/* vector of all sources for this target */
|
|
std::vector<cmSourceFile*> sources;
|
|
this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName);
|
|
|
|
/* vector of all groups defined for this target
|
|
* -- but the vector is not expanded with sub groups or in any useful order
|
|
*/
|
|
std::vector<cmSourceGroup> sourceGroups = this->Makefile->GetSourceGroups();
|
|
|
|
/* for each source file assign it to its group */
|
|
std::map<std::string, std::vector<cmSourceFile*>> groupFiles;
|
|
std::set<std::string> groupNames;
|
|
for (cmSourceFile* sf : sources) {
|
|
cmSourceGroup* sourceGroup =
|
|
this->Makefile->FindSourceGroup(sf->ResolveFullPath(), sourceGroups);
|
|
std::string gn = sourceGroup->GetFullName();
|
|
groupFiles[gn].push_back(sf);
|
|
groupNames.insert(std::move(gn));
|
|
}
|
|
|
|
/* list of known groups and the order they are displayed in a project file */
|
|
const std::vector<std::string> standardGroups = {
|
|
"CMake Rules", "Header Files", "Source Files",
|
|
"Object Files", "Object Libraries", "Resources"
|
|
};
|
|
|
|
/* list of groups in the order they are displayed in a project file*/
|
|
std::vector<std::string> groupFilesList(groupFiles.size());
|
|
|
|
/* put the groups in the order they should be listed
|
|
* - standard groups first, and then everything else
|
|
* in the order used by std::map.
|
|
*/
|
|
int i = 0;
|
|
for (const std::string& gn : standardGroups) {
|
|
auto n = groupNames.find(gn);
|
|
if (n != groupNames.end()) {
|
|
groupFilesList[i] = *n;
|
|
i += 1;
|
|
groupNames.erase(gn);
|
|
} else if (this->TagType == GhsMultiGpj::CUSTOM_TARGET &&
|
|
gn == "CMake Rules") {
|
|
/* make sure that rules folder always exists in case of custom targets
|
|
* that have no custom commands except for pre or post build events.
|
|
*/
|
|
groupFilesList.resize(groupFilesList.size() + 1);
|
|
groupFilesList[i] = gn;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
{ /* catch-all group - is last item */
|
|
std::string gn;
|
|
auto n = groupNames.find(gn);
|
|
if (n != groupNames.end()) {
|
|
groupFilesList.back() = *n;
|
|
groupNames.erase(gn);
|
|
}
|
|
}
|
|
|
|
for (const auto& n : groupNames) {
|
|
groupFilesList[i] = n;
|
|
i += 1;
|
|
}
|
|
|
|
/* sort the files within each group */
|
|
for (auto& n : groupFilesList) {
|
|
std::sort(groupFiles[n].begin(), groupFiles[n].end(),
|
|
[](cmSourceFile* l, cmSourceFile* r) {
|
|
return l->ResolveFullPath() < r->ResolveFullPath();
|
|
});
|
|
}
|
|
|
|
/* list of open project files */
|
|
std::vector<cmGeneratedFileStream*> gfiles;
|
|
|
|
/* write files into the proper project file
|
|
* -- groups go into main project file
|
|
* unless NO_SOURCE_GROUP_FILE property or variable is set.
|
|
*/
|
|
for (auto& sg : groupFilesList) {
|
|
std::ostream* fout;
|
|
bool useProjectFile =
|
|
cmIsOn(this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE")) ||
|
|
this->Makefile->IsOn("CMAKE_GHS_NO_SOURCE_GROUP_FILE");
|
|
if (useProjectFile || sg.empty()) {
|
|
fout = &fout_proj;
|
|
} else {
|
|
// Open the filestream in copy-if-different mode.
|
|
std::string gname = sg;
|
|
cmsys::SystemTools::ReplaceString(gname, "\\", "_");
|
|
std::string lpath = cmStrCat(
|
|
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
|
|
gname, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
|
|
std::string fpath = cmStrCat(
|
|
this->LocalGenerator->GetCurrentBinaryDirectory(), '/', lpath);
|
|
cmGeneratedFileStream* f = new cmGeneratedFileStream(fpath);
|
|
f->SetCopyIfDifferent(true);
|
|
gfiles.push_back(f);
|
|
fout = f;
|
|
this->GetGlobalGenerator()->WriteFileHeader(*f);
|
|
GhsMultiGpj::WriteGpjTag(GhsMultiGpj::SUBPROJECT, *f);
|
|
fout_proj << lpath << " ";
|
|
GhsMultiGpj::WriteGpjTag(GhsMultiGpj::SUBPROJECT, fout_proj);
|
|
}
|
|
|
|
if (useProjectFile) {
|
|
if (sg.empty()) {
|
|
*fout << "{comment} Others" << '\n';
|
|
} else {
|
|
*fout << "{comment} " << sg << '\n';
|
|
}
|
|
} else if (sg.empty()) {
|
|
*fout << "{comment} Others\n";
|
|
}
|
|
|
|
if (sg != "CMake Rules") {
|
|
/* output rule for each source file */
|
|
for (const cmSourceFile* si : groupFiles[sg]) {
|
|
bool compile = true;
|
|
// Convert filename to native system
|
|
// WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on
|
|
// windows when opening some files from the search window.
|
|
std::string fname(si->GetFullPath());
|
|
cmSystemTools::ConvertToOutputSlashes(fname);
|
|
|
|
/* For custom targets list any associated sources,
|
|
* comment out source code to prevent it from being
|
|
* compiled when processing this target.
|
|
* Otherwise, comment out any custom command (main) dependencies that
|
|
* are listed as source files to prevent them from being considered
|
|
* part of the build.
|
|
*/
|
|
std::string comment;
|
|
if ((this->TagType == GhsMultiGpj::CUSTOM_TARGET &&
|
|
!si->GetLanguage().empty()) ||
|
|
si->GetCustomCommand()) {
|
|
comment = "{comment} ";
|
|
compile = false;
|
|
}
|
|
|
|
*fout << comment << fname << '\n';
|
|
if (compile) {
|
|
if ("ld" != si->GetExtension() && "int" != si->GetExtension() &&
|
|
"bsp" != si->GetExtension()) {
|
|
WriteObjectLangOverride(*fout, si);
|
|
}
|
|
|
|
this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I");
|
|
this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D");
|
|
this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", "");
|
|
|
|
/* to avoid clutter in the GUI only print out the objectName if it
|
|
* has been renamed */
|
|
std::string objectName = this->GeneratorTarget->GetObjectName(si);
|
|
if (!objectName.empty() &&
|
|
this->GeneratorTarget->HasExplicitObjectName(si)) {
|
|
*fout << " -o " << objectName << '\n';
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
std::vector<cmSourceFile const*> customCommands;
|
|
if (this->ComputeCustomCommandOrder(customCommands)) {
|
|
std::string message = "The custom commands for target [" +
|
|
this->GeneratorTarget->GetName() + "] had a cycle.\n";
|
|
cmSystemTools::Error(message);
|
|
} else {
|
|
/* Custom targets do not have a dependency on SOURCES files.
|
|
* Therefore the dependency list may include SOURCES files after the
|
|
* custom target. Because nothing can depend on the custom target just
|
|
* move it to the last item.
|
|
*/
|
|
for (auto sf = customCommands.begin(); sf != customCommands.end();
|
|
++sf) {
|
|
if (((*sf)->GetLocation()).GetName() == this->Name + ".rule") {
|
|
std::rotate(sf, sf + 1, customCommands.end());
|
|
break;
|
|
}
|
|
}
|
|
int cmdcount = 0;
|
|
for (auto& sf : customCommands) {
|
|
const cmCustomCommand* cc = sf->GetCustomCommand();
|
|
cmCustomCommandGenerator ccg(*cc, this->ConfigName,
|
|
this->LocalGenerator);
|
|
|
|
// Open the filestream for this custom command
|
|
std::string fname = cmStrCat(
|
|
this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
|
|
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
|
|
'/', this->Name, "_cc", cmdcount++, '_',
|
|
(sf->GetLocation()).GetName(),
|
|
this->CmdWindowsShell ? ".bat" : ".sh");
|
|
cmGeneratedFileStream f(fname);
|
|
f.SetCopyIfDifferent(true);
|
|
this->WriteCustomCommandsHelper(f, ccg);
|
|
f.Close();
|
|
this->WriteCustomCommandLine(*fout, fname, ccg);
|
|
}
|
|
}
|
|
if (this->TagType == GhsMultiGpj::CUSTOM_TARGET) {
|
|
this->WriteBuildEvents(*fout);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (cmGeneratedFileStream* f : gfiles) {
|
|
f->Close();
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteCustomCommandLine(
|
|
std::ostream& fout, std::string& fname, cmCustomCommandGenerator const& ccg)
|
|
{
|
|
/* NOTE: Customization Files are not well documented. Testing showed
|
|
* that ":outputName=file" can only be used once per script. The
|
|
* script will only run if ":outputName=file" is missing or just run
|
|
* once if ":outputName=file" is not specified. If there are
|
|
* multiple outputs then the script needs to be listed multiple times
|
|
* for each output. Otherwise it won't rerun the script if one of
|
|
* the outputs is manually deleted.
|
|
*/
|
|
bool specifyExtra = true;
|
|
for (const auto& out : ccg.GetOutputs()) {
|
|
fout << fname << '\n';
|
|
fout << " :outputName=\"" << out << "\"\n";
|
|
if (specifyExtra) {
|
|
for (const auto& byp : ccg.GetByproducts()) {
|
|
fout << " :extraOutputFile=\"" << byp << "\"\n";
|
|
}
|
|
for (const auto& dep : ccg.GetDepends()) {
|
|
fout << " :depends=\"" << dep << "\"\n";
|
|
}
|
|
specifyExtra = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmGhsMultiTargetGenerator::WriteObjectLangOverride(
|
|
std::ostream& fout, const cmSourceFile* sourceFile)
|
|
{
|
|
cmProp rawLangProp = sourceFile->GetProperty("LANGUAGE");
|
|
if (rawLangProp) {
|
|
std::string sourceLangProp(*rawLangProp);
|
|
std::string const& extension = sourceFile->GetExtension();
|
|
if ("CXX" == sourceLangProp && ("c" == extension || "C" == extension)) {
|
|
fout << " -dotciscxx\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()
|
|
{
|
|
if (cmProp p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) {
|
|
return cmIsOn(*p);
|
|
}
|
|
std::vector<cmSourceFile*> sources;
|
|
this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName);
|
|
return std::any_of(sources.begin(), sources.end(),
|
|
[](cmSourceFile const* sf) -> bool {
|
|
return "int" == sf->GetExtension();
|
|
});
|
|
}
|
|
|
|
bool cmGhsMultiTargetGenerator::ComputeCustomCommandOrder(
|
|
std::vector<cmSourceFile const*>& order)
|
|
{
|
|
std::set<cmSourceFile const*> temp;
|
|
std::set<cmSourceFile const*> perm;
|
|
|
|
// Collect all custom commands for this target
|
|
std::vector<cmSourceFile const*> customCommands;
|
|
this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName);
|
|
|
|
for (cmSourceFile const* si : customCommands) {
|
|
bool r = this->VisitCustomCommand(temp, perm, order, si);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cmGhsMultiTargetGenerator::VisitCustomCommand(
|
|
std::set<cmSourceFile const*>& temp, std::set<cmSourceFile const*>& perm,
|
|
std::vector<cmSourceFile const*>& order, cmSourceFile const* si)
|
|
{
|
|
/* check if permanent mark is set*/
|
|
if (perm.find(si) == perm.end()) {
|
|
/* set temporary mark; check if revisit*/
|
|
if (temp.insert(si).second) {
|
|
for (const auto& di : si->GetCustomCommand()->GetDepends()) {
|
|
cmSourceFile const* sf =
|
|
this->GeneratorTarget->GetLocalGenerator()->GetSourceFileWithOutput(
|
|
di);
|
|
/* if sf exists then visit */
|
|
if (sf && this->VisitCustomCommand(temp, perm, order, sf)) {
|
|
return true;
|
|
}
|
|
}
|
|
/* mark as complete; insert into beginning of list*/
|
|
perm.insert(si);
|
|
order.push_back(si);
|
|
return false;
|
|
}
|
|
/* revisiting item - not a DAG */
|
|
return true;
|
|
}
|
|
/* already complete */
|
|
return false;
|
|
}
|