1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-06-13 10:52:36 +08:00
CMake/Source/cmGhsMultiTargetGenerator.cxx
Fred Baksik 83c47ef5b8 GHS: Update project layout to accommodate gbuild inconsistencies
-- Do not use reference projects, use build hierarchy instead.
   gbuild has three parallel levels:
   * low -- Parallelizes compiling source files within a single project (.gpj)
     file when safe to do so.
   * medium -- Parallelizes processing files within a single linked output when
     safe to do so.
   * high [default] -- Parallelizes processing files whenever safe to do so,
     including linking task.

   Testing showed that for some combinations of gbuild / MULTI there are issues
   with building a project that uses references to target project files along with
   using {nobuild} option.

   Sometimes the archiving of a library and linking of an executable were
   happening in parallel and the build would fail when linking because the
   archive wasn't complete.

   This behavior was also inconsistent when running the build from MULTI and
   from the command line with gbuild. In some cases MULTI did not parallelize
   archiving and linking, but gbuild performed these actions in parallel.

   The parallel build issue was not seen when using a build hierarchy where the
   project listed the project files normally instead of using a reference link.

   The other option was to add the -parallel_level=medium to the command line
   when using "cmake --build" but this wouldn't fix the issue if gbuild itself
   was used to and the user might not be aware of the extra option used by cmake.
2019-04-11 13:15:51 -04:00

