mirror of
https://github.com/Kitware/CMake.git
synced 2025-06-13 10:52:36 +08:00

-- 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.
800 lines
27 KiB
C++
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;
|
|
}
|