diff --git a/tools/projmgr/include/ProjMgrParser.h b/tools/projmgr/include/ProjMgrParser.h index 3124679d3..e2117aea6 100644 --- a/tools/projmgr/include/ProjMgrParser.h +++ b/tools/projmgr/include/ProjMgrParser.h @@ -80,6 +80,16 @@ struct PackItem { TypeFilter type; }; +/** + * @brief resolved pack item containing, + * pack ID, + * list of selected-by expressions (original expressions causing this pack to be added) +*/ +struct ResolvedPackItem { + std::string pack; + std::vector selectedBy; +}; + /** * @brief processor item containing * processor fpu, @@ -300,6 +310,21 @@ struct ContextDesc { TypeFilter type; }; +/** + * @brief cbuild pack descriptor containing + * filename, + * full path to cbuild pack file, + * containing directory, + * list of resolved packs +*/ +struct CbuildPackItem { + std::string name; + std::string path; + std::string directory; + + std::vector packs; +}; + /** * @brief default item containing * cdefault path, @@ -340,6 +365,7 @@ struct CsolutionItem { std::vector packs; bool enableCdefault; GeneratorsItem generators; + CbuildPackItem cbuildPack; }; /** diff --git a/tools/projmgr/include/ProjMgrUtils.h b/tools/projmgr/include/ProjMgrUtils.h index 5ed343775..21189ddd7 100644 --- a/tools/projmgr/include/ProjMgrUtils.h +++ b/tools/projmgr/include/ProjMgrUtils.h @@ -59,6 +59,18 @@ struct OutputTypes { OutputType cmse; }; +/** + * @brief pack info containing + * pack name, + * pack vendor, + * pack version +*/ +struct PackInfo { + std::string name; + std::string vendor; + std::string version; +}; + /** * @brief vector of ConnectionsCollection */ @@ -357,6 +369,29 @@ class ProjMgrUtils { */ static const std::string& GetDeviceAttribute(const std::string& key, const std::string& value); + /** + * @brief convert a pack ID to a pack info + * @param packId the pack id (YML format) + * @param packInfo the pack info struct + * @return true on success + */ + static bool ConvertToPackInfo(const std::string& packId, PackInfo& packInfo); + + /** + * @brief check if the two pack info structs match + * @param exactPackInfo fully qualified pack ID, without wildcards or ranges + * @param packInfoToMatch pack ID, may include wildcards or ranges + * @return true if match + */ + static bool IsMatchingPackInfo(const PackInfo& exactPackInfo, const PackInfo& packInfoToMatch); + + /** + * @brief convert version in YML format to CPRJ range format + * @param version version in YML format + * @return version in CPRJ range format + */ + static std::string ConvertToVersionRange(const std::string& version); + protected: static std::string ConstructID(const std::vector>& elements); /** diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index 604299414..bc3b918be 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -86,18 +86,6 @@ struct ToolchainItem { std::string config; }; -/** - * @brief pack info containing - * pack name, - * pack vendor, - * pack version -*/ -struct PackInfo { - std::string name; - std::string vendor; - std::string version; -}; - /** * @brief package item containing * pack information pack, @@ -275,6 +263,8 @@ struct ContextTypesItem { * device pack, * board pack, * boolean processed precedences + * map of user inputed pack ID to resolved pack ID + * set of absolute file paths of project local packs */ struct ContextItem { CdefaultItem* cdefault = nullptr; @@ -320,6 +310,8 @@ struct ContextItem { RtePackage* devicePack; RtePackage* boardPack; bool precedences; + std::map> userInputToResolvedPackIdMap; + std::set localPackPaths; }; /** @@ -675,7 +667,7 @@ class ProjMgrWorker { bool ProcessDevicePrecedence(StringCollection& item); bool ProcessBoardPrecedence(StringCollection& item); bool ProcessToolchain(ContextItem& context); - bool ProcessPackages(ContextItem& context); + bool ProcessPackages(ContextItem& context, const std::string& packRoot); bool ProcessComponents(ContextItem& context); RteComponent* ProcessComponent(ContextItem& context, ComponentItem& item, RteComponentMap& componentMap); bool ProcessGpdsc(ContextItem& context); @@ -759,6 +751,7 @@ class ProjMgrWorker { void CheckCompilerFilterSpelling(const std::string& compiler); bool ProcessGeneratedLayers(ContextItem& context); void CheckDeviceAttributes(const std::string& device, const ProcessorItem& userSelection, const StrMap& targetAttributes); + std::vector FindMatchingPacksInCbuildPack(const PackItem& needle, const std::vector& resolvedPacks); }; #endif // PROJMGRWORKER_H diff --git a/tools/projmgr/include/ProjMgrYamlEmitter.h b/tools/projmgr/include/ProjMgrYamlEmitter.h index 0d4242483..3e4e8c91b 100644 --- a/tools/projmgr/include/ProjMgrYamlEmitter.h +++ b/tools/projmgr/include/ProjMgrYamlEmitter.h @@ -60,6 +60,14 @@ class ProjMgrYamlEmitter { * @return true if executed successfully */ static bool GenerateCbuildSet(ProjMgrParser& parser, const std::vector contexts, const std::string& selectedCompiler); + + /** + * @brief generate cbuild pack file + * @param contexts vector with pointers to contexts + * @param keepExistingPackContent if true, all entries from existing cbuild-pack should be preserved + * @return true if executed successfully + */ + static bool GenerateCbuildPack(ProjMgrParser& parser, const std::vector contexts, bool keepExistingPackContent); }; #endif // PROJMGRYAMLEMITTER_H diff --git a/tools/projmgr/include/ProjMgrYamlParser.h b/tools/projmgr/include/ProjMgrYamlParser.h index 35798ca8d..c9a53d0dc 100644 --- a/tools/projmgr/include/ProjMgrYamlParser.h +++ b/tools/projmgr/include/ProjMgrYamlParser.h @@ -32,6 +32,7 @@ static constexpr const char* YAML_CBUILDS = "cbuilds"; static constexpr const char* YAML_CBUILD = "cbuild"; static constexpr const char* YAML_CBUILD_GENS = "cbuild-gens"; static constexpr const char* YAML_CBUILD_GEN = "cbuild-gen"; +static constexpr const char* YAML_CBUILD_PACK = "cbuild-pack"; static constexpr const char* YAML_CBUILD_SET = "cbuild-set"; static constexpr const char* YAML_CDEFAULT = "cdefault"; static constexpr const char* YAML_CLAYERS = "clayers"; @@ -123,6 +124,8 @@ static constexpr const char* YAML_PROJECTS = "projects"; static constexpr const char* YAML_PROJECT_TYPE = "project-type"; static constexpr const char* YAML_PROVIDES = "provides"; static constexpr const char* YAML_REGIONS = "regions"; +static constexpr const char* YAML_RESOLVED_PACK = "resolved-pack"; +static constexpr const char* YAML_RESOLVED_PACKS = "resolved-packs"; static constexpr const char* YAML_RTE = "rte"; static constexpr const char* YAML_RUN = "run"; static constexpr const char* YAML_SCOPE = "scope"; @@ -214,9 +217,11 @@ class ProjMgrYamlParser { std::map& generators, bool checkSchema); protected: + bool ParseCbuildPack(const std::string& input, CbuildPackItem& cbuildPack, bool checkSchema); void ParseMisc(const YAML::Node& parent, std::vector& misc); void ParseDefine(const YAML::Node& parent, std::vector& define); void ParsePacks(const YAML::Node& parent, const std::string& file, std::vector& packs); + void ParseResolvedPacks(const YAML::Node& parent, std::vector& resolvedPacks); void ParseProcessor(const YAML::Node& parent, ProcessorItem& processor); void ParseBoolean(const YAML::Node& parent, const std::string& key, bool& value, bool def); void ParseString(const YAML::Node& parent, const std::string& key, std::string& value); @@ -246,6 +251,7 @@ class ProjMgrYamlParser { bool ValidateCsolution(const std::string& input, const YAML::Node& root); bool ValidateCproject(const std::string& input, const YAML::Node& root); bool ValidateClayer(const std::string& input, const YAML::Node& root); + bool ValidateCbuildPack(const std::string& input, const YAML::Node& root); bool ValidateCbuildSet(const std::string& input, const YAML::Node& root); bool ValidateKeys(const std::string& input, const YAML::Node& parent, const std::set& keys); bool ValidateSequence(const std::string& input, const YAML::Node& parent, const std::string& seqKey); diff --git a/tools/projmgr/include/ProjMgrYamlSchemaChecker.h b/tools/projmgr/include/ProjMgrYamlSchemaChecker.h index 834eb8e42..995ec6358 100644 --- a/tools/projmgr/include/ProjMgrYamlSchemaChecker.h +++ b/tools/projmgr/include/ProjMgrYamlSchemaChecker.h @@ -21,6 +21,7 @@ class ProjMgrYamlSchemaChecker : public YmlSchemaChecker { PROJECT, LAYER, BUILD, + BUILD_PACK, BUILDIDX, BUILDSET, GENERATOR, diff --git a/tools/projmgr/schemas/cbuild-pack.schema.json b/tools/projmgr/schemas/cbuild-pack.schema.json new file mode 100644 index 000000000..338afc9f6 --- /dev/null +++ b/tools/projmgr/schemas/cbuild-pack.schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/schemas/projmgr/2.1.0/tools/projmgr/schemas/cbuild-pack.schema.json", + "title": "CMSIS cbuild-pack", + "description": "file containing all pack versions used, used for pack locking", + "version": "2.1.0", + "properties": { + "cbuild-pack": { + "$ref": "./common.schema.json#/definitions/BuildPackDescType" + } + }, + "required": [ "cbuild-pack" ] +} diff --git a/tools/projmgr/schemas/common.schema.json b/tools/projmgr/schemas/common.schema.json index 92de0f00a..90094cdf8 100644 --- a/tools/projmgr/schemas/common.schema.json +++ b/tools/projmgr/schemas/common.schema.json @@ -931,6 +931,32 @@ } } }, + "BuildPackDescType": { + "type": "object", + "properties": { + "resolved-packs": { "$ref": "#/definitions/ResolvedPacksType" } + }, + "additionalProperties": false, + "required": [ "resolved-packs" ] + }, + "ResolvedPacksType": { + "type": "array", + "uniqueItems": true, + "items": { "$ref": "#/definitions/ResolvedPackType" } + }, + "ResolvedPackType": { + "type": "object", + "properties": { + "resolved-pack": { "$ref": "#/definitions/PackID" }, + "selected-by": { + "type": "array", + "uniqueItems": true, + "items": { "$ref": "#/definitions/PackID" } + } + }, + "additionalProperties": false, + "required": [ "resolved-pack" ] + }, "BuildPacksType": { "type": "array", "uniqueItems": true, diff --git a/tools/projmgr/src/ProjMgr.cpp b/tools/projmgr/src/ProjMgr.cpp index dc8a86c35..4abad09f5 100644 --- a/tools/projmgr/src/ProjMgr.cpp +++ b/tools/projmgr/src/ProjMgr.cpp @@ -510,6 +510,18 @@ bool ProjMgr::RunConfigure(bool printConfig) { } } + // Generate cbuild files + for (auto& contextItem : m_processedContexts) { + if (!m_emitter.GenerateCbuild(contextItem)) { + return false; + } + + } + + // Generate cbuild-pack file + const bool isUsingContexts = m_contextSet || m_context.size() != 0; + m_emitter.GenerateCbuildPack(m_parser, m_processedContexts, isUsingContexts); + return !error; } @@ -539,13 +551,6 @@ bool ProjMgr::RunConvert(void) { } } - // Generate cbuild files - for (auto& contextItem : m_processedContexts) { - if (!m_emitter.GenerateCbuild(contextItem)) { - return false; - } - } - return !error; } diff --git a/tools/projmgr/src/ProjMgrUtils.cpp b/tools/projmgr/src/ProjMgrUtils.cpp index 4f85f043d..650eaaa28 100644 --- a/tools/projmgr/src/ProjMgrUtils.cpp +++ b/tools/projmgr/src/ProjMgrUtils.cpp @@ -356,3 +356,61 @@ vector ProjMgrUtils::GetFilteredContexts( } return selectedContexts; } + +bool ProjMgrUtils::ConvertToPackInfo(const string& packId, PackInfo& packInfo) { + string packInfoStr = packId; + if (packInfoStr.find("::") != string::npos) { + packInfo.vendor = RteUtils::RemoveSuffixByString(packInfoStr, "::"); + packInfoStr = RteUtils::RemovePrefixByString(packInfoStr, "::"); + packInfo.name = RteUtils::GetPrefix(packInfoStr, '@'); + } else { + packInfo.vendor = RteUtils::GetPrefix(packInfoStr, '@'); + } + packInfo.version = RteUtils::GetSuffix(packInfoStr, '@'); + + return true; +} + +bool ProjMgrUtils::IsMatchingPackInfo(const PackInfo& exactPackInfo, const PackInfo& packInfoToMatch) { + // Check if vendor matches + if (packInfoToMatch.vendor != exactPackInfo.vendor) { + // Not same vendor + return false; + } + + // Check if pack name matches + if (!packInfoToMatch.name.empty()) { + if (WildCards::IsWildcardPattern(packInfoToMatch.name)) { + // Check if filter matches + if (!WildCards::Match(packInfoToMatch.name, exactPackInfo.name)) { + // Name filter does not match needle + return false; + } + } else if (packInfoToMatch.name != exactPackInfo.name) { + // Not same pack name + return false; + } + } + + // Check if version matches + string reqVersionRange = ConvertToVersionRange(packInfoToMatch.version); + if (!reqVersionRange.empty() && VersionCmp::RangeCompare(exactPackInfo.version, reqVersionRange) != 0) { + // Version out of range + return false; + } + + // Needle matches this resolved pack + return true; +} + +string ProjMgrUtils::ConvertToVersionRange(const string& version) { + string versionRange = version; + if (!versionRange.empty()) { + if (versionRange.find(">=") != string::npos) { + versionRange = versionRange.substr(2); + } else { + versionRange = versionRange + ":" + versionRange; + } + } + return versionRange; +} diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 5aeee71fd..8a960a82e 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -222,21 +222,14 @@ void ProjMgrWorker::SetEnvironmentVariables(const StrVec& envVars) { } bool ProjMgrWorker::GetRequiredPdscFiles(ContextItem& context, const std::string& packRoot, std::set& errMsgs) { - if (!ProcessPackages(context)) { + if (!ProcessPackages(context, packRoot)) { return false; } for (auto packItem : context.packRequirements) { // parse required version range const auto& pack = packItem.pack; const auto& reqVersion = pack.version; - string reqVersionRange; - if (!reqVersion.empty()) { - if (reqVersion.find(">=") != string::npos) { - reqVersionRange = reqVersion.substr(2); - } else { - reqVersionRange = reqVersion + ":" + reqVersion; - } - } + string reqVersionRange = ProjMgrUtils::ConvertToVersionRange(reqVersion); if (packItem.path.empty()) { bool bPackFilter = (pack.name.empty() || WildCards::IsWildcardPattern(pack.name)); @@ -1336,7 +1329,49 @@ bool ProjMgrWorker::ProcessDevicePrecedence(StringCollection& item) { return true; } -bool ProjMgrWorker::ProcessPackages(ContextItem& context) { +/** + * @brief Takes a loosely defined needle pack id and tries to match it to a number of + * resolved pack items from the `cbuild-pack.yml` file. + * Project local packs are ignored. + * + * @param needle The loosely defined pack id + * @param resolvedPacks The cbuild-pack.yml resolved items + * @return The list of matched resolved items + */ +vector ProjMgrWorker::FindMatchingPacksInCbuildPack(const PackItem& needle, const vector& resolvedPacks) { + // This should never happen, assuming that the parser validates the pack ID + if (needle.pack.empty()) { + return {}; + } + + // Only consider non-project-local packs + if (!needle.path.empty()) { + return {}; + } + + PackInfo needleInfo; + ProjMgrUtils::ConvertToPackInfo(needle.pack, needleInfo); + + vector matches; + for (const auto& resolvedPack : resolvedPacks) { + // First try exact matching + if (find(resolvedPack.selectedBy.cbegin(), resolvedPack.selectedBy.cend(), needle.pack) != resolvedPack.selectedBy.end()) { + matches.push_back(resolvedPack); + } else { + // Next, try fuzzy matching + PackInfo resolvedInfo; + ProjMgrUtils::ConvertToPackInfo(resolvedPack.pack, resolvedInfo); + + if (ProjMgrUtils::IsMatchingPackInfo(resolvedInfo, needleInfo)) { + matches.push_back(resolvedPack); + } + } + } + + return matches; +} + +bool ProjMgrWorker::ProcessPackages(ContextItem& context, const string& packRoot) { vector packRequirements; // Solution package requirements @@ -1351,8 +1386,7 @@ bool ProjMgrWorker::ProcessPackages(ContextItem& context) { for (const auto& [_, clayer] : context.clayers) { InsertPackRequirements(clayer->packs, packRequirements, clayer->directory); } - AddPackRequirements(context, packRequirements); - return true; + return AddPackRequirements(context, packRequirements); } void ProjMgrWorker::InsertPackRequirements(const vector& src, vector& dst, string base) { @@ -1364,7 +1398,18 @@ void ProjMgrWorker::InsertPackRequirements(const vector& src, vector

packRequirements) { + const vector& resolvedPacks = context.csolution->cbuildPack.packs; + // Filter context specific package requirements vector packages; for (const auto& packItem : packRequirements) { @@ -1372,22 +1417,73 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vector matches = FindMatchingPacksInCbuildPack(packageEntry, resolvedPacks); + if (matches.size()) { + // Cbuild pack content matches, so use it + for (const auto& resolvedPack : matches) { + PackageItem package; + ProjMgrUtils::ConvertToPackInfo(resolvedPack.pack, package.pack); + context.userInputToResolvedPackIdMap[packageEntry.pack].insert(resolvedPack.pack); + context.packRequirements.push_back(package); + } + } else { + // Not matching cbuild pack, add it unless a wildcard entry + PackageItem package; + ProjMgrUtils::ConvertToPackInfo(packageEntry.pack, package.pack); + + // Resolve version range using installed packs + if (!package.pack.name.empty() && !WildCards::IsWildcardPattern(package.pack.name)) { + string reqVersionRange = ProjMgrUtils::ConvertToVersionRange(package.pack.version); + string path = m_packRoot + '/' + package.pack.vendor + '/' + package.pack.name; + string installedVersion = RteFsUtils::GetInstalledPackVersion(path, reqVersionRange); + + // Only remember the version of the pack if we had it installed + // Will be used when serializing the cbuild-pack.yml file later + if (!installedVersion.empty() && package.pack.version != installedVersion) { + const string newPackId = RtePackage::ComposePackageID(package.pack.vendor, package.pack.name, installedVersion); + context.userInputToResolvedPackIdMap[packageEntry.pack].insert(newPackId); + package.pack.version = installedVersion; + } + context.packRequirements.push_back(package); + } + } + } else { + // Project local pack - add as-is + PackageItem package; + package.path = packageEntry.path; + RteFsUtils::NormalizePath(package.path, context.csolution->directory + "/"); + if (!RteFsUtils::Exists(package.path)) { + ProjMgrLogger::Error("pack path: " + packageEntry.path + " does not exist"); + return false; + } + ProjMgrUtils::ConvertToPackInfo(packageEntry.pack, package.pack); + string pdscFile = package.pack.vendor + '.' + package.pack.name + ".pdsc"; + RteFsUtils::NormalizePath(pdscFile, package.path + "/"); + if (!RteFsUtils::Exists(pdscFile)) { + ProjMgrLogger::Error("pdsc file was not found in: " + packageEntry.path); + return false; + } + context.packRequirements.push_back(package); + context.localPackPaths.insert(package.path); + } + } + + // Add wildcard entries last so that they can be re-expanded if needed for (const auto& packageEntry : packages) { PackageItem package; package.path = packageEntry.path; - auto& pack = package.pack; - string packInfoStr = packageEntry.pack; - if (packInfoStr.find("::") != string::npos) { - pack.vendor = RteUtils::RemoveSuffixByString(packInfoStr, "::"); - packInfoStr = RteUtils::RemovePrefixByString(packInfoStr, "::"); - pack.name = RteUtils::GetPrefix(packInfoStr, '@'); - } else { - pack.vendor = RteUtils::GetPrefix(packInfoStr, '@'); + ProjMgrUtils::ConvertToPackInfo(packageEntry.pack, package.pack); + + if (package.pack.name.empty() || WildCards::IsWildcardPattern(package.pack.name)) { + context.packRequirements.push_back(package); } - pack.version = RteUtils::GetSuffix(packInfoStr, '@'); - context.packRequirements.push_back(package); } + return true; } diff --git a/tools/projmgr/src/ProjMgrYamlEmitter.cpp b/tools/projmgr/src/ProjMgrYamlEmitter.cpp index 8f9a63949..ceb710501 100644 --- a/tools/projmgr/src/ProjMgrYamlEmitter.cpp +++ b/tools/projmgr/src/ProjMgrYamlEmitter.cpp @@ -71,6 +71,129 @@ class ProjMgrYamlCbuildIdx : public ProjMgrYamlBase { ProjMgrYamlCbuildIdx(YAML::Node node, const vector& processedContexts, ProjMgrParser& parser, const string& directory); }; +class ProjMgrYamlCbuildPack : public ProjMgrYamlBase { +private: + friend class ProjMgrYamlEmitter; + ProjMgrYamlCbuildPack(YAML::Node node, const vector& processedContexts, ProjMgrParser& parser, bool keepExistingPackContent); +}; + + +ProjMgrYamlCbuildPack::ProjMgrYamlCbuildPack(YAML::Node node, const vector& processedContexts, ProjMgrParser& parser, bool keepExistingPackContent) : + ProjMgrYamlBase(false) +{ + const auto& csolution = parser.GetCsolution(); + + struct ModelItem { + PackInfo info; + ResolvedPackItem resolvedPack; + }; + + map model; + + // Stage 1: Add all known items from the current cbuild pack file, if considering all contexts + if (keepExistingPackContent) { + for (auto& resolvedItem : csolution.cbuildPack.packs) { + ModelItem modelItem; + ProjMgrUtils::ConvertToPackInfo(resolvedItem.pack, modelItem.info); + modelItem.resolvedPack = resolvedItem; + ProjMgrUtils::PushBackUniquely(modelItem.resolvedPack.selectedBy, resolvedItem.pack); + model[resolvedItem.pack] = modelItem; + } + } + + // Stage 2: Process packs that are required by used components + for (const auto& context : processedContexts) { + for (const auto& [packId, package] : context->packages) { + // Skip project local packs + const string& packPath = package->GetRootFilePath(false); + if (context->localPackPaths.find(packPath) != context->localPackPaths.end()) { + continue; + } + + if (model.find(packId) == model.end()) { + // Add pack + ModelItem modelItem; + ProjMgrUtils::ConvertToPackInfo(packId, modelItem.info); + modelItem.resolvedPack.pack = packId; + modelItem.resolvedPack.selectedBy.push_back(packId); + model[packId] = modelItem; + } + } + } + + // Stage 3: Add all packs that do not contain wildcard and also not used by any component + for (const auto& context : processedContexts) { + for (const auto& packItem : context->packRequirements) { + // Skip project local packs + if (!packItem.path.empty()) { + continue; + } + + const auto& reqInfo = packItem.pack; + + // Skip wildcard packs + if (reqInfo.name.empty() || WildCards::IsWildcardPattern(reqInfo.name)) { + continue; + } + + const string packId = RtePackage::ComposePackageID(reqInfo.vendor, reqInfo.name, reqInfo.version); + if (model.find(packId) == model.end()) { + // Pack does not have a version range, so add it + ModelItem modelItem; + ProjMgrUtils::ConvertToPackInfo(packId, modelItem.info); + modelItem.resolvedPack.pack = packId; + modelItem.resolvedPack.selectedBy.push_back(packId); + model[packId] = modelItem; + } + } + } + + // Stage 4: Add all user input expression on the matching resolved pack + for (const auto& context : processedContexts) { + for (const auto& [userInput, resolvedPacks] : context->userInputToResolvedPackIdMap) { + for (const auto& resolvedPack : resolvedPacks) { + if (model.find(resolvedPack) != model.end()) { + ProjMgrUtils::PushBackUniquely(model[resolvedPack].resolvedPack.selectedBy, userInput); + } + } + } + } + + // Stage 5: Process all wildcard patterns from user and add to selected-by list + for (const auto& context : processedContexts) { + for (const auto& packItem : context->packRequirements) { + // Skip project local packs + if (!packItem.path.empty()) { + continue; + } + + const PackInfo& reqInfo = packItem.pack; + if (reqInfo.name.empty() || WildCards::IsWildcardPattern(reqInfo.name)) { + const string packId = RtePackage::ComposePackageID(reqInfo.vendor, reqInfo.name, reqInfo.version); + + for (auto& [_, item] : model) { + if (ProjMgrUtils::IsMatchingPackInfo(item.info, reqInfo)) { + ProjMgrUtils::PushBackUniquely(item.resolvedPack.selectedBy, packId); + } + } + } + } + } + + // Produce the yml output + for (auto& [packId, modelItem] : model) { + YAML::Node resolvedPackNode; + auto& packItem = modelItem.resolvedPack; + + SetNodeValue(resolvedPackNode[YAML_RESOLVED_PACK], packId); + + sort(packItem.selectedBy.begin(), packItem.selectedBy.end()); + SetNodeValue(resolvedPackNode[YAML_SELECTED_BY], packItem.selectedBy); + + node[YAML_RESOLVED_PACKS].push_back(resolvedPackNode); + } +} + ProjMgrYamlBase::ProjMgrYamlBase(bool useAbsolutePaths) : m_useAbsolutePaths(useAbsolutePaths) { } @@ -582,7 +705,16 @@ bool ProjMgrYamlBase::CompareNodes(const YAML::Node& lhs, const YAML::Node& rhs) bool ProjMgrYamlBase::WriteFile(YAML::Node& rootNode, const std::string& filename) { // Compare yaml contents - if (!CompareFile(filename, rootNode)) { + if (rootNode.size() == 0) { + // Remove file as nothing to write. + if (RteFsUtils::Exists(filename)) { + RteFsUtils::RemoveFile(filename); + ProjMgrLogger::Info(filename, "file has been removed"); + } else { + ProjMgrLogger::Info(filename, "file skipped"); + } + } + else if (!CompareFile(filename, rootNode)) { if (!RteFsUtils::MakeSureFilePath(filename)) { ProjMgrLogger::Error(filename, "destination directory can not be created"); return false; @@ -693,3 +825,13 @@ bool ProjMgrYamlEmitter::GenerateCbuildGenIndex(ProjMgrParser& parser, const vec ProjMgrYamlCbuild cbuild(rootNode[YAML_BUILD_GEN_IDX], siblings, type, output, gendir); return cbuild.WriteFile(rootNode, filename); } + +bool ProjMgrYamlEmitter::GenerateCbuildPack(ProjMgrParser& parser, const vector contexts, bool keepExistingPackContent) { + // generate cbuild-pack.yml + const string& filename = parser.GetCsolution().directory + "/" + parser.GetCsolution().name + ".cbuild-pack.yml"; + + YAML::Node rootNode; + ProjMgrYamlCbuildPack cbuildPack(rootNode[YAML_CBUILD_PACK], contexts, parser, keepExistingPackContent); + + return cbuildPack.WriteFile(rootNode, filename); +} diff --git a/tools/projmgr/src/ProjMgrYamlParser.cpp b/tools/projmgr/src/ProjMgrYamlParser.cpp index e3e20d7bb..ff6e34f0c 100644 --- a/tools/projmgr/src/ProjMgrYamlParser.cpp +++ b/tools/projmgr/src/ProjMgrYamlParser.cpp @@ -53,6 +53,14 @@ bool ProjMgrYamlParser::ParseCdefault(const string& input, bool ProjMgrYamlParser::ParseCsolution(const string& input, CsolutionItem& csolution, bool checkSchema) { + + string cbuildPackFile = RteUtils::RemoveSuffixByString(input, ".csolution.yml") + ".cbuild-pack.yml"; + if (fs::exists(cbuildPackFile)) { + if (!ParseCbuildPack(cbuildPackFile, csolution.cbuildPack, checkSchema)) { + return false; + } + } + try { // Validate file schema if (checkSchema && @@ -102,6 +110,36 @@ bool ProjMgrYamlParser::ParseCsolution(const string& input, return true; } +bool ProjMgrYamlParser::ParseCbuildPack(const string& input, + CbuildPackItem& cbuildPack, bool checkSchema) { + try { + // Validate file schema + if (checkSchema && + !ProjMgrYamlSchemaChecker().Validate( + input, ProjMgrYamlSchemaChecker::FileType::BUILD_PACK)) { + return false; + } + + cbuildPack.path = RteFsUtils::MakePathCanonical(input); + cbuildPack.directory = RteFsUtils::ParentPath(cbuildPack.path); + cbuildPack.name = fs::path(input).stem().stem().stem().generic_string(); + + const YAML::Node& root = YAML::LoadFile(input); + if (!ValidateCbuildPack(input, root)) { + return false; + } + + const YAML::Node& cbuildPackNode = root[YAML_CBUILD_PACK]; + + ParseResolvedPacks(cbuildPackNode, cbuildPack.packs); + + } catch (YAML::Exception& e) { + ProjMgrLogger::Error(input, e.mark.line + 1, e.mark.column + 1, e.msg); + return false; + } + return true; +} + bool ProjMgrYamlParser::ParseCproject(const string& input, CsolutionItem& csolution, map& cprojects, bool single, bool checkSchema) { @@ -541,6 +579,18 @@ void ProjMgrYamlParser::ParseMisc(const YAML::Node& parent, vector& mi } } +void ProjMgrYamlParser::ParseResolvedPacks(const YAML::Node& parent, vector& packs) { + if (parent[YAML_RESOLVED_PACKS].IsDefined()) { + const YAML::Node& packsNode = parent[YAML_RESOLVED_PACKS]; + for (const auto& packEntry : packsNode) { + ResolvedPackItem packItem; + ParseString(packEntry, YAML_RESOLVED_PACK, packItem.pack); + ParseVector(packEntry, YAML_SELECTED_BY, packItem.selectedBy); + packs.push_back(packItem); + } + } +} + void ProjMgrYamlParser::ParsePacks(const YAML::Node& parent, const string& file, vector& packs) { if (parent[YAML_PACKS].IsDefined()) { const YAML::Node& packNode = parent[YAML_PACKS]; @@ -934,6 +984,10 @@ const set cbuildSetKeys = { YAML_COMPILER, }; +const set cbuildPackKeys = { + YAML_RESOLVED_PACKS, +}; + const set targetTypeKeys = { YAML_TYPE, YAML_DEVICE, @@ -1021,6 +1075,11 @@ const set packsKeys = { YAML_NOTFORCONTEXT, }; +const set resolvedPacksKeys = { + YAML_RESOLVED_PACK, + YAML_SELECTED_BY, +}; + const set componentsKeys = { YAML_COMPONENT, YAML_CONDITION, @@ -1110,6 +1169,7 @@ const map> sequences = { {YAML_BUILDTYPES, buildTypeKeys}, {YAML_MISC, miscKeys}, {YAML_PACKS, packsKeys}, + {YAML_RESOLVED_PACKS, resolvedPacksKeys}, {YAML_COMPONENTS, componentsKeys}, {YAML_CONNECTIONS, connectionsKeys}, {YAML_LAYERS, layersKeys}, @@ -1183,6 +1243,20 @@ bool ProjMgrYamlParser::ValidateClayer(const string& input, const YAML::Node& ro return true; } +bool ProjMgrYamlParser::ValidateCbuildPack(const string& input, const YAML::Node& root) { + const set rootKeys = { + YAML_CBUILD_PACK, + }; + if (!ValidateKeys(input, root, rootKeys)) { + return false; + } + const YAML::Node& cbuildPackNode = root[YAML_CBUILD_PACK]; + if (!ValidateKeys(input, cbuildPackNode, cbuildPackKeys)) { + return false; + } + return true; +} + bool ProjMgrYamlParser::ValidateCbuildSet(const string& input, const YAML::Node& root) { const set rootKeys = { YAML_CBUILD_SET, diff --git a/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp b/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp index 10ab61ea8..c0ab084b0 100644 --- a/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp +++ b/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp @@ -73,6 +73,9 @@ bool ProjMgrYamlSchemaChecker::GetSchemaFile(string& schemaFile, const ProjMgrYa case ProjMgrYamlSchemaChecker::FileType::BUILDIDX: schemaFileName = "cbuild-idx.schema.json"; break; + case ProjMgrYamlSchemaChecker::FileType::BUILD_PACK: + schemaFileName = "cbuild-pack.schema.json"; + break; case ProjMgrYamlSchemaChecker::FileType::BUILDSET: schemaFileName = "cbuild-set.schema.json"; break; diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content.cbuild-pack.yml new file mode 100644 index 000000000..2f8526165 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content.cbuild-pack.yml @@ -0,0 +1,6 @@ +cbuild-pack: + resolvd-packs: + - resolved-pack: ARM::RteTest_DFP@0.1.1 + selected-by: + - ARM::RteTest_DFP + - ARM::RteTest_DFP@0.1.1 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content.csolution.yml new file mode 100644 index 000000000..f80fca762 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content.csolution.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content2.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content2.cbuild-pack.yml new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content2.cbuild-pack.yml @@ -0,0 +1 @@ +foo diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content2.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content2.csolution.yml new file mode 100644 index 000000000..f80fca762 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_invalid_content2.csolution.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_unused_local_pack_ignored.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_unused_local_pack_ignored.csolution.yml new file mode 100644 index 000000000..8c05b801e --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_unused_local_pack_ignored.csolution.yml @@ -0,0 +1,12 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + - pack: ARM::RteTest + path: ../../SolutionSpecificPack2 + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_used_local_pack_ignored.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_used_local_pack_ignored.csolution.yml new file mode 100644 index 000000000..6c4006840 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_used_local_pack_ignored.csolution.yml @@ -0,0 +1,12 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + - pack: ARM::RteTest + path: ../../SolutionSpecificPack2 + projects: + - project: ./project_with_rtetest_and_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field.cbuild-pack.yml new file mode 100644 index 000000000..83581b59f --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field.cbuild-pack.yml @@ -0,0 +1,7 @@ +misc: 1 +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest_DFP@0.1.1 + selected-by: + - ARM::RteTest_DFP + - ARM::RteTest_DFP@0.1.1 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field.csolution.yml new file mode 100644 index 000000000..f80fca762 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field.csolution.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field2.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field2.cbuild-pack.yml new file mode 100644 index 000000000..2c138ef0f --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field2.cbuild-pack.yml @@ -0,0 +1,2 @@ +cbuild-pack: + misc: 1 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field2.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field2.csolution.yml new file mode 100644 index 000000000..f80fca762 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_disallowed_field2.csolution.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_unmatched_vendor.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_unmatched_vendor.cbuild-pack.yml new file mode 100644 index 000000000..ab9679528 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_unmatched_vendor.cbuild-pack.yml @@ -0,0 +1,6 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: SomeVendor::RteTest_DFP@0.1.1 + selected-by: + - SomeVendor::RteTest_DFP + - SomeVendor::RteTest_DFP@0.1.1 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_unmatched_vendor.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_unmatched_vendor.csolution.yml new file mode 100644 index 000000000..f80fca762 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/cbuild_pack_with_unmatched_vendor.csolution.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/lock_pack_version.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/lock_pack_version.cbuild-pack.yml new file mode 100644 index 000000000..7bc8d20ac --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/lock_pack_version.cbuild-pack.yml @@ -0,0 +1,6 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest_DFP@0.1.1 + selected-by: + - ARM::RteTest_DFP + - ARM::RteTest_DFP@0.1.1 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/lock_pack_version.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/lock_pack_version.csolution.yml new file mode 100644 index 000000000..f80fca762 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/lock_pack_version.csolution.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_added_pack.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_added_pack.cbuild-pack.yml new file mode 100644 index 000000000..7bc8d20ac --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_added_pack.cbuild-pack.yml @@ -0,0 +1,6 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest_DFP@0.1.1 + selected-by: + - ARM::RteTest_DFP + - ARM::RteTest_DFP@0.1.1 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_added_pack.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_added_pack.csolution.yml new file mode 100644 index 000000000..532d29345 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_added_pack.csolution.yml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + - pack: ARM::RteTest + projects: + - project: ./project_with_rtetest_and_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_version_range.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_version_range.csolution.yml new file mode 100644 index 000000000..f19ac478e --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/pack_lock_with_version_range.csolution.yml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + - pack: ARM::RteTest@>=0.0.1 + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.cbuild-pack.yml new file mode 100644 index 000000000..7bc8d20ac --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.cbuild-pack.yml @@ -0,0 +1,6 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest_DFP@0.1.1 + selected-by: + - ARM::RteTest_DFP + - ARM::RteTest_DFP@0.1.1 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.cproject.yml b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.cproject.yml new file mode 100644 index 000000000..2989621d0 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.cproject.yml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + compiler: GCC + packs: + - pack: ARM::RteTest_DFP + - pack: ARM::RteTest + components: + - component: Startup + - component: CORE + - component: RteTest:ComponentLevel diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.csolution.yml new file mode 100644 index 000000000..fe9b9ccf9 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_added_pack.csolution.yml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + projects: + - project: ./project_pack_lock_with_added_pack.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_version_range.csolution.yml b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_version_range.csolution.yml new file mode 100644 index 000000000..f19ac478e --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/project_pack_lock_with_version_range.csolution.yml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + packs: + - pack: ARM::RteTest_DFP + - pack: ARM::RteTest@>=0.0.1 + projects: + - project: ./project_with_dfp_components.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/project_with_dfp_components.cproject.yml b/tools/projmgr/test/data/TestSolution/PackLocking/project_with_dfp_components.cproject.yml new file mode 100644 index 000000000..16422289b --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/project_with_dfp_components.cproject.yml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + compiler: GCC + components: + - component: Startup + - component: CORE diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/project_with_rtetest_and_dfp_components.cproject.yml b/tools/projmgr/test/data/TestSolution/PackLocking/project_with_rtetest_and_dfp_components.cproject.yml new file mode 100644 index 000000000..ee5e0f349 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/project_with_rtetest_and_dfp_components.cproject.yml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + compiler: GCC + components: + - component: Startup + - component: CORE + - component: RteTest:ComponentLevel diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/ref/pack_lock_with_version_range.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/ref/pack_lock_with_version_range.cbuild-pack.yml new file mode 100644 index 000000000..89b796196 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/ref/pack_lock_with_version_range.cbuild-pack.yml @@ -0,0 +1,10 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest@0.1.0 + selected-by: + - ARM::RteTest@0.1.0 + - ARM::RteTest@>=0.0.1 + - resolved-pack: ARM::RteTest_DFP@0.2.0 + selected-by: + - ARM::RteTest_DFP + - ARM::RteTest_DFP@0.2.0 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/ref/project_pack_lock_with_version_range.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/PackLocking/ref/project_pack_lock_with_version_range.cbuild-pack.yml new file mode 100644 index 000000000..89b796196 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/ref/project_pack_lock_with_version_range.cbuild-pack.yml @@ -0,0 +1,10 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest@0.1.0 + selected-by: + - ARM::RteTest@0.1.0 + - ARM::RteTest@>=0.0.1 + - resolved-pack: ARM::RteTest_DFP@0.2.0 + selected-by: + - ARM::RteTest_DFP + - ARM::RteTest_DFP@0.2.0 diff --git a/tools/projmgr/test/data/TestSolution/PackLocking/ref/project_with_dfp_components+CM0.cprj b/tools/projmgr/test/data/TestSolution/PackLocking/ref/project_with_dfp_components+CM0.cprj new file mode 100644 index 000000000..740bf0a03 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/PackLocking/ref/project_with_dfp_components+CM0.cprj @@ -0,0 +1,31 @@ + + + + + + Automatically generated project + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/projmgr/test/data/TestSolution/ref/test.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/ref/test.cbuild-pack.yml new file mode 100644 index 000000000..cea9ebdcd --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ref/test.cbuild-pack.yml @@ -0,0 +1,5 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest_DFP@0.2.0 + selected-by: + - ARM::RteTest_DFP@0.2.0 diff --git a/tools/projmgr/test/data/TestSolution/ref/test2.Debug+TestGen_export.cprj b/tools/projmgr/test/data/TestSolution/ref/test2.Debug+TestGen_export.cprj index 528c91f8a..10ea3b479 100644 --- a/tools/projmgr/test/data/TestSolution/ref/test2.Debug+TestGen_export.cprj +++ b/tools/projmgr/test/data/TestSolution/ref/test2.Debug+TestGen_export.cprj @@ -8,7 +8,7 @@ - + diff --git a/tools/projmgr/test/data/TestSolution/ref/test_pack_selection.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/ref/test_pack_selection.cbuild-pack.yml new file mode 100644 index 000000000..b16893f75 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ref/test_pack_selection.cbuild-pack.yml @@ -0,0 +1,9 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTestGenerator@0.1.0 + selected-by: + - ARM::RteTestGenerator@0.1.0 + - resolved-pack: ARM::RteTest_DFP@0.2.0 + selected-by: + - ARM::RteTest_DFP@0.2.0 + - ARM::RteTest_DFP@>=0.2.0 diff --git a/tools/projmgr/test/src/ProjMgrTestEnv.cpp b/tools/projmgr/test/src/ProjMgrTestEnv.cpp index 383759329..a4fcd8862 100644 --- a/tools/projmgr/test/src/ProjMgrTestEnv.cpp +++ b/tools/projmgr/test/src/ProjMgrTestEnv.cpp @@ -92,6 +92,13 @@ void ProjMgrTestEnv::SetUp() { } RteFsUtils::CreateDirectories(destPackPath); fs::copy(fs::path(srcPackPath), fs::path(destPackPath), fs::copy_options::recursive, ec); + srcPackPath = testcmsispack_folder + "/ARM/RteTest/0.1.0"; + destPackPath = testinput_folder + "/SolutionSpecificPack2"; + if (RteFsUtils::Exists(destPackPath)) { + RteFsUtils::RemoveDir(destPackPath); + } + RteFsUtils::CreateDirectories(destPackPath); + fs::copy(fs::path(srcPackPath), fs::path(destPackPath), fs::copy_options::recursive, ec); // copy invalid packs string srcInvalidPacks, destInvalidPacks; diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index f42f75f23..2cbac6b9a 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -574,6 +574,10 @@ TEST_F(ProjMgrUnitTests, RunProjMgrSolution) { ProjMgrYamlSchemaChecker::FileType::BUILD)); EXPECT_TRUE(ProjMgrYamlSchemaChecker().Validate(testoutput_folder + "/test2.Debug+CM3.cbuild.yml", ProjMgrYamlSchemaChecker::FileType::BUILD)); + + // Check generated cbuild-pack file + ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/test.cbuild-pack.yml", + testinput_folder + "/TestSolution/ref/test.cbuild-pack.yml"); } TEST_F(ProjMgrUnitTests, RunProjMgrSolution_PositionalArguments) { @@ -696,6 +700,245 @@ TEST_F(ProjMgrUnitTests, RunProjMgrLayers) { EXPECT_TRUE(RteFsUtils::Exists(testinput_folder + "/TestLayers/Layer3/RTE/RteTest/MyDir")); } +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_LockPackVersion) { + char* argv[6]; + + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/lock_pack_version.csolution.yml"; + const string cbuildPack = testinput_folder + "/TestSolution/PackLocking/lock_pack_version.cbuild-pack.yml"; + const string cbuildPackBackup = RteFsUtils::BackupFile(cbuildPack); + const string output = testoutput_folder + "/testpacklock"; + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check in the generated CPRJ that RteTest_DFP::0.1.1 is still used, even if 0.2.0 is available + ProjMgrTestEnv::CompareFile(testoutput_folder + "/testpacklock/project_with_dfp_components+CM0.cprj", + testinput_folder + "/TestSolution/PackLocking/ref/project_with_dfp_components+CM0.cprj"); + + // Check that the cbuild-pack file hasn't been modified by this operation + ProjMgrTestEnv::CompareFile(cbuildPackBackup, cbuildPack); + RteFsUtils::RemoveFile(cbuildPackBackup); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_CbuildPackLocalPackIgnored) { + char* argv[6]; + + // convert --solution solution.yml + const string csolution1 = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_unused_local_pack_ignored.csolution.yml"; + const string cbuildPack1 = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_unused_local_pack_ignored.cbuild-pack.yml"; + const string csolution2 = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_used_local_pack_ignored.csolution.yml"; + const string cbuildPack2 = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_used_local_pack_ignored.cbuild-pack.yml"; + const string output = testoutput_folder + "/testpacklock"; + + EXPECT_FALSE(RteFsUtils::Exists(cbuildPack1)); + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution1.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + EXPECT_FALSE(RteFsUtils::Exists(cbuildPack2)); + argv[3] = (char*)csolution2.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check that the cbuild-pack files contains the system wide pack but not the local + string buf1; + EXPECT_TRUE(RteFsUtils::ReadFile(cbuildPack1, buf1)); + EXPECT_TRUE(buf1.find("- resolved-pack: ARM::RteTest_DFP@") != string::npos); + EXPECT_FALSE(buf1.find("- resolved-pack: ARM::RteTest@") != string::npos); + string buf2; + EXPECT_TRUE(RteFsUtils::ReadFile(cbuildPack2, buf2)); + EXPECT_TRUE(buf2.find("- resolved-pack: ARM::RteTest_DFP@") != string::npos); + EXPECT_FALSE(buf2.find("- resolved-pack: ARM::RteTest@") != string::npos); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_CbuildPackInvalidContent) { + char* argv[6]; + StdStreamRedirect streamRedirect; + + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_invalid_content.csolution.yml"; + const string csolution2 = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_invalid_content2.csolution.yml"; + const string output = testoutput_folder + "/testpacklock"; + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_NE(0, RunProjMgr(6, argv, 0)); + const string expected = "error csolution: required property 'resolved-packs' not found in object"; + auto errStr = streamRedirect.GetErrorString(); + EXPECT_NE(string::npos, errStr.find(expected)); + + streamRedirect.ClearStringStreams(); + argv[3] = (char*)csolution2.c_str(); + EXPECT_NE(0, RunProjMgr(6, argv, 0)); + const string expected2 = "error csolution: operator[] call on a scalar (key: \"cbuild-pack\")"; + errStr = streamRedirect.GetErrorString(); + EXPECT_NE(string::npos, errStr.find(expected2)); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_CbuildPackWithDisallowedField) { + char* argv[7]; + StdStreamRedirect streamRedirect; + + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_with_disallowed_field.csolution.yml"; + const string csolution2 = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_with_disallowed_field2.csolution.yml"; + const string output = testoutput_folder + "/testpacklock"; + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + + // Run without "--no-check-schema" + EXPECT_NE(0, RunProjMgr(6, argv, 0)); + const string expected1 = "warning csolution: key 'misc' was not recognized"; + const string expected2 = "error csolution: node 'misc' shall contain sequence elements"; + auto errStr = streamRedirect.GetErrorString(); + EXPECT_NE(string::npos, errStr.find(expected1)); + EXPECT_NE(string::npos, errStr.find(expected2)); + + // Run with "--no-check-schema" + streamRedirect.ClearStringStreams(); + argv[6] = (char*)"--no-check-schema"; + EXPECT_NE(0, RunProjMgr(7, argv, 0)); + errStr = streamRedirect.GetErrorString(); + EXPECT_NE(string::npos, errStr.find(expected1)); + EXPECT_NE(string::npos, errStr.find(expected2)); + + streamRedirect.ClearStringStreams(); + argv[3] = (char*)csolution2.c_str(); + EXPECT_NE(0, RunProjMgr(7, argv, 0)); + errStr = streamRedirect.GetErrorString(); + EXPECT_NE(string::npos, errStr.find(expected1)); + EXPECT_NE(string::npos, errStr.find(expected2)); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_CbuildPackWithUnmatchedVendor) { + char* argv[6]; + + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/cbuild_pack_with_unmatched_vendor.csolution.yml"; + const string output = testoutput_folder + "/testpacklock"; + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_LockedPackVersionNotChangedByAddedPack) { + char* argv[6]; + + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/pack_lock_with_added_pack.csolution.yml"; + const string cbuildPack = testinput_folder + "/TestSolution/PackLocking/pack_lock_with_added_pack.cbuild-pack.yml"; + const string output = testoutput_folder + "/testpacklock"; + + // Check that there is a newer version of the locked pack + vector packs; + m_worker.SetLoadPacksPolicy(LoadPacksPolicy::ALL); + EXPECT_TRUE(m_worker.ListPacks(packs, false, "ARM::RteTest_DFP@0.1.1")); + EXPECT_TRUE(m_worker.ListPacks(packs, false, "ARM::RteTest_DFP@0.2.0")); + + // Check that the cbuild-pack file contains the first pack (locked to an old version) but not the second + string buf; + EXPECT_TRUE(RteFsUtils::ReadFile(cbuildPack, buf)); + EXPECT_TRUE(buf.find("- resolved-pack: ARM::RteTest_DFP@0.1.1") != string::npos); + EXPECT_FALSE(buf.find("- resolved-pack: ARM::RteTest@") != string::npos); + + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check that the cbuild-pack file contains both packs and that the first still has the same version + EXPECT_TRUE(RteFsUtils::ReadFile(cbuildPack, buf)); + EXPECT_TRUE(buf.find("- resolved-pack: ARM::RteTest_DFP@0.1.1") != string::npos); + EXPECT_TRUE(buf.find("- resolved-pack: ARM::RteTest@") != string::npos); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_LockedProjectPackVersionNotChangedByAddedPack) { + char* argv[6]; + + // Same as previous test but with packs listed in project + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/project_pack_lock_with_added_pack.csolution.yml"; + const string cbuildPack = testinput_folder + "/TestSolution/PackLocking/project_pack_lock_with_added_pack.cbuild-pack.yml"; + const string output = testoutput_folder + "/testpacklock"; + + // Check that there is a newer version of the locked pack + vector packs; + m_worker.SetLoadPacksPolicy(LoadPacksPolicy::ALL); + EXPECT_TRUE(m_worker.ListPacks(packs, false, "ARM::RteTest_DFP@0.1.1")); + EXPECT_TRUE(m_worker.ListPacks(packs, false, "ARM::RteTest_DFP@0.2.0")); + + // Check that the cbuild-pack file contains the first pack (locked to an old version) but not the second + string buf; + EXPECT_TRUE(RteFsUtils::ReadFile(cbuildPack, buf)); + EXPECT_TRUE(buf.find("- resolved-pack: ARM::RteTest_DFP@0.1.1") != string::npos); + EXPECT_FALSE(buf.find("- resolved-pack: ARM::RteTest@") != string::npos); + + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check that the cbuild-pack file contains both packs and that the first still has the same version + EXPECT_TRUE(RteFsUtils::ReadFile(cbuildPack, buf)); + EXPECT_TRUE(buf.find("- resolved-pack: ARM::RteTest_DFP@0.1.1") != string::npos); + EXPECT_TRUE(buf.find("- resolved-pack: ARM::RteTest@") != string::npos); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_LockPackWithVersionRange) { + char* argv[6]; + + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/pack_lock_with_version_range.csolution.yml"; + const string output = testoutput_folder + "/testpacklock"; + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check the generated cbuild-pack file + ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/PackLocking/pack_lock_with_version_range.cbuild-pack.yml", + testinput_folder + "/TestSolution/PackLocking/ref/pack_lock_with_version_range.cbuild-pack.yml"); +} + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_LockProjectPackWithVersionRange) { + char* argv[6]; + + // Same as previous test but with packs listed in project + // convert --solution solution.yml + const string csolution = testinput_folder + "/TestSolution/PackLocking/project_pack_lock_with_version_range.csolution.yml"; + const string output = testoutput_folder + "/testpacklock"; + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)output.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check the generated cbuild-pack file + ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/PackLocking/project_pack_lock_with_version_range.cbuild-pack.yml", + testinput_folder + "/TestSolution/PackLocking/ref/project_pack_lock_with_version_range.cbuild-pack.yml"); +} + TEST_F(ProjMgrUnitTests, RunProjMgrLayers2) { char* argv[4]; @@ -2402,6 +2645,10 @@ TEST_F(ProjMgrUnitTests, RunProjMgrSolution_Pack_Selection) { testinput_folder + "/TestSolution/ref/test2.Debug+CM0_pack_selection.cprj"); ProjMgrTestEnv:: CompareFile(testoutput_folder + "/test2.Debug+TestGen.cprj", testinput_folder + "/TestSolution/ref/test2.Debug+TestGen.cprj"); + + // Check generated cbuild-pack file + ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/test_pack_selection.cbuild-pack.yml", + testinput_folder + "/TestSolution/ref/test_pack_selection.cbuild-pack.yml"); } TEST_F(ProjMgrUnitTests, RunProjMgrSolution_No_Packs) { @@ -3496,6 +3743,8 @@ info csolution: config files for each component:\n\ - .*/TestSolution/TestProject1/RTE/Device/RteTest_ARMCM0/startup_ARMCM0.c \\(base@1.1.1\\) \\(update@2.0.3\\)\n\ - .*/TestSolution/TestProject1/RTE/Device/RteTest_ARMCM0/system_ARMCM0.c \\(base@1.0.0\\)\n\ .*/test.cbuild-idx.yml - info csolution: file generated successfully\n\ +.*/test1.*.cbuild.yml - info csolution: file generated successfully\n\ +.*/test.cbuild-pack.yml - info csolution: file (generated successfully|is already up-to-date)\n\ "; auto outStr = streamRedirect.GetOutString(); diff --git a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp index 134b0fd4a..f7b393a4b 100644 --- a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp @@ -322,3 +322,174 @@ TEST_F(ProjMgrUtilsUnitTests, GetFilteredContexts) { "failed for input \"" << contextFilter << "\""; } } + +TEST_F(ProjMgrUtilsUnitTests, ConvertToPackInfo) { + PackInfo packInfo; + + packInfo = {"", "", ""}; + EXPECT_TRUE(ProjMgrUtils::ConvertToPackInfo("", packInfo)); + EXPECT_EQ("", packInfo.vendor); + EXPECT_EQ("", packInfo.name); + EXPECT_EQ("", packInfo.version); + + packInfo = {"", "", ""}; + EXPECT_TRUE(ProjMgrUtils::ConvertToPackInfo("ARM", packInfo)); + EXPECT_EQ("ARM", packInfo.vendor); + EXPECT_EQ("", packInfo.name); + EXPECT_EQ("", packInfo.version); + + packInfo = {"", "", ""}; + EXPECT_TRUE(ProjMgrUtils::ConvertToPackInfo("ARM@5.8.0", packInfo)); + EXPECT_EQ("ARM", packInfo.vendor); + EXPECT_EQ("", packInfo.name); + EXPECT_EQ("5.8.0", packInfo.version); + + packInfo = {"", "", ""}; + EXPECT_TRUE(ProjMgrUtils::ConvertToPackInfo("ARM@>=5.8.0", packInfo)); + EXPECT_EQ("ARM", packInfo.vendor); + EXPECT_EQ("", packInfo.name); + EXPECT_EQ(">=5.8.0", packInfo.version); + + packInfo = {"", "", ""}; + EXPECT_TRUE(ProjMgrUtils::ConvertToPackInfo("ARM::CMSIS", packInfo)); + EXPECT_EQ("ARM", packInfo.vendor); + EXPECT_EQ("CMSIS", packInfo.name); + EXPECT_EQ("", packInfo.version); + + packInfo = {"", "", ""}; + EXPECT_TRUE(ProjMgrUtils::ConvertToPackInfo("ARM::CMSIS@5.8.0", packInfo)); + EXPECT_EQ("ARM", packInfo.vendor); + EXPECT_EQ("CMSIS", packInfo.name); + EXPECT_EQ("5.8.0", packInfo.version); + + packInfo = {"", "", ""}; + EXPECT_TRUE(ProjMgrUtils::ConvertToPackInfo("ARM::CMSIS@>=5.8.0", packInfo)); + EXPECT_EQ("ARM", packInfo.vendor); + EXPECT_EQ("CMSIS", packInfo.name); + EXPECT_EQ(">=5.8.0", packInfo.version); +} + +TEST_F(ProjMgrUtilsUnitTests, IsMatchingPackInfo) { + + // Vendor + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "ARM", ""})); + + // Wrong name + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test", "ARM", ""})); + + // Vendor + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "ARM", "5.7.0"})); + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "ARM", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "ARM", "5.9.0"})); + + // Vendor + ranges + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "ARM", ">=5.7.0"})); + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "ARM", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "ARM", ">=5.9.0"})); + + // Vendor + wildcard name + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "ARM", ""})); + + // Vendor + wildcard name + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "ARM", "5.7.0"})); + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "ARM", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "ARM", "5.9.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSI.", "ARM", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "AR.", "5.8.0"})); + + // Vendor + wildcard name + ranges + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "ARM", ">=5.7.0"})); + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "ARM", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "ARM", ">=5.9.0"})); + + // Vendor + wrong wildcard name + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "ARM", ""})); + + // Vendor + wrong wildcard name + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "ARM", "5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "ARM", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "ARM", "5.9.0"})); + + // Vendor + wrong wildcard name + ranges + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "ARM", ">=5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "ARM", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "ARM", ">=5.9.0"})); + + // Vendor + name + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "ARM", ""})); + + // Vendor + name + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "ARM", "5.7.0"})); + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "ARM", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "ARM", "5.9.0"})); + + // Vendor + name + ranges + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "ARM", ">=5.7.0"})); + EXPECT_TRUE (ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "ARM", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "ARM", ">=5.9.0"})); + + + + // Wrong vendor + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "Test", ""})); + + // Wrong vendor + wrong name + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test", "Test", ""})); + + // Wrong vendor + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "Test", "5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "Test", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "Test", "5.9.0"})); + + // Wrong vendor + ranges + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "Test", ">=5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "Test", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"", "Test", ">=5.9.0"})); + + // Wrong vendor + wildcard name + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "Test", ""})); + + // Wrong vendor + wildcard name + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "Test", "5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "Test", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "Test", "5.9.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSI.", "Test", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Tes.", "5.8.0"})); + + // Wrong vendor + wildcard name + ranges + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "Test", ">=5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "Test", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CM*", "Test", ">=5.9.0"})); + + // Wrong vendor + wrong wildcard name + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "Test", ""})); + + // Wrong vendor + wrong wildcard name + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "Test", "5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "Test", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "Test", "5.9.0"})); + + // Wrong vendor + wrong wildcard name + ranges + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "Test", ">=5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "Test", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"Test*", "Test", ">=5.9.0"})); + + // Wrong vendor + name + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Test", ""})); + + // Wrong vendor + name + exact version + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Test", "5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Test", "5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Test", "5.9.0"})); + + // Wrong vendor + name + ranges + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Test", ">=5.7.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Test", ">=5.8.0"})); + EXPECT_FALSE(ProjMgrUtils::IsMatchingPackInfo({"CMSIS", "ARM", "5.8.0"}, {"CMSIS", "Test", ">=5.9.0"})); +} + +TEST_F(ProjMgrUtilsUnitTests, ConvertToVersionRange) { + EXPECT_EQ("", ProjMgrUtils::ConvertToVersionRange("")); + EXPECT_EQ("1.2.3:1.2.3", ProjMgrUtils::ConvertToVersionRange("1.2.3")); + EXPECT_EQ("1.2.3", ProjMgrUtils::ConvertToVersionRange(">=1.2.3")); +} diff --git a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp index e74422932..b5f4d0605 100644 --- a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp @@ -1589,3 +1589,56 @@ TEST_F(ProjMgrWorkerUnitTests, CheckDeviceAttributes) { EXPECT_TRUE(errStr.find("warning csolution: device 'TestDevice' does not support 'trustzone: secure'") != string::npos); EXPECT_TRUE(errStr.find("warning csolution: device 'TestDevice' does not support 'branch-protection: bti'") != string::npos); }; + +TEST_F(ProjMgrWorkerUnitTests, FindMatchingPacksInCbuildPack) { + ResolvedPackItem match; + + const ResolvedPackItem pack1 = {"ARM::CMSIS@5.8.0", {"ARM::CMSIS@5.8.0", "ARM::CMSIS", "ARM", "ARM::CMSIS@>=5.7.0"}}; + const ResolvedPackItem pack2 = {"Test::PackTest@1.2.3", {"Test::PackTest@1.2.3"}}; + const ResolvedPackItem pack3 = {"Test::PackTest@2.0.0", {"Test::PackTest"}}; + const ResolvedPackItem pack4 = {"Test::Pack4@0.9.0", {"Test::Pack4"}}; + const vector resolvedPacks = {pack1, pack2, pack3, pack4}; + vector matches; + + // Should not match anything if pack name is empty + matches = FindMatchingPacksInCbuildPack({}, resolvedPacks); + EXPECT_EQ(0, matches.size()); + + // Project local pack should never match anything + matches = FindMatchingPacksInCbuildPack({"ARM::CMSIS", "/path/to/pack"}, resolvedPacks); + EXPECT_EQ(0, matches.size()); + + // Should match one entry in "selected-by" + matches = FindMatchingPacksInCbuildPack({"ARM::CMSIS"}, resolvedPacks); + EXPECT_EQ(1, matches.size()); + EXPECT_EQ(matches[0].pack, pack1.pack); + EXPECT_EQ(matches[0].selectedBy, pack1.selectedBy); + + // Should match one entry (wildcard) + matches = FindMatchingPacksInCbuildPack({"ARM::CM*"}, resolvedPacks); + EXPECT_EQ(1, matches.size()); + EXPECT_EQ(matches[0].pack, pack1.pack); + EXPECT_EQ(matches[0].selectedBy, pack1.selectedBy); + + // Vendor matching should match all entries for that vendor + matches = FindMatchingPacksInCbuildPack({"Test"}, resolvedPacks); + EXPECT_EQ(3, matches.size()); + EXPECT_EQ(matches[0].pack, pack2.pack); + EXPECT_EQ(matches[0].selectedBy, pack2.selectedBy); + EXPECT_EQ(matches[1].pack, pack3.pack); + EXPECT_EQ(matches[1].selectedBy, pack3.selectedBy); + EXPECT_EQ(matches[2].pack, pack4.pack); + EXPECT_EQ(matches[2].selectedBy, pack4.selectedBy); + + // Vendor matching with version range should match 2 entries for that vendor + matches = FindMatchingPacksInCbuildPack({"Test@>=1.0.0"}, resolvedPacks); + EXPECT_EQ(2, matches.size()); + EXPECT_EQ(matches[0].pack, pack2.pack); + EXPECT_EQ(matches[0].selectedBy, pack2.selectedBy); + EXPECT_EQ(matches[1].pack, pack3.pack); + EXPECT_EQ(matches[1].selectedBy, pack3.selectedBy); + + // No pack matches this needle + matches = FindMatchingPacksInCbuildPack({"Test@>=3.0.0"}, resolvedPacks); + EXPECT_EQ(0, matches.size()); +}