From 77202164e935bc6eaf3954c37a239b308df71ee6 Mon Sep 17 00:00:00 2001 From: Evgueni Driouk Date: Mon, 14 Jul 2025 18:58:23 +0200 Subject: [PATCH] Rpc protocol enhancements --- libs/rtemodel/src/RteComponent.cpp | 4 +- libs/rtemodel/src/RteItem.cpp | 6 +- libs/rtemodel/src/RteProject.cpp | 16 +- libs/rtemodel/test/src/RteItemTest.cpp | 14 ++ tools/projmgr/CMakeLists.txt | 4 +- tools/projmgr/include/ProjMgrRpcServerData.h | 6 +- tools/projmgr/include/ProjMgrWorker.h | 4 +- tools/projmgr/src/ProjMgrRpcServer.cpp | 160 ++++++++---- tools/projmgr/src/ProjMgrRpcServerData.cpp | 85 ++++++- tools/projmgr/src/ProjMgrWorker.cpp | 20 +- tools/projmgr/test/src/ProjMgrRpcTests.cpp | 237 +++++++++++++++--- .../test/src/ProjMgrWorkerUnitTests.cpp | 2 +- 12 files changed, 445 insertions(+), 113 deletions(-) diff --git a/libs/rtemodel/src/RteComponent.cpp b/libs/rtemodel/src/RteComponent.cpp index a2a0d253f..d06a1125a 100644 --- a/libs/rtemodel/src/RteComponent.cpp +++ b/libs/rtemodel/src/RteComponent.cpp @@ -571,7 +571,7 @@ void RteComponentAggregate::AddComponent(RteComponent* c) RtePackage* devicePack = GetTarget()->GetEffectiveDevicePackage(); if (insertedPack == devicePack) return; // component from device pack is already installed - if (insertedPack && devicePack && pack != devicePack) { + if (insertedPack && devicePack && pack && pack != devicePack) { const string& packVersion = pack->GetVersionString(); const string& insertedPackVersion = insertedPack->GetVersionString(); if (VersionCmp::Compare(packVersion, insertedPackVersion) < 0) @@ -610,6 +610,8 @@ void RteComponentAggregate::SetComponentInstance(RteComponentInstance* ci, int c m_selectedVariant = ei->GetCvariantName(); m_selectedVersion = ei->GetVersionString(); AssignAttribute("layer", *ci); + AssignAttribute("explicitVendor", *ci); + AssignAttribute("explicitVersion", *ci); if (m_components.empty()) { if (c) { diff --git a/libs/rtemodel/src/RteItem.cpp b/libs/rtemodel/src/RteItem.cpp index 695dcda03..befa77f2d 100644 --- a/libs/rtemodel/src/RteItem.cpp +++ b/libs/rtemodel/src/RteItem.cpp @@ -250,9 +250,13 @@ void RteItem::SetAttributesFomComponentId(const std::string& componentId) if (componentId.find(RteConstants::SUFFIX_CVENDOR) != string::npos) { string vendor = RteUtils::RemoveSuffixByString(id, RteConstants::SUFFIX_CVENDOR); AddAttribute("Cvendor", vendor); + SetAttribute("explicitVendor", true); id = RteUtils::RemovePrefixByString(componentId, RteConstants::SUFFIX_CVENDOR); } - AddAttribute("Cversion", RteUtils::GetSuffix(id, RteConstants::PREFIX_CVERSION_CHAR)); + auto explicitVersion = RteUtils::GetSuffix(id, RteConstants::PREFIX_CVERSION_CHAR, true); + AddAttribute("explicitVersion", explicitVersion); + AddAttribute("Cversion", RteUtils::GetSuffix(explicitVersion, RteConstants::PREFIX_CVERSION_CHAR)); + id = RteUtils::GetPrefix(id, RteConstants::PREFIX_CVERSION_CHAR); list segments; RteUtils::SplitString(segments, id, RteConstants::COLON_CHAR); diff --git a/libs/rtemodel/src/RteProject.cpp b/libs/rtemodel/src/RteProject.cpp index 22ef685a0..6252c612d 100644 --- a/libs/rtemodel/src/RteProject.cpp +++ b/libs/rtemodel/src/RteProject.cpp @@ -15,6 +15,7 @@ #include "RteProject.h" #include "RteComponent.h" +#include "RteConstants.h" #include "RteFile.h" #include "RteGenerator.h" #include "RteModel.h" @@ -366,7 +367,7 @@ RteComponentInstance* RteProject::AddComponent(RteComponent* c, int instanceCoun if (!ci) { ci = new RteComponentInstance(this); AddItem(ci); - m_components[c->GetID()] = ci; + m_components[id] = ci; ci->Init(c); // check if we have previous aggregate with Excluded flag for the target } @@ -832,7 +833,18 @@ bool RteProject::Apply() } } ci = AddComponent(c, count, target, ci); - ci->AssignAttribute("layer", *a); + // pass aggregate options and create ymlID attribute + if(!ci->IsApi()) { + ci->AssignAttribute("layer", *a); + ci->AssignAttribute("explicitVendor", *a); + ci->AssignAttribute("explicitVersion", *a); + string ymlID = RteUtils::GetPrefix(ci->GetID(), RteConstants::PREFIX_CVERSION_CHAR); + if(!a->GetAttributeAsBool("explicitVendor")) { + ymlID = RteUtils::RemovePrefixByString(ymlID, RteConstants::SUFFIX_CVENDOR); + } + ymlID += a->GetAttribute("explicitVersion"); + ci->AddAttribute("ymlID", ymlID); + } // add API if any RteApi* api = c->GetApi(target, true); diff --git a/libs/rtemodel/test/src/RteItemTest.cpp b/libs/rtemodel/test/src/RteItemTest.cpp index 5feeab896..c26d51d2d 100644 --- a/libs/rtemodel/test/src/RteItemTest.cpp +++ b/libs/rtemodel/test/src/RteItemTest.cpp @@ -82,6 +82,8 @@ TEST(RteItemTest, ComponentAttributesFromId) { RteItem item("component", nullptr); item.SetAttributesFomComponentId(id); EXPECT_EQ(id, item.GetComponentID(true)); + EXPECT_EQ(item.GetAttribute("explicitVersion"), "@9.9.9"); + EXPECT_TRUE(item.GetAttributeAsBool("explicitVendor")); id = "Class&Bundle:Group:Sub&Variant@9.9.9"; item.SetAttributesFomComponentId(id); @@ -98,6 +100,18 @@ TEST(RteItemTest, ComponentAttributesFromId) { id = "Class:Group:&Variant"; item.SetAttributesFomComponentId(id); EXPECT_EQ("Class:Group&Variant", item.GetComponentID(true)); + + id = "Group:Sub"; + item.SetAttributesFomComponentId(id); + EXPECT_EQ(id, item.GetComponentID(true)); + EXPECT_TRUE(item.GetAttribute("explicitVersion").empty()); + EXPECT_FALSE(item.GetAttributeAsBool("explicitVendor")); + + id = "Group:Sub@^9.0.0"; + item.SetAttributesFomComponentId(id); + EXPECT_EQ(id, item.GetComponentID(true)); + EXPECT_EQ(item.GetAttribute("explicitVersion"),"@^9.0.0"); + EXPECT_EQ(item.GetVersionString(),"^9.0.0"); } TEST(RteItemTest, SemVer) { diff --git a/tools/projmgr/CMakeLists.txt b/tools/projmgr/CMakeLists.txt index 517975276..d659de728 100644 --- a/tools/projmgr/CMakeLists.txt +++ b/tools/projmgr/CMakeLists.txt @@ -17,8 +17,8 @@ include(FetchContent) FetchContent_Declare( rpc-interface DOWNLOAD_EXTRACT_TIMESTAMP ON - URL https://github.com/Open-CMSIS-Pack/csolution-rpc/releases/download/v0.0.1/csolution-rpc.zip - URL_HASH SHA256=b86a9e63c5d269c9375fe7c389234a05e7ce59ebb26bde81858b669e5664f3f6 + URL https://github.com/Open-CMSIS-Pack/csolution-rpc/releases/download/v0.0.2/csolution-rpc.zip + URL_HASH SHA256=bc00342a240d2fded19981524bb286c91541504d4271e936f68c646a8b62908f ) FetchContent_MakeAvailable(rpc-interface) diff --git a/tools/projmgr/include/ProjMgrRpcServerData.h b/tools/projmgr/include/ProjMgrRpcServerData.h index 7f76ee3f3..584cc2ef9 100644 --- a/tools/projmgr/include/ProjMgrRpcServerData.h +++ b/tools/projmgr/include/ProjMgrRpcServerData.h @@ -8,6 +8,8 @@ #include "RpcInterface.h" +#include + using namespace std; class RteTarget; @@ -30,8 +32,10 @@ class RpcDataCollector { RpcArgs::ComponentInstance FromComponentInstance(const RteComponentInstance* rteCi) const; RteItem* GetTaxonomyItem(const RteComponentGroup* rteGroup) const; -protected: + std::optional OptionsFromRteItem(const RteItem* item) const; + std::string ResultStringFromRteItem(const RteItem* item) const; +protected: void CollectCtBundles(RpcArgs::CtClass& ctClass, RteComponentGroup* rteClass) const; void CollectCtChildren(RpcArgs::CtTreeItem& parent, RteComponentGroup* rteGroup, const string& bundleName) const; void CollectCtAggregates(RpcArgs::CtTreeItem& parent, RteComponentGroup* rteGroup, const string& bundleName) const; diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index e28a7ac18..9966bedb8 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -908,10 +908,10 @@ class ProjMgrWorker { * @param context item * @return true if there is no error */ - bool ValidateContext(ContextItem& context); + RteItem::ConditionResult ValidateContext(ContextItem& context); /** - * @brief populate active target set + * @brief populate active target set * @param active target set command line option * @return true if there is no error */ diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp index ed1f0233a..c5c299696 100644 --- a/tools/projmgr/src/ProjMgrRpcServer.cpp +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -76,20 +76,20 @@ class RpcHandler : public RpcMethods { m_manager(server.GetManager()), m_worker(server.GetManager().GetWorker()) {} - string GetVersion(void); - bool Shutdown(void); - bool Apply(const string& context); - bool LoadPacks(void); - bool LoadSolution(const string& solution); - RpcArgs::UsedItems GetUsedItems(const string& context); - RpcArgs::PacksInfo GetPacksInfo(const string& context); - RpcArgs::CtRoot GetComponentsTree(const string& context, const bool& all); - bool SelectComponent(const string& context, const string& aggregateId, const int& count); - bool SelectVariant(const string& context, const string& aggregateId, const string& variantName); - bool SelectVersion(const string& context, const string& aggregateId, const string& version); - bool SelectBundle(const string& context, const string& className, const string& bundleName); - RpcArgs::Results ValidateComponents(const string& context); - RpcArgs::LogMessages GetLogMessages(void); + RpcArgs::GetVersionResult GetVersion(void) override; + RpcArgs::SuccessResult Shutdown(void) override; + RpcArgs::SuccessResult Apply(const string& context) override; + RpcArgs::SuccessResult Resolve(const string& context) override; + RpcArgs::SuccessResult LoadPacks(void) override; + RpcArgs::SuccessResult LoadSolution(const string& solution) override; + RpcArgs::UsedItems GetUsedItems(const string& context) override; + RpcArgs::PacksInfo GetPacksInfo(const string& context) override; + RpcArgs::CtRoot GetComponentsTree(const string& context, const bool& all) override; + RpcArgs::SuccessResult SelectComponent(const string& context, const string& id, const int& count, const RpcArgs::Options& options) override; + RpcArgs::SuccessResult SelectVariant(const string& context, const string& aggregateId, const string& variantName) override; + RpcArgs::SuccessResult SelectBundle(const string& context, const string& className, const string& bundleName) override; + RpcArgs::Results ValidateComponents(const string& context) override; + RpcArgs::LogMessages GetLogMessages(void) override; protected: enum Exception @@ -193,58 +193,78 @@ RteComponentAggregate* RpcHandler::GetComponentAggregate(const string& context, return rteAggregate; } -string RpcHandler::GetVersion(void) { - return VERSION_STRING; +RpcArgs::GetVersionResult RpcHandler::GetVersion(void) { + RpcArgs::GetVersionResult res = {true}; + res.message = string("Running ") + INTERNAL_NAME + " " + VERSION_STRING; + res.version = VERSION_STRING; + return res; } -bool RpcHandler::Shutdown(void) { +RpcArgs::SuccessResult RpcHandler::Shutdown(void) { m_server.SetShutdown(true); - return true; + return {true, "Shutdown initiated..."}; } -bool RpcHandler::Apply(const string& context) { - +RpcArgs::SuccessResult RpcHandler::Apply(const string& context) { + RpcArgs::SuccessResult result = {false}; auto rteProject = GetActiveTarget(context)->GetProject(); if(rteProject) { - return rteProject->Apply(); + rteProject->Apply(); + // Apply returns if list of gpdc files needs to be updated: irrelevant for csolution + result.success = true; } - return false; + return result; } -RpcArgs::UsedItems RpcHandler::GetUsedItems(const string& context) { +RpcArgs::SuccessResult RpcHandler::Resolve(const string& context) { + RpcArgs::SuccessResult result = {false}; + auto rteTarget = GetActiveTarget(context); + auto rteProject = rteTarget->GetProject(); + if(rteProject) { + result.success = rteProject->ResolveDependencies(rteTarget); + } + return result; +} +RpcArgs::UsedItems RpcHandler::GetUsedItems(const string& context) { RpcArgs::UsedItems usedItems; + usedItems.success = true; RpcDataCollector dc(GetActiveTarget(context)); dc.CollectUsedItems(usedItems); return usedItems; } -bool RpcHandler::LoadPacks(void) { +RpcArgs::SuccessResult RpcHandler::LoadPacks(void) { + RpcArgs::SuccessResult result = {false}; m_manager.Clear(); m_solutionLoaded = false; m_worker.InitializeModel(); m_worker.SetLoadPacksPolicy(LoadPacksPolicy::ALL); m_packsLoaded = m_worker.LoadAllRelevantPacks(); m_worker.SetLoadPacksPolicy(LoadPacksPolicy::DEFAULT); + result.success = m_packsLoaded; if(!m_packsLoaded) { - throw JsonRpcException(PACKS_LOADING_FAIL, "packs failed to load"); + result.message = "Packs failed to load"; } - return true; + return result; } -bool RpcHandler::LoadSolution(const string& solution) { - if (!m_packsLoaded) { - throw JsonRpcException(PACKS_NOT_LOADED, "packs must be loaded before proceeding"); - } +RpcArgs::SuccessResult RpcHandler::LoadSolution(const string& solution) { + RpcArgs::SuccessResult result = {false}; const auto csolutionFile = RteFsUtils::MakePathCanonical(solution); if (!regex_match(csolutionFile, regex(".*\\.csolution\\.(yml|yaml)"))) { - throw JsonRpcException(SOLUTION_NOT_FOUND, solution + " is not a *.csolution.yml file"); + result.message = solution + " is not a *.csolution.yml file"; + return result; + } + if (!m_packsLoaded) { + result.message = "Packs must be loaded before loading solution"; + return result; } - m_solutionLoaded = m_manager.LoadSolution(csolutionFile); + result.success = m_solutionLoaded = m_manager.LoadSolution(csolutionFile); if (!m_solutionLoaded) { - throw JsonRpcException(SOLUTION_NOT_VALID, "failed to load and process solution " + csolutionFile); + result.message = "failed to load and process solution " + csolutionFile; } - return true; + return result; } RpcArgs::PacksInfo RpcHandler::GetPacksInfo(const string& context) { @@ -279,7 +299,7 @@ RpcArgs::PacksInfo RpcHandler::GetPacksInfo(const string& context) { } packsInfo.packs.push_back(p); } - + packsInfo.success = true; return packsInfo; } @@ -343,24 +363,59 @@ RpcArgs::CtRoot RpcHandler::GetComponentsTree(const string& context, const bool& RpcDataCollector dc(rteTarget); RpcArgs::CtRoot ctRoot; dc.CollectCtClasses(ctRoot); + ctRoot.success = true; return ctRoot; } - -bool RpcHandler::SelectComponent(const string& context, const string& id, const int& count) { +RpcArgs::SuccessResult RpcHandler::SelectComponent(const string& context, const string& id, const int& count, const RpcArgs::Options& options) { // first try full component ID - RteComponent* rteComponent = GetContext(context).rteActiveTarget->GetComponent(id); + RteTarget* activeTarget = GetActiveTarget(context); + RteComponent* rteComponent = activeTarget->GetComponent(id); + RteComponentAggregate* rteAggregate = nullptr; + RpcArgs::SuccessResult result = {false}; if(rteComponent) { - return GetActiveTarget(context)->SelectComponent(rteComponent, count, true); + result.success = GetActiveTarget(context)->SelectComponent(rteComponent, count, true); + rteAggregate = activeTarget->GetComponentAggregate(rteComponent); + } else { + rteAggregate = GetComponentAggregate(context, id); + result.success = activeTarget->SelectComponent(rteAggregate, count, true); + rteComponent = rteAggregate->GetComponent(); } - return GetActiveTarget(context)->SelectComponent(GetComponentAggregate(context, id), count, true); -} -bool RpcHandler::SelectVariant(const string& context, const string& id, const string& variant) { - return SelectVariantOrVersion(context, id, variant, true); + // 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); + } + + // TODO: check if version is plausible + if(options.explicitVersion.has_value()) { + rteAggregate->AddAttribute("explicitVersion", options.explicitVersion.value(), false); + } + + return result; } -bool RpcHandler::SelectVersion(const string& context, const string& id, const string& version) { - return SelectVariantOrVersion(context, id, version, false); +RpcArgs::SuccessResult RpcHandler::SelectVariant(const string& context, const string& id, const string& variant) { + RpcArgs::SuccessResult result = {false}; + RteComponentAggregate* rteAggregate = GetComponentAggregate(context, id); + if(rteAggregate->GetSelectedVariant() == variant) { + return result; + } + + auto availableVariants = rteAggregate->GetVariants(); + if(std::find(availableVariants.begin(), availableVariants.end(), variant) == availableVariants.end()) { + result.message = "Variant '" + variant + "' is not found for component " + id; + return result; + } + + rteAggregate->SetSelectedVariant(variant); + if(rteAggregate->IsSelected()) { + GetActiveTarget(context)->EvaluateComponentDependencies(); + } + result.success = true; + return result; } bool RpcHandler::SelectVariantOrVersion(const string& context, const string& id, const string& value, bool bVariant) { @@ -390,7 +445,8 @@ bool RpcHandler::SelectVariantOrVersion(const string& context, const string& id, -bool RpcHandler::SelectBundle(const string& context, const string& className, const string& bundleName) { +RpcArgs::SuccessResult RpcHandler::SelectBundle(const string& context, const string& className, const string& bundleName) { + RpcArgs::SuccessResult result = {false}; RteTarget* rteTarget = GetActiveTarget(context); RteComponentClass* rteClass = rteTarget->GetComponentClass(className); if(!rteClass) { @@ -398,19 +454,17 @@ bool RpcHandler::SelectBundle(const string& context, const string& className, co } rteClass->SetSelectedBundleName(bundleName, true); GetActiveTarget(context)->EvaluateComponentDependencies(); - return true; + return result; } RpcArgs::Results RpcHandler::ValidateComponents(const string& context) { auto contextItem = GetContext(context); - if (!m_worker.CheckRteErrors()) { - throw JsonRpcException(RTE_MODEL_ERROR, "rte model reported error"); - } - RpcArgs::Results results; - if (!m_worker.ValidateContext(contextItem)) { + auto validationRes = m_worker.ValidateContext(contextItem); + results.result = RteItem::ConditionResultToString(validationRes); + if (validationRes < RteItem::ConditionResult::FULFILLED) { results.validation = vector{}; for (const auto& validation : contextItem.validationResults) { RpcArgs::Result r; @@ -433,6 +487,7 @@ RpcArgs::Results RpcHandler::ValidateComponents(const string& context) { results.validation->push_back(r); } } + results.success = true; return results; } @@ -465,6 +520,7 @@ RpcArgs::LogMessages RpcHandler::GetLogMessages(void) { if (!warningsVec.empty()) { messages.warnings = warningsVec; } + messages.success = true; return messages; } diff --git a/tools/projmgr/src/ProjMgrRpcServerData.cpp b/tools/projmgr/src/ProjMgrRpcServerData.cpp index 5d7994c54..98b6b2387 100644 --- a/tools/projmgr/src/ProjMgrRpcServerData.cpp +++ b/tools/projmgr/src/ProjMgrRpcServerData.cpp @@ -59,19 +59,64 @@ RpcArgs::Component RpcDataCollector::FromRteComponent(const RteComponent* rteCom return c; } +std::optional RpcDataCollector::OptionsFromRteItem(const RteItem* item) const { + if (!item) { + return std::nullopt; + } + Options opt; + bool bHasOpt = false; + + if (item->HasAttribute("layer")) { + opt.layer = item->GetAttribute("layer"); + bHasOpt = true; + } + if (item->HasAttribute("explicitVersion")) { + opt.explicitVersion = item->GetAttribute("explicitVersion"); + bHasOpt = true; + } + if (item->GetAttributeAsBool("explicitVendor")) { + opt.explicitVendor = true; + bHasOpt = true; + } + if (bHasOpt) { + return opt; + } + return std::nullopt; +} + +std::string RpcDataCollector::ResultStringFromRteItem(const RteItem* item) const { + string result; + RteTarget* rteTarget = GetTarget(); + if(item && rteTarget) { + RteItem::ConditionResult res = item->GetConditionResult(rteTarget->GetDependencySolver()); + if(res > RteItem::ConditionResult::UNDEFINED && res < RteItem::ConditionResult::FULFILLED) { + result = RteItem::ConditionResultToString(res); + } + } + return result; +} + RpcArgs::ComponentInstance RpcDataCollector::FromComponentInstance(const RteComponentInstance* rteCi) const { ComponentInstance ci; if(rteCi) { - ci.id = rteCi->GetDisplayName(); + ci.id = rteCi->GetAttribute("ymlID"); ci.selectedCount = rteCi->GetInstanceCount(m_target->GetName()); - auto& layer = rteCi->GetAttribute("layer"); - if(!layer.empty()) { - ci.layer = layer; - } auto rteComponent = rteCi->GetResolvedComponent(m_target->GetName()); if(rteComponent) { ci.resolvedComponent = FromRteComponent(rteComponent); } + + auto& gen = rteCi->GetAttribute("generator"); + if(!gen.empty()) { + ci.generator = gen; + } + if(rteCi->GetAttributeAsBool("generated") && !rteCi->GetAttributeAsBool("selectable")) { + ci.fixed = true; + } + auto opt = OptionsFromRteItem(rteCi); + if(opt) { + ci.options = opt; + } } return ci; } @@ -86,14 +131,15 @@ RteItem* RpcDataCollector::GetTaxonomyItem(const RteComponentGroup* rteGroup) co } void RpcDataCollector::CollectUsedItems(RpcArgs::UsedItems& usedItems) const { - auto rteProject = m_target ? m_target->GetProject() : nullptr; if(!rteProject) { return; } for(auto [_id, rteCi] : rteProject->GetComponentInstances()) { - usedItems.components.push_back(FromComponentInstance(rteCi)); + if(!rteCi->IsApi()) { + usedItems.components.push_back(FromComponentInstance(rteCi)); + } } RtePackageMap packs; rteProject->GetUsedPacks(packs, m_target->GetName()); @@ -121,6 +167,10 @@ void RpcDataCollector::CollectCtClasses(RpcArgs::CtRoot& root) const { if(taxonomyItem) { ctClass.taxonomy = FromRteItem(taxonomyItem->GetTaxonomyDescriptionID(), taxonomyItem); } + auto res = ResultStringFromRteItem(rteClass); + if(!res.empty()) { + ctClass.result = res; + } CollectCtBundles(ctClass, rteClass); root.classes.push_back(ctClass); } @@ -164,6 +214,10 @@ void RpcDataCollector::CollectCtChildren(RpcArgs::CtTreeItem& parent, RteCompone if(taxonomyItem) { g.taxonomy = FromRteItem(taxonomyItem->GetTaxonomyDescriptionID(), taxonomyItem); } + auto res = ResultStringFromRteItem(rteGroup); + if(!res.empty()) { + g.result = res; + } // subgroups CollectCtChildren(g, rteGroup, bundleName); CollectCtAggregates(g, rteGroup,bundleName); @@ -193,11 +247,22 @@ void RpcDataCollector::CollectCtAggregates(RpcArgs::CtTreeItem& parent, RteCompo auto selectedCount = rteAggregate->IsSelected(); if(selectedCount) { a.selectedCount = selectedCount; + auto res = ResultStringFromRteItem(rteAggregate); + if(!res.empty()) { + a.result = res; + } } - auto layer = rteAggregate->GetAttribute("layer"); - if(!layer.empty()) { - a.layer = layer; + auto& gen = rteAggregate->GetAttribute("generator"); + if(!gen.empty()) { + a.generator = gen; + } + if(rteAggregate->GetAttributeAsBool("generated") && !rteAggregate->GetAttributeAsBool("selectable")) { + a.fixed = true; + } + auto opt = OptionsFromRteItem(rteAggregate); + if(opt) { + a.options = opt; } CollectCtVariants(a, rteAggregate); diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 3e26cc93b..babbabf05 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -1968,7 +1968,7 @@ RteComponentInstance* ProjMgrWorker::ProcessComponent(ContextItem& context, Comp RteComponentInstance* ci = new RteComponentInstance(nullptr); ci->SetTag("component"); ci->SetAttributesFomComponentId(item.component); - ci->AddAttribute("id", item.component); + ci->AddAttribute("ymlID", item.component); bool bEnforced = !item.condition.empty(); if(bEnforced) { ci->AddAttribute("condition", item.condition); @@ -2628,11 +2628,13 @@ bool ProjMgrWorker::IsPreIncludeByTarget(const RteTarget* activeTarget, const st return false; } -bool ProjMgrWorker::ValidateContext(ContextItem& context) { +RteItem::ConditionResult ProjMgrWorker::ValidateContext(ContextItem& context) { context.validationResults.clear(); map results; - context.rteActiveTarget->GetDepsResult(results, context.rteActiveTarget); - + auto depResult = context.rteActiveTarget->GetDepsResult(results, context.rteActiveTarget); + if(depResult >= RteItem::ConditionResult::FULFILLED) { + return depResult; + } for (const auto& [item, result] : results) { ValidationResult validation; validation.result = result.GetResult(); @@ -2654,11 +2656,7 @@ bool ProjMgrWorker::ValidateContext(ContextItem& context) { context.validationResults.push_back(validation); } - - if (context.validationResults.empty()) { - return true; - } - return false; + return depResult; } bool ProjMgrWorker::ProcessGpdsc(ContextItem& context) { @@ -3881,7 +3879,7 @@ bool ProjMgrWorker::ProcessContext(ContextItem& context, bool loadGenFiles, bool // TODO: Add uniquely identified missing dependencies to RTE Model // Get dependency validation results - if (!ValidateContext(context)) { + if (ValidateContext(context) < RteItem::ConditionResult::FULFILLED ) { string msg = "dependency validation for context '" + context.name + "' failed:"; set results; FormatValidationResults(results, context); @@ -4165,7 +4163,7 @@ bool ProjMgrWorker::ListDependencies(vector& dependencies, const string& if (!ProcessContext(context, true, false, false)) { return false; } - if (!ValidateContext(context)) { + if (ValidateContext(context) < RteItem::ConditionResult::FULFILLED) { for (const auto& validation : context.validationResults) { if ((validation.result == RteItem::MISSING) || (validation.result == RteItem::SELECTABLE)) { for (const auto& condition : validation.conditions) { diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp index e33cd7d6d..341dc61cb 100644 --- a/tools/projmgr/test/src/ProjMgrRpcTests.cpp +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -25,6 +25,9 @@ class ProjMgrRpcTests : public ProjMgr, public ::testing::Test { string FormatRequest(const int id, const string& method, const json& params); vector RunRpcMethods(const string& strIn); string RunRpcMethodsWithContent(const string& strIn); + + string CreateLoadRequests(const string& solution, const vector& contextList = RteUtils::EMPTY_STRING_VECTOR); + }; string ProjMgrRpcTests::FormatRequest(const int id, const string& method, const json& params = json()) { @@ -38,6 +41,26 @@ string ProjMgrRpcTests::FormatRequest(const int id, const string& method, const return request.dump(); } +string ProjMgrRpcTests::CreateLoadRequests(const string& solution, const vector& contextList) +{ + auto csolutionPath = testinput_folder + solution; + if(!contextList.empty()) { + YAML::Node cbuildset; + cbuildset["cbuild-set"]["generated-by"] = "ProjMrgUnitTests"; + for(const auto& context : contextList) { + cbuildset["cbuild-set"]["contexts"].push_back(map{ { "context", context } }); + } + auto cbuildsetPath = csolutionPath; + RteUtils::ReplaceAll(cbuildsetPath, ".csolution.yml", ".cbuild-set.yml"); + + ofstream cbuildsetFile; + cbuildsetFile.open(cbuildsetPath, fstream::trunc); + cbuildsetFile << cbuildset << std::endl; + cbuildsetFile.close(); + } + return FormatRequest(1, "LoadPacks") + FormatRequest(2, "LoadSolution", json({ { "solution", csolutionPath } })); +} + vector ProjMgrRpcTests::RunRpcMethods(const string& strIn) { StdStreamRedirect streamRedirect; streamRedirect.SetInString(strIn); @@ -81,7 +104,8 @@ TEST_F(ProjMgrRpcTests, RpcGetVersion) { const auto& responses = RunRpcMethods(requests); EXPECT_EQ("2.0", responses[0]["jsonrpc"]); EXPECT_EQ(1, responses[0]["id"]); - EXPECT_EQ(string(VERSION_STRING), responses[0]["result"]); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_EQ(string(VERSION_STRING), responses[0]["result"]["version"]); } TEST_F(ProjMgrRpcTests, RpcGetVersionWithContent) { @@ -91,26 +115,47 @@ TEST_F(ProjMgrRpcTests, RpcGetVersionWithContent) { } TEST_F(ProjMgrRpcTests, RpcLoadSolution) { - const string& csolution = testinput_folder + "/TestRpc/minimal.csolution.yml"; - const auto& requests = - FormatRequest(1, "LoadPacks") + - FormatRequest(2, "LoadSolution", json({{ "solution", csolution }})); + const auto& requests = CreateLoadRequests("/TestRpc/minimal.csolution.yml"); + const auto& responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_TRUE(responses[1]["result"]["success"]); +} + +TEST_F(ProjMgrRpcTests, RpcLoadUndefinedSolution) { + const auto& requests = CreateLoadRequests("/TestRpc/undefined.csolution.yml"); + const auto& responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_FALSE(responses[1]["result"]["success"]); + string msg = responses[1]["result"]["message"]; + EXPECT_TRUE(msg.find("failed to load and process solution") == 0); +} +TEST_F(ProjMgrRpcTests, RpcLoadNotSolution) { + const auto& requests = CreateLoadRequests("/TestRpc/undefined.yml"); const auto& responses = RunRpcMethods(requests); - EXPECT_TRUE(responses[0]["result"]); - EXPECT_TRUE(responses[1]["result"]); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_FALSE(responses[1]["result"]["success"]); + string msg = responses[1]["result"]["message"]; + EXPECT_TRUE(msg.find("is not a *.csolution.yml file") != string::npos); +} + +TEST_F(ProjMgrRpcTests, RpcLoadSolutionNoPacks) { + auto csolutionPath = testinput_folder + "/TestRpc/minimal.csolution.yml"; + const auto& requests = FormatRequest(1, "LoadSolution", json({ { "solution", csolutionPath } })); + const auto& responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[0]["result"]["success"]); + string msg = responses[0]["result"]["message"]; + EXPECT_EQ(msg, "Packs must be loaded before loading solution"); } TEST_F(ProjMgrRpcTests, RpcLoadSolution_UnknownComponent) { const string& csolution = testinput_folder + "/TestRpc/unknown-component.csolution.yml"; - const auto& requests = - FormatRequest(1, "LoadPacks") + - FormatRequest(2, "LoadSolution", json({ { "solution", csolution } })) + + auto requests = CreateLoadRequests("/TestRpc/unknown-component.csolution.yml") + FormatRequest(3, "GetLogMessages"); const auto& responses = RunRpcMethods(requests); - EXPECT_TRUE(responses[0]["result"]); - EXPECT_TRUE(responses[1]["result"]); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_TRUE(responses[1]["result"]["success"]); EXPECT_EQ("no component was found with identifier 'ARM::UNKNOWN:COMPONENT'", responses[2]["result"]["errors"][0]); } @@ -123,33 +168,19 @@ TEST_F(ProjMgrRpcTests, RpcValidateComponents) { "incompatible+CM0", "incompatible-variant+CM0", }; - YAML::Node cbuildset; - cbuildset["cbuild-set"]["generated-by"] = "ProjMrgUnitTests"; - for (const auto& context : contextList) { - cbuildset["cbuild-set"]["contexts"].push_back(map{ { "context", context } }); - } - const string& cbuildsetPath = testinput_folder + "/Validation/dependencies.cbuild-set.yml"; - - ofstream cbuildsetFile; - cbuildsetFile.open(cbuildsetPath, fstream::trunc); - cbuildsetFile << cbuildset << std::endl; - cbuildsetFile.close(); - - const string& csolution = testinput_folder + "/Validation/dependencies.csolution.yml"; - auto requests = - FormatRequest(1, "LoadPacks") + - FormatRequest(2, "LoadSolution", json({ { "solution", csolution } })); + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", contextList); int id = 3; for (const auto& context : contextList) { requests += FormatRequest(id++, "ValidateComponents", json({ { "context", context } })); } const auto& responses = RunRpcMethods(requests); - EXPECT_TRUE(responses[0]["result"]); - EXPECT_TRUE(responses[1]["result"]); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_TRUE(responses[1]["result"]["success"]); // selectable auto validation = responses[2]["result"]["validation"][0]; + EXPECT_EQ(responses[2]["result"]["result"], "SELECTABLE"); EXPECT_EQ("ARM::Device:Startup&RteTest Startup@2.0.3", validation["id"]); EXPECT_EQ("SELECTABLE", validation["result"]); EXPECT_EQ("require RteTest:CORE", validation["conditions"][0]["expression"]); @@ -157,12 +188,14 @@ TEST_F(ProjMgrRpcTests, RpcValidateComponents) { // missing validation = responses[3]["result"]["validation"][0]; + EXPECT_EQ(responses[3]["result"]["result"], "MISSING"); EXPECT_EQ("ARM::RteTest:Check:Missing@0.9.9", validation["id"]); EXPECT_EQ("MISSING", validation["result"]); EXPECT_EQ("require RteTest:Dependency:Missing", validation["conditions"][0]["expression"]); // conflict validation = responses[4]["result"]["validation"][0]; + EXPECT_EQ(responses[4]["result"]["result"], "CONFLICT"); EXPECT_EQ("RteTest:ApiExclusive@1.0.0", validation["id"]); EXPECT_EQ("CONFLICT", validation["result"]); EXPECT_EQ("ARM::RteTest:ApiExclusive:S1", validation["aggregates"][0]); @@ -170,6 +203,7 @@ TEST_F(ProjMgrRpcTests, RpcValidateComponents) { // incompatible validation = responses[5]["result"]["validation"][0]; + EXPECT_EQ(responses[5]["result"]["result"], "INCOMPATIBLE"); EXPECT_EQ("ARM::RteTest:Check:Incompatible@0.9.9", validation["id"]); EXPECT_EQ("INCOMPATIBLE", validation["result"]); EXPECT_EQ("deny RteTest:Dependency:Incompatible_component", validation["conditions"][0]["expression"]); @@ -177,10 +211,153 @@ TEST_F(ProjMgrRpcTests, RpcValidateComponents) { // incompatible variant validation = responses[6]["result"]["validation"][0]; + EXPECT_EQ(responses[6]["result"]["result"], "INCOMPATIBLE_VARIANT"); + EXPECT_EQ("ARM::RteTest:Check:IncompatibleVariant@0.9.9", validation["id"]); + EXPECT_EQ("INCOMPATIBLE_VARIANT", validation["result"]); + EXPECT_EQ("require RteTest:Dependency:Variant&Compatible", validation["conditions"][0]["expression"]); + EXPECT_EQ("ARM::RteTest:Dependency:Variant", validation["conditions"][0]["aggregates"][0]); +} + +TEST_F(ProjMgrRpcTests, RpcResolveComponents) { + string context = "selectable+CM0"; + vector contextList = { + context + }; + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", contextList); + int id = 3; + requests += FormatRequest(3, "ValidateComponents", json({{ "context", context }})); + requests += FormatRequest(4, "Resolve", json({{ "context", context }})); + requests += FormatRequest(5, "ValidateComponents", json({{ "context", context }})); + + const auto& responses = RunRpcMethods(requests); + // selectable + auto validation = responses[2]["result"]["validation"][0]; + EXPECT_EQ("ARM::Device:Startup&RteTest Startup@2.0.3", validation["id"]); + EXPECT_EQ("SELECTABLE", validation["result"]); + EXPECT_EQ("require RteTest:CORE", validation["conditions"][0]["expression"]); + EXPECT_EQ("ARM::RteTest:CORE", validation["conditions"][0]["aggregates"][0]); + + EXPECT_TRUE(responses[3]["result"]["success"]); // components resolved + + EXPECT_EQ(responses[4]["result"]["result"], "FULFILLED"); + EXPECT_FALSE(responses[4]["result"].contains("validation")); +} + +TEST_F(ProjMgrRpcTests, RpcSelectComponent) { + string context = "selectable+CM0"; + vector contextList = { + context + }; + RpcArgs::Options opt{""}; + json param; + param["context"] = context; + param["id"] = "ARM::RteTest:CORE"; + param["count"] = 1; + param["options"] = json::object(); + + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", contextList); + int id = 3; + requests += FormatRequest(3, "ValidateComponents", json({{ "context", context }})); + requests += FormatRequest(4, "GetComponentsTree", json({{ "context", context }, {"all", false}})); + requests += FormatRequest(5, "SelectComponent", param); + requests += FormatRequest(6, "ValidateComponents", json({{ "context", context }})); + + const auto& responses = RunRpcMethods(requests); + // selectable + auto validation = responses[2]["result"]["validation"][0]; + EXPECT_EQ("ARM::Device:Startup&RteTest Startup@2.0.3", validation["id"]); + EXPECT_EQ("SELECTABLE", validation["result"]); + EXPECT_EQ("require RteTest:CORE", validation["conditions"][0]["expression"]); + EXPECT_EQ("ARM::RteTest:CORE", validation["conditions"][0]["aggregates"][0]); + + EXPECT_FALSE(responses[3]["result"]["classes"][0].contains("result")); + string res = responses[3]["result"]["classes"][2]["result"]; + EXPECT_EQ(res, "SELECTABLE"); + + EXPECT_TRUE(responses[4]["result"]["success"]); // components resolved + EXPECT_EQ(responses[5]["result"]["result"], "FULFILLED"); + EXPECT_FALSE(responses[5]["result"].contains("validation")); +} + +TEST_F(ProjMgrRpcTests, RpcSelectVariant) { + string context = "incompatible-variant+CM0"; + vector contextList = { + context + }; + json param; + param["context"] = context; + param["id"] = "ARM::RteTest:Dependency:Variant"; + param["variant"] = "Compatible"; + + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", contextList); + int id = 3; + requests += FormatRequest(3, "ValidateComponents", json({{ "context", context }})); + requests += FormatRequest(4, "SelectVariant", param); + requests += FormatRequest(5, "ValidateComponents", json({{ "context", context }})); + param["variant"] = "undefined"; + requests += FormatRequest(6, "SelectVariant", param); + + const auto& responses = RunRpcMethods(requests); + // incompatible variant + auto validation = responses[2]["result"]["validation"][0]; EXPECT_EQ("ARM::RteTest:Check:IncompatibleVariant@0.9.9", validation["id"]); EXPECT_EQ("INCOMPATIBLE_VARIANT", validation["result"]); EXPECT_EQ("require RteTest:Dependency:Variant&Compatible", validation["conditions"][0]["expression"]); EXPECT_EQ("ARM::RteTest:Dependency:Variant", validation["conditions"][0]["aggregates"][0]); + + EXPECT_TRUE(responses[3]["result"]["success"]); // variant changed + + EXPECT_EQ(responses[4]["result"]["result"], "FULFILLED"); + EXPECT_FALSE(responses[4]["result"].contains("validation")); + + EXPECT_FALSE(responses[5]["result"]["success"]); // variant not changed + EXPECT_EQ(responses[5]["result"]["message"], "Variant 'undefined' is not found for component ARM::RteTest:Dependency:Variant"); // variant not found +} + + +TEST_F(ProjMgrRpcTests, RpcGetUsedItems) { + string context = "selectable+CM0"; + vector contextList = { + context + }; + RpcArgs::Options opt{ "corelayer.yml", "@>=0.1.0", true}; + json param; + param["context"] = context; + param["id"] = "ARM::RteTest:CORE"; + param["count"] = 1; + RpcArgs::to_json(param["options"], opt); + + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", contextList); + int id = 3; + requests += FormatRequest(3, "GetUsedItems", json({{ "context", context }})); + requests += FormatRequest(4, "SelectComponent", param); + requests += FormatRequest(5, "Apply", param); + requests += FormatRequest(6, "GetUsedItems", json({{ "context", context }})); + + const auto& responses = RunRpcMethods(requests); + + 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(components[0]["id"], "Device:Startup&RteTest Startup"); + EXPECT_EQ(components[0]["resolvedComponent"]["id"], "ARM::Device:Startup&RteTest Startup@2.0.3"); + + 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(components[0]["id"], "Device:Startup&RteTest Startup"); + EXPECT_EQ(components[0]["resolvedComponent"]["id"], "ARM::Device:Startup&RteTest Startup@2.0.3"); + + EXPECT_EQ(components[1]["id"], "ARM::RteTest:CORE@>=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"]["explicitVersion"], "@>=0.1.0"); + EXPECT_TRUE(components[1]["options"]["explicitVendor"]); } + // end of ProjMgrRpcTests.cpp diff --git a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp index 2b30161b6..371f9b914 100644 --- a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp @@ -525,7 +525,7 @@ TEST_F(ProjMgrWorkerUnitTests, ProcessDependencies) { EXPECT_TRUE(SetTargetAttributes(context, context.targetAttributes)); EXPECT_TRUE(ProcessComponents(context)); EXPECT_TRUE(ProcessGpdsc(context)); - EXPECT_FALSE(ValidateContext(context)); + EXPECT_FALSE(ValidateContext(context) >= RteItem::ConditionResult::FULFILLED); ASSERT_EQ(expected.size(), context.validationResults.size()); map> dependenciesMap; for (const auto& validation : context.validationResults) {