/* 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 "cmCustomCommandLines.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalGhsMultiGenerator.h" #include "cmLinkLineComputer.h" #include "cmLocalGenerator.h" #include "cmLocalGhsMultiGenerator.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmSourceGroup.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" #include #include #include #include #include cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) : GeneratorTarget(target) , LocalGenerator( static_cast(target->GetLocalGenerator())) , Makefile(target->Target->GetMakefile()) , Name(target->GetName()) { // 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( 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( MODULE ...) not supported: "; msg += this->Name; cmSystemTools::Message(msg); return; } case cmStateEnums::UTILITY: { std::string msg = "add_custom_target( ...) not supported: "; msg += this->Name; cmSystemTools::Message(msg); return; } 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 filestream in copy-if-different mode. std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory(); fname += "/"; fname += this->Name; fname += cmGlobalGhsMultiGenerator::FILE_EXTENSION; cmGeneratedFileStream fout(fname); fout.SetCopyIfDifferent(true); this->GetGlobalGenerator()->WriteFileHeader(fout); GhsMultiGpj::WriteGpjTag(this->TagType, fout); 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->WriteCustomCommands(fout); this->WriteSources(fout); this->WriteReferences(fout); fout.Close(); } cmGlobalGhsMultiGenerator* cmGhsMultiTargetGenerator::GetGlobalGenerator() const { return static_cast( 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::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::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::iterator i = this->DefinesByLanguage.find(language); if (i == this->DefinesByLanguage.end()) { std::set 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::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::iterator flagsByLangI = this->FlagsByLanguage.find(language); if (flagsByLangI != this->FlagsByLanguage.end()) { if (!flagsByLangI->second.empty()) { std::vector 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 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 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 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 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 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 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::WriteCustomCommands(std::ostream& fout) { WriteCustomCommandsHelper(fout, this->GeneratorTarget->GetPreBuildCommands(), cmTarget::PRE_BUILD); WriteCustomCommandsHelper( fout, this->GeneratorTarget->GetPostBuildCommands(), cmTarget::POST_BUILD); } void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper( std::ostream& fout, std::vector const& commandsSet, cmTarget::CustomCommandType const commandType) { for (cmCustomCommand const& customCommand : commandsSet) { cmCustomCommandLines const& commandLines = customCommand.GetCommandLines(); for (cmCustomCommandLine const& command : commandLines) { switch (commandType) { case cmTarget::PRE_BUILD: fout << " :preexecShellSafe="; break; case cmTarget::POST_BUILD: fout << " :postexecShellSafe="; break; default: assert("Only pre and post are supported"); } bool firstIteration = true; for (std::string const& commandLine : command) { std::string subCommandE = this->LocalGenerator->EscapeForShell(commandLine, true); fout << (firstIteration ? "'" : " "); // Need to double escape backslashes cmSystemTools::ReplaceString(subCommandE, "\\", "\\\\"); fout << subCommandE; firstIteration = false; } if (!command.empty()) { fout << "'" << 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 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 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 sourceGroups = this->Makefile->GetSourceGroups(); /* for each source file assign it to its group */ std::map> groupFiles; std::set 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 standardGroups = { "Header Files", "Source Files", "CMake Rules", "Object Files", "Object Libraries", "Resources" }; /* list of groups in the order they are displayed in a project file*/ std::vector 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); } } { /* 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 gfiles; /* write files into the proper project file * -- groups go into main project file * unless FOLDER 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; } } /* output rule for each source file */ for (const cmSourceFile* si : groupFiles[sg]) { // 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); *fout << fname << std::endl; 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; } } } for (cmGeneratedFileStream* f : gfiles) { f->Close(); } } 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; } } } void cmGhsMultiTargetGenerator::WriteReferences(std::ostream& fout) { // This only applies to INTEGRITY Applications if (this->TagType != GhsMultiGpj::INTERGRITY_APPLICATION) { return; } // Get the targets that this one depends upon cmTargetDependSet unordered = this->GetGlobalGenerator()->GetTargetDirectDepends(this->GeneratorTarget); cmGlobalGhsMultiGenerator::OrderedTargetDependSet ordered(unordered, this->Name); for (auto& t : ordered) { std::string tname = t->GetName(); std::string tpath = t->LocalGenerator->GetCurrentBinaryDirectory(); std::string rootpath = this->LocalGenerator->GetCurrentBinaryDirectory(); std::string outpath = this->LocalGenerator->MaybeConvertToRelativePath(rootpath, tpath) + "/" + tname + "REF" + cmGlobalGhsMultiGenerator::FILE_EXTENSION; fout << outpath; fout << " "; GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fout); // Tell the global generator that a refernce project needs to be created t->Target->SetProperty("GHS_REFERENCE_PROJECT", "ON"); } } 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 sources; this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName); for (auto& sf : sources) { if ("int" == sf->GetExtension()) { return true; } } return false; }