From deac0d1e4b963afdf8f31e8c52c42d62e821df29 Mon Sep 17 00:00:00 2001 From: Evgueni Driouk Date: Mon, 8 Dec 2025 14:38:10 +0100 Subject: [PATCH] Rpc pack management (#1368) * Support for SelectPack RPC interface * Adjusted to changes in RPC protocol * Enhanced unit tests and fixes * increment csolution-rpc version dependency * Fix Compiler errors * Fix Compiler errors * Update tools/projmgr/include/ProjMgrRpcServerData.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update tools/projmgr/src/ProjMgrRpcServer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update tools/projmgr/src/ProjMgrRpcServer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Spell corrections * Update tools/projmgr/src/ProjMgrRpcServer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update tools/projmgr/test/src/ProjMgrRpcTests.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- libs/rtemodel/include/RtePackage.h | 8 + libs/rtemodel/src/RtePackage.cpp | 5 + libs/rteutils/include/CollectionUtils.h | 15 + tools/projmgr/CMakeLists.txt | 4 +- tools/projmgr/include/ProjMgrRpcServerData.h | 7 +- tools/projmgr/include/ProjMgrWorker.h | 7 +- tools/projmgr/src/ProjMgrCbuildPack.cpp | 2 +- tools/projmgr/src/ProjMgrRpcServer.cpp | 390 ++++++++++++------- tools/projmgr/src/ProjMgrRpcServerData.cpp | 18 +- tools/projmgr/src/ProjMgrWorker.cpp | 23 +- tools/projmgr/test/src/ProjMgrRpcTests.cpp | 263 +++++++++++-- 11 files changed, 542 insertions(+), 200 deletions(-) diff --git a/libs/rtemodel/include/RtePackage.h b/libs/rtemodel/include/RtePackage.h index a97a77320..83a4fd8d7 100644 --- a/libs/rtemodel/include/RtePackage.h +++ b/libs/rtemodel/include/RtePackage.h @@ -85,6 +85,14 @@ class RtePackage : public RteRootItem */ const std::string& GetPackageFileName() const override { return GetRootFileName(); } + + /** + * @brief get path to overview document + * @return file path + */ + const std::string& GetDocValue() const override; + + /** * @brief get pack common ID, also known as 'pack family ID', does not contain version * @return ID string in the form PackVendor.PackName diff --git a/libs/rtemodel/src/RtePackage.cpp b/libs/rtemodel/src/RtePackage.cpp index 0ab6bc62e..3afa5beaf 100644 --- a/libs/rtemodel/src/RtePackage.cpp +++ b/libs/rtemodel/src/RtePackage.cpp @@ -115,6 +115,11 @@ void RtePackage::Clear() RteItem::Clear(); } +const std::string& RtePackage::GetDocValue() const +{ + return GetChildAttribute("description", "overview"); +} + bool RtePackage::IsDeprecated() const { if (m_nDeprecated >= 0) diff --git a/libs/rteutils/include/CollectionUtils.h b/libs/rteutils/include/CollectionUtils.h index ae1bcdf11..7639a8e0e 100644 --- a/libs/rteutils/include/CollectionUtils.h +++ b/libs/rteutils/include/CollectionUtils.h @@ -72,6 +72,21 @@ bool contains_key(const M& m, const typename M::key_type& k) { return m.find(k) != m.end(); } +/** + * @brief Returns set of key in the map + * @tparam M template parameter representing a map + * @param m a key-value map + * @return std::set of keys +*/ +template +auto key_set(const M& m) { + using Key = typename M::key_type; + std::set ks; + for (const auto& kv : m) + ks.insert(kv.first); + return ks; +} + /** * @brief string pair */ diff --git a/tools/projmgr/CMakeLists.txt b/tools/projmgr/CMakeLists.txt index 8bca0c1d1..0d7122dc3 100644 --- a/tools/projmgr/CMakeLists.txt +++ b/tools/projmgr/CMakeLists.txt @@ -24,8 +24,8 @@ include(FetchContent) FetchContent_Declare( rpc-interface DOWNLOAD_EXTRACT_TIMESTAMP ON - URL https://github.com/Open-CMSIS-Pack/csolution-rpc/releases/download/v0.0.5/csolution-rpc.zip - URL_HASH SHA256=94bd65ec0f8054dc7685a60812f00bc32cc7f9227bec5abdbaa7215fb6b6bd27 + URL https://github.com/Open-CMSIS-Pack/csolution-rpc/releases/download/v0.0.6/csolution-rpc.zip + URL_HASH SHA256=86bce42eaef998cb37f3623931068056625d471e80032695946081531359ba5f ) FetchContent_MakeAvailable(rpc-interface) diff --git a/tools/projmgr/include/ProjMgrRpcServerData.h b/tools/projmgr/include/ProjMgrRpcServerData.h index 44f59ebcc..d0b19bc3b 100644 --- a/tools/projmgr/include/ProjMgrRpcServerData.h +++ b/tools/projmgr/include/ProjMgrRpcServerData.h @@ -10,9 +10,12 @@ #include #include +#include using namespace std; +using PackReferenceVector = std::vector; + class RteTarget; class RteBundle; class RteComponent; @@ -30,7 +33,9 @@ class RpcDataCollector { RteTarget* GetTarget() const { return m_target; } void CollectCtClasses(RpcArgs::CtRoot& ctRoot) const; - void CollectUsedItems(RpcArgs::UsedItems& usedItems) const; + void CollectUsedComponents(vector< RpcArgs::ComponentInstance>& usedComponents) const; + + std::set GetUsedPacks() const; void CollectDeviceList(RpcArgs::DeviceList& deviceList, const std::string& namePattern, const std::string& vendor) const; void CollectDeviceInfo(RpcArgs::DeviceInfo& deviceInfo, const std::string& id) const; diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index 769e99e2d..583ffea47 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -101,6 +101,7 @@ struct PackageItem { PackInfo pack; std::string path; std::string origin; + std::string selectedBy; }; /** @@ -522,7 +523,7 @@ struct ContextItem { RtePackage* devicePack = nullptr; RtePackage* boardPack = nullptr; bool precedences; - std::map> userInputToResolvedPackIdMap; + std::map > userInputToResolvedPackIdMap; StrSet localPackPaths; StrVec dependsOn; std::map packLayers; @@ -1070,7 +1071,7 @@ class ProjMgrWorker { /** * @brief collect examples * @param context item - * @param environments filter + * @param environments filter * @return vector of example items */ std::vector CollectExamples(const ContextItem& context, const StrVec& filter); @@ -1081,7 +1082,7 @@ class ProjMgrWorker { * @return vector of template items */ std::vector CollectTemplates(const ContextItem& context); - + /** * @brief check if all selected contexts have lib output * @param reference to processed contexts diff --git a/tools/projmgr/src/ProjMgrCbuildPack.cpp b/tools/projmgr/src/ProjMgrCbuildPack.cpp index 2da835b37..6abbab9da 100644 --- a/tools/projmgr/src/ProjMgrCbuildPack.cpp +++ b/tools/projmgr/src/ProjMgrCbuildPack.cpp @@ -63,7 +63,7 @@ ProjMgrCbuildPack::ProjMgrCbuildPack(YAML::Node node, const vector // Stage 3: 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) { + for (const auto& [resolvedPack, _] : resolvedPacks) { if (model.find(resolvedPack) == model.end()) { ModelItem modelItem; ProjMgrUtils::ConvertToPackInfo(resolvedPack, modelItem.info); diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp index 916bf57b9..dd5d60b9f 100644 --- a/tools/projmgr/src/ProjMgrRpcServer.cpp +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -34,11 +34,11 @@ const string ProjMgrRpcServer::GetRequestFromStdinWithLength(void) { string line; int contentLength = 0; const string& header = CONTENT_LENGTH_HEADER; - while (getline(cin, line) && !cin.fail()) { - if (line.find(header) == 0) { + while(getline(cin, line) && !cin.fail()) { + if(line.find(header) == 0) { contentLength = RteUtils::StringToInt(line.substr(header.length()), 0); } - if (line.empty() || line.front() == '\r' || line.front() == '\n') { + if(line.empty() || line.front() == '\r' || line.front() == '\n') { break; } } @@ -52,18 +52,18 @@ const string ProjMgrRpcServer::GetRequestFromStdin(void) { int braces = 0; bool inJson = false; char c; - while (cin.get(c) && !cin.fail()) { - if (c == '{') { + while(cin.get(c) && !cin.fail()) { + if(c == '{') { braces++; inJson = true; } - if (c == '}') { + if(c == '}') { braces--; } - if (inJson) { + if(inJson) { jsonData += c; } - if (inJson && braces == 0) { + if(inJson && braces == 0) { break; } } @@ -84,7 +84,8 @@ class RpcHandler : public RpcMethods { RpcArgs::SuccessResult LoadPacks(void) override; RpcArgs::SuccessResult LoadSolution(const string& solution, const string& activeTarget) override; RpcArgs::UsedItems GetUsedItems(const string& context) override; - RpcArgs::PacksInfo GetPacksInfo(const string& context) override; + RpcArgs::PacksInfo GetPacksInfo(const string& context, const bool& all) override; + RpcArgs::SuccessResult SelectPack(const string& context, const RpcArgs::PackReference& pack) override; RpcArgs::DeviceList GetDeviceList(const string& context, const string& namePattern, const string& vendor) override; RpcArgs::DeviceInfo GetDeviceInfo(const string& id) override; RpcArgs::BoardList GetBoardList(const string& context, const string& namePattern, const string& vendor) override; @@ -103,16 +104,16 @@ class RpcHandler : public RpcMethods { protected: enum Exception { - SOLUTION_NOT_FOUND = -1, - SOLUTION_NOT_VALID = -2, - SOLUTION_NOT_LOADED = -3, - CONTEXT_NOT_FOUND = -4, - CONTEXT_NOT_VALID = -5, - COMPONENT_NOT_FOUND = -6, + SOLUTION_NOT_FOUND = -1, + SOLUTION_NOT_VALID = -2, + SOLUTION_NOT_LOADED = -3, + CONTEXT_NOT_FOUND = -4, + CONTEXT_NOT_VALID = -5, + COMPONENT_NOT_FOUND = -6, COMPONENT_NOT_RESOLVED = -7, - PACKS_NOT_LOADED = -8, - PACKS_LOADING_FAIL = -9, - RTE_MODEL_ERROR = -10, + PACKS_NOT_LOADED = -8, + PACKS_LOADING_FAIL = -9, + RTE_MODEL_ERROR = -10, }; ProjMgrRpcServer& m_server; @@ -122,7 +123,16 @@ class RpcHandler : public RpcMethods { bool m_solutionLoaded = false; bool m_bUseAllPacks = false; + map m_packReferences; // packsInfo is used to simplify creation and access to references + void StoreSelectedComponents(RteTarget* rteTarget, map& selectedComponents); + PackReferenceVector& GetPackReferences(const string& context); + PackReferenceVector CollectPackReferences(const string& context); + PackReferenceVector GetPackReferencesForPack(const string& context, const string& packId); + RpcArgs::PackReference& EnsurePackReferenceForPack(const string& context, const string& packId, const string& origin, bool bVersion); + RpcArgs::PackReference& EnsurePackReference(const string& context, const RpcArgs::PackReference& packRef); + + void UpdateFilter(const string& context, RteTarget* rteTarget, bool bAll); // returns true if changed const ContextItem& GetContext(const string& context) const; RteTarget* GetActiveTarget(const string& context) const; @@ -135,18 +145,18 @@ bool ProjMgrRpcServer::Run(void) { JsonRpc2Server jsonServer; RpcHandler handler(*this, jsonServer); - while (!m_shutdown && !cin.fail()) { + while(!m_shutdown && !cin.fail()) { // Get request const auto request = m_contextLength ? GetRequestFromStdinWithLength() : GetRequestFromStdin(); - if (request.empty()) { + if(request.empty()) { continue; } ofstream log; - if (m_debug) { + if(m_debug) { log.open(RteFsUtils::GetCurrentFolder(true) + "csolution-rpc-log.txt", fstream::app); log << request << std::endl; } @@ -155,7 +165,7 @@ bool ProjMgrRpcServer::Run(void) { const auto response = jsonServer.HandleRequest(request); // Send response - if (m_contextLength) { + if(m_contextLength) { // compliant to https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#baseProtocol cout << CONTENT_LENGTH_HEADER << response.size() << CrossPlatformUtils::Crlf() << CrossPlatformUtils::Crlf() << response << std::flush; @@ -163,7 +173,7 @@ bool ProjMgrRpcServer::Run(void) { cout << response << std::endl; } - if (m_debug) { + if(m_debug) { log << response << std::endl; log.close(); } @@ -173,12 +183,12 @@ bool ProjMgrRpcServer::Run(void) { } bool RpcHandler::CheckSolutionArg(string& solution, optional& message) const { - if (!regex_match(solution, regex(".*\\.csolution\\.(yml|yaml)"))) { + if(!regex_match(solution, regex(".*\\.csolution\\.(yml|yaml)"))) { message = solution + " is not a *.csolution.yml file"; return false; } solution = RteFsUtils::MakePathCanonical(solution); - if (!RteFsUtils::Exists(solution)) { + if(!RteFsUtils::Exists(solution)) { message = solution + " file does not exist"; return false; } @@ -186,14 +196,14 @@ bool RpcHandler::CheckSolutionArg(string& solution, optional& message) c } const ContextItem& RpcHandler::GetContext(const string& context) const { - if (!m_solutionLoaded) { + if(!m_solutionLoaded) { throw JsonRpcException(SOLUTION_NOT_LOADED, "a valid solution must be loaded before proceeding"); } - if (context.empty()) { + if(context.empty()) { throw JsonRpcException(CONTEXT_NOT_VALID, "'context' argument cannot be empty"); } const auto selected = m_worker.GetSelectedContexts(); - if (find(selected.begin(), selected.end(), context) == selected.end()) { + if(find(selected.begin(), selected.end(), context) == selected.end()) { throw JsonRpcException(CONTEXT_NOT_FOUND, context + " was not found among selected contexts"); } map* contexts = nullptr; @@ -233,8 +243,16 @@ RpcArgs::SuccessResult RpcHandler::Apply(const string& context) { auto rteProject = GetActiveTarget(context)->GetProject(); if(rteProject) { rteProject->Apply(); - // Apply returns if list of gpdc files needs to be updated: irrelevant for csolution + // Apply returns true if list of gpdc files needs to be updated: irrelevant for csolution result.success = true; + // purge unselected packs + + auto& packRefs = GetPackReferences(context); + packRefs.erase( + std::remove_if(packRefs.begin(), packRefs.end(), + [](RpcArgs::PackReference& ref) { return !ref.selected; }), + packRefs.end() + ); } return result; } @@ -249,39 +267,6 @@ RpcArgs::SuccessResult RpcHandler::Resolve(const string& context) { return result; } -RpcArgs::PacksInfo RpcHandler::GetPacksInfo(const string& context) { - const auto contextItem = GetContext(context); - map> packRefs; - for (const auto& packItem : contextItem.packRequirements) { - if (!packItem.origin.empty()) { - const auto packId = RtePackage::ComposePackageID(packItem.pack.vendor, packItem.pack.name, packItem.pack.version); - CollectionUtils::PushBackUniquely(packRefs[packId], packItem.origin); - } - } - RpcArgs::PacksInfo packsInfo; - for (auto& [pack, packItem] : contextItem.rteActiveTarget->GetFilteredModel()->GetPackages()) { - RpcArgs::Pack p; - p.id = packItem->GetPackageID(true); - const auto& description = packItem->GetDescription(); - if (!description.empty()) { - p.description = description; - } - string overview = packItem->GetChildAttribute("description", "overview"); - if (!overview.empty()) { - RteFsUtils::NormalizePath(overview, packItem->GetAbsolutePackagePath()); - p.overview = overview; - } - if (contextItem.packages.find(p.id) != contextItem.packages.end()) { - p.used = true; - if (packRefs.find(p.id) != packRefs.end()) { - p.references = packRefs.at(p.id); - } - } - packsInfo.packs.push_back(p); - } - packsInfo.success = true; - return packsInfo; -} RpcArgs::SuccessResult RpcHandler::LoadPacks(void) { RpcArgs::SuccessResult result = {false}; @@ -299,18 +284,19 @@ RpcArgs::SuccessResult RpcHandler::LoadPacks(void) { } RpcArgs::SuccessResult RpcHandler::LoadSolution(const string& solution, const string& activeTarget) { + m_packReferences.clear(); RpcArgs::SuccessResult result = {false}; const auto csolutionFile = RteFsUtils::MakePathCanonical(solution); - if (!regex_match(csolutionFile, regex(".*\\.csolution\\.(yml|yaml)"))) { + if(!regex_match(csolutionFile, regex(".*\\.csolution\\.(yml|yaml)"))) { result.message = solution + " is not a *.csolution.yml file"; return result; } - if (!m_packsLoaded) { + if(!m_packsLoaded) { result.message = "Packs must be loaded before loading solution"; return result; } result.success = m_solutionLoaded = m_manager.LoadSolution(csolutionFile, activeTarget); - if (!m_solutionLoaded) { + if(!m_solutionLoaded) { result.message = "failed to load and process solution " + csolutionFile; } return result; @@ -340,19 +326,13 @@ void RpcHandler::UpdateFilter(const string& context, RteTarget* rteTarget, bool RtePackageFilter packFilter; if(!all) { // construct and apply filter - auto& contextItem = GetContext(context); - // use pack ID's from context + // use resolved pack ID's from selected references set packIds; - for(const auto& [_, packs] : contextItem.userInputToResolvedPackIdMap) { - for (const auto& id : packs) { - packIds.insert(id); + for(auto& ref : GetPackReferences(context)) { + if(ref.selected && ref.resolvedPack.has_value()) { + packIds.insert(ref.resolvedPack.value()); } } - // add new packs from current selection otherwise we will loose the selection - for(auto [c, _count] : selectedComponents) { - auto id = c->GetPackageID(); - packIds.insert(id); - } packFilter.SetSelectedPackages(packIds); packFilter.SetUseAllPacks(false); } @@ -369,18 +349,135 @@ void RpcHandler::UpdateFilter(const string& context, RteTarget* rteTarget, bool } } +RpcArgs::SuccessResult RpcHandler::SelectPack(const string& context, const RpcArgs::PackReference& packRef) { + RpcArgs::SuccessResult result = {true}; + // find reference if exists, otherwise add new one + auto& ref = EnsurePackReference(context, packRef); + ref.selected = packRef.selected; + if(!ref.resolvedPack.has_value()) { + // TODO: resolve pack + } + return result; +} + RpcArgs::UsedItems RpcHandler::GetUsedItems(const string& context) { RpcArgs::UsedItems usedItems; usedItems.success = true; RpcDataCollector dc(GetActiveTarget(context)); - dc.CollectUsedItems(usedItems); + dc.CollectUsedComponents(usedItems.components); + // get all references, even if they are not selected , because it is useful for client to remove them from files + usedItems.packs = GetPackReferences(context); return usedItems; } + +PackReferenceVector& RpcHandler::GetPackReferences(const string& context) { + // emplace returns pair + // return the value from the iterator + return m_packReferences.emplace(context, RpcHandler::CollectPackReferences(context)).first->second; +} + +PackReferenceVector RpcHandler::CollectPackReferences(const string& context) { + PackReferenceVector packRefs; + auto contextItem = GetContext(context); + for(const auto& packItem : contextItem.packRequirements) { + const auto packId = RtePackage::ComposePackageID(packItem.pack.vendor, packItem.pack.name, packItem.pack.version); + RpcArgs::PackReference packRef; + packRef.pack = packItem.selectedBy; + packRef.resolvedPack = packId; + packRef.origin = packItem.origin; + packRef.path = packItem.path; + packRef.selected = true; // initially pack is selected; + packRefs.push_back(packRef); + } + return packRefs; +} + +PackReferenceVector RpcHandler::GetPackReferencesForPack(const string& context, const string& packId) { + PackReferenceVector packRefs; + for(auto& ref : GetPackReferences(context)) { + if(ref.resolvedPack.has_value() && ref.resolvedPack == packId) { + packRefs.push_back(ref); + } + } + return packRefs; +} + +RpcArgs::PackReference& RpcHandler::EnsurePackReferenceForPack(const string& context, const string& packId, const string& origin, bool bExplicitVersion) { + auto& packRefs = GetPackReferences(context); + for(auto& ref : packRefs) { + if(ref.resolvedPack.has_value() && ref.resolvedPack == packId && + (origin.empty() || ref.origin == origin)) { + ref.selected = true; // ensure selected + return ref; + } + } + // no reference->add one to list of all references + RpcArgs::PackReference newRef; + newRef.pack = bExplicitVersion ? packId : RtePackage::CommonIdFromId(packId); + newRef.resolvedPack = packId; + if(!origin.empty()) { + newRef.origin = origin; + } + newRef.selected = true; + return GetPackReferences(context).emplace_back(newRef); +} + + +RpcArgs::PackReference& RpcHandler::EnsurePackReference(const string& context, const RpcArgs::PackReference& packRef) { + auto& packRefs = GetPackReferences(context); + for(auto& ref : packRefs) { + if(ref.pack == packRef.pack && + (!packRef.path.has_value() || ref.path == packRef.path) && + (packRef.origin.empty() || ref.origin == packRef.origin)) { + return ref; + } + } + // no reference->add supplied + return packRefs.emplace_back(packRef); +} + + +RpcArgs::PacksInfo RpcHandler::GetPacksInfo(const string& context, const bool& all) { + + RteTarget* rteTarget = GetActiveTarget(context); + + UpdateFilter(context, rteTarget, all); + RpcDataCollector dc(GetActiveTarget(context)); + auto usedPacks = dc.GetUsedPacks(); + + RpcArgs::PacksInfo packsInfo; + for(auto& [packId, rtePackage] : rteTarget->GetFilteredModel()->GetPackages()) { + RpcArgs::Pack p; + p.id = rtePackage->GetPackageID(true); + const auto& description = rtePackage->GetDescription(); + if(!description.empty()) { + p.description = description; + } + auto overview = rtePackage->GetDocFile(); + if(!overview.empty()) { + p.doc = overview; + } + if(contains_key(usedPacks, p.id)) { + p.used = true; + } + auto packRefs = GetPackReferencesForPack(context, p.id); + + if(!packRefs.empty()) { + p.references = packRefs; + } + packsInfo.packs.push_back(p); + } + // TODO: add unresolved packs from unresolved references + + packsInfo.success = true; + return packsInfo; +} + RpcArgs::DeviceList RpcHandler::GetDeviceList(const string& context, const string& namePattern, const string& vendor) { RpcArgs::DeviceList deviceList{{false}}; - if (!m_packsLoaded) { + if(!m_packsLoaded) { deviceList.message = "Packs must be loaded before accessing device info"; } else { RteTarget* rteTarget = context.empty() ? nullptr : GetActiveTarget(context); @@ -395,7 +492,7 @@ RpcArgs::DeviceList RpcHandler::GetDeviceList(const string& context, const strin RpcArgs::DeviceInfo RpcHandler::GetDeviceInfo(const string& id) { RpcArgs::DeviceInfo deviceInfo{{false}}; - if (!m_packsLoaded) { + if(!m_packsLoaded) { deviceInfo.message = "Packs must be loaded before accessing device info"; } else { RpcDataCollector dc(nullptr, ProjMgrKernel::Get()->GetGlobalModel()); @@ -407,7 +504,7 @@ RpcArgs::DeviceInfo RpcHandler::GetDeviceInfo(const string& id) RpcArgs::BoardList RpcHandler::GetBoardList(const string& context, const string& namePattern, const string& vendor) { RpcArgs::BoardList boardList{{false}}; - if (!m_packsLoaded) { + if(!m_packsLoaded) { boardList.message = "Packs must be loaded before accessing board info"; } else { RteTarget* rteTarget = context.empty() ? nullptr : GetActiveTarget(context); @@ -423,7 +520,7 @@ RpcArgs::BoardList RpcHandler::GetBoardList(const string& context, const string& RpcArgs::BoardInfo RpcHandler::GetBoardInfo(const string& id) { RpcArgs::BoardInfo boardInfo{{false}}; - if (!m_packsLoaded) { + if(!m_packsLoaded) { boardInfo.message = "Packs must be loaded before accessing board info"; } else { RpcDataCollector dc(nullptr, ProjMgrKernel::Get()->GetGlobalModel()); @@ -447,7 +544,7 @@ RpcArgs::CtRoot RpcHandler::GetComponentsTree(const string& context, const bool& } RpcArgs::SuccessResult RpcHandler::SelectComponent(const string& context, const string& id, const int& count, const RpcArgs::Options& options) { -// first try full component ID + // first try full component ID RteTarget* activeTarget = GetActiveTarget(context); RteComponent* rteComponent = activeTarget->GetComponent(id); RteComponentAggregate* rteAggregate = nullptr; @@ -460,20 +557,21 @@ RpcArgs::SuccessResult RpcHandler::SelectComponent(const string& context, const result.success = activeTarget->SelectComponent(rteAggregate, count, true); rteComponent = rteAggregate->GetComponent(); } - // set options - if(options.layer.has_value()) { - rteAggregate->AddAttribute("layer", options.layer.value(), false); - } - if(options.explicitVendor.has_value()) { - rteAggregate->AddAttribute("explicitVendor", options.explicitVendor.value() ? "1" : "", false); - } + auto& layer = options.layer.has_value() ? options.layer.value() : RteUtils::EMPTY_STRING; + rteAggregate->AddAttribute("layer", layer, false); + bool explicitVendor = options.explicitVendor.has_value() ? options.explicitVendor.value() : false; + rteAggregate->AddAttribute("explicitVendor", explicitVendor ? "1" : "", false); + auto& explicitVersion = options.explicitVersion.has_value() ? options.explicitVersion.value() : RteUtils::EMPTY_STRING; // TODO: check if version is plausible - if(options.explicitVersion.has_value()) { - rteAggregate->AddAttribute("explicitVersion", options.explicitVersion.value(), false); - } + rteAggregate->AddAttribute("explicitVersion", explicitVersion, false); + // ensure Pack reference is added when component is selected + if(count > 0 && rteComponent) { + auto& packId = rteComponent->GetPackage()->GetID(); + EnsurePackReferenceForPack(context, packId, layer, !explicitVersion.empty()); + } return result; } @@ -536,7 +634,7 @@ RpcArgs::SuccessResult RpcHandler::SelectBundle(const string& context, const str return result; // no change => false } if(!contains_key(rteClass->GetBundleNames(), bundleName)) { - result.message = "Bundle '" + bundleName + "' is not found for component class '" + className +"'"; + result.message = "Bundle '" + bundleName + "' is not found for component class '" + className + "'"; return result; // error => false } rteClass->SetSelectedBundleName(bundleName, true); @@ -552,21 +650,21 @@ RpcArgs::Results RpcHandler::ValidateComponents(const string& context) { RpcArgs::Results results; auto validationRes = m_worker.ValidateContext(contextItem); results.result = RteItem::ConditionResultToString(validationRes); - if (validationRes < RteItem::ConditionResult::FULFILLED) { + if(validationRes < RteItem::ConditionResult::FULFILLED) { results.validation = vector{}; - for (const auto& validation : contextItem.validationResults) { + for(const auto& validation : contextItem.validationResults) { RpcArgs::Result r; r.result = RteItem::ConditionResultToString(validation.result); r.id = validation.id; - if (!validation.aggregates.empty()) { + if(!validation.aggregates.empty()) { r.aggregates = vector(validation.aggregates.begin(), validation.aggregates.end()); } - if (!validation.conditions.empty()) { + if(!validation.conditions.empty()) { RpcArgs::Condition c; r.conditions = vector{}; - for (const auto& condition : validation.conditions) { + for(const auto& condition : validation.conditions) { c.expression = condition.expression; - if (!condition.aggregates.empty()) { + if(!condition.aggregates.empty()) { c.aggregates = vector(condition.aggregates.begin(), condition.aggregates.end()); } r.conditions->push_back(c); @@ -581,31 +679,31 @@ RpcArgs::Results RpcHandler::ValidateComponents(const string& context) { RpcArgs::LogMessages RpcHandler::GetLogMessages(void) { StrVec infoVec; - for (const auto& [_, info] : ProjMgrLogger::Get().GetInfos()) { - for (const auto& msg : info) { + for(const auto& [_, info] : ProjMgrLogger::Get().GetInfos()) { + for(const auto& msg : info) { CollectionUtils::PushBackUniquely(infoVec, msg); } } StrVec errorsVec; - for (const auto& [_, errors] : ProjMgrLogger::Get().GetErrors()) { - for (const auto& msg : errors) { + for(const auto& [_, errors] : ProjMgrLogger::Get().GetErrors()) { + for(const auto& msg : errors) { CollectionUtils::PushBackUniquely(errorsVec, msg); } } StrVec warningsVec; - for (const auto& [_, warnings] : ProjMgrLogger::Get().GetWarns()) { - for (const auto& msg : warnings) { + for(const auto& [_, warnings] : ProjMgrLogger::Get().GetWarns()) { + for(const auto& msg : warnings) { CollectionUtils::PushBackUniquely(warningsVec, msg); } } RpcArgs::LogMessages messages; - if (!infoVec.empty()) { + if(!infoVec.empty()) { messages.info = infoVec; } - if (!errorsVec.empty()) { + if(!errorsVec.empty()) { messages.errors = errorsVec; } - if (!warningsVec.empty()) { + if(!warningsVec.empty()) { messages.warnings = warningsVec; } messages.success = true; @@ -616,7 +714,7 @@ RpcArgs::DraftProjectsInfo RpcHandler::GetDraftProjects(const RpcArgs::DraftProj RpcArgs::DraftProjectsInfo applications; applications.success = false; - if (!m_packsLoaded) { + if(!m_packsLoaded) { applications.message = "Packs must be loaded before retrieving draft projects"; return applications; } @@ -624,10 +722,10 @@ RpcArgs::DraftProjectsInfo RpcHandler::GetDraftProjects(const RpcArgs::DraftProj // initialize context and target attributes with board and device ContextItem context; m_worker.InitializeTarget(context); - if (filter.board.has_value() || filter.device.has_value()) { + if(filter.board.has_value() || filter.device.has_value()) { context.board = filter.board.has_value() ? filter.board.value() : ""; context.device = filter.device.has_value() ? filter.device.value() : ""; - if (!m_worker.ProcessDevice(context, BoardOrDevice::SkipProcessor)) { + if(!m_worker.ProcessDevice(context, BoardOrDevice::SkipProcessor)) { applications.message = "Board or device processing failed"; return applications; } @@ -642,61 +740,61 @@ RpcArgs::DraftProjectsInfo RpcHandler::GetDraftProjects(const RpcArgs::DraftProj vector examples, refApps; const auto& environments = filter.environments.has_value() ? filter.environments.value() : StrVec(); const auto& collectedExamples = m_worker.CollectExamples(context, environments); - for (const auto& example : collectedExamples) { + for(const auto& example : collectedExamples) { RpcArgs::ExampleProject e; e.name = example.name; e.pack = example.pack; e.doc = example.doc; e.description = example.description; - if (!example.version.empty()) { + if(!example.version.empty()) { e.version = example.version; } - if (!example.archive.empty()) { + if(!example.archive.empty()) { e.archive = example.archive; } - for (const auto& [name, environment] : example.environments) { + for(const auto& [name, environment] : example.environments) { RpcArgs::ExampleEnvironment env; env.name = name; env.file = environment.load; env.folder = environment.folder; e.environments.push_back(env); } - if (!example.components.empty()) { + if(!example.components.empty()) { e.components = example.components; } - if (!example.categories.empty()) { + if(!example.categories.empty()) { e.categories = example.categories; } - if (!example.keywords.empty()) { + if(!example.keywords.empty()) { e.keywords = example.keywords; } // classify the example as ref-app if it does not specify boards auto& ref = example.boards.empty() ? refApps : examples; ref.push_back(e); } - if (!examples.empty()) { + if(!examples.empty()) { applications.examples = examples; } - if (!refApps.empty()) { + if(!refApps.empty()) { applications.refApps = refApps; } // collect templates vector templates; const auto& csolutionTemplates = m_worker.CollectTemplates(context); - for (const auto& csolutionTemplate : csolutionTemplates) { + for(const auto& csolutionTemplate : csolutionTemplates) { RpcArgs::SolutionTemplate t; t.name = csolutionTemplate.name; t.pack = csolutionTemplate.pack; t.description = csolutionTemplate.description; t.file = csolutionTemplate.file; t.folder = csolutionTemplate.path; - if (!csolutionTemplate.copyTo.empty()) { + if(!csolutionTemplate.copyTo.empty()) { t.copyTo = csolutionTemplate.copyTo; } templates.push_back(t); } - if (!templates.empty()) { + if(!templates.empty()) { applications.templates = templates; } @@ -707,19 +805,19 @@ RpcArgs::DraftProjectsInfo RpcHandler::GetDraftProjects(const RpcArgs::DraftProj RpcArgs::ConvertSolutionResult RpcHandler::ConvertSolution(const string& solution, const string& activeTarget, const bool& updateRte) { RpcArgs::ConvertSolutionResult result = {{ false }}; string csolutionFile = solution; - if (!CheckSolutionArg(csolutionFile, result.message)) { + if(!CheckSolutionArg(csolutionFile, result.message)) { return result; } - if (!m_manager.RunConvert(csolutionFile, activeTarget, updateRte) || !ProjMgrLogger::Get().GetErrors().empty()) { - if (m_worker.HasVarDefineError()) { + if(!m_manager.RunConvert(csolutionFile, activeTarget, updateRte) || !ProjMgrLogger::Get().GetErrors().empty()) { + if(m_worker.HasVarDefineError()) { const auto& vars = m_worker.GetUndefLayerVars(); result.undefinedLayers = StrVec(vars.begin(), vars.end()); const auto& selectCompiler = m_worker.GetSelectableCompilers(); - if (!selectCompiler.empty()) { + if(!selectCompiler.empty()) { result.selectCompiler = selectCompiler; } result.message = "Layer variables undefined, names can be found under 'undefinedLayers'"; - } else if (m_worker.HasCompilerDefineError()) { + } else if(m_worker.HasCompilerDefineError()) { result.selectCompiler = m_worker.GetSelectableCompilers(); result.message = "Compiler undefined, selectable values can be found under 'selectCompiler'"; } else { @@ -734,49 +832,49 @@ RpcArgs::ConvertSolutionResult RpcHandler::ConvertSolution(const string& solutio RpcArgs::DiscoverLayersInfo RpcHandler::DiscoverLayers(const string& solution, const string& activeTarget) { RpcArgs::DiscoverLayersInfo result = {{ false }}; string csolutionFile = solution; - if (!CheckSolutionArg(csolutionFile, result.message)) { + if(!CheckSolutionArg(csolutionFile, result.message)) { return result; } - if (!m_manager.SetupContexts(csolutionFile, activeTarget)) { + if(!m_manager.SetupContexts(csolutionFile, activeTarget)) { result.message = "Setup of solution contexts failed"; return result; } m_worker.SetUpCommand(true); StrVec layers; StrSet fails; - if (!m_worker.ListLayers(layers, "", fails) || !m_worker.ElaborateVariablesConfigurations()) { + if(!m_worker.ListLayers(layers, "", fails) || !m_worker.ElaborateVariablesConfigurations()) { result.message = "No compatible software layer found. Review required connections of the project"; return result; } else { // retrieve valid configurations vector vcVec; const auto& processedContexts = m_worker.GetProcessedContexts(); - for (const auto& context : processedContexts) { - if (!context->variablesConfigurations.empty()) { - for (const auto& configuration : context->variablesConfigurations) { + for(const auto& context : processedContexts) { + if(!context->variablesConfigurations.empty()) { + for(const auto& configuration : context->variablesConfigurations) { RpcArgs::VariablesConfiguration vc; vector lvVec; - for (const auto& variable : configuration.variables) { - RpcArgs::LayerVariable lv = { variable.name, variable.clayer }; + for(const auto& variable : configuration.variables) { + RpcArgs::LayerVariable lv = {variable.name, variable.clayer}; vector settings; - for (const auto& s : variable.settings) { - if (!s.set.empty()) { + for(const auto& s : variable.settings) { + if(!s.set.empty()) { settings.push_back(RpcArgs::SettingsType{s.set}); } } - if (!settings.empty()) { + if(!settings.empty()) { lv.settings = settings; } - if (!variable.path.empty()) { + if(!variable.path.empty()) { lv.path = variable.path; } - if (!variable.file.empty()) { + if(!variable.file.empty()) { lv.file = variable.file; } - if (!variable.description.empty()) { + if(!variable.description.empty()) { lv.description = variable.description; } - if (!variable.copyTo.empty()) { + if(!variable.copyTo.empty()) { lv.copyTo = variable.copyTo; } lvVec.push_back(lv); @@ -789,7 +887,7 @@ RpcArgs::DiscoverLayersInfo RpcHandler::DiscoverLayers(const string& solution, c break; } } - if (!vcVec.empty()) { + if(!vcVec.empty()) { result.configurations = vcVec; } result.success = true; @@ -800,16 +898,16 @@ RpcArgs::DiscoverLayersInfo RpcHandler::DiscoverLayers(const string& solution, c RpcArgs::ListMissingPacksResult RpcHandler::ListMissingPacks(const string& solution, const string& activeTarget) { RpcArgs::ListMissingPacksResult result = {{ false }}; string csolutionFile = solution; - if (!CheckSolutionArg(csolutionFile, result.message)) { + if(!CheckSolutionArg(csolutionFile, result.message)) { return result; } - if (!m_manager.SetupContexts(csolutionFile, activeTarget)) { + if(!m_manager.SetupContexts(csolutionFile, activeTarget)) { result.message = "Setup of solution contexts failed"; return result; } StrVec missingPacks; m_worker.ListPacks(missingPacks, true); - if (!missingPacks.empty()) { + if(!missingPacks.empty()) { result.packs = missingPacks; } result.success = true; diff --git a/tools/projmgr/src/ProjMgrRpcServerData.cpp b/tools/projmgr/src/ProjMgrRpcServerData.cpp index 6da51fc81..1dc56ba7e 100644 --- a/tools/projmgr/src/ProjMgrRpcServerData.cpp +++ b/tools/projmgr/src/ProjMgrRpcServerData.cpp @@ -135,24 +135,24 @@ RteItem* RpcDataCollector::GetTaxonomyItem(const RteComponentGroup* rteGroup) co return nullptr; } -void RpcDataCollector::CollectUsedItems(RpcArgs::UsedItems& usedItems) const { +void RpcDataCollector::CollectUsedComponents(vector< RpcArgs::ComponentInstance>& usedComponents) const { auto rteProject = m_target ? m_target->GetProject() : nullptr; if(!rteProject) { return; } - for(auto [_id, rteCi] : rteProject->GetComponentInstances()) { if(!rteCi->IsApi()) { - usedItems.components.push_back(FromComponentInstance(rteCi)); + usedComponents.push_back(FromComponentInstance(rteCi)); } } - RtePackageMap packs; - rteProject->GetUsedPacks(packs, m_target->GetName()); - for(auto [id, rtePack] : packs) { - Pack p; - p.id = id; - usedItems.packs.push_back(p); +} +std::set RpcDataCollector::GetUsedPacks() const { + RtePackageMap usedPacks; + auto rteProject = m_target ? m_target->GetProject() : nullptr; + if(rteProject) { + rteProject->GetUsedPacks(usedPacks, m_target->GetName()); } + return key_set(usedPacks); } RpcArgs::Device RpcDataCollector::FromRteDevice( RteDevice* rteDevice, bool bIncludeProperties) const { diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 2931556ce..4c9882eaa 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -108,7 +108,7 @@ bool ProjMgrWorker::AddContexts(ProjMgrParser& parser, ContextDesc& descriptor, context.cproject->name = context.west.projectId; context.cproject->output.type = { "elf", "hex" }; CollectionUtils::PushBackUniquely(context.west.westDefs, "CONFIG_BUILD_OUTPUT_HEX=y"); - } + } // No build/target-types if (context.csolution->buildTypes.empty() && context.csolution->targetTypes.empty()) { @@ -1716,15 +1716,16 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vectordirectory + "/"); if (!RteFsUtils::Exists(package.path)) { ProjMgrLogger::Get().Error("pack path: " + packageEntry.path + " does not exist", context.name); @@ -1765,6 +1767,8 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vector ProjMgrWorker::CollectExamples(const ContextItem& conte if (find(examples.begin(), examples.end(), example) == examples.end()) { examples.push_back(example); } - } + } return examples; } @@ -5860,7 +5865,7 @@ bool ProjMgrWorker::PopulateActiveTargetSet(const string& activeTargetSet) { // selected target-type does not have a default target-set: take first named target-set // use default context selection (ParseTargetSetContextSelection) m_activeTargetSet = type.targetSet.front(); - } + } break; } } @@ -5979,12 +5984,12 @@ void ProjMgrWorker::FormatResolvedPackIds() { for (auto& contextName : m_selectedContexts) { auto& contextItem = m_contexts[contextName]; for (auto& [_, resolvedPackIds] : contextItem.userInputToResolvedPackIdMap) { - StrSet formattedPackIds; - for (auto& resolvedPackId : resolvedPackIds) { + map formattedPackIds; + for (auto& [resolvedPackId, package] : resolvedPackIds) { const auto& lowerCasePackId = RteUtils::ToLower(resolvedPackId); const auto& packId = realPackIds.find(lowerCasePackId) != realPackIds.end() ? realPackIds.at(lowerCasePackId) : resolvedPackId; - formattedPackIds.insert(packId); + formattedPackIds.insert(make_pair(packId, package)); } resolvedPackIds = formattedPackIds; } diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp index 38b609c72..d5387f4d1 100644 --- a/tools/projmgr/test/src/ProjMgrRpcTests.cpp +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -40,7 +40,7 @@ string ProjMgrRpcTests::FormatRequest(const int id, const string& method, const request["jsonrpc"] = "2.0"; request["id"] = id; request["method"] = method; - if (!params.is_null()) { + if(!params.is_null()) { request["params"] = params; } return request.dump(); @@ -51,7 +51,7 @@ string ProjMgrRpcTests::CreateLoadRequests(const string& solution, const string& string loadSolutionRequest; if(!solution.empty()) { auto csolutionPath = testinput_folder + solution; - loadSolutionRequest = FormatRequest(2, "LoadSolution", json({{ "solution", csolutionPath }, { "activeTarget", activeTarget } })); + loadSolutionRequest = FormatRequest(2, "LoadSolution", json({{ "solution", csolutionPath }, { "activeTarget", activeTarget }})); if(!contextList.empty()) { YAML::Node cbuildset; cbuildset["cbuild-set"]["generated-by"] = "ProjMrgUnitTests"; @@ -80,12 +80,12 @@ bool ProjMgrRpcTests::CompareRpcResponse(const json& response, const string& ref vector ProjMgrRpcTests::RunRpcMethods(const string& strIn) { StdStreamRedirect streamRedirect; streamRedirect.SetInString(strIn); - char* argv[] = { (char*)"csolution", (char*)"rpc" }; + char* argv[] = {(char*)"csolution", (char*)"rpc"}; EXPECT_EQ(0, RunProjMgr(2, argv, m_envp)); string line; vector responses; istringstream iss(streamRedirect.GetOutString()); - while (getline(iss, line)) { + while(getline(iss, line)) { responses.push_back(json::parse(line)); } return responses; @@ -94,7 +94,7 @@ vector ProjMgrRpcTests::RunRpcMethods(const string& strIn) { string ProjMgrRpcTests::RunRpcMethodsWithContent(const string& strIn) { StdStreamRedirect streamRedirect; streamRedirect.SetInString(strIn); - char* argv[] = { (char*)"csolution", (char*)"rpc", (char*)"--content-length" }; + char* argv[] = {(char*)"csolution", (char*)"rpc", (char*)"--content-length"}; EXPECT_EQ(0, RunProjMgr(3, argv, m_envp)); return streamRedirect.GetOutString(); } @@ -158,7 +158,7 @@ TEST_F(ProjMgrRpcTests, RpcLoadNotSolution) { TEST_F(ProjMgrRpcTests, RpcLoadSolutionNoPacks) { auto csolutionPath = testinput_folder + "/TestRpc/minimal.csolution.yml"; - const auto& requests = FormatRequest(1, "LoadSolution", json({ { "solution", csolutionPath }, { "activeTarget", "TestHW" } })); + const auto& requests = FormatRequest(1, "LoadSolution", json({{ "solution", csolutionPath }, { "activeTarget", "TestHW" }})); const auto& responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); string msg = responses[0]["result"]["message"]; @@ -167,7 +167,7 @@ TEST_F(ProjMgrRpcTests, RpcLoadSolutionNoPacks) { TEST_F(ProjMgrRpcTests, RpcDeviceListNoPacks) { const auto requests = FormatRequest(1, "GetDeviceList", json({{"context", ""},{ "namePattern", ""}, {"vendor", ""}})) + - FormatRequest(2, "GetDeviceInfo", json({{ "id", "ARM::RteTest_ARMCM0"}})); + FormatRequest(2, "GetDeviceInfo", json({{ "id", "ARM::RteTest_ARMCM0"}})); const auto& responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); string msg = responses[0]["result"]["message"]; @@ -299,7 +299,7 @@ TEST_F(ProjMgrRpcTests, RpcDeviceInfo) { TEST_F(ProjMgrRpcTests, RpcBoardListNoPacks) { const auto requests = FormatRequest(1, "GetBoardList", json({{"context", ""},{ "namePattern", ""}, {"vendor", ""}})) + - FormatRequest(2, "GetBoardInfo", json({{ "id", "ARM::RteTest_ARMCM0"}})); + FormatRequest(2, "GetBoardInfo", json({{ "id", "ARM::RteTest_ARMCM0"}})); const auto& responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); string msg = responses[0]["result"]["message"]; @@ -345,7 +345,7 @@ TEST_F(ProjMgrRpcTests, RpcBoardListNoContext) { TEST_F(ProjMgrRpcTests, RpcBoardListContext) { - string context = "selectable+CM0"; + string context = "selectable+CM0"; vector contextList = { context }; @@ -384,7 +384,7 @@ TEST_F(ProjMgrRpcTests, RpcBoardInfo) { // only vendor => no ID requests += FormatRequest(7, "GetBoardInfo", json({{ "id", "Keil::"}})); // board with debugger - requests += FormatRequest(8, "GetBoardInfo", json({{ "id", "Keil::RteTest-Test-board With.Memory:1.1.1" }})); + requests += FormatRequest(8, "GetBoardInfo", json({{ "id", "Keil::RteTest-Test-board With.Memory:1.1.1" }})); const auto responses = RunRpcMethods(requests); EXPECT_TRUE(responses[0]["result"]["success"]); @@ -467,8 +467,8 @@ TEST_F(ProjMgrRpcTests, RpcValidateComponents) { }; auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", "", contextList); int id = 3; - for (const auto& context : contextList) { - requests += FormatRequest(id++, "ValidateComponents", json({ { "context", context } })); + for(const auto& context : contextList) { + requests += FormatRequest(id++, "ValidateComponents", json({{ "context", context }})); } const auto& responses = RunRpcMethods(requests); @@ -644,7 +644,7 @@ TEST_F(ProjMgrRpcTests, RpcGetUsedItems) { vector contextList = { context }; - RpcArgs::Options opt{ "corelayer.yml", "@>=0.1.0", true}; + RpcArgs::Options opt{"core.clayer.yml", "@>=0.1.0", true}; json param; param["context"] = context; param["id"] = "ARM::RteTest:CORE"; @@ -654,7 +654,7 @@ TEST_F(ProjMgrRpcTests, RpcGetUsedItems) { auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", "", contextList); requests += FormatRequest(3, "GetUsedItems", json({{ "context", context }})); requests += FormatRequest(4, "SelectComponent", param); - requests += FormatRequest(5, "Apply", param); + requests += FormatRequest(5, "Apply", json({{ "context", context }})); requests += FormatRequest(6, "GetUsedItems", json({{ "context", context }})); const auto& responses = RunRpcMethods(requests); @@ -662,16 +662,27 @@ TEST_F(ProjMgrRpcTests, RpcGetUsedItems) { EXPECT_TRUE(responses[2]["result"]["success"]); auto components = responses[2]["result"]["components"]; auto packs = responses[2]["result"]["packs"]; - EXPECT_EQ(packs[0]["id"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(packs.size(), 2); + EXPECT_EQ(packs[0]["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(packs[0]["resolvedPack"], "ARM::RteTest_DFP@0.2.0"); EXPECT_EQ(components[0]["id"], "Device:Startup&RteTest Startup"); EXPECT_EQ(components[0]["resolvedComponent"]["id"], "ARM::Device:Startup&RteTest Startup@2.0.3"); + EXPECT_TRUE(responses[3]["result"]["success"]); // select successful + EXPECT_TRUE(responses[4]["result"]["success"]); // apply successful - EXPECT_TRUE(responses[5]["result"]["success"]); // select successful components = responses[5]["result"]["components"]; packs = responses[5]["result"]["packs"]; - EXPECT_EQ(packs[0]["id"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(packs.size(), 3); // added reference to layer file + EXPECT_EQ(packs[0]["pack"], "ARM::RteTest_DFP@0.2.0"); + string origin = packs[0]["origin"]; + EXPECT_TRUE(origin.find(".csolution.yml") != string::npos); + EXPECT_TRUE(!!packs[0]["selected"]); + EXPECT_EQ(packs[2]["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(packs[2]["origin"], "core.clayer.yml"); + EXPECT_TRUE(!!packs[2]["selected"]); + EXPECT_EQ(components[0]["id"], "Device:Startup&RteTest Startup"); EXPECT_EQ(components[0]["resolvedComponent"]["id"], "ARM::Device:Startup&RteTest Startup@2.0.3"); @@ -680,11 +691,205 @@ TEST_F(ProjMgrRpcTests, RpcGetUsedItems) { EXPECT_EQ(RteUtils::ExtractPrefix(id, "::"), "ARM"); EXPECT_EQ(RteUtils::ExtractSuffix(id, "@", true), "@>=0.1.0"); EXPECT_EQ(components[1]["resolvedComponent"]["id"], "ARM::RteTest:CORE@0.1.1"); - EXPECT_EQ(components[1]["options"]["layer"], "corelayer.yml"); + EXPECT_EQ(components[1]["options"]["layer"], "core.clayer.yml"); EXPECT_EQ(components[1]["options"]["explicitVersion"], "@>=0.1.0"); EXPECT_TRUE(components[1]["options"]["explicitVendor"]); + +} + +TEST_F(ProjMgrRpcTests, RpcGetPacksInfoSimple) { + string context = "selectable+CM0"; + vector contextList = { + context + }; + + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", "", contextList); + requests += FormatRequest(3, "GetUsedItems", json({{ "context", context }})); + requests += FormatRequest(4, "GetPacksInfo", json({{ "context", context }, {"all", false}})); + requests += FormatRequest(5, "GetPacksInfo", json({{ "context", context }, {"all", true}})); + + const auto& responses = RunRpcMethods(requests); + + EXPECT_TRUE(responses[2]["result"]["success"]); + auto packs = responses[2]["result"]["packs"]; + EXPECT_EQ(packs[0]["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(packs[0]["resolvedPack"], "ARM::RteTest_DFP@0.2.0"); + string origin = packs[0]["origin"]; + EXPECT_TRUE(origin.find(".csolution.yml") != string::npos); + + EXPECT_TRUE(responses[3]["result"]["success"]); // get pack infos + auto packInfos = responses[3]["result"]["packs"]; + EXPECT_EQ(packInfos.size(), 2); + auto& pack = packInfos[1]; + + EXPECT_EQ(pack["id"], "ARM::RteTest_DFP@0.2.0"); + auto& refs = pack["references"]; + EXPECT_EQ(refs.size(), 1); + + EXPECT_TRUE(!pack["description"].empty()); + + packInfos = responses[4]["result"]["packs"]; + EXPECT_EQ(packInfos.size(), 8); + EXPECT_EQ(packInfos[7]["id"], "SomeVendor::RteTest@0.0.1"); + EXPECT_EQ(packInfos[7]["references"].size(), 0); +} + +TEST_F(ProjMgrRpcTests, RpcGetPacksInfo) { + string context = "test1.Release+CM0"; + vector contextList = { + context + }; + + auto requests = CreateLoadRequests("/TestSolution/test_pack_requirements.csolution.yml", "", contextList); + requests += FormatRequest(3, "GetUsedItems", json({{ "context", context }})); + requests += FormatRequest(4, "GetPacksInfo", json({{ "context", context }, {"all", false}})); + requests += FormatRequest(5, "GetPacksInfo", json({{ "context", context }, {"all", true}})); + + const auto& responses = RunRpcMethods(requests); + + EXPECT_TRUE(responses[2]["result"]["success"]); + auto packs = responses[2]["result"]["packs"]; + EXPECT_EQ(packs[0]["pack"], "ARM::RteTest_DFP@>=0.2.0"); + EXPECT_EQ(packs[0]["resolvedPack"], "ARM::RteTest_DFP@0.2.0"); + + EXPECT_TRUE(responses[3]["result"]["success"]); // get pack infos + auto packInfos = responses[3]["result"]["packs"]; + EXPECT_EQ(packInfos.size(), 2); + EXPECT_EQ(packInfos[1]["id"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_TRUE(packInfos[1]["used"]); + + packInfos = responses[4]["result"]["packs"]; + EXPECT_EQ(packInfos.size(), 8); + EXPECT_EQ(packInfos[7]["id"], "SomeVendor::RteTest@0.0.1"); + EXPECT_FALSE(packInfos[7].contains("used")); } + +TEST_F(ProjMgrRpcTests, RpcSelectPack) { + string context = "test1.Release+CM0"; + vector contextList = { + context + }; + + auto requests = CreateLoadRequests("/TestSolution/test_pack_requirements.csolution.yml", "", contextList); + requests += FormatRequest(3, "GetUsedItems", json({{ "context", context }})); + requests += FormatRequest(4, "GetPacksInfo", json({{ "context", context }, {"all", true}})); + + // unselect pack + RpcArgs::PackReference ref; + ref.pack = "ARM::RteTestRequired"; + ref.selected = false; + requests += FormatRequest(5, "SelectPack", json({{ "context", context }, {"pack", ref}})); + + // select in another origin + ref.origin = "my.clayer.yml"; + ref.resolvedPack = "ARM::RteTestRequired@1.0.1-local"; // pretend we keep resolved value + ref.selected = true; + requests += FormatRequest(6, "SelectPack", json({{ "context", context }, {"pack", ref}})); + + // select another pack + ref.pack = "SomeVendor::RteTest@^0.0.1"; + ref.resolvedPack = "SomeVendor::RteTest@0.0.1"; + ref.selected = true; + requests += FormatRequest(7, "SelectPack", json({{ "context", context }, {"pack", ref}})); // select in another origin + + // select non-existing pack with path + ref.pack = "Foo::Bar"; + ref.origin = "my.cproject.yml"; + ref.resolvedPack = ""; + ref.selected = true; + ref.path = "path/To/Foo/Bar"; + requests += FormatRequest(8, "SelectPack", json({{ "context", context }, {"pack", ref}})); // select in another origin + + requests += FormatRequest(9, "GetUsedItems", json({{ "context", context }})); + requests += FormatRequest(10, "Apply", json({{ "context", context }})); + requests += FormatRequest(11, "GetUsedItems", json({{ "context", context }})); // should purge non-selected + requests += FormatRequest(12, "GetPacksInfo", json({{ "context", context }, {"all", false}})); + + const auto& responses = RunRpcMethods(requests); + + EXPECT_TRUE(responses[2]["result"]["success"]); + auto packs = responses[2]["result"]["packs"]; + EXPECT_EQ(packs.size(), 2); + + EXPECT_EQ(packs[0]["pack"], "ARM::RteTest_DFP@>=0.2.0"); + EXPECT_EQ(packs[0]["resolvedPack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(packs[1]["pack"], "ARM::RteTestRequired"); + EXPECT_EQ(packs[1]["resolvedPack"], "ARM::RteTestRequired@1.0.1-local"); + + string origin = packs[1]["origin"]; + EXPECT_TRUE(origin.find(".csolution.yml") != string::npos); + + EXPECT_TRUE(responses[3]["result"]["success"]); // get pack infos + auto packInfos = responses[3]["result"]["packs"]; + + packInfos = responses[3]["result"]["packs"]; + EXPECT_EQ(packInfos.size(), 8); + EXPECT_EQ(packInfos[3]["id"], "ARM::RteTestRequired@1.0.1-local"); + EXPECT_FALSE(packInfos[3].contains("used")); + EXPECT_EQ(packInfos[5]["id"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_TRUE(packInfos[5]["used"]); + EXPECT_EQ(packInfos[7]["id"], "SomeVendor::RteTest@0.0.1"); + EXPECT_FALSE(packInfos[7].contains("used")); + +// before apply + packs = responses[8]["result"]["packs"]; + EXPECT_EQ(packs.size(), 5); + EXPECT_EQ(packs[0]["pack"], "ARM::RteTest_DFP@>=0.2.0"); + EXPECT_EQ(packs[0]["resolvedPack"], "ARM::RteTest_DFP@0.2.0"); + + EXPECT_EQ(packs[1]["pack"], "ARM::RteTestRequired"); + EXPECT_EQ(packs[1]["resolvedPack"], "ARM::RteTestRequired@1.0.1-local"); + EXPECT_FALSE(packs[1]["selected"]); + + EXPECT_EQ(packs[2]["pack"], "ARM::RteTestRequired"); + EXPECT_EQ(packs[2]["resolvedPack"], "ARM::RteTestRequired@1.0.1-local"); + EXPECT_EQ(packs[2]["origin"], "my.clayer.yml"); + EXPECT_TRUE(packs[2]["selected"]); + + EXPECT_EQ(packs[3]["pack"], "SomeVendor::RteTest@^0.0.1"); + EXPECT_EQ(packs[3]["resolvedPack"], "SomeVendor::RteTest@0.0.1"); + EXPECT_EQ(packs[3]["origin"], "my.clayer.yml"); + EXPECT_TRUE(packs[3]["selected"]); + + EXPECT_EQ(packs[4]["pack"], "Foo::Bar"); + string resolved = packs[4].contains("resolvedPack") ? packs[4]["resolvedPack"] : ""; + EXPECT_TRUE(resolved.empty()); + EXPECT_EQ(packs[4]["origin"], "my.cproject.yml"); + EXPECT_TRUE(packs[4]["selected"]); + +// after apply + + packs = responses[10]["result"]["packs"]; + EXPECT_EQ(packs.size(), 4); + EXPECT_EQ(packs[0]["pack"], "ARM::RteTest_DFP@>=0.2.0"); + EXPECT_EQ(packs[0]["resolvedPack"], "ARM::RteTest_DFP@0.2.0"); + + EXPECT_EQ(packs[1]["pack"], "ARM::RteTestRequired"); + EXPECT_EQ(packs[1]["resolvedPack"], "ARM::RteTestRequired@1.0.1-local"); + EXPECT_EQ(packs[1]["origin"], "my.clayer.yml"); + EXPECT_TRUE(packs[1]["selected"]); + + EXPECT_EQ(packs[2]["pack"], "SomeVendor::RteTest@^0.0.1"); + EXPECT_EQ(packs[2]["resolvedPack"], "SomeVendor::RteTest@0.0.1"); + EXPECT_EQ(packs[2]["origin"], "my.clayer.yml"); + EXPECT_TRUE(packs[2]["selected"]); + + EXPECT_EQ(packs[3]["pack"], "Foo::Bar"); + resolved = packs[3].contains("resolvedPack") ? packs[3]["resolvedPack"] : ""; + EXPECT_TRUE(resolved.empty()); + EXPECT_EQ(packs[3]["origin"], "my.cproject.yml"); + EXPECT_TRUE(packs[3]["selected"]); + + // list packs + // TODO: updated test when unresolved packs will be listed + packInfos = responses[11]["result"]["packs"]; + EXPECT_EQ(packInfos.size(), 3); + +} + + + TEST_F(ProjMgrRpcTests, RpcGetDraftProjects) { // filter 'board' auto requests = @@ -728,7 +933,7 @@ TEST_F(ProjMgrRpcTests, RpcGetDraftProjects) { // filter 'device' that's not mounted on any board requests = FormatRequest(1, "LoadPacks") + - FormatRequest(2, "GetDraftProjects", json{ { "filter", {{ "device", "RteTestGen_ARMCM0" }}} }); + FormatRequest(2, "GetDraftProjects", json{{ "filter", {{ "device", "RteTestGen_ARMCM0" }}}}); responses = RunRpcMethods(requests); EXPECT_TRUE(responses[1]["result"]["success"]); EXPECT_FALSE(responses[1]["result"].contains("examples")); @@ -773,7 +978,7 @@ TEST_F(ProjMgrRpcTests, RpcGetDraftProjects) { TEST_F(ProjMgrRpcTests, RpcConvertSolution) { auto csolutionPath = testinput_folder + "/TestRpc/minimal.csolution.yml"; auto requests = FormatRequest(1, "ConvertSolution", - json({ { "solution", csolutionPath }, { "activeTarget", "TestHW" }, { "updateRte", true } })); + json({{ "solution", csolutionPath }, { "activeTarget", "TestHW" }, { "updateRte", true }})); auto responses = RunRpcMethods(requests); EXPECT_TRUE(responses[0]["result"]["success"]); EXPECT_TRUE(RteFsUtils::Exists(testinput_folder + "/TestRpc/minimal.cbuild-idx.yml")); @@ -784,14 +989,14 @@ TEST_F(ProjMgrRpcTests, RpcConvertSolution) { // convert fail csolutionPath = testinput_folder + "/TestRpc/unknown-component.csolution.yml"; requests = FormatRequest(1, "ConvertSolution", - json({ { "solution", csolutionPath }, { "activeTarget", "" }, { "updateRte", true } })); + json({{ "solution", csolutionPath }, { "activeTarget", "" }, { "updateRte", true }})); responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); // undefined compiler csolutionPath = testinput_folder + "/TestSolution/SelectableToolchains/select-compiler.csolution.yml"; requests = FormatRequest(1, "ConvertSolution", - json({ { "solution", csolutionPath }, { "activeTarget", "" }, { "updateRte", true } })); + json({{ "solution", csolutionPath }, { "activeTarget", "" }, { "updateRte", true }})); responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); EXPECT_EQ(responses[0]["result"]["selectCompiler"][0], "AC6@>=6.0.0"); @@ -800,7 +1005,7 @@ TEST_F(ProjMgrRpcTests, RpcConvertSolution) { // undefined layer csolutionPath = testinput_folder + "/TestLayers/variables-notdefined.csolution.yml"; requests = FormatRequest(1, "ConvertSolution", - json({ { "solution", csolutionPath }, { "activeTarget", "" }, { "updateRte", true } })); + json({{ "solution", csolutionPath }, { "activeTarget", "" }, { "updateRte", true }})); responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); EXPECT_EQ(responses[0]["result"]["undefinedLayers"][0], "NotDefined"); @@ -809,7 +1014,7 @@ TEST_F(ProjMgrRpcTests, RpcConvertSolution) { TEST_F(ProjMgrRpcTests, RpcDiscoverLayers) { auto csolutionPath = testinput_folder + "/TestLayers/config.csolution.yml"; auto requests = FormatRequest(1, "DiscoverLayers", - json({ { "solution", csolutionPath }, { "activeTarget", "" } })); + json({{ "solution", csolutionPath }, { "activeTarget", "" }})); auto responses = RunRpcMethods(requests); // valid configurations: compare response with golden reference @@ -819,7 +1024,7 @@ TEST_F(ProjMgrRpcTests, RpcDiscoverLayers) { // unknown active target set csolutionPath = testinput_folder + "/TestLayers/config.csolution.yml"; requests = FormatRequest(1, "DiscoverLayers", - json({ { "solution", csolutionPath }, { "activeTarget", "unknown" } })); + json({{ "solution", csolutionPath }, { "activeTarget", "unknown" }})); responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); EXPECT_EQ(responses[0]["result"]["message"], "Setup of solution contexts failed"); @@ -827,7 +1032,7 @@ TEST_F(ProjMgrRpcTests, RpcDiscoverLayers) { // no compatible layer combination csolutionPath = testinput_folder + "/TestLayers/variables-notdefined.csolution.yml"; requests = FormatRequest(1, "DiscoverLayers", - json({ { "solution", csolutionPath }, { "activeTarget", "" } })); + json({{ "solution", csolutionPath }, { "activeTarget", "" }})); responses = RunRpcMethods(requests); EXPECT_FALSE(responses[0]["result"]["success"]); EXPECT_EQ(responses[0]["result"]["message"], "No compatible software layer found. Review required connections of the project"); @@ -836,9 +1041,9 @@ TEST_F(ProjMgrRpcTests, RpcDiscoverLayers) { TEST_F(ProjMgrRpcTests, RpcListMissingPacks) { auto csolutionPath = testinput_folder + "/TestSolution/pack_missing.csolution.yml"; auto requests = - FormatRequest(1, "ListMissingPacks", json({ { "solution", csolutionPath }, { "activeTarget", "CM0" } })) + - FormatRequest(2, "ListMissingPacks", json({ { "solution", csolutionPath }, { "activeTarget", "Gen" } })) + - FormatRequest(3, "ListMissingPacks", json({ { "solution", csolutionPath }, { "activeTarget", "Unknown" } })); + FormatRequest(1, "ListMissingPacks", json({{ "solution", csolutionPath }, { "activeTarget", "CM0" }})) + + FormatRequest(2, "ListMissingPacks", json({{ "solution", csolutionPath }, { "activeTarget", "Gen" }})) + + FormatRequest(3, "ListMissingPacks", json({{ "solution", csolutionPath }, { "activeTarget", "Unknown" }})); auto responses = RunRpcMethods(requests); // valid responses