/* 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 not specified; defaulting to \""; message += tsn; message += "\""; cmSystemTools::Message(message.c_str()); /* store the toolset for later use * -- already done if -T 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__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 not specified; defaulting to \"arm\""); std::string arch = "arm"; /* store the platform name for later use * -- already done if -A 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 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 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(this->LocalGenerators[i]); const std::vector& tgts = lg->GetGeneratorTargets(); this->UpdateBuildFiles(tgts); } } cmDeleteAll(TargetFolderBuildStreams); this->TargetFolderBuildStreams.clear(); } void cmGlobalGhsMultiGenerator::GenerateBuildCommand( std::vector& 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 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 expandedList; cmSystemTools::ExpandListArgument(std::string(ghsGpjMacros), expandedList); for (std::vector::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* targetFolderBuildStreams, char const* homeOutputDirectory, std::string const& path, GhsMultiGpj::Types projType, std::string const& relPath) { std::string workingPath(path); cmSystemTools::ConvertToUnixSlashes(workingPath); std::vector splitPath = cmSystemTools::SplitString(workingPath); std::string workingRelPath(relPath); cmSystemTools::ConvertToUnixSlashes(workingRelPath); if (!workingRelPath.empty()) { workingRelPath += "/"; } std::string pathUpTo; for (std::vector::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* 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* 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* targetFolderBuildStreams, std::string const& pathUpTo, std::vector::const_iterator splitPathI, std::vector::const_iterator end, GhsMultiGpj::Types const projType) { std::vector::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 splitPath = cmSystemTools::SplitString(output); output += "/" + splitPath.back() + FILE_EXTENSION; } return output; } void cmGlobalGhsMultiGenerator::UpdateBuildFiles( const std::vector& tgts) { for (std::vector::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 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 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; }