| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmGhsMultiTargetGenerator.h" | |
| #include <algorithm> | |
| #include <memory> | |
| #include <ostream> | |
| #include <set> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/optional> | |
| #include <cmext/string_view> | |
| #include "cmCustomCommand.h" | |
| #include "cmCustomCommandGenerator.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorFileSets.h" | |
| #include "cmGeneratorOptions.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalGhsMultiGenerator.h" | |
| #include "cmLinkLineComputer.h" // IWYU pragma: keep | |
| #include "cmList.h" | |
| #include "cmListFileCache.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 "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) | |
| : GeneratorTarget(target) | |
| , LocalGenerator( | |
| static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator())) | |
| , Makefile(target->Target->GetMakefile()) | |
| , Name(target->GetName()) | |
| { | |
| // Store the configuration name that is being used | |
| if (cmValue 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 cm::TargetType::EXECUTABLE: { | |
| // Get the name of the executable to generate. | |
| this->TargetNameReal = | |
| this->GeneratorTarget->GetExecutableNames(this->ConfigName).Real; | |
| if (this->cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()) { | |
| this->TagType = GhsMultiGpj::INTEGRITY_APPLICATION; | |
| } else { | |
| this->TagType = GhsMultiGpj::PROGRAM; | |
| } | |
| break; | |
| } | |
| case cm::TargetType::STATIC_LIBRARY: { | |
| this->TargetNameReal = | |
| this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real; | |
| this->TagType = GhsMultiGpj::LIBRARY; | |
| break; | |
| } | |
| case cm::TargetType::SHARED_LIBRARY: { | |
| std::string msg = | |
| cmStrCat("add_library(<name> SHARED ...) not supported: ", this->Name); | |
| cmSystemTools::Message(msg); | |
| return; | |
| } | |
| case cm::TargetType::OBJECT_LIBRARY: { | |
| this->TargetNameReal = | |
| this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real; | |
| this->TagType = GhsMultiGpj::SUBPROJECT; | |
| break; | |
| } | |
| case cm::TargetType::MODULE_LIBRARY: { | |
| std::string msg = | |
| cmStrCat("add_library(<name> MODULE ...) not supported: ", this->Name); | |
| cmSystemTools::Message(msg); | |
| return; | |
| } | |
| case cm::TargetType::UTILITY: { | |
| this->TargetNameReal = this->GeneratorTarget->GetName(); | |
| this->TagType = GhsMultiGpj::CUSTOM_TARGET; | |
| break; | |
| } | |
| case cm::TargetType::GLOBAL_TARGET: { | |
| this->TargetNameReal = this->GeneratorTarget->GetName(); | |
| if (this->TargetNameReal == | |
| this->GetGlobalGenerator()->GetInstallTargetName()) { | |
| this->TagType = GhsMultiGpj::CUSTOM_TARGET; | |
| } else { | |
| return; | |
| } | |
| break; | |
| } | |
| default: | |
| return; | |
| } | |
| this->GenerateTarget(); | |
| } | |
| void cmGhsMultiTargetGenerator::GenerateTarget() | |
| { | |
| if (this->GeneratorTarget->GetType() == cm::TargetType::EXECUTABLE && | |
| !this->GeneratorTarget | |
| ->GetLinkerTypeProperty( | |
| this->GeneratorTarget->GetLinkerLanguage(this->ConfigName), | |
| this->ConfigName) | |
| .empty()) { | |
| // Green Hill MULTI does not support this feature. | |
| cmSystemTools::Message( | |
| cmStrCat("'LINKER_TYPE' property, specified on target '", | |
| this->GeneratorTarget->GetName(), | |
| "', is not supported by this generator.")); | |
| } | |
| // Open the target file in copy-if-different mode. | |
| std::string fproj = | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', this->Name, | |
| cmGlobalGhsMultiGenerator::FILE_EXTENSION); | |
| // Tell the global generator the name of the project file | |
| this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME", fproj); | |
| this->GeneratorTarget->Target->SetProperty( | |
| "GENERATOR_FILE_NAME_EXT", GhsMultiGpj::GetGpjTag(this->TagType)); | |
| cmGeneratedFileStream fout(fproj); | |
| fout.SetCopyIfDifferent(true); | |
| this->GetGlobalGenerator()->WriteFileHeader(fout); | |
| GhsMultiGpj::WriteGpjTag(this->TagType, fout); | |
| if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { | |
| std::string const 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, | |
| std::string const& config) | |
| { | |
| std::string outpath; | |
| /* Determine paths from the target project file to where the output artifacts | |
| * need to be located. | |
| */ | |
| if (this->TagType != GhsMultiGpj::SUBPROJECT) { | |
| // set target binary file destination | |
| std::string binpath = this->GeneratorTarget->GetSupportDirectory(); | |
| outpath = cmSystemTools::RelativePath( | |
| binpath, this->GeneratorTarget->GetDirectory(config)); | |
| /* clang-format off */ | |
| fout << " :binDirRelative=\"" << outpath << "\"\n" | |
| " -o \"" << this->TargetNameReal << "\"\n"; | |
| /* clang-format on */ | |
| } | |
| // set target object file destination | |
| outpath = "."; | |
| fout << " :outputDirRelative=\"" << outpath << "\"\n"; | |
| } | |
| void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config, | |
| std::string const& language) | |
| { | |
| auto i = this->FlagsByLanguage.find(language); | |
| if (i == this->FlagsByLanguage.end()) { | |
| std::string flags; | |
| this->LocalGenerator->AddLanguageFlags( | |
| flags, this->GeneratorTarget, cmBuildStep::Compile, language, config); | |
| this->LocalGenerator->AddFeatureFlags(flags, this->GeneratorTarget, | |
| language, config); | |
| this->LocalGenerator->AddVisibilityPresetFlags( | |
| flags, this->GeneratorTarget, language); | |
| this->LocalGenerator->AddColorDiagnosticsFlags(flags, 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(std::string const& 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&, | |
| std::string const& 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 (std::string const& f : ghsCompFlags) { | |
| fout << " " << f << '\n'; | |
| } | |
| } | |
| } | |
| } | |
| void cmGhsMultiTargetGenerator::WriteCompilerDefinitions( | |
| std::ostream& fout, std::string const& config, std::string const& 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, | |
| std::string const& config, | |
| std::string const& 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::INTEGRITY_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 (std::string const& 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 (std::string const& 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 (std::string const& 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"), | |
| #ifdef _WIN32 | |
| std::string("preexecShell") | |
| #else | |
| std::string("preexec") | |
| #endif | |
| ); | |
| if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { | |
| this->WriteBuildEventsHelper(fout, | |
| this->GeneratorTarget->GetPreLinkCommands(), | |
| std::string("prelink"), | |
| #ifdef _WIN32 | |
| std::string("preexecShell") | |
| #else | |
| std::string("preexec") | |
| #endif | |
| ); | |
| } | |
| this->WriteBuildEventsHelper(fout, | |
| this->GeneratorTarget->GetPostBuildCommands(), | |
| std::string("postbuild"), | |
| #ifdef _WIN32 | |
| std::string("postexecShell") | |
| #else | |
| std::string("postexec") | |
| #endif | |
| ); | |
| } | |
| void cmGhsMultiTargetGenerator::WriteBuildEventsHelper( | |
| std::ostream& fout, std::vector<cmCustomCommand> const& ccv, | |
| std::string const& name, std::string const& cmd) | |
| { | |
| int cmdcount = 0; | |
| #ifdef _WIN32 | |
| std::string fext = ".bat"; | |
| std::string shell; | |
| #else | |
| std::string fext = ".sh"; | |
| std::string shell = "/bin/sh "; | |
| #endif | |
| for (cmCustomCommand const& cc : ccv) { | |
| cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator); | |
| // Open the filestream for this custom command | |
| std::string fname = cmStrCat(this->GeneratorTarget->GetSupportDirectory(), | |
| '/', this->Name, '_', name, cmdcount++, fext); | |
| cmGeneratedFileStream f(fname); | |
| f.SetCopyIfDifferent(true); | |
| this->WriteCustomCommandsHelper(f, ccg); | |
| f.Close(); | |
| if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { | |
| fout << " :" << cmd << "=\"" << shell << fname << "\"\n"; | |
| } else { | |
| fout << fname << "\n :outputName=\"" << fname << ".rule\"\n"; | |
| } | |
| for (auto const& 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. | |
| if (cm::optional<std::string> comment = ccg.GetComment()) { | |
| std::string escapedComment = this->LocalGenerator->EscapeForShell( | |
| *comment, ccg.GetCC().GetEscapeAllowMakeVars()); | |
| std::string echocmd = cmStrCat("echo ", escapedComment); | |
| 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; | |
| #ifdef _WIN32 | |
| std::string suffix; | |
| if (cmd.size() > 4) { | |
| suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4)); | |
| if (suffix == ".bat" || suffix == ".cmd") { | |
| useCall = true; | |
| } | |
| } | |
| #endif | |
| 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("./", std::move(cmd)); | |
| } | |
| cmd = this->LocalGenerator->ConvertToOutputFormat( | |
| cmd, cmOutputConverter::SHELL); | |
| if (useCall) { | |
| cmd = cmStrCat("call ", std::move(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, cmSourceFile const* sf, std::string const& propName, | |
| std::string const& propFlag) | |
| { | |
| cmValue prop = sf->GetProperty(propName); | |
| if (prop) { | |
| cmList list{ *prop }; | |
| for (std::string const& p : list) { | |
| fout << " " << propFlag << p << '\n'; | |
| } | |
| } | |
| } | |
| void cmGhsMultiTargetGenerator::WriteFileSetProperty( | |
| std::ostream& fout, cmGeneratorFileSet const* fileSet, | |
| std::string const& lang, cm::string_view propName, cm::string_view propFlag) | |
| { | |
| if (!fileSet) { | |
| return; | |
| } | |
| std::vector<BT<std::string>> entries; | |
| if (propName == "COMPILE_OPTIONS"_s) { | |
| entries = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileOptions(this->ConfigName, lang) | |
| : fileSet->GetInterfaceCompileOptions(this->ConfigName, lang); | |
| } else if (propName == "COMPILE_DEFINITIONS"_s) { | |
| entries = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileDefinitions(this->ConfigName, lang) | |
| : fileSet->GetInterfaceCompileDefinitions(this->ConfigName, lang); | |
| } else if (propName == "INCLUDE_DIRECTORIES"_s) { | |
| entries = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetIncludeDirectories(this->ConfigName, lang) | |
| : fileSet->GetInterfaceIncludeDirectories(this->ConfigName, lang); | |
| } | |
| for (auto const& entry : entries) { | |
| fout << " " << propFlag << entry.Value << '\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); | |
| /* 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 const* sourceGroup = this->LocalGenerator->FindSourceGroup( | |
| this->GeneratorTarget, sf, this->ConfigName); | |
| 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 */ | |
| std::vector<std::string> const 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 (std::string const& 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 const& 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 = | |
| this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE").IsOn() || | |
| 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(gname, cmGlobalGhsMultiGenerator::FILE_EXTENSION); | |
| std::string fpath = | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', 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 (cmSourceFile const* 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 << WriteObjectLangOverride(si) << '\n'; | |
| if (compile) { | |
| // lookup for the associated file set, if any. | |
| auto const* fileSet = | |
| this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( | |
| this->ConfigName, si); | |
| this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), | |
| "INCLUDE_DIRECTORIES"_s, ""); | |
| this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); | |
| this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); | |
| this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), | |
| "COMPILE_DEFINITIONS"_s, ""); | |
| this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); | |
| this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), | |
| "COMPILE_OPTIONS"_s, ""); | |
| /* 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 = | |
| cmStrCat("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; | |
| #ifdef _WIN32 | |
| std::string fext = ".bat"; | |
| #else | |
| std::string fext = ".sh"; | |
| #endif | |
| for (auto& sf : customCommands) { | |
| cmCustomCommand const* cc = sf->GetCustomCommand(); | |
| cmCustomCommandGenerator ccg(*cc, this->ConfigName, | |
| this->LocalGenerator); | |
| // Open the filestream for this custom command | |
| std::string fname = cmStrCat( | |
| this->GeneratorTarget->GetSupportDirectory(), '/', this->Name, | |
| "_cc", cmdcount++, '_', (sf->GetLocation()).GetName(), fext); | |
| 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 const& out : ccg.GetOutputs()) { | |
| fout << fname << '\n'; | |
| fout << " :outputName=\"" << out << "\"\n"; | |
| if (specifyExtra) { | |
| for (auto const& byp : ccg.GetByproducts()) { | |
| fout << " :extraOutputFile=\"" << byp << "\"\n"; | |
| } | |
| for (auto const& dep : ccg.GetDepends()) { | |
| fout << " :depends=\"" << dep << "\"\n"; | |
| } | |
| specifyExtra = false; | |
| } | |
| } | |
| } | |
| std::string cmGhsMultiTargetGenerator::WriteObjectLangOverride( | |
| cmSourceFile const* sourceFile) | |
| { | |
| std::string ret; | |
| cmValue rawLangProp = sourceFile->GetProperty("LANGUAGE"); | |
| if (rawLangProp) { | |
| ret = cmStrCat(" [", *rawLangProp, ']'); | |
| } | |
| return ret; | |
| } | |
| bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() | |
| { | |
| if (cmValue p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) { | |
| return p.IsOn(); | |
| } | |
| 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 (auto const& 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; | |
| } |