| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmGlobalGhsMultiGenerator.h" | |
| #include <algorithm> | |
| #include <functional> | |
| #include <map> | |
| #include <ostream> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cm/optional> | |
| #include <cm/string> | |
| #include <cm/string_view> | |
| #include <cmext/algorithm> | |
| #include <cmext/memory> | |
| #include "cmCustomCommand.h" | |
| #include "cmCustomCommandLines.h" | |
| #include "cmDiagnostics.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGhsMultiGpj.h" | |
| #include "cmList.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmLocalGhsMultiGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmSourceFile.h" | |
| #include "cmState.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| #include "cmVersion.h" | |
| #include "cmake.h" | |
| char const* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj"; | |
| #ifdef __linux__ | |
| char const* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild"; | |
| #elif defined(_WIN32) | |
| char const* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe"; | |
| #endif | |
| char const* cmGlobalGhsMultiGenerator::CHECK_BUILD_SYSTEM_TARGET = | |
| "RERUN_CMAKE"; | |
| cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm) | |
| : cmGlobalGenerator(cm) | |
| { | |
| cm->GetState()->SetGhsMultiIDE(true); | |
| } | |
| cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default; | |
| std::unique_ptr<cmLocalGenerator> | |
| cmGlobalGhsMultiGenerator::CreateLocalGenerator(cmMakefile* mf) | |
| { | |
| return std::unique_ptr<cmLocalGenerator>( | |
| cm::make_unique<cmLocalGhsMultiGenerator>(this, mf)); | |
| } | |
| cmDocumentationEntry cmGlobalGhsMultiGenerator::GetDocumentation() | |
| { | |
| return { | |
| GetActualName(), | |
| "Generates Green Hills MULTI files (experimental, work-in-progress)." | |
| }; | |
| } | |
| void cmGlobalGhsMultiGenerator::ComputeTargetObjectDirectory( | |
| cmGeneratorTarget* gt) const | |
| { | |
| // Compute full path to object file directory for this target. | |
| std::string dir = cmStrCat(gt->GetSupportDirectory(), '/'); | |
| gt->ObjectDirectory = dir; | |
| } | |
| bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, | |
| bool build, cmMakefile* mf) | |
| { | |
| /* In build mode nothing to be done. | |
| * Toolset already determined and build tool absolute path is cached. | |
| */ | |
| if (build) { | |
| return true; | |
| } | |
| /* Determine the absolute directory for the toolset */ | |
| std::string tsp; | |
| this->GetToolset(mf, tsp, ts); | |
| /* no toolset was found */ | |
| if (tsp.empty()) { | |
| return false; | |
| } | |
| /* set the build tool to use */ | |
| std::string gbuild = | |
| cmStrCat(tsp, ((tsp.back() == '/') ? "" : "/"), DEFAULT_BUILD_PROGRAM); | |
| cmValue prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM"); | |
| /* check if the toolset changed from last generate */ | |
| if (cmNonempty(prevTool) && !cmSystemTools::ComparePath(gbuild, *prevTool)) { | |
| std::string const& e = | |
| cmStrCat("toolset build tool: ", gbuild, | |
| "\n" | |
| "Does not match the previously used build tool: ", | |
| *prevTool, | |
| "\n" | |
| "Either remove the CMakeCache.txt file and CMakeFiles " | |
| "directory or choose a different binary directory."); | |
| mf->IssueMessage(MessageType::FATAL_ERROR, e); | |
| return false; | |
| } | |
| /* store the toolset that is being used for this build */ | |
| mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild, "build program to use", | |
| cmStateEnums::INTERNAL, true); | |
| mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp); | |
| return true; | |
| } | |
| bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p, | |
| cmMakefile* mf) | |
| { | |
| /* set primary target */ | |
| cmValue t = mf->GetDefinition("GHS_PRIMARY_TARGET"); | |
| if (t.IsOff()) { | |
| /* Use the value from `-A` or use `arm` */ | |
| std::string arch = "arm"; | |
| if (!cmIsOff(p)) { | |
| arch = p; | |
| } | |
| cmValue platform = mf->GetDefinition("GHS_TARGET_PLATFORM"); | |
| std::string tgt = cmStrCat(arch, '_', platform, ".tgt"); | |
| /* update the primary target name*/ | |
| mf->AddDefinition("GHS_PRIMARY_TARGET", tgt); | |
| } | |
| return true; | |
| } | |
| void cmGlobalGhsMultiGenerator::EnableLanguage( | |
| std::vector<std::string> const& l, cmMakefile* mf, bool optional) | |
| { | |
| mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI"); | |
| mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files | |
| this->cmGlobalGenerator::EnableLanguage(l, mf, optional); | |
| } | |
| bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/) | |
| { | |
| // The GHS generator only knows how to lookup its build tool | |
| // during generation of the project files, but this | |
| // can only be done after the toolset is specified. | |
| return true; | |
| } | |
| void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsp, | |
| std::string const& ts) | |
| { | |
| /* Determine tsp - full path of the toolset from ts (toolset hint via -T) */ | |
| std::string root = mf->GetSafeDefinition("GHS_TOOLSET_ROOT"); | |
| // Check if `-T` was set by user | |
| if (ts.empty()) { | |
| // Enter toolset search mode | |
| std::vector<std::string> output; | |
| // Make sure root exists... | |
| if (!cmSystemTools::PathExists(root)) { | |
| std::string msg = | |
| cmStrCat("GHS_TOOLSET_ROOT directory \"", root, "\" does not exist."); | |
| mf->IssueMessage(MessageType::FATAL_ERROR, msg); | |
| tsp = ""; | |
| return; | |
| } | |
| // Add a directory separator | |
| if (root.back() != '/') { | |
| root += '/'; | |
| } | |
| // Get all compiler directories in toolset root | |
| cmSystemTools::Glob(root, "comp_[^;]+", output); | |
| if (output.empty()) { | |
| // No compiler directories found | |
| std::string msg = | |
| cmStrCat("No GHS toolsets found in GHS_TOOLSET_ROOT \"", root, "\"."); | |
| mf->IssueMessage(MessageType::FATAL_ERROR, msg); | |
| tsp = ""; | |
| } else { | |
| // Use latest? version | |
| tsp = root + output.back(); | |
| } | |
| } else { | |
| // Toolset was provided by user | |
| std::string tryPath; | |
| // NOTE: CollapseFullPath() will determine if user toolset was full path or | |
| // or relative path. | |
| tryPath = cmSystemTools::CollapseFullPath(ts, root); | |
| if (!cmSystemTools::FileExists(tryPath)) { | |
| std::string msg = | |
| cmStrCat("GHS toolset \"", tryPath, "\" does not exist."); | |
| mf->IssueMessage(MessageType::FATAL_ERROR, msg); | |
| tsp = ""; | |
| } else { | |
| tsp = tryPath; | |
| } | |
| } | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout) | |
| { | |
| /* clang-format off */ | |
| fout << "#!gbuild\n" | |
| "#\n" | |
| "# CMAKE generated file: DO NOT EDIT!\n" | |
| "# Generated by \"" << GetActualName() << "\"" | |
| " Generator, CMake Version " << cmVersion::GetMajorVersion() << '.' | |
| << cmVersion::GetMinorVersion() << "\n" | |
| "#\n\n"; | |
| /* clang-format on */ | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout) | |
| { | |
| fout << "Commands {\n" | |
| " Custom_Rule_Command {\n" | |
| " name = \"Custom Rule Command\"\n" | |
| " exec = \"" | |
| #ifdef _WIN32 | |
| "cmd.exe" | |
| #else | |
| "/bin/sh" | |
| #endif | |
| "\"\n" | |
| " options = {\"SpecialOptions\"}\n" | |
| " }\n" | |
| "}\n" | |
| "\n\n" | |
| "FileTypes {\n" | |
| " CmakeRule {\n" | |
| " name = \"Custom Rule\"\n" | |
| " action = \"&Run\"\n" | |
| " extensions = {\"" | |
| #ifdef _WIN32 | |
| "bat" | |
| #else | |
| "sh" | |
| #endif | |
| "\"}\n" | |
| " grepable = false\n" | |
| " command = \"Custom Rule Command\"\n" | |
| " commandLine = \"$COMMAND " | |
| #ifdef _WIN32 | |
| "/c" | |
| #endif | |
| " $INPUTFILE\"\n" | |
| " progress = \"Processing Custom Rule\"\n" | |
| " promoteToFirstPass = true\n" | |
| " outputType = \"None\"\n" | |
| " color = \"#800080\"\n" | |
| " }\n" | |
| "}\n"; | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout) | |
| { | |
| fout << "FileTypes {\n" | |
| " CmakeTarget {\n" | |
| " name = \"Custom Target\"\n" | |
| " action = \"&Execute\"\n" | |
| " grepable = false\n" | |
| " outputType = \"None\"\n" | |
| " color = \"#800080\"\n" | |
| " }\n" | |
| "}\n"; | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout, | |
| cmLocalGenerator* root) | |
| { | |
| this->WriteFileHeader(fout); | |
| this->WriteMacros(fout, root); | |
| this->WriteHighLevelDirectives(fout, root); | |
| GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout); | |
| fout << "# Top Level Project File\n"; | |
| // Specify BSP option if supplied by user | |
| // -- not all platforms require this entry in the project file | |
| cmValue bspName = root->GetMakefile()->GetDefinition("GHS_BSP_NAME"); | |
| if (!bspName.IsOff()) { | |
| fout << " -bsp " << *bspName << '\n'; | |
| } | |
| // Specify OS DIR if supplied by user | |
| // -- not all platforms require this entry in the project file | |
| cmValue osDir = root->GetMakefile()->GetDefinition("GHS_OS_DIR"); | |
| if (!osDir.IsOff()) { | |
| cmValue osDirOption = | |
| root->GetMakefile()->GetDefinition("GHS_OS_DIR_OPTION"); | |
| fout << " "; | |
| if (osDirOption.IsOff()) { | |
| fout << ""; | |
| } else { | |
| fout << *osDirOption; | |
| } | |
| fout << "\"" << osDir << "\"\n"; | |
| } | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout, | |
| bool filterPredefined) | |
| { | |
| std::set<std::string> predefinedTargets; | |
| predefinedTargets.insert(this->GetInstallTargetName()); | |
| predefinedTargets.insert(this->GetAllTargetName()); | |
| predefinedTargets.insert(std::string(CHECK_BUILD_SYSTEM_TARGET)); | |
| // All known targets | |
| for (cmGeneratorTarget const* target : this->ProjectTargets) { | |
| if (target->GetType() == cm::TargetType::INTERFACE_LIBRARY || | |
| target->GetType() == cm::TargetType::MODULE_LIBRARY || | |
| target->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| (target->GetType() == cm::TargetType::GLOBAL_TARGET && | |
| target->GetName() != this->GetInstallTargetName())) { | |
| continue; | |
| } | |
| /* Check if the current target is a predefined CMake target */ | |
| bool predefinedTarget = | |
| predefinedTargets.find(target->GetName()) != predefinedTargets.end(); | |
| if ((filterPredefined && predefinedTarget) || | |
| (!filterPredefined && !predefinedTarget)) { | |
| fout << cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION) | |
| << " [Project]\n"; | |
| } | |
| } | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteProjectLine( | |
| std::ostream& fout, cmGeneratorTarget const* target, | |
| std::string& rootBinaryDir) | |
| { | |
| cmValue projFile = target->GetProperty("GENERATOR_FILE_NAME"); | |
| cmValue projType = target->GetProperty("GENERATOR_FILE_NAME_EXT"); | |
| /* If either value is not valid then this particular target is an | |
| * unsupported target type and should be skipped. | |
| */ | |
| if (projFile && projType) { | |
| std::string path = cmSystemTools::RelativePath(rootBinaryDir, *projFile); | |
| fout << path; | |
| fout << ' ' << *projType << '\n'; | |
| } | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root) | |
| { | |
| std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); | |
| // All known targets | |
| for (cmGeneratorTarget const* target : this->ProjectTargets) { | |
| if (target->GetType() == cm::TargetType::INTERFACE_LIBRARY || | |
| target->GetType() == cm::TargetType::MODULE_LIBRARY || | |
| target->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| (target->GetType() == cm::TargetType::GLOBAL_TARGET && | |
| target->GetName() != this->GetInstallTargetName())) { | |
| continue; | |
| } | |
| // create target build file | |
| std::string name = cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION); | |
| std::string fname = cmStrCat(rootBinaryDir, '/', name); | |
| cmGeneratedFileStream fbld(fname); | |
| fbld.SetCopyIfDifferent(true); | |
| this->WriteFileHeader(fbld); | |
| GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld); | |
| std::vector<cmGeneratorTarget const*> build; | |
| if (this->ComputeTargetBuildOrder(target, build)) { | |
| cmSystemTools::Error( | |
| cmStrCat("The inter-target dependency graph for target [", | |
| target->GetName(), "] had a cycle.\n")); | |
| } else { | |
| for (auto& tgt : build) { | |
| this->WriteProjectLine(fbld, tgt, rootBinaryDir); | |
| } | |
| } | |
| fbld.Close(); | |
| } | |
| } | |
| void cmGlobalGhsMultiGenerator::Generate() | |
| { | |
| std::string fname; | |
| // first do the superclass method | |
| this->cmGlobalGenerator::Generate(); | |
| // output top-level projects | |
| for (auto& it : this->ProjectMap) { | |
| this->OutputTopLevelProject(it.second[0], it.second); | |
| } | |
| // create custom rule BOD file | |
| fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + | |
| "/CMakeFiles/custom_rule.bod"; | |
| cmGeneratedFileStream frule(fname); | |
| frule.SetCopyIfDifferent(true); | |
| this->WriteFileHeader(frule); | |
| this->WriteCustomRuleBOD(frule); | |
| frule.Close(); | |
| // create custom target BOD file | |
| fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + | |
| "/CMakeFiles/custom_target.bod"; | |
| cmGeneratedFileStream ftarget(fname); | |
| ftarget.SetCopyIfDifferent(true); | |
| this->WriteFileHeader(ftarget); | |
| this->WriteCustomTargetBOD(ftarget); | |
| ftarget.Close(); | |
| } | |
| void cmGlobalGhsMultiGenerator::OutputTopLevelProject( | |
| cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators) | |
| { | |
| std::string fname; | |
| if (generators.empty()) { | |
| return; | |
| } | |
| // Collect all targets under this root generator and the transitive | |
| // closure of their dependencies. | |
| TargetDependSet const projectTargets = | |
| this->GetTargetsForProject(root, generators); | |
| OrderedTargetDependSet sortedProjectTargets(projectTargets, ""); | |
| this->ProjectTargets.clear(); | |
| for (cmGeneratorTarget const* t : sortedProjectTargets) { | |
| /* save list of all targets in sorted order */ | |
| this->ProjectTargets.push_back(t); | |
| } | |
| /* Name top-level projects as filename.top.gpj to avoid name clashes | |
| * with target projects. This avoid the issue where the project has | |
| * the same name as the executable target. | |
| */ | |
| fname = cmStrCat(root->GetCurrentBinaryDirectory(), '/', | |
| root->GetProjectName(), ".top", FILE_EXTENSION); | |
| cmGeneratedFileStream top(fname); | |
| top.SetCopyIfDifferent(true); | |
| this->WriteTopLevelProject(top, root); | |
| this->WriteTargets(root); | |
| this->WriteSubProjects(top, true); | |
| this->WriteSubProjects(top, false); | |
| top.Close(); | |
| } | |
| std::vector<cmGlobalGenerator::GeneratedMakeCommand> | |
| cmGlobalGhsMultiGenerator::GenerateBuildCommand( | |
| std::string const& makeProgram, std::string const& projectName, | |
| std::string const& projectDir, std::vector<std::string> const& targetNames, | |
| std::string const& /*config*/, int jobs, bool verbose, | |
| cmBuildOptions /*buildOptions*/, std::vector<std::string> const& makeOptions, | |
| BuildTryCompile /*isInTryCompile*/) | |
| { | |
| GeneratedMakeCommand makeCommand; | |
| makeCommand.Add(this->SelectMakeProgram(makeProgram)); | |
| if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { | |
| if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { | |
| makeCommand.Add("-parallel"); | |
| } else { | |
| makeCommand.Add("-parallel=" + std::to_string(jobs)); | |
| } | |
| } | |
| /* determine the top-project file in the project directory */ | |
| std::string proj = cmStrCat(projectName, ".top", FILE_EXTENSION); | |
| std::vector<std::string> files; | |
| cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files); | |
| if (!files.empty()) { | |
| /* use the real top-level project in the directory */ | |
| proj = files.at(0); | |
| } | |
| makeCommand.Add("-top", proj); | |
| /* determine targets to build */ | |
| bool build_all = false; | |
| if (!targetNames.empty()) { | |
| for (auto const& tname : targetNames) { | |
| if (!tname.empty()) { | |
| if (tname == "clean") { | |
| makeCommand.Add("-clean"); | |
| } else { | |
| makeCommand.Add(tname + ".tgt.gpj"); | |
| } | |
| } else { | |
| build_all = true; | |
| } | |
| } | |
| } else { | |
| build_all = true; | |
| } | |
| if (build_all) { | |
| /* transform name to default build */; | |
| std::string all = cmStrCat(this->GetAllTargetName(), ".tgt.gpj"); | |
| makeCommand.Add(all); | |
| } | |
| if (verbose) { | |
| makeCommand.Add("-commands"); | |
| } | |
| makeCommand.Add(makeOptions.begin(), makeOptions.end()); | |
| return { std::move(makeCommand) }; | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout, | |
| cmLocalGenerator* root) | |
| { | |
| fout << "macro PROJ_NAME=" << root->GetProjectName() << '\n'; | |
| cmValue ghsGpjMacros = root->GetMakefile()->GetDefinition("GHS_GPJ_MACROS"); | |
| if (ghsGpjMacros) { | |
| cmList expandedList{ *ghsGpjMacros }; | |
| for (std::string const& arg : expandedList) { | |
| fout << "macro " << arg << '\n'; | |
| } | |
| } | |
| } | |
| void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives( | |
| std::ostream& fout, cmLocalGenerator* root) | |
| { | |
| /* put primary target and customization files into project file */ | |
| cmValue const tgt = root->GetMakefile()->GetDefinition("GHS_PRIMARY_TARGET"); | |
| /* clang-format off */ | |
| fout << "primaryTarget=" << tgt << "\n" | |
| "customization=" << root->GetBinaryDirectory() | |
| << "/CMakeFiles/custom_rule.bod\n" | |
| "customization=" << root->GetBinaryDirectory() | |
| << "/CMakeFiles/custom_target.bod" << '\n'; | |
| /* clang-format on */ | |
| cmValue const customization = | |
| root->GetMakefile()->GetDefinition("GHS_CUSTOMIZATION"); | |
| if (cmNonempty(customization)) { | |
| fout << "customization=" | |
| << cmGlobalGhsMultiGenerator::TrimQuotes(*customization) << '\n'; | |
| this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION"); | |
| } | |
| } | |
| std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string str) | |
| { | |
| cm::erase(str, '"'); | |
| return str; | |
| } | |
| bool cmGlobalGhsMultiGenerator::TargetCompare::operator()( | |
| cmGeneratorTarget const* l, cmGeneratorTarget const* r) const | |
| { | |
| // Make sure a given named target is ordered first, | |
| // e.g. to set ALL_BUILD as the default active project. | |
| // When the empty string is named this is a no-op. | |
| if (r->GetName() == this->First) { | |
| return false; | |
| } | |
| if (l->GetName() == this->First) { | |
| return true; | |
| } | |
| return l->GetName() < r->GetName(); | |
| } | |
| cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet( | |
| TargetDependSet const& targets, std::string const& first) | |
| : derived(TargetCompare(first)) | |
| { | |
| this->insert(targets.begin(), targets.end()); | |
| } | |
| bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( | |
| cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build) | |
| { | |
| std::vector<cmGeneratorTarget const*> t{ tgt }; | |
| return this->ComputeTargetBuildOrder(t, build); | |
| } | |
| bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( | |
| std::vector<cmGeneratorTarget const*>& tgt, | |
| std::vector<cmGeneratorTarget const*>& build) | |
| { | |
| std::set<cmGeneratorTarget const*> temp; | |
| std::set<cmGeneratorTarget const*> perm; | |
| for (auto const* const ti : tgt) { | |
| bool r = this->VisitTarget(temp, perm, build, ti); | |
| if (r) { | |
| return r; | |
| } | |
| } | |
| return false; | |
| } | |
| bool cmGlobalGhsMultiGenerator::VisitTarget( | |
| std::set<cmGeneratorTarget const*>& temp, | |
| std::set<cmGeneratorTarget const*>& perm, | |
| std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti) | |
| { | |
| /* check if permanent mark is set*/ | |
| if (perm.find(ti) == perm.end()) { | |
| /* set temporary mark; check if revisit*/ | |
| if (temp.insert(ti).second) { | |
| /* sort targets lexicographically to ensure that nodes are always visited | |
| * in the same order */ | |
| OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti), | |
| ""); | |
| for (auto const& di : sortedTargets) { | |
| if (this->VisitTarget(temp, perm, order, di)) { | |
| return true; | |
| } | |
| } | |
| /* mark as complete; insert into beginning of list*/ | |
| perm.insert(ti); | |
| order.push_back(ti); | |
| return false; | |
| } | |
| /* revisiting item - not a DAG */ | |
| return true; | |
| } | |
| /* already complete */ | |
| return false; | |
| } | |
| bool cmGlobalGhsMultiGenerator::AddCheckTarget() | |
| { | |
| // Skip the target if no regeneration is to be done. | |
| if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { | |
| return false; | |
| } | |
| // Get the generators. | |
| std::vector<std::unique_ptr<cmLocalGenerator>> const& generators = | |
| this->LocalGenerators; | |
| auto& lg = | |
| cm::static_reference_cast<cmLocalGhsMultiGenerator>(generators[0]); | |
| // The name of the output file for the custom command. | |
| this->StampFile = cmStrCat(lg.GetBinaryDirectory(), "/CMakeFiles/", | |
| CHECK_BUILD_SYSTEM_TARGET); | |
| // Add a custom rule to re-run CMake if any input files changed. | |
| { | |
| // Collect the input files used to generate all targets in this | |
| // project. | |
| std::vector<std::string> listFiles; | |
| for (auto const& gen : generators) { | |
| cm::append(listFiles, gen->GetMakefile()->GetListFiles()); | |
| } | |
| // Add the cache file. | |
| listFiles.emplace_back(cmStrCat( | |
| this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeCache.txt")); | |
| // Print not implemented warning. | |
| if (this->GetCMakeInstance()->DoWriteGlobVerifyTarget()) { | |
| this->GetCMakeInstance()->IssueDiagnostic( | |
| cmDiagnostics::CMD_AUTHOR, | |
| "Any pre-check scripts, such as those generated for file(GLOB " | |
| "CONFIGURE_DEPENDS), will not be run by gbuild."); | |
| } | |
| // Sort the list of input files and remove duplicates. | |
| std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); | |
| auto newEnd = std::unique(listFiles.begin(), listFiles.end()); | |
| listFiles.erase(newEnd, listFiles.end()); | |
| // Create a rule to re-run CMake and create output file. | |
| cmCustomCommandLines commandLines; | |
| commandLines.emplace_back( | |
| cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "rm", "-f", | |
| this->StampFile })); | |
| std::string argS = cmStrCat("-S", lg.GetSourceDirectory()); | |
| std::string argB = cmStrCat("-B", lg.GetBinaryDirectory()); | |
| commandLines.emplace_back( | |
| cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), argS, argB })); | |
| commandLines.emplace_back(cmMakeCommandLine( | |
| { cmSystemTools::GetCMakeCommand(), "-E", "touch", this->StampFile })); | |
| /* Create the target(Exclude from ALL_BUILD). | |
| * | |
| * The build tool, currently, does not support rereading the project files | |
| * if they get updated. So do not run this target as part of ALL_BUILD. | |
| */ | |
| auto cc = cm::make_unique<cmCustomCommand>(); | |
| cmTarget* tgt = | |
| lg.AddUtilityCommand(CHECK_BUILD_SYSTEM_TARGET, true, std::move(cc)); | |
| auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg); | |
| auto* gt = ptr.get(); | |
| lg.AddGeneratorTarget(std::move(ptr)); | |
| // Add the rule. | |
| cc = cm::make_unique<cmCustomCommand>(); | |
| cc->SetOutputs(this->StampFile); | |
| cc->SetDepends(listFiles); | |
| cc->SetCommandLines(commandLines); | |
| cc->SetComment("Checking Build System"); | |
| cc->SetEscapeOldStyle(false); | |
| cc->SetStdPipesUTF8(true); | |
| if (cmSourceFile* file = | |
| lg.AddCustomCommandToOutput(std::move(cc), true)) { | |
| gt->AddSource(file->ResolveFullPath()); | |
| } else { | |
| cmSystemTools::Error("Error adding rule for " + this->StampFile); | |
| } | |
| // Organize in the "predefined targets" folder: | |
| if (this->UseFolderProperty()) { | |
| tgt->SetProperty("FOLDER", this->GetPredefinedTargetsFolder()); | |
| } | |
| } | |
| return true; | |
| } | |
| void cmGlobalGhsMultiGenerator::AddAllTarget() | |
| { | |
| // Add a special target that depends on ALL projects for easy build | |
| // of one configuration only. | |
| for (auto const& it : this->ProjectMap) { | |
| std::vector<cmLocalGenerator*> const& gen = it.second; | |
| // add the ALL_BUILD to the first local generator of each project | |
| if (!gen.empty()) { | |
| // Use no actual command lines so that the target itself is not | |
| // considered always out of date. | |
| auto cc = cm::make_unique<cmCustomCommand>(); | |
| cc->SetEscapeOldStyle(false); | |
| cc->SetComment("Build all projects"); | |
| cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(), | |
| true, std::move(cc)); | |
| gen[0]->AddGeneratorTarget( | |
| cm::make_unique<cmGeneratorTarget>(allBuild, gen[0])); | |
| // Organize in the "predefined targets" folder: | |
| if (this->UseFolderProperty()) { | |
| allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder()); | |
| } | |
| // Now make all targets depend on the ALL_BUILD target | |
| for (cmLocalGenerator const* i : gen) { | |
| for (auto const& tgt : i->GetGeneratorTargets()) { | |
| // Skip global or imported targets | |
| if (tgt->GetType() == cm::TargetType::GLOBAL_TARGET || | |
| tgt->IsImported()) { | |
| continue; | |
| } | |
| // Skip Exclude From All Targets | |
| if (!this->IsExcluded(gen[0], tgt.get())) { | |
| allBuild->AddUtility(tgt->GetName(), false); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void cmGlobalGhsMultiGenerator::AddExtraIDETargets() | |
| { | |
| // Add a special target that depends on ALL projects. | |
| this->AddAllTarget(); | |
| /* Add Custom Target to check if CMake needs to be rerun. | |
| * | |
| * The build tool, currently, does not support rereading the project files | |
| * if they get updated. So do not make the other targets dependent on this | |
| * check. | |
| */ | |
| this->AddCheckTarget(); | |
| } |