| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmGlobalGhsMultiGenerator.h" |
| |
| #include "cmsys/SystemTools.hxx" |
| |
| #include "cmAlgorithms.h" |
| #include "cmDocumentationEntry.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmGeneratorTarget.h" |
| #include "cmGhsMultiTargetGenerator.h" |
| #include "cmLocalGhsMultiGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmVersion.h" |
| |
| const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj"; |
| const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe"; |
| const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "C:/ghs"; |
| |
| cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm) |
| : cmGlobalGenerator(cm) |
| , OSDirRelative(false) |
| { |
| } |
| |
| cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() |
| { |
| cmDeleteAll(TargetFolderBuildStreams); |
| } |
| |
| cmLocalGenerator* cmGlobalGhsMultiGenerator::CreateLocalGenerator( |
| cmMakefile* mf) |
| { |
| return new cmLocalGhsMultiGenerator(this, mf); |
| } |
| |
| void cmGlobalGhsMultiGenerator::GetDocumentation(cmDocumentationEntry& entry) |
| { |
| entry.Name = GetActualName(); |
| entry.Brief = |
| "Generates Green Hills MULTI files (experimental, work-in-progress)."; |
| } |
| |
| bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, |
| cmMakefile* mf) |
| { |
| std::string tsp; /* toolset path */ |
| std::string tsn = ts; /* toolset name */ |
| |
| GetToolset(mf, tsp, tsn); |
| |
| /* no toolset was found */ |
| if (tsn.empty()) { |
| return false; |
| } else if (ts.empty()) { |
| std::string message; |
| message = |
| "Green Hills MULTI: -T <toolset> not specified; defaulting to \""; |
| message += tsn; |
| message += "\""; |
| cmSystemTools::Message(message.c_str()); |
| |
| /* store the toolset for later use |
| * -- already done if -T<toolset> was specified |
| */ |
| mf->AddCacheDefinition("CMAKE_GENERATOR_TOOLSET", tsn.c_str(), |
| "Name of generator toolset.", |
| cmStateEnums::INTERNAL); |
| } |
| |
| /* set the build tool to use */ |
| const char* prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM"); |
| std::string gbuild(tsp + "/" + tsn + "/" + DEFAULT_BUILD_PROGRAM); |
| |
| /* check if the toolset changed from last generate */ |
| if (prevTool != NULL && (gbuild != prevTool)) { |
| std::string message = "generator toolset: "; |
| message += gbuild; |
| message += "\nDoes not match the toolset used previously: "; |
| message += prevTool; |
| message += "\nEither remove the CMakeCache.txt file and CMakeFiles " |
| "directory or choose a different binary directory."; |
| cmSystemTools::Error(message.c_str()); |
| } else { |
| /* store the toolset that is being used for this build */ |
| mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild.c_str(), |
| "build program to use", cmStateEnums::INTERNAL, |
| true); |
| } |
| |
| mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsn.c_str()); |
| |
| // FIXME: compiler detection not implemented |
| // gbuild uses the primaryTarget setting in the top-level project |
| // file to determine which compiler to use. Because compiler |
| // detection is not implemented these variables must be |
| // set to skip past these tests. However cmake will verify that |
| // the executable pointed to by CMAKE_<LANG>_COMPILER exists. |
| // To pass this additional check gbuild is used as a place holder for the |
| // actual compiler. |
| mf->AddDefinition("CMAKE_C_COMPILER", gbuild.c_str()); |
| mf->AddDefinition("CMAKE_C_COMPILER_ID_RUN", "TRUE"); |
| mf->AddDefinition("CMAKE_C_COMPILER_ID", "GHS"); |
| mf->AddDefinition("CMAKE_C_COMPILER_FORCED", "TRUE"); |
| |
| mf->AddDefinition("CMAKE_CXX_COMPILER", gbuild.c_str()); |
| mf->AddDefinition("CMAKE_CXX_COMPILER_ID_RUN", "TRUE"); |
| mf->AddDefinition("CMAKE_CXX_COMPILER_ID", "GHS"); |
| mf->AddDefinition("CMAKE_CXX_COMPILER_FORCED", "TRUE"); |
| |
| return true; |
| } |
| |
| bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p, |
| cmMakefile* mf) |
| { |
| if (p == "") { |
| cmSystemTools::Message( |
| "Green Hills MULTI: -A <arch> not specified; defaulting to \"arm\""); |
| std::string arch = "arm"; |
| |
| /* store the platform name for later use |
| * -- already done if -A<arch> was specified |
| */ |
| mf->AddCacheDefinition("CMAKE_GENERATOR_PLATFORM", arch.c_str(), |
| "Name of generator platform.", |
| cmStateEnums::INTERNAL); |
| } |
| |
| const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM"); |
| if (tgtPlatform == nullptr) { |
| tgtPlatform = "integrity"; |
| } |
| |
| /* store the platform name for later use */ |
| mf->AddCacheDefinition("GHS_TARGET_PLATFORM", tgtPlatform, |
| "Name of GHS target platform.", |
| cmStateEnums::INTERNAL); |
| |
| 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& tsd, |
| std::string& ts) |
| { |
| const char* ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT"); |
| |
| if (!ghsRoot) { |
| ghsRoot = DEFAULT_TOOLSET_ROOT; |
| } |
| tsd = ghsRoot; |
| |
| if (ts.empty()) { |
| std::vector<std::string> output; |
| |
| // Use latest? version |
| cmSystemTools::Glob(tsd, "comp_[^;]+", output); |
| |
| if (output.empty()) { |
| cmSystemTools::Error("GHS toolset not found in ", tsd.c_str()); |
| ts = ""; |
| } else { |
| ts = output.back(); |
| } |
| } else { |
| std::string tryPath = tsd + std::string("/") + ts; |
| if (!cmSystemTools::FileExists(tryPath)) { |
| cmSystemTools::Error("GHS toolset \"", ts.c_str(), "\" not found in ", |
| tsd.c_str()); |
| ts = ""; |
| } |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::OpenBuildFileStream( |
| std::string const& filepath, cmGeneratedFileStream** filestream) |
| { |
| // Get a stream where to generate things. |
| if (NULL == *filestream) { |
| *filestream = new cmGeneratedFileStream(filepath.c_str()); |
| if (NULL != *filestream) { |
| OpenBuildFileStream(*filestream); |
| } |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::OpenBuildFileStream( |
| cmGeneratedFileStream* filestream) |
| { |
| *filestream << "#!gbuild" << std::endl; |
| } |
| |
| void cmGlobalGhsMultiGenerator::OpenBuildFileStream() |
| { |
| // Compute GHS MULTI's build file path. |
| std::string buildFilePath = |
| this->GetCMakeInstance()->GetHomeOutputDirectory(); |
| buildFilePath += "/"; |
| buildFilePath += "default"; |
| buildFilePath += FILE_EXTENSION; |
| |
| this->Open(std::string(""), buildFilePath, &this->TargetFolderBuildStreams); |
| OpenBuildFileStream(GetBuildFileStream()); |
| |
| char const* osDir = |
| this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR"); |
| if (NULL == osDir) { |
| osDir = ""; |
| cmSystemTools::Error("GHS_OS_DIR cache variable must be set"); |
| } else { |
| this->GetCMakeInstance()->MarkCliAsUsed("GHS_OS_DIR"); |
| } |
| std::string fOSDir(this->trimQuotes(osDir)); |
| std::replace(fOSDir.begin(), fOSDir.end(), '\\', '/'); |
| if (!fOSDir.empty() && ('c' == fOSDir[0] || 'C' == fOSDir[0])) { |
| this->OSDirRelative = false; |
| } else { |
| this->OSDirRelative = true; |
| } |
| |
| std::string bspName; |
| char const* bspCache = |
| this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME"); |
| if (bspCache) { |
| bspName = bspCache; |
| this->GetCMakeInstance()->MarkCliAsUsed("GHS_BSP_NAME"); |
| } |
| if (bspName.empty() || bspName.compare("IGNORE") == 0) { |
| const char* a = |
| this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM"); |
| bspName = "sim"; |
| bspName += (a ? a : ""); |
| } |
| |
| this->WriteMacros(); |
| this->WriteHighLevelDirectives(); |
| |
| GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, this->GetBuildFileStream()); |
| this->WriteDisclaimer(this->GetBuildFileStream()); |
| *this->GetBuildFileStream() << "# Top Level Project File" << std::endl; |
| *this->GetBuildFileStream() << " -bsp " << bspName << std::endl; |
| |
| this->WriteCompilerOptions(fOSDir); |
| } |
| |
| void cmGlobalGhsMultiGenerator::CloseBuildFileStream( |
| cmGeneratedFileStream** filestream) |
| { |
| if (filestream) { |
| delete *filestream; |
| *filestream = NULL; |
| } else { |
| cmSystemTools::Error("Build file stream was not open."); |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::Generate() |
| { |
| this->cmGlobalGenerator::Generate(); |
| |
| if (!this->LocalGenerators.empty()) { |
| this->OpenBuildFileStream(); |
| |
| // Build all the folder build files |
| for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) { |
| cmLocalGhsMultiGenerator* lg = |
| static_cast<cmLocalGhsMultiGenerator*>(this->LocalGenerators[i]); |
| const std::vector<cmGeneratorTarget*>& tgts = lg->GetGeneratorTargets(); |
| this->UpdateBuildFiles(tgts); |
| } |
| } |
| |
| cmDeleteAll(TargetFolderBuildStreams); |
| this->TargetFolderBuildStreams.clear(); |
| } |
| |
| void cmGlobalGhsMultiGenerator::GenerateBuildCommand( |
| std::vector<std::string>& makeCommand, const std::string& makeProgram, |
| const std::string& /*projectName*/, const std::string& /*projectDir*/, |
| const std::string& targetName, const std::string& /*config*/, bool /*fast*/, |
| int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions) |
| { |
| const char* gbuild = |
| this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM"); |
| makeCommand.push_back( |
| this->SelectMakeProgram(makeProgram, (std::string)gbuild)); |
| |
| if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { |
| makeCommand.push_back("-parallel"); |
| if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { |
| makeCommand.push_back(std::to_string(jobs)); |
| } |
| } |
| |
| makeCommand.insert(makeCommand.end(), makeOptions.begin(), |
| makeOptions.end()); |
| if (!targetName.empty()) { |
| if (targetName == "clean") { |
| makeCommand.push_back("-clean"); |
| } else { |
| makeCommand.push_back(targetName); |
| } |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::WriteMacros() |
| { |
| char const* ghsGpjMacros = |
| this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS"); |
| if (NULL != ghsGpjMacros) { |
| std::vector<std::string> expandedList; |
| cmSystemTools::ExpandListArgument(std::string(ghsGpjMacros), expandedList); |
| for (std::vector<std::string>::const_iterator expandedListI = |
| expandedList.begin(); |
| expandedListI != expandedList.end(); ++expandedListI) { |
| *this->GetBuildFileStream() << "macro " << *expandedListI << std::endl; |
| } |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives() |
| { |
| /* set primary target */ |
| std::string tgt; |
| const char* t = |
| this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET"); |
| if (t) { |
| tgt = t; |
| this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET"); |
| } else { |
| const char* a = |
| this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM"); |
| const char* p = |
| this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM"); |
| tgt = (a ? a : ""); |
| tgt += "_"; |
| tgt += (p ? p : ""); |
| tgt += ".tgt"; |
| } |
| |
| *this->GetBuildFileStream() << "primaryTarget=" << tgt << std::endl; |
| |
| char const* const customization = |
| this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION"); |
| if (NULL != customization && strlen(customization) > 0) { |
| *this->GetBuildFileStream() |
| << "customization=" << trimQuotes(customization) << std::endl; |
| this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION"); |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::WriteCompilerOptions(std::string const& fOSDir) |
| { |
| *this->GetBuildFileStream() |
| << " -os_dir=\"" << fOSDir << "\"" << std::endl; |
| } |
| |
| void cmGlobalGhsMultiGenerator::WriteDisclaimer(std::ostream* os) |
| { |
| (*os) << "#" << std::endl |
| << "# CMAKE generated file: DO NOT EDIT!" << std::endl |
| << "# Generated by \"" << GetActualName() << "\"" |
| << " Generator, CMake Version " << cmVersion::GetMajorVersion() << "." |
| << cmVersion::GetMinorVersion() << std::endl |
| << "#" << std::endl; |
| } |
| |
| void cmGlobalGhsMultiGenerator::AddFilesUpToPath( |
| cmGeneratedFileStream* mainBuildFile, |
| std::map<std::string, cmGeneratedFileStream*>* targetFolderBuildStreams, |
| char const* homeOutputDirectory, std::string const& path, |
| GhsMultiGpj::Types projType, std::string const& relPath) |
| { |
| std::string workingPath(path); |
| cmSystemTools::ConvertToUnixSlashes(workingPath); |
| std::vector<std::string> splitPath = cmSystemTools::SplitString(workingPath); |
| std::string workingRelPath(relPath); |
| cmSystemTools::ConvertToUnixSlashes(workingRelPath); |
| if (!workingRelPath.empty()) { |
| workingRelPath += "/"; |
| } |
| std::string pathUpTo; |
| for (std::vector<std::string>::const_iterator splitPathI = splitPath.begin(); |
| splitPath.end() != splitPathI; ++splitPathI) { |
| pathUpTo += *splitPathI; |
| if (targetFolderBuildStreams->end() == |
| targetFolderBuildStreams->find(pathUpTo)) { |
| AddFilesUpToPathNewBuildFile( |
| mainBuildFile, targetFolderBuildStreams, homeOutputDirectory, pathUpTo, |
| splitPath.begin() == splitPathI, workingRelPath, projType); |
| } |
| AddFilesUpToPathAppendNextFile(targetFolderBuildStreams, pathUpTo, |
| splitPathI, splitPath.end(), projType); |
| pathUpTo += "/"; |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::Open( |
| std::string const& mapKeyName, std::string const& fileName, |
| std::map<std::string, cmGeneratedFileStream*>* fileMap) |
| { |
| if (fileMap->end() == fileMap->find(fileName)) { |
| cmGeneratedFileStream* temp(new cmGeneratedFileStream); |
| temp->open(fileName.c_str()); |
| (*fileMap)[mapKeyName] = temp; |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::AddFilesUpToPathNewBuildFile( |
| cmGeneratedFileStream* mainBuildFile, |
| std::map<std::string, cmGeneratedFileStream*>* targetFolderBuildStreams, |
| char const* homeOutputDirectory, std::string const& pathUpTo, |
| bool const isFirst, std::string const& relPath, |
| GhsMultiGpj::Types const projType) |
| { |
| // create folders up to file path |
| std::string absPath = std::string(homeOutputDirectory) + "/" + relPath; |
| std::string newPath = absPath + pathUpTo; |
| if (!cmSystemTools::FileExists(newPath.c_str())) { |
| cmSystemTools::MakeDirectory(newPath.c_str()); |
| } |
| |
| // Write out to filename for first time |
| std::string relFilename(GetFileNameFromPath(pathUpTo)); |
| std::string absFilename = absPath + relFilename; |
| Open(pathUpTo, absFilename, targetFolderBuildStreams); |
| OpenBuildFileStream((*targetFolderBuildStreams)[pathUpTo]); |
| GhsMultiGpj::WriteGpjTag(projType, (*targetFolderBuildStreams)[pathUpTo]); |
| WriteDisclaimer((*targetFolderBuildStreams)[pathUpTo]); |
| |
| // Add to main build file |
| if (isFirst) { |
| *mainBuildFile << relFilename << " "; |
| GhsMultiGpj::WriteGpjTag(projType, mainBuildFile); |
| } |
| } |
| |
| void cmGlobalGhsMultiGenerator::AddFilesUpToPathAppendNextFile( |
| std::map<std::string, cmGeneratedFileStream*>* targetFolderBuildStreams, |
| std::string const& pathUpTo, |
| std::vector<std::string>::const_iterator splitPathI, |
| std::vector<std::string>::const_iterator end, |
| GhsMultiGpj::Types const projType) |
| { |
| std::vector<std::string>::const_iterator splitPathNextI = splitPathI + 1; |
| if (end != splitPathNextI && |
| targetFolderBuildStreams->end() == |
| targetFolderBuildStreams->find(pathUpTo + "/" + *splitPathNextI)) { |
| std::string nextFilename(*splitPathNextI); |
| nextFilename = GetFileNameFromPath(nextFilename); |
| *(*targetFolderBuildStreams)[pathUpTo] << nextFilename << " "; |
| GhsMultiGpj::WriteGpjTag(projType, (*targetFolderBuildStreams)[pathUpTo]); |
| } |
| } |
| |
| std::string cmGlobalGhsMultiGenerator::GetFileNameFromPath( |
| std::string const& path) |
| { |
| std::string output(path); |
| if (!path.empty()) { |
| cmSystemTools::ConvertToUnixSlashes(output); |
| std::vector<std::string> splitPath = cmSystemTools::SplitString(output); |
| output += "/" + splitPath.back() + FILE_EXTENSION; |
| } |
| return output; |
| } |
| |
| void cmGlobalGhsMultiGenerator::UpdateBuildFiles( |
| const std::vector<cmGeneratorTarget*>& tgts) |
| { |
| for (std::vector<cmGeneratorTarget*>::const_iterator tgtsI = tgts.begin(); |
| tgtsI != tgts.end(); ++tgtsI) { |
| const cmGeneratorTarget* tgt = *tgtsI; |
| if (IsTgtForBuild(tgt)) { |
| std::string folderName = tgt->GetEffectiveFolderName(); |
| if (this->TargetFolderBuildStreams.end() == |
| this->TargetFolderBuildStreams.find(folderName)) { |
| this->AddFilesUpToPath( |
| GetBuildFileStream(), &this->TargetFolderBuildStreams, |
| this->GetCMakeInstance()->GetHomeOutputDirectory().c_str(), |
| folderName, GhsMultiGpj::PROJECT); |
| } |
| std::vector<std::string> splitPath = cmSystemTools::SplitString( |
| cmGhsMultiTargetGenerator::GetRelBuildFileName(tgt)); |
| std::string foldNameRelBuildFile(*(splitPath.end() - 2) + "/" + |
| splitPath.back()); |
| *this->TargetFolderBuildStreams[folderName] << foldNameRelBuildFile |
| << " "; |
| GhsMultiGpj::WriteGpjTag(cmGhsMultiTargetGenerator::GetGpjTag(tgt), |
| this->TargetFolderBuildStreams[folderName]); |
| } |
| } |
| } |
| |
| bool cmGlobalGhsMultiGenerator::IsTgtForBuild(const cmGeneratorTarget* tgt) |
| { |
| const std::string config = |
| tgt->Target->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
| std::vector<cmSourceFile*> tgtSources; |
| tgt->GetSourceFiles(tgtSources, config); |
| bool tgtInBuild = true; |
| char const* excludeFromAll = tgt->GetProperty("EXCLUDE_FROM_ALL"); |
| if (NULL != excludeFromAll && '1' == excludeFromAll[0] && |
| '\0' == excludeFromAll[1]) { |
| tgtInBuild = false; |
| } |
| return !tgtSources.empty() && tgtInBuild; |
| } |
| |
| std::string cmGlobalGhsMultiGenerator::trimQuotes(std::string const& str) |
| { |
| std::string result; |
| result.reserve(str.size()); |
| for (const char* ch = str.c_str(); *ch != '\0'; ++ch) { |
| if (*ch != '"') { |
| result += *ch; |
| } |
| } |
| return result; |
| } |