800 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 "cmCustomCommand.h"
#include "cmCustomCommandGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGhsMultiGenerator.h"
#include "cmLinkLineComputer.h"
#include "cmLocalGenerator.h"
#include "cmLocalGhsMultiGenerator.h"
#include "cmMakefile.h"
#include "cmOutputConverter.h"
#include "cmSourceFile.h"
#include "cmSourceFileLocation.h"
#include "cmSourceGroup.h"
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include <algorithm>
#include <ostream>
#include <set>
#include <utility>
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 (const char* 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 (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 = "add_library(<name> SHARED ...) not supported: ";
msg += 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 = "add_library(<name> MODULE ...) not supported: ";
msg += 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.c_str());
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 = this->LocalGenerator->GetCurrentBinaryDirectory();
fproj += "/";
fproj += this->Name;
fproj += 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;
std::string rootpath = this->LocalGenerator->GetCurrentBinaryDirectory();
if (this->TagType != GhsMultiGpj::SUBPROJECT) {
// set target binary file destination
outpath = this->GeneratorTarget->GetDirectory(config);
outpath =
this->LocalGenerator->MaybeConvertToRelativePath(rootpath, outpath);
fout << " :binDirRelative=\"" << outpath << "\"" << std::endl;
fout << " -o \"" << this->TargetNameReal << "\"" << std::endl;
}
// set target object file destination
outpath = this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
fout << " :outputDirRelative=\"" << outpath << "\"" << std::endl;
}
void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config,
const std::string& language)
{
std::map<std::string, std::string>::iterator i =
this->FlagsByLanguage.find(language);
if (i == this->FlagsByLanguage.end()) {
std::string flags;
const char* lang = language.c_str();
this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, lang,
config);
this->LocalGenerator->AddCMP0018Flags(flags, this->GeneratorTarget, lang,
config);
this->LocalGenerator->AddVisibilityPresetFlags(
flags, this->GeneratorTarget, lang);
// 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, lang,
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)
{
std::map<std::string, std::string>::iterator i =
this->DefinesByLanguage.find(language);
if (i == this->DefinesByLanguage.end()) {
std::set<std::string> defines;
const char* lang = language.c_str();
// Add preprocessor definitions for this target and configuration.
this->LocalGenerator->GetTargetDefines(this->GeneratorTarget, config,
language, defines);
std::string definesString;
this->LocalGenerator->JoinDefines(defines, definesString, lang);
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)
{
std::map<std::string, std::string>::iterator flagsByLangI =
this->FlagsByLanguage.find(language);
if (flagsByLangI != this->FlagsByLanguage.end()) {
if (!flagsByLangI->second.empty()) {
std::vector<std::string> ghsCompFlags =
cmSystemTools::ParseArguments(flagsByLangI->second);
for (auto& f : ghsCompFlags) {
fout << " " << f << std::endl;
}
}
}
}
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 << std::endl;
}
}
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 << "\"" << std::endl;
}
}
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 (auto& l : lopts) {
fout << " " << l << std::endl;
}
// write out link search paths
// must be quoted for paths that contain spaces
std::vector<std::string> lpath = cmSystemTools::ParseArguments(linkPath);
for (auto& l : lpath) {
fout << " -L\"" << l << "\"" << std::endl;
}
// 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 (auto& l : llibs) {
if (l.compare(0, 2, "-l") == 0) {
fout << " \"" << l << "\"" << std::endl;
} else {
std::string rl = cmSystemTools::CollapseFullPath(l, cbd);
fout << " -l\"" << rl << "\"" << std::endl;
}
}
}
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 = this->LocalGenerator->GetCurrentBinaryDirectory();
fname +=
"/" + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
fname += "/" + this->Name + "_" + name;
fname += std::to_string(cmdcount++);
fname += 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 << "\"" << std::endl;
} else {
fout << fname << std::endl;
fout << " :outputName=\"" << fname << ".rule\"" << std::endl;
}
for (auto& byp : ccg.GetByproducts()) {
fout << " :extraOutputFile=\"" << byp << "\"" << std::endl;
}
}
}
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 currentBinDir = dir;
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 = "echo ";
echocmd += 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->MaybeConvertToRelativePath(currentBinDir, 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 = "./" + cmd;
}
cmd = this->LocalGenerator->ConvertToOutputFormat(
cmd, cmOutputConverter::SHELL);
if (useCall) {
cmd = "call " + cmd;
}
ccg.AppendArguments(c, cmd);
cmdLines.push_back(std::move(cmd));
}
}
// push back the custom commands
for (auto const& c : cmdLines) {
fout << c << std::endl;
fout << check_error << std::endl;
}
}
void cmGhsMultiTargetGenerator::WriteSourceProperty(
std::ostream& fout, const cmSourceFile* sf, std::string const& propName,
std::string const& propFlag)
{
const char* prop = sf->GetProperty(propName);
if (prop) {
std::vector<std::string> list;
cmSystemTools::ExpandListArgument(prop, list);
for (auto& p : list) {
fout << " " << propFlag << p << std::endl;
}
}
}
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 (auto& sf : sources) {
cmSourceGroup* sourceGroup =
this->Makefile->FindSourceGroup(sf->GetFullPath(), 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 (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->GetFullPath() < r->GetFullPath();
});
}
/* 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 =
cmSystemTools::IsOn(
this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE")) ||
cmSystemTools::IsOn(
this->Makefile->GetDefinition("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 =
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
lpath += "/";
lpath += gname;
lpath += cmGlobalGhsMultiGenerator::FILE_EXTENSION;
std::string fpath = this->LocalGenerator->GetCurrentBinaryDirectory();
fpath += "/";
fpath += 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" << std::endl;
} else {
*fout << "{comment} " << sg << std::endl;
}
} else if (sg.empty()) {
*fout << "{comment} Others" << std::endl;
}
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 << std::endl;
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 << std::endl;
}
}
}
} else {
std::vector<cmSourceFile const*> customCommands;
if (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 =
this->LocalGenerator->GetCurrentBinaryDirectory();
fname += "/" +
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
fname += "/" + this->Name + "_cc";
fname += std::to_string(cmdcount++) + "_";
fname += (sf->GetLocation()).GetName();
fname += 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 (auto& out : ccg.GetOutputs()) {
fout << fname << std::endl;
fout << " :outputName=\"" << out << "\"" << std::endl;
if (specifyExtra) {
for (auto& byp : ccg.GetByproducts()) {
fout << " :extraOutputFile=\"" << byp << "\"" << std::endl;
}
for (auto& dep : ccg.GetDepends()) {
fout << " :depends=\"" << dep << "\"" << std::endl;
}
specifyExtra = false;
}
}
}
void cmGhsMultiTargetGenerator::WriteObjectLangOverride(
std::ostream& fout, const cmSourceFile* sourceFile)
{
const char* rawLangProp = sourceFile->GetProperty("LANGUAGE");
if (nullptr != rawLangProp) {
std::string sourceLangProp(rawLangProp);
std::string const& extension = sourceFile->GetExtension();
if ("CXX" == sourceLangProp && ("c" == extension || "C" == extension)) {
fout << " -dotciscxx" << std::endl;
}
}
}
bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()
{
const char* p = this->GeneratorTarget->GetProperty("ghs_integrity_app");
if (p) {
return cmSystemTools::IsOn(
this->GeneratorTarget->GetProperty("ghs_integrity_app"));
}
std::vector<cmSourceFile*> sources;
this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName);
for (auto& sf : sources) {
if ("int" == sf->GetExtension()) {
return true;
}
}
return false;
}
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 = 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 (auto& di : si->GetCustomCommand()->GetDepends()) {
cmSourceFile const* sf = this->GeneratorTarget->GetLocalGenerator()
->GetMakefile()
->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;
}