From 29f55fd74abd66e67576d01073e9082345f7020b Mon Sep 17 00:00:00 2001 From: Evgueni Driouk Date: Tue, 3 Jun 2025 14:18:55 +0200 Subject: [PATCH] Rpc development (#1200) * [projmgr] Introduce JSON-RPC server mode * Add `GetComponentsInfo` request * Add `GetPacksInfo` and `ValidateComponents` requests * Add `GetLogMessages` requests * Add `bundles` to `GetComponentsInfo` request * GetComponentsTree request added * Make more RPC data optional * Taxonomy * Fix for cclass level Initial unit tests preparation * Only report aggregates belonging to the bundle * report selected components in the tree * Fix adding api and taxonomy to group * Select methods and use of individual contexts * Check if component exists for variant change * add flag to list components from all packs * Components from all/solution packs * Extend jrpc commands and structures. Always write active bundle * Correct GetUsedData * Add layer information consistent use of selectedCount * Add layer filed to CtAggregate Ensure value is set and propagated. Remove Component Instance from CtAggregate structure Add helper method in XmlItem class to assign an attribute from one object to another * [json-rpc] Add `RpcInterface.h` --------- Co-authored-by: Daniel Brondani --- .github/.cppcheck_suppressions | 1 + .gitmodules | 3 + external/json-rpc-cxx | 1 + libs/rtemodel/src/RteComponent.cpp | 7 +- libs/rtemodel/src/RteProject.cpp | 2 + libs/rtemodel/test/src/RteModelTest.cpp | 6 + libs/xmltree/include/XmlItem.h | 10 +- libs/xmltree/src/XmlItem.cpp | 11 +- libs/xmltree/test/src/XmlTreeTest.cpp | 13 + tools/projmgr/CMakeLists.txt | 11 +- tools/projmgr/include/ProjMgr.h | 23 +- tools/projmgr/include/ProjMgrExtGenerator.h | 4 + tools/projmgr/include/ProjMgrParser.h | 10 + tools/projmgr/include/ProjMgrRpcServer.h | 68 +++ tools/projmgr/include/ProjMgrRpcServerData.h | 44 ++ tools/projmgr/include/ProjMgrRunDebug.h | 4 + tools/projmgr/include/ProjMgrWorker.h | 92 +++- tools/projmgr/include/RpcInterface.h | 266 +++++++++++ tools/projmgr/src/ProjMgr.cpp | 97 ++-- tools/projmgr/src/ProjMgrRpcServer.cpp | 433 ++++++++++++++++++ tools/projmgr/src/ProjMgrRpcServerData.cpp | 226 +++++++++ tools/projmgr/src/ProjMgrWorker.cpp | 83 ++-- tools/projmgr/src/ProjMgrYamlParser.cpp | 1 + tools/projmgr/test/CMakeLists.txt | 2 +- .../RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf | 45 ++ .../RteTest_ARMCM0/ARMCM.dbgconf.base@0.0.2 | 45 ++ .../RteTest_ARMCM0/ac6_linker_script.sct.src | 109 +++++ .../RteTest_ARMCM0/regions_RteTest_ARMCM0.h | 101 ++++ .../RTE/_Debug_TEST_TARGET/RTE_Components.h | 20 + .../test/data/TestSolution/simple/main.c | 1 + .../project.Debug+TEST_TARGET.cbuild.yml | 76 +++ .../TestSolution/simple/project.cproject.yml | 11 + .../simple/test+TEST_TARGET.cbuild-run.yml | 102 +++++ .../TestSolution/simple/test.cbuild-idx.yml | 20 + .../TestSolution/simple/test.cbuild-pack.yml | 5 + .../TestSolution/simple/test.cbuild-set.yml | 4 + .../TestSolution/simple/test.csolution.yml | 16 + tools/projmgr/test/src/ProjMgrRpcTests.cpp | 30 ++ tools/projmgr/test/src/ProjMgrUnitTests.cpp | 24 +- .../test/src/ProjMgrWorkerUnitTests.cpp | 6 +- 40 files changed, 1950 insertions(+), 83 deletions(-) create mode 160000 external/json-rpc-cxx create mode 100644 tools/projmgr/include/ProjMgrRpcServer.h create mode 100644 tools/projmgr/include/ProjMgrRpcServerData.h create mode 100644 tools/projmgr/include/RpcInterface.h create mode 100644 tools/projmgr/src/ProjMgrRpcServer.cpp create mode 100644 tools/projmgr/src/ProjMgrRpcServerData.cpp create mode 100644 tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf create mode 100644 tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf.base@0.0.2 create mode 100644 tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ac6_linker_script.sct.src create mode 100644 tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/regions_RteTest_ARMCM0.h create mode 100644 tools/projmgr/test/data/TestSolution/simple/RTE/_Debug_TEST_TARGET/RTE_Components.h create mode 100644 tools/projmgr/test/data/TestSolution/simple/main.c create mode 100644 tools/projmgr/test/data/TestSolution/simple/project.Debug+TEST_TARGET.cbuild.yml create mode 100644 tools/projmgr/test/data/TestSolution/simple/project.cproject.yml create mode 100644 tools/projmgr/test/data/TestSolution/simple/test+TEST_TARGET.cbuild-run.yml create mode 100644 tools/projmgr/test/data/TestSolution/simple/test.cbuild-idx.yml create mode 100644 tools/projmgr/test/data/TestSolution/simple/test.cbuild-pack.yml create mode 100644 tools/projmgr/test/data/TestSolution/simple/test.cbuild-set.yml create mode 100644 tools/projmgr/test/data/TestSolution/simple/test.csolution.yml create mode 100644 tools/projmgr/test/src/ProjMgrRpcTests.cpp diff --git a/.github/.cppcheck_suppressions b/.github/.cppcheck_suppressions index 87586715c..cfb00bf2a 100644 --- a/.github/.cppcheck_suppressions +++ b/.github/.cppcheck_suppressions @@ -3,3 +3,4 @@ unusedFunction:libs/xmltree/src/XMLTree.cpp:47 unusedFunction:libs/xmlschemachecker/src/XmlErrorHandler.cpp:27 unusedFunction:libs/xmlschemachecker/src/XmlErrorHandler.cpp:32 +unusedFunction:tools/projmgr/src/ProjMgrRpcServer.cpp diff --git a/.gitmodules b/.gitmodules index 31f5b34b5..c36eb1c30 100644 --- a/.gitmodules +++ b/.gitmodules @@ -29,3 +29,6 @@ path = external/xerces-c url = https://github.com/apache/xerces-c.git ignore = dirty +[submodule "external/json-rpc-cxx"] + path = external/json-rpc-cxx + url = https://github.com/jsonrpcx/json-rpc-cxx.git diff --git a/external/json-rpc-cxx b/external/json-rpc-cxx new file mode 160000 index 000000000..a0e195b57 --- /dev/null +++ b/external/json-rpc-cxx @@ -0,0 +1 @@ +Subproject commit a0e195b575d62cb07016321ac9cd7e1b9e048fe5 diff --git a/libs/rtemodel/src/RteComponent.cpp b/libs/rtemodel/src/RteComponent.cpp index 370dc2539..a2a0d253f 100644 --- a/libs/rtemodel/src/RteComponent.cpp +++ b/libs/rtemodel/src/RteComponent.cpp @@ -20,6 +20,7 @@ #include "RteInstance.h" #include "RteProject.h" #include "RteGenerator.h" +#include "RteConstants.h" #include "XMLTree.h" @@ -608,6 +609,7 @@ void RteComponentAggregate::SetComponentInstance(RteComponentInstance* ci, int c RteItem* ei = ci->GetEffectiveItem(targetName); m_selectedVariant = ei->GetCvariantName(); m_selectedVersion = ei->GetVersionString(); + AssignAttribute("layer", *ci); if (m_components.empty()) { if (c) { @@ -1625,8 +1627,9 @@ string RteComponentGroup::GetTaxonomyDescriptionID() const string id; if (m_parent) id = m_parent->GetTaxonomyDescriptionID(); - if (!id.empty()) - id += "."; + if(!id.empty()) { + id += RteConstants::PREFIX_CGROUP; + } id += GetName(); return id; } diff --git a/libs/rtemodel/src/RteProject.cpp b/libs/rtemodel/src/RteProject.cpp index 7b432b2bb..3926dbc80 100644 --- a/libs/rtemodel/src/RteProject.cpp +++ b/libs/rtemodel/src/RteProject.cpp @@ -832,6 +832,8 @@ bool RteProject::Apply() } } ci = AddComponent(c, count, target, ci); + ci->AssignAttribute("layer", *a); + // add API if any RteApi* api = c->GetApi(target, true); if (api) { diff --git a/libs/rtemodel/test/src/RteModelTest.cpp b/libs/rtemodel/test/src/RteModelTest.cpp index 686785c43..77c832d6b 100644 --- a/libs/rtemodel/test/src/RteModelTest.cpp +++ b/libs/rtemodel/test/src/RteModelTest.cpp @@ -1127,6 +1127,12 @@ TEST_F(RteModelPrjTest, LoadCprjM4) { EXPECT_EQ(allLayerDescriptors.size(), 10); auto& filteredLayerDescriptors = activeTarget->GetFilteredModel()->GetLayerDescriptors(); EXPECT_EQ(filteredLayerDescriptors.size(), 10); + ca = activeTarget->GetComponentAggregate("ARM::Device:Startup"); + ASSERT_NE(ca, nullptr); + EXPECT_EQ(ca->GetAttribute("layer"), "LayerOne"); + ci = ca->GetComponentInstance(); + EXPECT_EQ(ci->GetAttribute("layer"), "LayerOne"); + ASSERT_NE(ci, nullptr); const string projDir = RteUtils::ExtractFilePath(RteTestM4_cprj, true); const string rteDir = projDir + "RTE/"; diff --git a/libs/xmltree/include/XmlItem.h b/libs/xmltree/include/XmlItem.h index 7a4c719e0..252cc730c 100644 --- a/libs/xmltree/include/XmlItem.h +++ b/libs/xmltree/include/XmlItem.h @@ -160,11 +160,19 @@ class XmlItem /** * @brief replace instance attributes with the given ones - * @param attributes given instance of XmlItem + * @param attributes instance of XmlItem containing attributes to assign * @return true if attributes are set */ bool SetAttributes(const XmlItem &attributes); + /** + * @brief assign specified attribute value from supplied object to this one + * @param name attribute name + * @param from instance of XmlItem to get value from + * @return true if attribute is changed + */ + bool AssignAttribute(const std::string& name, const XmlItem &from); + /** * @brief remove attribute * @param name attribute name diff --git a/libs/xmltree/src/XmlItem.cpp b/libs/xmltree/src/XmlItem.cpp index ff9e5c7a4..b195b3a57 100644 --- a/libs/xmltree/src/XmlItem.cpp +++ b/libs/xmltree/src/XmlItem.cpp @@ -62,9 +62,12 @@ bool XmlItem::AddAttribute(const string& name, const string& value, bool insertE return true; } } - if (insertEmpty || !value.empty()) + + if(insertEmpty || !value.empty()) { m_attributes[name] = value; - return true; + return true; + } + return false; } bool XmlItem::SetAttribute(const char* name, const char* value) @@ -104,6 +107,10 @@ bool XmlItem::SetAttributes(const XmlItem& attributes) return SetAttributes(attributes.GetAttributes()); } +bool XmlItem::AssignAttribute(const std::string& name, const XmlItem& from) { + return AddAttribute(name, from.GetAttribute(name), false); +} + bool XmlItem::RemoveAttribute(const std::string& name) { map::iterator it = m_attributes.find(name); diff --git a/libs/xmltree/test/src/XmlTreeTest.cpp b/libs/xmltree/test/src/XmlTreeTest.cpp index 8308cc638..62c892c92 100644 --- a/libs/xmltree/test/src/XmlTreeTest.cpp +++ b/libs/xmltree/test/src/XmlTreeTest.cpp @@ -26,6 +26,19 @@ TEST(XmlTreeTest, GetAttribute) { e.AddAttribute("zeroTen", "010"); e.AddAttribute("minusOne", "-1"); + XMLTreeElement another; + EXPECT_FALSE(another.AssignAttribute("unknown",e)); + EXPECT_FALSE(another.HasAttribute("unknown")); + EXPECT_TRUE(another.SetAttribute("unknown", "unknown")); + EXPECT_TRUE(another.AssignAttribute("unknown",e)); + EXPECT_FALSE(another.HasAttribute("unknown")); + + EXPECT_TRUE(another.AssignAttribute("ten",e)); + EXPECT_TRUE(another.HasAttribute("ten")); + EXPECT_FALSE(another.AssignAttribute("ten",e)); // already assigned + EXPECT_TRUE(another.SetAttribute("ten","010")); + EXPECT_TRUE(another.AssignAttribute("ten",e)); + EXPECT_EQ(e.GetAttribute("unknown").empty(), true); EXPECT_EQ(e.GetAttribute("string"), "strVal"); EXPECT_EQ(e.GetAttribute("empty").empty(), true); diff --git a/tools/projmgr/CMakeLists.txt b/tools/projmgr/CMakeLists.txt index 1d1a61022..bad6f8fda 100644 --- a/tools/projmgr/CMakeLists.txt +++ b/tools/projmgr/CMakeLists.txt @@ -22,12 +22,15 @@ SET(PROJMGR_SOURCE_FILES ProjMgr.cpp ProjMgrKernel.cpp ProjMgrCallback.cpp ProjMgrCbuildBase.cpp ProjMgrCbuild.cpp ProjMgrCbuildIdx.cpp ProjMgrCbuildGenIdx.cpp ProjMgrCbuildPack.cpp ProjMgrCbuildSet.cpp ProjMgrCbuildRun.cpp ProjMgrRunDebug.cpp + ProjMgrRpcServer.cpp ProjMgrRpcServerData.cpp ) SET(PROJMGR_HEADER_FILES ProjMgr.h ProjMgrKernel.h ProjMgrCallback.h ProjMgrParser.h ProjMgrWorker.h ProjMgrGenerator.h ProjMgrXmlParser.h ProjMgrYamlParser.h ProjMgrLogger.h ProjMgrYamlSchemaChecker.h ProjMgrYamlEmitter.h ProjMgrUtils.h ProjMgrExtGenerator.h ProjMgrCbuildBase.h ProjMgrRunDebug.h + ProjMgrRpcServer.h ProjMgrRpcServerData.h + RpcInterface.h ) list(TRANSFORM PROJMGR_SOURCE_FILES PREPEND src/) @@ -37,8 +40,12 @@ add_library(projmgrlib OBJECT ${PROJMGR_SOURCE_FILES} ${PROJMGR_HEADER_FILES}) target_link_libraries(projmgrlib PUBLIC CrossPlatform RteFsUtils RteUtils XmlTree XmlTreeSlim XmlReader - RteModel cxxopts yaml-cpp YmlSchemaChecker) -target_include_directories(projmgrlib PRIVATE include ${PROJECT_BINARY_DIR}) + RteModel cxxopts yaml-cpp YmlSchemaChecker +) +target_include_directories(projmgrlib PUBLIC include ${PROJECT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/external/json + ${CMAKE_SOURCE_DIR}/external/json-rpc-cxx/include +) if(SWIG_LIBS) # projmgr swig diff --git a/tools/projmgr/include/ProjMgr.h b/tools/projmgr/include/ProjMgr.h index 6dfa68935..c75a849c6 100644 --- a/tools/projmgr/include/ProjMgr.h +++ b/tools/projmgr/include/ProjMgr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2024 Arm Limited. All rights reserved. + * Copyright (c) 2020-2025 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,6 +12,7 @@ #include "ProjMgrGenerator.h" #include "ProjMgrYamlEmitter.h" #include "ProjMgrRunDebug.h" +#include "ProjMgrRpcServer.h" #include @@ -50,6 +51,18 @@ class ProjMgr { */ static int RunProjMgr(int argc, char **argv, char** envp); + /** + * @brief get worker object + * @return reference to m_worker + */ + ProjMgrWorker& GetWorker() { return m_worker; }; + + /** + * @brief load solution + * @param path to .csolution.yml file + * @return processing status + */ + bool LoadSolution(const std::string& csolution); protected: /** @@ -88,12 +101,6 @@ class ProjMgr { */ ProjMgrParser& GetParser() { return m_parser; }; - /** - * @brief get worker object - * @return reference to m_worker - */ - ProjMgrWorker& GetWorker() { return m_worker; }; - /** * @brief get generator object * @return reference to m_generator @@ -137,6 +144,7 @@ class ProjMgr { ProjMgrGenerator m_generator; ProjMgrYamlEmitter m_emitter; ProjMgrRunDebug m_runDebug; + ProjMgrRpcServer m_rpcServer; std::string m_csolutionFile; std::string m_cdefaultFile; @@ -193,6 +201,7 @@ class ProjMgr { bool GenerateYMLConfigurationFiles(bool previousResult); bool UpdateRte(); bool ParseAndValidateContexts(); + bool ProcessContexts(); }; #endif // PROJMGR_H diff --git a/tools/projmgr/include/ProjMgrExtGenerator.h b/tools/projmgr/include/ProjMgrExtGenerator.h index 3948e189a..f60055d2a 100644 --- a/tools/projmgr/include/ProjMgrExtGenerator.h +++ b/tools/projmgr/include/ProjMgrExtGenerator.h @@ -101,6 +101,10 @@ class ProjMgrExtGenerator { */ ClayerItem* GetGeneratorImport(const std::string& contextId, bool& success); + void Clear() { + m_usedGenerators.clear(); + } + protected: ProjMgrParser* m_parser = nullptr; GeneratorContextVecMap m_usedGenerators; diff --git a/tools/projmgr/include/ProjMgrParser.h b/tools/projmgr/include/ProjMgrParser.h index 8f710c60f..9b0f617a8 100644 --- a/tools/projmgr/include/ProjMgrParser.h +++ b/tools/projmgr/include/ProjMgrParser.h @@ -75,11 +75,13 @@ struct MiscItem { * pack name * pack path * type filter + * origin file */ struct PackItem { std::string pack; std::string path; TypeFilter type; + std::string origin; }; /** @@ -733,6 +735,14 @@ class ProjMgrParser { */ CbuildSetItem& GetCbuildSetItem(void); + void Clear() { + m_cdefault = {}; + m_csolution = {}; + m_cbuildSet = {}; + m_cprojects.clear(); + m_clayers.clear(); + m_genericClayers.clear(); + } /** * @brief get debug adapters * @return debug adapters list diff --git a/tools/projmgr/include/ProjMgrRpcServer.h b/tools/projmgr/include/ProjMgrRpcServer.h new file mode 100644 index 000000000..699cc0996 --- /dev/null +++ b/tools/projmgr/include/ProjMgrRpcServer.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef PROJMGRRPCSERVER_H +#define PROJMGRRPCSERVER_H + +#include +#include + +/** + * Forward declarations +*/ +class ProjMgr; + +/** + * @brief projmgr rpc server +*/ +class ProjMgrRpcServer { +public: + /** + * @brief class constructor + */ + ProjMgrRpcServer(ProjMgr& manager); + + /** + * @brief class destructor + */ + ~ProjMgrRpcServer(void); + + /** + * @brief run rpc server + * @return true if terminated successfully, otherwise false + */ + bool Run(void); + + /** + * @brief get parser object + * @return reference to m_manager + */ + ProjMgr& GetManager() { return m_manager; }; + + void SetDebug(bool debug) { m_debug = debug; } + + /** + * @brief set m_shutdown flag + * @param boolean value + */ + void SetShutdown(bool value) { m_shutdown = value; } + + /** + * @brief set m_contextLength flag + * @param boolean value + */ + void SetContentLengthHeader(bool value) { m_contextLength = value; } + +protected: + ProjMgr& m_manager; + bool m_debug = false; + bool m_shutdown = false; + bool m_contextLength = false; + const std::string GetRequestFromStdinWithLength(void); + const std::string GetRequestFromStdin(void); +}; + +#endif // PROJMGRRPCSERVER_H diff --git a/tools/projmgr/include/ProjMgrRpcServerData.h b/tools/projmgr/include/ProjMgrRpcServerData.h new file mode 100644 index 000000000..3f1075201 --- /dev/null +++ b/tools/projmgr/include/ProjMgrRpcServerData.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef PROJMGRRPCSERVERDATA_H +#define PROJMGRRPCSERVERDATA_H + +#include + +using namespace std; + +class RteTarget; +class RteBundle; +class RteComponent; +class RteComponentInstance; +class RteComponentAggregate; +class RteComponentGroup; +class RteItem; +class RpcDataCollector { +public: + RpcDataCollector(RteTarget* t); + + RteTarget* GetTarget() const { return m_target; } + + void CollectCtClasses(Args::CtRoot& ctRoot) const; + void CollectUsedItems(Args::UsedItems& usedItems) const; + + Args::Component FromRteComponent(const RteComponent* rteComponent) const; + Args::ComponentInstance FromComponentInstance(const RteComponentInstance* rteCi) const; + RteItem* GetTaxonomyItem(const RteComponentGroup* rteGroup) const; + +protected: + + void CollectCtBundles(Args::CtClass& ctClass, RteComponentGroup* rteClass) const; + void CollectCtChildren(Args::CtTreeItem& parent, RteComponentGroup* rteGroup, const string& bundleName) const; + void CollectCtAggregates(Args::CtTreeItem& parent, RteComponentGroup* rteGroup, const string& bundleName) const; + void CollectCtVariants(Args::CtAggregate& ctAggregate, RteComponentAggregate* rteAggregate) const; + +private: + RteTarget* m_target; +}; + +#endif // PROJMGRRPCSERVERDATA_ diff --git a/tools/projmgr/include/ProjMgrRunDebug.h b/tools/projmgr/include/ProjMgrRunDebug.h index e930427aa..6d3fa0666 100644 --- a/tools/projmgr/include/ProjMgrRunDebug.h +++ b/tools/projmgr/include/ProjMgrRunDebug.h @@ -205,6 +205,10 @@ class ProjMgrRunDebug { */ bool CollectSettings(const std::vector& contexts, const DebugAdaptersItem& adapters); + void Clear() { + m_runDebug = {}; + } + protected: RunDebugType m_runDebug; void GetDebugSequenceBlock(const RteItem* item, DebugSequencesBlockType& block); diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index 760d902c4..64b247f13 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -94,11 +94,13 @@ struct ToolchainItem { /** * @brief package item containing * pack information pack, - * path to pack path + * path to pack path, + * origin file, */ struct PackageItem { PackInfo pack; std::string path; + std::string origin; }; /** @@ -243,6 +245,31 @@ struct ContextTypesItem { }; /** + * @brief validation condition item containing + * expression (accept/deny/require ...) + * related aggregates +*/ +struct ValidationCondition { + std::string expression; + StrSet aggregates; +}; + +/** + * @brief validation result containing + * result according to enum ConditionResult + * component/api identifier + * direct related aggregates + * conditions (expressions and related identifiers) +*/ + +struct ValidationResult { + RteItem::ConditionResult result; + std::string id; + StrSet aggregates; + std::vector conditions; +}; + +/** * @brief gdb server item containing * port number of processor * processor name @@ -368,7 +395,7 @@ struct ContextItem { std::map>> apis; std::map bootstrapComponents; StrMap bootstrapMap; - std::vector, std::set>> validationResults; + std::vector validationResults; std::map> configFiles; std::map plmStatus; std::map> componentFiles; @@ -844,6 +871,62 @@ class ProjMgrWorker { */ bool CheckRteErrors(void); + /** + * @brief load packs + * @param context item + * @return true if there is no error + */ + bool LoadPacks(ContextItem& context); + + /** + * @brief set target attributes + * @param context item + * @param map of attributes + * @return true if there is no error + */ + bool SetTargetAttributes(ContextItem& context, std::map& attributes); + + /** + * @brief add required components + * @param context item + * @return true if there is no error + */ + bool AddRequiredComponents(ContextItem& context); + + /** + * @brief validate context + * @param context item + * @return true if there is no error + */ + bool ValidateContext(ContextItem& context); + + /** + * @brief clear worker members for reloading a solution + * @return true if there is no error + */ + void Clear() { + for (auto context : m_contexts) { + for (auto componentItem : context.second.components) { + delete componentItem.second.instance; + } + } + m_contexts.clear(); + m_ymlOrderedContexts.clear(); + m_contextsPtr->clear(); + m_contextErrMap.clear(); + m_selectedContexts.clear(); + m_outputDir.clear(); + m_selectedToolchain.clear(); + m_rootDir.clear(); + m_undefLayerVars.clear(); + m_packMetadata.clear(); + m_executes.clear(); + m_toolchainErrors.clear(); + m_selectableCompilers.clear(); + m_missingFiles.clear(); + m_types = {}; + }; + protected: ProjMgrParser* m_parser = nullptr; ProjMgrKernel* m_kernel = nullptr; @@ -886,7 +969,6 @@ class ProjMgrWorker { std::string m_activeTargetType; TargetSetItem m_activeTargetSet; - bool LoadPacks(ContextItem& context); bool CheckMissingPackRequirements(const std::string& contextName); void CheckMissingLinkerScript(ContextItem& context); bool CollectRequiredPdscFiles(ContextItem& context, const std::string& packRoot); @@ -897,7 +979,6 @@ class ProjMgrWorker { bool GetTypeContent(ContextItem& context); bool GetProjectSetup(ContextItem& context); bool InitializeTarget(ContextItem& context); - bool SetTargetAttributes(ContextItem& context, std::map& attributes); bool ProcessPrecedences(ContextItem& context, BoardOrDevice process = BoardOrDevice::None, bool rerun = false); bool ProcessPrecedence(StringCollection& item); bool ProcessCompilerPrecedence(StringCollection& item, bool acceptRedefinition = false); @@ -924,7 +1005,6 @@ class ProjMgrWorker { bool ProcessLinkerOptions(ContextItem& context, const LinkerItem& linker, const std::string& ref); bool ProcessProcessorOptions(ContextItem& context); void AddContext(ContextDesc& descriptor, const TypePair& type, ContextItem& parentContext); - bool ValidateContext(ContextItem& context); bool FormatValidationResults(std::set& results, const ContextItem& context); void UpdateMisc(std::vector& vec, const std::string& compiler); void AddMiscUniquely(MiscItem& dst, std::vector*>& srcVec); @@ -932,7 +1012,6 @@ class ProjMgrWorker { bool AddGroup(const GroupNode& src, std::vector& dst, ContextItem& context, const std::string root); bool AddFile(const FileNode& src, std::vector& dst, ContextItem& context, const std::string root); bool AddComponent(const ComponentItem& src, const std::string& layer, std::vector>& dst, TypePair type, ContextItem& context); - bool AddRequiredComponents(ContextItem& context); void GetDeviceItem(const std::string& element, DeviceItem& device) const; void GetBoardItem (const std::string& element, BoardItem& board) const; bool GetPrecedentValue(std::string& outValue, const std::string& element) const; @@ -994,6 +1073,7 @@ class ProjMgrWorker { StrVec CollectSelectableCompilers(); void ProcessTmpDir(std::string& tmpdir, const std::string& base); bool IsCreatedByExecute(const std::string file, const std::string dir); + bool CollectAllRequiredPdscFiles(); bool ParseTargetSetContextSelection(const std::string& activeTargetSet); bool GetActiveTargetSet(const std::string& activeTargetSet); }; diff --git a/tools/projmgr/include/RpcInterface.h b/tools/projmgr/include/RpcInterface.h new file mode 100644 index 000000000..62f2c3cfd --- /dev/null +++ b/tools/projmgr/include/RpcInterface.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2025 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef RPCINTERFACE_H +#define RPCINTERFACE_H + +#include +#include +#include +#include + +using namespace std; +using namespace jsonrpccxx; + +namespace Args { + + struct PackElement { + string id; + optional description; + optional doc; + }; + + // GetPacksInfo + struct Pack : public PackElement { + optional overview; + optional used; + optional> references; + }; + struct PacksInfo { + vector packs; + }; + + // GetCoponentTree + struct Component : public PackElement { + string fromPack; + optional implements; + optional maxInstances; + }; + + using Api = PackElement; + using Taxonomy = PackElement; + using Bundle = PackElement; + + // component Tree + struct CtItem { + string name; + }; + + struct ComponentInstance { + string id; + int selectedCount; // number of selected instances + optional resolvedComponent; + optional layer; + }; + + struct CtVariant : public CtItem { + vector components; // version-sorted components + }; + + struct CtAggregate : public CtItem { + string id; + optional activeVariant; + optional activeVersion; + optional selectedCount; // selection flag, represents selected number of instances + vector variants; + optional layer; + }; + + struct CtGroup; + struct CtAggregate; + struct CtTreeItem : public CtItem { + optional> groups; + optional> aggregates; + }; + + struct CtGroup : public CtTreeItem { + optional api; + optional taxonomy; + }; + + struct CtBundle : public CtTreeItem { + optional bundle; + }; + + struct CtClass : public CtItem{ + optional taxonomy; + optional activeBundle; + vector bundles; + }; + + struct CtRoot { + vector classes; + }; + + // ValidateComponents + struct Condition { + string expression; + optional> aggregates; + }; + struct Result { + string result; + string id; + optional> aggregates; + optional> conditions; + }; + struct Results { + optional> validation; + }; + + struct UsedItems { + vector components; + vector packs; + }; + + // GetLogMessages + struct LogMessages { + optional> info; + optional> errors; + optional> warnings; + }; + + // to_json converters + template void to_json(nlohmann::json& j, const string& key, const T& value) { + j[key] = value; + } + template void to_json(nlohmann::json& j, const string& key, const optional& opt) { + if (opt.has_value()) { + j[key] = opt.value(); + } + } + inline void to_json(nlohmann::json& j, const Pack& p) { + to_json(j, "id", p.id); + to_json(j, "description", p.description); + to_json(j, "overview", p.overview); + to_json(j, "used", p.used); + to_json(j, "references", p.references); + } + inline void to_json(nlohmann::json& j, const PacksInfo& info) { + to_json(j, "packs", info.packs); + } + + inline void to_json(nlohmann::json& j, const Component& c) { + to_json(j, "id", c.id); + to_json(j, "description", c.description); + to_json(j, "doc", c.doc); + to_json(j, "from-pack", c.fromPack); + to_json(j, "implements", c.implements); + to_json(j, "maxInstances", c.maxInstances); + } + inline void to_json(nlohmann::json& j, const PackElement& e) { + to_json(j, "id", e.id); + to_json(j, "description", e.description); + to_json(j, "doc", e.doc); + } + + inline void to_json(nlohmann::json& j, const ComponentInstance& ci) { + to_json(j, "id", ci.id); + to_json(j, "selectedCount", ci.selectedCount); + to_json(j, "layer", ci.layer); + to_json(j, "resolvedComponent", ci.resolvedComponent); + } + + inline void to_json(nlohmann::json& j, const Condition& c) { + to_json(j, "expression", c.expression); + to_json(j, "aggregates", c.aggregates); + } + inline void to_json(nlohmann::json& j, const Result& r) { + to_json(j, "result", r.result); + to_json(j, "id", r.id); + to_json(j, "aggregates", r.aggregates); + to_json(j, "conditions", r.conditions); + } + inline void to_json(nlohmann::json& j, const Results& r) { + to_json(j, "validation", r.validation); + } + + inline void to_json(nlohmann::json& j, const LogMessages& m) { + to_json(j, "info", m.info); + to_json(j, "errors", m.errors); + to_json(j, "warnings", m.warnings); + } + + inline void to_json(nlohmann::json& j, const CtVariant& v) { + to_json(j, "name", v.name); + to_json(j, "components", v.components); + } + + inline void to_json(nlohmann::json& j, const CtAggregate& a) { + to_json(j, "name", a.name); + to_json(j, "id", a.id); + to_json(j, "selectedCount", a.selectedCount); + to_json(j, "activeVariant", a.activeVariant); + to_json(j, "activeVersion", a.activeVersion); + to_json(j, "layer", a.layer); + to_json(j, "variants", a.variants); + } + + inline void to_json(nlohmann::json& j, const CtGroup& g) { + to_json(j, "name", g.name); + to_json(j, "api", g.api); + to_json(j, "taxonomy", g.taxonomy); + to_json(j, "groups", g.groups); + to_json(j, "aggregates", g.aggregates); + } + + inline void to_json(nlohmann::json& j, const CtBundle& b) { + to_json(j, "name", b.name); + to_json(j, "bundle", b.bundle); + to_json(j, "groups", b.groups); + to_json(j, "aggregates", b.aggregates); + } + + inline void to_json(nlohmann::json& j, const CtClass& c) { + to_json(j, "name", c.name); + to_json(j, "activeBundle", c.activeBundle); + to_json(j, "bundles", c.bundles); + to_json(j, "taxonomy", c.taxonomy); + } + + inline void to_json(nlohmann::json& j, const UsedItems& u) { + to_json(j, "components", u.components); + to_json(j, "packs", u.packs); + } + + inline void to_json(nlohmann::json& j, const CtRoot& r) { + to_json(j, "classes", r.classes); + } +} + +class RpcMethods { +public: + RpcMethods(JsonRpc2Server& jsonServer) { + jsonServer.Add("GetVersion", GetHandle(&RpcMethods::GetVersion, *this)); + jsonServer.Add("Shutdown", GetHandle(&RpcMethods::Shutdown, *this)); + jsonServer.Add("Apply", GetHandle(&RpcMethods::Apply, *this), { "context" }); + jsonServer.Add("LoadPacks", GetHandle(&RpcMethods::LoadPacks, *this)); + jsonServer.Add("LoadSolution", GetHandle(&RpcMethods::LoadSolution, *this), { "solution" }); + jsonServer.Add("GetPacksInfo", GetHandle(&RpcMethods::GetPacksInfo, *this), { "context" }); + jsonServer.Add("GetUsedItems", GetHandle(&RpcMethods::GetUsedItems, *this), { "context" }); + jsonServer.Add("GetComponentsTree", GetHandle(&RpcMethods::GetComponentsTree, *this), { "context", "all" }); + jsonServer.Add("SelectComponent", GetHandle(&RpcMethods::SelectComponent, *this), { "context", "id", "count" }); + jsonServer.Add("SelectVariant", GetHandle(&RpcMethods::SelectVariant, *this), { "context", "id", "variant" }); + jsonServer.Add("SelectVersion", GetHandle(&RpcMethods::SelectVersion, *this), { "context", "id", "version" }); + jsonServer.Add("SelectBundle", GetHandle(&RpcMethods::SelectBundle, *this), { "context", "class", "bundle" }); + jsonServer.Add("ValidateComponents", GetHandle(&RpcMethods::ValidateComponents, *this), { "context" }); + jsonServer.Add("GetLogMessages", GetHandle(&RpcMethods::GetLogMessages, *this)); + } + virtual const string GetVersion(void) { return string(); } + virtual const bool Shutdown(void) { return bool(); } + virtual const bool Apply(const string& context) { return bool(); } + virtual const bool LoadPacks(void) { return bool(); } + virtual const bool LoadSolution(const string& solution) { return bool(); } + virtual const Args::UsedItems GetUsedItems(const string& context) { return Args::UsedItems(); } + virtual const Args::PacksInfo GetPacksInfo(const string& context) { return Args::PacksInfo(); } + virtual const Args::CtRoot GetComponentsTree(const string& context, bool all) { return Args::CtRoot(); } + virtual const bool SelectComponent(const string& context, const string& aggregateId, int count) { return bool(); } + virtual const bool SelectVariant(const string& context, const string& aggregateId, const string& variantName) { return bool(); } + virtual const bool SelectVersion(const string& context, const string& aggregateId, const string& version) { return bool(); } + virtual const bool SelectBundle(const string& context, const string& className, const string& bundleName) { return bool(); } + virtual const Args::Results ValidateComponents(const string& context) { return Args::Results(); } + virtual const Args::LogMessages GetLogMessages(void) { return Args::LogMessages(); } +}; + +#endif // RPCINTERFACE_H diff --git a/tools/projmgr/src/ProjMgr.cpp b/tools/projmgr/src/ProjMgr.cpp index fae0e3fb5..d170b19c4 100644 --- a/tools/projmgr/src/ProjMgr.cpp +++ b/tools/projmgr/src/ProjMgr.cpp @@ -36,6 +36,7 @@ Commands:\n\ list target-sets Print list of target-sets in a .csolution.yml\n\ list toolchains Print list of supported toolchains\n\ run Run code generator\n\ + rpc Run remote procedure call server\n\ update-rte Create/update configuration files and validate solution\n\n\ Options:\n\ -a, --active arg Select active target-set: [@]\n\ @@ -65,6 +66,7 @@ ProjMgr::ProjMgr() : m_extGenerator(&m_parser), m_worker(&m_parser, &m_extGenerator), m_emitter(&m_parser, &m_worker), + m_rpcServer(*this), m_checkSchema(false), m_missingPacks(false), m_updateRteFiles(true), @@ -164,6 +166,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) { cxxopts::Option updateIdx("update-idx", "Update cbuild-idx file with layer info", cxxopts::value()->default_value("false")); cxxopts::Option quiet("q,quiet", "Run silently, printing only error messages", cxxopts::value()->default_value("false")); cxxopts::Option cbuildgen("cbuildgen", "Generate legacy *.cprj files", cxxopts::value()->default_value("false")); + cxxopts::Option contentLength("content-length", "Prepend 'Content-Length' header to JSON RPC requests and responses", cxxopts::value()->default_value("false")); cxxopts::Option activeTargetSet("a,active", "Select active target-set: [@]", cxxopts::value()); // command options dictionary @@ -184,6 +187,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) { {"list layers", { false, {context, contextSet, activeTargetSet, debug, load, clayerSearchPath, quiet, schemaCheck, toolchain, verbose, updateIdx}}}, {"list toolchains", { false, {context, contextSet, activeTargetSet, debug, quiet, toolchain, verbose}}}, {"list environment", { true, {}}}, + {"rpc", { true, {contentLength}}}, }; try { @@ -192,7 +196,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) { solution, context, contextSet, filter, generator, load, clayerSearchPath, missing, schemaCheck, noUpdateRte, output, outputAlt, help, version, verbose, debug, dryRun, exportSuffix, toolchain, ymlOrder, - relativePaths, frozenPacks, updateIdx, quiet, cbuildgen, activeTargetSet + relativePaths, frozenPacks, updateIdx, quiet, cbuildgen, contentLength, activeTargetSet }); options.parse_positional({ "positional" }); @@ -219,6 +223,8 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) { m_cbuildgen = parseResult.count("cbuildgen"); m_worker.SetCbuild2Cmake(!m_cbuildgen); ProjMgrLogger::m_quiet = parseResult.count("quiet"); + m_rpcServer.SetContentLengthHeader(parseResult.count("content-length")); + m_rpcServer.SetDebug(m_debug); vector positionalArguments; if (parseResult.count("positional")) { @@ -415,6 +421,12 @@ int ProjMgr::ProcessCommands() { if (!RunCodeGenerator()) { return ErrorCode::ERROR; } + } else if (m_command == "rpc") { + // Launch 'rpc' server over stdin/stdout + ProjMgrLogger::m_silent = true; + if (!m_rpcServer.Run()) { + return ErrorCode::ERROR; + } } else { ProjMgrLogger::Get().Error(" was not found"); return ErrorCode::ERROR; @@ -607,31 +619,8 @@ bool ProjMgr::Configure() { ProjMgrLogger::Get().Error(errMsg); } - // Get context pointers - map* contexts = nullptr; - m_worker.GetContexts(contexts); - - vector orderedContexts; - m_worker.GetYmlOrderedContexts(orderedContexts); - // Process contexts - bool error = false; - m_allContexts.clear(); - m_processedContexts.clear(); - m_failedContext.clear(); - for (auto& contextName : orderedContexts) { - auto& contextItem = (*contexts)[contextName]; - m_allContexts.push_back(&contextItem); - if (!m_worker.IsContextSelected(contextName)) { - continue; - } - if (!m_worker.ProcessContext(contextItem, true, true, false)) { - ProjMgrLogger::Get().Error("processing context '" + contextName + "' failed", contextName); - m_failedContext.insert(contextItem.name); - error = true; - } - m_processedContexts.push_back(&contextItem); - } + bool error = !ProcessContexts(); if (m_worker.HasToolchainErrors()) { error = true; @@ -672,6 +661,35 @@ bool ProjMgr::Configure() { return !error; } +bool ProjMgr::ProcessContexts() { + // Get context pointers + map* contexts = nullptr; + m_worker.GetContexts(contexts); + + vector orderedContexts; + m_worker.GetYmlOrderedContexts(orderedContexts); + + // Process contexts + bool success = true; + m_allContexts.clear(); + m_processedContexts.clear(); + m_failedContext.clear(); + for (auto& contextName : orderedContexts) { + auto& contextItem = (*contexts)[contextName]; + m_allContexts.push_back(&contextItem); + if (!m_worker.IsContextSelected(contextName)) { + continue; + } + if (!m_worker.ProcessContext(contextItem, true, true, false)) { + ProjMgrLogger::Get().Error("processing context '" + contextName + "' failed", contextName); + m_failedContext.insert(contextItem.name); + success = false; + } + m_processedContexts.push_back(&contextItem); + } + return success; +} + bool ProjMgr::UpdateRte() { // Update the RTE files for (auto& contextItem : m_processedContexts) { @@ -1151,6 +1169,33 @@ const string ProjMgr::GetToolboxVersion(const string& toolboxDir) { return matchResult[1].str(); } +bool ProjMgr::LoadSolution(const std::string& csolution) { + if (!m_csolutionFile.empty()) { + m_parser.Clear(); + m_extGenerator.Clear(); + m_worker.Clear(); + m_runDebug.Clear(); + ProjMgrLogger::Get().Clear(); + } + + m_csolutionFile = csolution; + m_rootDir = RteUtils::ExtractFilePath(m_csolutionFile, false); + + m_contextSet = true; + m_updateRteFiles = false; + + if (!PopulateContexts()) { + return false; + } + if (!ParseAndValidateContexts()) { + return false; + } + if (!ProcessContexts()) { + return false; + } + return true; +} + const string ProjMgr::GetDebugAdaptersFile(void) { error_code ec; const string exePath = RteUtils::ExtractFilePath(CrossPlatformUtils::GetExecutablePath(ec), true); @@ -1159,4 +1204,4 @@ const string ProjMgr::GetDebugAdaptersFile(void) { return debugAdapterFile; } return RteUtils::EMPTY_STRING; -} +} \ No newline at end of file diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp new file mode 100644 index 000000000..2016196a1 --- /dev/null +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2025 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ProjMgrRpcServer.h" +#include "ProjMgrRpcServerData.h" +#include "ProjMgrLogger.h" +#include "ProjMgr.h" +#include "ProductInfo.h" +#include + +#include +#include +#include + +using namespace std; +using namespace jsonrpccxx; + +static constexpr const char* CONTENT_LENGTH_HEADER = "Content-Length:"; + +ProjMgrRpcServer::ProjMgrRpcServer(ProjMgr& manager) : + m_manager(manager) { +} + +ProjMgrRpcServer::~ProjMgrRpcServer(void) { + // Reserved +} + +const string ProjMgrRpcServer::GetRequestFromStdinWithLength(void) { + string line; + int contentLength = 0; + const string& header = CONTENT_LENGTH_HEADER; + while (getline(cin, line) && !line.empty() && !cin.fail()) { + if (line.find(header) == 0) { + contentLength = RteUtils::StringToInt(line.substr(header.length()), 0); + } + } + string request(contentLength, '\0'); + cin.read(&request[0], contentLength); + return request; +} + +const string ProjMgrRpcServer::GetRequestFromStdin(void) { + string jsonData; + int braces = 0; + bool inJson = false; + char c; + while (cin.get(c) && !cin.fail()) { + if (c == '{') { + braces++; + inJson = true; + } + if (c == '}') { + braces--; + } + if (inJson) { + jsonData += c; + } + if (inJson && braces == 0) { + break; + } + } + return jsonData; +} + +class RpcHandler : public RpcMethods { +public: + RpcHandler(ProjMgrRpcServer& server, JsonRpc2Server& jsonServer) : RpcMethods(jsonServer), + m_server(server), + m_manager(server.GetManager()), + m_worker(server.GetManager().GetWorker()) {} + + const string GetVersion(void); + const bool Shutdown(void); + const bool Apply(const string& context); + const bool LoadPacks(void); + const bool LoadSolution(const string& solution); + const Args::UsedItems GetUsedItems(const string& context); + const Args::PacksInfo GetPacksInfo(const string& context); + const Args::CtRoot GetComponentsTree(const string& context, bool all); + const bool SelectComponent(const string& context, const string& aggregateId, int count); + const bool SelectVariant(const string& context, const string& aggregateId, const string& variantName); + const bool SelectVersion(const string& context, const string& aggregateId, const string& version); + const bool SelectBundle(const string& context, const string& className, const string& bundleName); + const Args::Results ValidateComponents(const string& context); + const Args::LogMessages GetLogMessages(void); + +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, + COMPONENT_NOT_RESOLVED = -7, + PACKS_NOT_LOADED = -8, + PACKS_LOADING_FAIL = -9, + RTE_MODEL_ERROR = -10, + }; + + ProjMgrRpcServer& m_server; + ProjMgr& m_manager; + ProjMgrWorker& m_worker; + ContextItem m_globalContext; + bool m_packsLoaded = false; + bool m_solutionLoaded = false; + const ContextItem& GetContext(const string& context) const; + RteTarget* GetActiveTarget(const string& context) const; + RteComponentAggregate* GetComponentAggregate(const string& context, const string& id) const; + const bool SelectVariantOrVersion(const string& context, const string& id, const string& value, bool bVariant); +}; + +bool ProjMgrRpcServer::Run(void) { + JsonRpc2Server jsonServer; + RpcHandler handler(*this, jsonServer); + + while (!m_shutdown && !cin.fail()) { + // Get request + const auto request = m_contextLength ? + GetRequestFromStdinWithLength() : + GetRequestFromStdin(); + + if (request.empty()) { + continue; + } + + ofstream log; + if (m_debug) { + log.open(RteFsUtils::GetCurrentFolder(true) + "csolution-rpc-log.txt", fstream::app); + log << request << std::endl; + } + + // Handle request + const auto response = jsonServer.HandleRequest(request); + + // Send response + if (m_contextLength) { + cout << CONTENT_LENGTH_HEADER << response.size() << std::endl << std::endl; + } + cout << response << std::flush; + + if (m_debug) { + log << response << std::endl; + log.close(); + } + + } + return true; +} + +const ContextItem& RpcHandler::GetContext(const string& context) const { + if (!m_solutionLoaded) { + throw JsonRpcException(SOLUTION_NOT_LOADED, "a valid solution must be loaded before proceeding"); + } + 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()) { + throw JsonRpcException(CONTEXT_NOT_FOUND, context + " was not found among selected contexts"); + } + map* contexts = nullptr; + m_worker.GetContexts(contexts); + return (*contexts)[context]; +} + +RteTarget* RpcHandler::GetActiveTarget(const string& context) const { + const auto& contextItem = GetContext(context); + return contextItem.rteActiveTarget; +} + + +RteComponentAggregate* RpcHandler::GetComponentAggregate(const string& context, const string& id) const { + auto rteAggregate = GetContext(context).rteActiveTarget->GetComponentAggregate(id); + if(!rteAggregate) { + throw JsonRpcException(COMPONENT_NOT_FOUND, id + ": component not found"); + } + return rteAggregate; +} + +const string RpcHandler::GetVersion(void) { + return VERSION_STRING; +} + +const bool RpcHandler::Shutdown(void) { + m_server.SetShutdown(true); + return true; +} + +const bool RpcHandler::Apply(const string& context) { + + auto rteProject = GetActiveTarget(context)->GetProject(); + if(rteProject) { + return rteProject->Apply(); + } + return false; +} + +const Args::UsedItems RpcHandler::GetUsedItems(const string& context) { + + Args::UsedItems usedItems; + RpcDataCollector dc(GetActiveTarget(context)); + dc.CollectUsedItems(usedItems); + return usedItems; +} + +const bool RpcHandler::LoadPacks(void) { + m_packsLoaded = m_worker.LoadPacks(m_globalContext); + if (!m_packsLoaded) { + throw JsonRpcException(PACKS_LOADING_FAIL, "packs failed to load"); + } + return true; +} + +const bool RpcHandler::LoadSolution(const string& solution) { + if (!m_packsLoaded) { + throw JsonRpcException(PACKS_NOT_LOADED, "packs must be loaded before proceeding"); + } + 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"); + } + m_solutionLoaded = m_manager.LoadSolution(csolutionFile); + if (!m_solutionLoaded) { + throw JsonRpcException(SOLUTION_NOT_VALID, "failed to load and process solution " + csolutionFile); + } + return true; +} + +const Args::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); + } + } + + Args::PacksInfo packsInfo; + for (auto& [pack, packItem] : m_globalContext.rteActiveTarget->GetFilteredModel()->GetPackages()) { + Args::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); + } + + return packsInfo; +} + +const Args::CtRoot RpcHandler::GetComponentsTree(const string& context, bool all) { + Args::CtRoot ctRoot; + RteTarget* rteTarget = GetActiveTarget(context); + // store selected components, not aggregates: they will be destroyed + map selectedComponents; + auto& selectedAggregates = rteTarget->CollectSelectedComponentAggregates(); + for(auto [aggregate, count] : selectedAggregates) { + RteComponent* c = aggregate->GetComponent(); + if(c) { + // consider only components, instances are added from project anyway + selectedComponents[c] = count; + } + } + RtePackageFilter packFilter; + if(!all) { + // construct and apply filter + auto& contextItem = GetContext(context); + // use pack ID's from context + set packIds; + for(auto [id, pack] : contextItem.packages) { + packIds.insert(id); + } + packFilter.SetSelectedPackages(packIds); + } + // only update filter if differs from current state + if(!packFilter.IsEqual(rteTarget->GetPackageFilter())) { + rteTarget->SetPackageFilter(packFilter); + rteTarget->UpdateFilterModel(); // updates available components + rteTarget->GetProject()->UpdateModel(); // inserts already instantiated components + // restore selection + for(auto [c, count] : selectedComponents) { + rteTarget->SelectComponent(c, count, false, false); + } + rteTarget->EvaluateComponentDependencies(); + } + + RpcDataCollector dc(rteTarget); + dc.CollectCtClasses(ctRoot); + return ctRoot; +} + +const bool RpcHandler::SelectComponent(const string& context, const string& id, int count) { +// first try full component ID + RteComponent* rteComponent = GetContext(context).rteActiveTarget->GetComponent(id); + if(rteComponent) { + return GetActiveTarget(context)->SelectComponent(rteComponent, count, true); + } + return GetActiveTarget(context)->SelectComponent(GetComponentAggregate(context, id), count, true); +} + +const bool RpcHandler::SelectVariant(const string& context, const string& id, const string& variant) { + return SelectVariantOrVersion(context, id, variant, true); +} + +const bool RpcHandler::SelectVersion(const string& context, const string& id, const string& version) { + return SelectVariantOrVersion(context, id, version, false); +} + +const bool RpcHandler::SelectVariantOrVersion(const string& context, const string& id, const string& value, bool bVariant) { + RteComponentAggregate* rteAggregate = GetComponentAggregate(context, id); + + auto& selectedValue = bVariant ? rteAggregate->GetSelectedVariant() : rteAggregate->GetSelectedVersion(); + if(selectedValue == value) { + return false; + } + + if(bVariant || !value.empty()) { + auto availableValues = bVariant ? rteAggregate->GetVariants() : rteAggregate->GetVersions(rteAggregate->GetSelectedVariant()); + if(std::find(availableValues.begin(), availableValues.end(), value) == availableValues.end()) { + return false; + } + } + if(bVariant) { + rteAggregate->SetSelectedVariant(value); + } else { + rteAggregate->SetSelectedVersion(value); + } + if(rteAggregate->IsSelected()) { + GetActiveTarget(context)->EvaluateComponentDependencies(); + } + return true; +} + + + +const bool RpcHandler::SelectBundle(const string& context, const string& className, const string& bundleName) { + RteTarget* rteTarget = GetActiveTarget(context); + RteComponentClass* rteClass = rteTarget->GetComponentClass(className); + if(!rteClass) { + throw JsonRpcException(COMPONENT_NOT_FOUND, className + ": component class not found"); + } + rteClass->SetSelectedBundleName(bundleName, true); + GetActiveTarget(context)->EvaluateComponentDependencies(); + return true; +} + + +const Args::Results RpcHandler::ValidateComponents(const string& context) { + auto contextItem = GetContext(context); + + if (!m_worker.CheckRteErrors()) { + throw JsonRpcException(RTE_MODEL_ERROR, "rte model reported error"); + } + + Args::Results results; + if (!m_worker.ValidateContext(contextItem)) { + results.validation = vector{}; + for (const auto& validation : contextItem.validationResults) { + Args::Result r; + r.result = RteItem::ConditionResultToString(validation.result); + r.id = validation.id; + if (!validation.aggregates.empty()) { + r.aggregates = vector(validation.aggregates.begin(), validation.aggregates.end()); + } + if (!validation.conditions.empty()) { + Args::Condition c; + r.conditions = vector{}; + for (const auto& condition : validation.conditions) { + c.expression = condition.expression; + if (!condition.aggregates.empty()) { + c.aggregates = vector(condition.aggregates.begin(), condition.aggregates.end()); + } + r.conditions->push_back(c); + } + } + results.validation->push_back(r); + } + } + return results; +} + +const Args::LogMessages RpcHandler::GetLogMessages(void) { + StrVec infoVec; + 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) { + CollectionUtils::PushBackUniquely(errorsVec, msg); + } + } + StrVec warningsVec; + for (const auto& [_, warnings] : ProjMgrLogger::Get().GetWarns()) { + for (const auto& msg : warnings) { + CollectionUtils::PushBackUniquely(warningsVec, msg); + } + } + Args::LogMessages messages; + if (!infoVec.empty()) { + messages.info = infoVec; + } + if (!errorsVec.empty()) { + messages.errors = errorsVec; + } + if (!warningsVec.empty()) { + messages.warnings = warningsVec; + } + return messages; +} + +// end of ProkMgrRpcServer.cpp diff --git a/tools/projmgr/src/ProjMgrRpcServerData.cpp b/tools/projmgr/src/ProjMgrRpcServerData.cpp new file mode 100644 index 000000000..a1238234b --- /dev/null +++ b/tools/projmgr/src/ProjMgrRpcServerData.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2025 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ProjMgrRpcServerData.h" + +#include "RteComponent.h" +#include "RteInstance.h" +#include "RtePackage.h" +#include "RteProject.h" +#include "RteTarget.h" +#include "RteModel.h" + +#include "CollectionUtils.h" + +using namespace Args; + +template +T FromRteItem(const string& id, RteItem* rteItem) { + T e; + e.id = id; + const auto& description = rteItem->GetDescription(); + if(!description.empty()) { + e.description = description; + } + const auto& doc = rteItem->GetDocFile(); + if(!doc.empty()) { + e.doc = doc; + } + return e; +} + +RpcDataCollector::RpcDataCollector(RteTarget* t) : + m_target(t) +{ +} + +Args::Component RpcDataCollector::FromRteComponent(const RteComponent* rteComponent) const{ + Args::Component c; + c.id = rteComponent->GetComponentID(true); + c.fromPack = rteComponent->GetPackageID(true); + const auto& description = rteComponent->GetDescription(); + if (!description.empty()) { + c.description = description; + } + const auto& doc = rteComponent->GetDocFile(); + if (!doc.empty()) { + c.doc = doc; + } + const auto& api = rteComponent->GetApi(m_target, true); + if (api) { + c.implements = api->ConstructComponentID(true); + } + if (rteComponent->HasMaxInstances()) { + c.maxInstances = rteComponent->GetMaxInstances(); + } + return c; +} + +Args::ComponentInstance RpcDataCollector::FromComponentInstance(const RteComponentInstance* rteCi) const { + ComponentInstance ci; + if(rteCi) { + ci.id = rteCi->GetDisplayName(); + 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); + } + } + return ci; +} + + +RteItem* RpcDataCollector::GetTaxonomyItem(const RteComponentGroup* rteGroup) const { + if(m_target && rteGroup) { + auto taxonimyId = rteGroup->GetTaxonomyDescriptionID(); + return m_target->GetFilteredModel()->GetTaxonomyItem(taxonimyId); + } + return nullptr; +} + +void RpcDataCollector::CollectUsedItems(Args::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)); + } + RtePackageMap packs; + rteProject->GetUsedPacks(packs, m_target->GetName()); + for(auto [id, rtePack] : packs) { + Pack p; + p.id = id; + usedItems.packs.push_back(p); + } +} + + +void RpcDataCollector::CollectCtClasses(Args::CtRoot& root) const { + + auto classContainer = m_target ? m_target->GetClasses() : nullptr; + if(!classContainer) { + return; // can happen if no solution is loaded + } + + for(auto [name, rteClass] : classContainer->GetGroups()) { + Args::CtClass ctClass; + ctClass.name = name; + auto activeBundle = rteClass->GetSelectedBundleName(); + ctClass.activeBundle = activeBundle; + auto taxonomyItem = GetTaxonomyItem(rteClass); + if(taxonomyItem) { + ctClass.taxonomy = FromRteItem(taxonomyItem->GetTaxonomyDescriptionID(), taxonomyItem); + } + CollectCtBundles(ctClass, rteClass); + root.classes.push_back(ctClass); + } +} + +void RpcDataCollector::CollectCtBundles(Args::CtClass& ctClass, RteComponentGroup* rteClass) const { + for(auto [bundleName, bundleId] : rteClass->GetBundleNames()) { + m_target->GetFilteredBundles(); + CtBundle ctBundle; + ctBundle.name = bundleName; + RteBundle* rteBundle = get_or_null(GetTarget()->GetFilteredBundles(), bundleId); + if(rteBundle) { + ctBundle.bundle = FromRteItem(bundleName, rteBundle); + } + CollectCtChildren(ctBundle, rteClass, bundleName); + // add to parent collection + ctClass.bundles.push_back(ctBundle); + } +} + +void RpcDataCollector::CollectCtChildren(Args::CtTreeItem& parent, RteComponentGroup* parentRteGroup, const string& bundleName) const +{ + // collect aggregates to this level + CollectCtAggregates(parent, parentRteGroup, bundleName); + auto& rteGroups = parentRteGroup->GetGroups(); + if(rteGroups.empty()) { + return; + } + vector groups; + for(auto [name, rteGroup] : rteGroups) { + if(!rteGroup->HasBundleName(bundleName)) { + continue; + } + CtGroup g; + g.name = name; + auto rteApi = rteGroup->GetApi(); + if(rteApi) { + g.api = FromRteItem(rteApi->GetID(), rteApi); + } + auto taxonomyItem = GetTaxonomyItem(rteGroup); + if(taxonomyItem) { + g.taxonomy = FromRteItem(taxonomyItem->GetTaxonomyDescriptionID(), taxonomyItem); + } + // subgroups + CollectCtChildren(g, rteGroup, bundleName); + CollectCtAggregates(g, rteGroup,bundleName); + // add to parent collection + groups.push_back(g); + } + parent.groups = groups; +} + +void RpcDataCollector::CollectCtAggregates(Args::CtTreeItem& parent, RteComponentGroup* parentRteGroup, const string& bundleName) const +{ + // aggregates + vector aggregates; + for(auto child : parentRteGroup->GetChildren()) { + RteComponentAggregate* rteAggregate = dynamic_cast(child); + if(!rteAggregate || rteAggregate->GetCbundleName() != bundleName ) { + continue; + } + CtAggregate a; + a.name = rteAggregate->GetDisplayName(); + a.id = rteAggregate->GetID(); + a.activeVersion = rteAggregate->GetEffectiveVersion(); + auto& activeVariant = rteAggregate->GetSelectedVariant(); + if(!activeVariant.empty()) { + a.activeVariant = rteAggregate->GetSelectedVariant(); + } + auto selectedCount = rteAggregate->IsSelected(); + if(selectedCount) { + a.selectedCount = selectedCount; + } + + auto layer = rteAggregate->GetAttribute("layer"); + if(!layer.empty()) { + a.layer = layer; + } + + CollectCtVariants(a, rteAggregate); + aggregates.push_back(a); + } + if(!aggregates.empty()) { + parent.aggregates = aggregates; + } +} + +void RpcDataCollector::CollectCtVariants(Args::CtAggregate& ctAggregate, RteComponentAggregate* rteAggregate) const +{ + for(auto& variantName : rteAggregate->GetVariants()) { + CtVariant v; + v.name = variantName; + auto components = rteAggregate->GetComponentVersions(variantName); + if(components) { + for(auto [version, rteComponent] : *components) { + v.components.push_back(FromRteComponent(rteComponent)); + } + } + ctAggregate.variants.push_back(v); + } +} + +// end of ProjMgrRpcServerData.cpp diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 4698f12c8..dd34c60e6 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -414,9 +414,8 @@ bool ProjMgrWorker::InitializeModel() { return m_kernel->Init(); } -bool ProjMgrWorker::LoadAllRelevantPacks() { - // Get required pdsc files - std::list pdscFiles; +bool ProjMgrWorker::CollectAllRequiredPdscFiles() { + // Check and collect requirements if (m_selectedContexts.empty()) { for (const auto& [context,_] : m_contexts) { m_selectedContexts.push_back(context); @@ -430,6 +429,15 @@ bool ProjMgrWorker::LoadAllRelevantPacks() { success &= false; continue; } + } + return success; +} + +bool ProjMgrWorker::LoadAllRelevantPacks() { + // Get required pdsc files + std::list pdscFiles; + for (const auto& context : m_selectedContexts) { + auto& contextItem = m_contexts.at(context); for (const auto& [pdscFile, pathVer] : contextItem.pdscFiles) { const string& path = pathVer.first; if (!path.empty()) { @@ -444,9 +452,6 @@ bool ProjMgrWorker::LoadAllRelevantPacks() { } } } - if (!success) { - return false; - } // Check load packs policy if (pdscFiles.empty() && (m_loadPacksPolicy == LoadPacksPolicy::REQUIRED)) { ProjMgrLogger::Get().Error("required packs must be specified"); @@ -488,10 +493,13 @@ bool ProjMgrWorker::LoadPacks(ContextItem& context) { if (!InitializeTarget(context)) { return false; } - if (m_loadedPacks.empty() && !LoadAllRelevantPacks()) { + if (!CollectAllRequiredPdscFiles()) { PrintContextErrors(context.name); return false; } + if (m_loadedPacks.empty() && !LoadAllRelevantPacks()) { + return false; + } // Filter context specific packs set selectedPacks; const bool allOrLatest = (m_loadPacksPolicy == LoadPacksPolicy::ALL) || (m_loadPacksPolicy == LoadPacksPolicy::LATEST); @@ -1628,6 +1636,7 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vectordirectory + "/"); if (!RteFsUtils::Exists(package.path)) { @@ -1682,6 +1693,7 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vectordirectory, context.cproject->directory, ec).append("RTE").generic_string(); matchedComponentInstance->AddAttribute("rtedir", rteDir); + matchedComponentInstance->AddAttribute("layer", layer); } // Get generator @@ -2553,21 +2566,26 @@ bool ProjMgrWorker::ValidateContext(ContextItem& context) { map results; context.rteActiveTarget->GetDepsResult(results, context.rteActiveTarget); - for (const auto& [component, result] : results) { - RteItem::ConditionResult validationResult = result.GetResult(); - const auto& componentID = component->GetComponentID(true); - const auto& depResults = result.GetResults(); - const auto& aggregates = result.GetComponentAggregates(); + for (const auto& [item, result] : results) { + ValidationResult validation; + validation.result = result.GetResult(); + validation.id = item->ConstructComponentID(true); - set aggregatesSet; - for (const auto& aggregate : aggregates) { - aggregatesSet.insert(aggregate->GetComponentAggregateID()); + for (const auto& aggregate : result.GetComponentAggregates()) { + validation.aggregates.insert(aggregate->ConstructComponentID(true)); } - set expressionsSet; - for (const auto& [item, _] : depResults) { - expressionsSet.insert(item->GetDependencyExpressionID()); + + const auto& depResults = result.GetResults(); + for (const auto& [item, result] : depResults) { + ValidationCondition condition; + condition.expression = item->GetDependencyExpressionID(); + for (const auto& aggregate : result.GetComponentAggregates()) { + condition.aggregates.insert(aggregate->ConstructComponentID(true)); + } + validation.conditions.push_back(condition); } - context.validationResults.push_back({ validationResult, componentID, expressionsSet, aggregatesSet }); + + context.validationResults.push_back(validation); } if (context.validationResults.empty()) { @@ -4055,10 +4073,10 @@ bool ProjMgrWorker::ListDependencies(vector& dependencies, const string& return false; } if (!ValidateContext(context)) { - for (const auto& [result, component, expressions, _] : context.validationResults) { - if ((result == RteItem::MISSING) || (result == RteItem::SELECTABLE)) { - for (const auto& expression : expressions) { - dependenciesSet.insert(component + " " + expression); + for (const auto& validation : context.validationResults) { + if ((validation.result == RteItem::MISSING) || (validation.result == RteItem::SELECTABLE)) { + for (const auto& condition : validation.conditions) { + dependenciesSet.insert(validation.id + " " + condition.expression); } } } @@ -4079,13 +4097,16 @@ bool ProjMgrWorker::ListDependencies(vector& dependencies, const string& } bool ProjMgrWorker::FormatValidationResults(set& results, const ContextItem& context) { - for (const auto& [result, component, expressions, aggregates] : context.validationResults) { - string resultStr = RteItem::ConditionResultToString(result) + " " + component; - for (const auto& expression : expressions) { - resultStr += "\n " + expression; + for (const auto& validation : context.validationResults) { + string resultStr = RteItem::ConditionResultToString(validation.result) + " " + validation.id; + for (const auto& condition : validation.conditions) { + resultStr += "\n " + condition.expression; + for (const auto& id : condition.aggregates) { + resultStr += "\n " + id; + } } - for (const auto& aggregate : aggregates) { - resultStr += "\n " + aggregate; + for (const auto& id : validation.aggregates) { + resultStr += "\n " + id; } results.insert(resultStr); } @@ -5142,6 +5163,10 @@ bool ProjMgrWorker::ProcessGeneratedLayers(ContextItem& context) { vector packRequirements; InsertPackRequirements(cgen->packs, packRequirements, cgen->directory); AddPackRequirements(context, packRequirements); + if (!CollectAllRequiredPdscFiles()) { + PrintContextErrors(context.name); + return false; + } if (!LoadAllRelevantPacks() || !LoadPacks(context)) { PrintContextErrors(context.name); return false; diff --git a/tools/projmgr/src/ProjMgrYamlParser.cpp b/tools/projmgr/src/ProjMgrYamlParser.cpp index 349bf5b89..c933785ab 100644 --- a/tools/projmgr/src/ProjMgrYamlParser.cpp +++ b/tools/projmgr/src/ProjMgrYamlParser.cpp @@ -728,6 +728,7 @@ void ProjMgrYamlParser::ParsePacks(const YAML::Node& parent, const string& file, const YAML::Node& packNode = parent[YAML_PACKS]; for (const auto& packEntry : packNode) { PackItem packItem; + packItem.origin = file; ParseString(packEntry, YAML_PACK, packItem.pack); ParsePortablePath(packEntry, file, YAML_PATH, packItem.path); ParseTypeFilter(packEntry, packItem.type); diff --git a/tools/projmgr/test/CMakeLists.txt b/tools/projmgr/test/CMakeLists.txt index a2c641a28..a12eb8655 100644 --- a/tools/projmgr/test/CMakeLists.txt +++ b/tools/projmgr/test/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable(ProjMgrUnitTests src/ProjMgrUnitTests.cpp src/ProjMgrTestEnv.cpp - src/ProjMgrWorkerUnitTests.cpp src/ProjMgrGeneratorUnitTests.cpp + src/ProjMgrWorkerUnitTests.cpp src/ProjMgrGeneratorUnitTests.cpp src/ProjMgrRpcTests.cpp src/ProjMgrSchemaCheckerUnitTests.cpp src/ProjMgrUtilsUnitTests.cpp src/ProjMgrYamlParserUnitTest.cpp src/ProjMgrTestEnv.h) diff --git a/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf new file mode 100644 index 000000000..f9c9009de --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf @@ -0,0 +1,45 @@ +// <<< Use Configuration Wizard in Context Menu >>> + +// version from from DFP 0.2.0 + +// Debug Setup + +// Release M0 On Connect +// <0=> No +// <1=> Yes +// Debugger releases the M0 Application processor from reset when connecting to it. +ReleaseM0OnConnect = 1; + +// Release M0 Sub-System On Connect +// <0=> No +// <1=> Yes +// Debugger releases the M0 Sub-System from reset when connecting to it (LPC437x only). +ReleaseM0SubOnConnect = 1; + +// Vector Reset +// <0=> Processor Only +// <1=> Processor and Peripherals +// Select if to additionally reset peripherals (LCD, USB0, USB1, DMA, SDIO, ETHERNET) after a Vector Reset +VecResetWithPeriph = 1; + +// + +// TPIU Pin Routing (TRACECLK fixed on PF_4) +// Configure the TPIU pin routing as used on your target platform. +// TRACEDATA0 +// <0=> Pin PF_5 +// <1=> Pin P7_4 +// TRACEDATA1 +// <0=> Pin PF_6 +// <1=> Pin P7_5 +// TRACEDATA2 +// <0=> Pin PF_7 +// <1=> Pin P7_6 +// TRACEDATA3 +// <0=> Pin PF_8 +// <1=> Pin P7_7 +RoutingTPIU = 0x00000000; + +// + +// <<< end of configuration section >>> diff --git a/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf.base@0.0.2 b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf.base@0.0.2 new file mode 100644 index 000000000..f9c9009de --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf.base@0.0.2 @@ -0,0 +1,45 @@ +// <<< Use Configuration Wizard in Context Menu >>> + +// version from from DFP 0.2.0 + +// Debug Setup + +// Release M0 On Connect +// <0=> No +// <1=> Yes +// Debugger releases the M0 Application processor from reset when connecting to it. +ReleaseM0OnConnect = 1; + +// Release M0 Sub-System On Connect +// <0=> No +// <1=> Yes +// Debugger releases the M0 Sub-System from reset when connecting to it (LPC437x only). +ReleaseM0SubOnConnect = 1; + +// Vector Reset +// <0=> Processor Only +// <1=> Processor and Peripherals +// Select if to additionally reset peripherals (LCD, USB0, USB1, DMA, SDIO, ETHERNET) after a Vector Reset +VecResetWithPeriph = 1; + +// + +// TPIU Pin Routing (TRACECLK fixed on PF_4) +// Configure the TPIU pin routing as used on your target platform. +// TRACEDATA0 +// <0=> Pin PF_5 +// <1=> Pin P7_4 +// TRACEDATA1 +// <0=> Pin PF_6 +// <1=> Pin P7_5 +// TRACEDATA2 +// <0=> Pin PF_7 +// <1=> Pin P7_6 +// TRACEDATA3 +// <0=> Pin PF_8 +// <1=> Pin P7_7 +RoutingTPIU = 0x00000000; + +// + +// <<< end of configuration section >>> diff --git a/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ac6_linker_script.sct.src b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ac6_linker_script.sct.src new file mode 100644 index 000000000..7820e1f12 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/ac6_linker_script.sct.src @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* ---------------------------------------------------------------------------- + Stack seal size definition + *----------------------------------------------------------------------------*/ +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#define __STACKSEAL_SIZE 8 +#else +#define __STACKSEAL_SIZE 0 +#endif + +/*---------------------------------------------------------------------------- + Scatter File Definitions definition + *----------------------------------------------------------------------------*/ + +LR_ROM0 __ROM0_BASE __ROM0_SIZE { + + ER_ROM0 __ROM0_BASE __ROM0_SIZE { + *.o (RESET, +First) + *(InRoot$$Sections) + *(+RO +XO) + } + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + ER_CMSE_VENEER AlignExpr(+0, 32) (__ROM0_SIZE - AlignExpr(ImageLength(ER_ROM0), 32)) { + *(Veneer$$CMSE) + } +#endif + + RW_NOINIT __RAM0_BASE UNINIT (__RAM0_SIZE - __HEAP_SIZE - __STACK_SIZE - __STACKSEAL_SIZE) { + *.o(.bss.noinit) + *.o(.bss.noinit.*) + } + + RW_RAM0 AlignExpr(+0, 8) (__RAM0_SIZE - __HEAP_SIZE - __STACK_SIZE - __STACKSEAL_SIZE - AlignExpr(ImageLength(RW_NOINIT), 8)) { + *(+RW +ZI) + } + +#if __HEAP_SIZE > 0 + ARM_LIB_HEAP (AlignExpr(+0, 8)) EMPTY __HEAP_SIZE { ; Reserve empty region for heap + } +#endif + + ARM_LIB_STACK (__RAM0_BASE + __RAM0_SIZE - __STACKSEAL_SIZE) EMPTY -__STACK_SIZE { ; Reserve empty region for stack + } + +#if __STACKSEAL_SIZE > 0 + STACKSEAL +0 EMPTY __STACKSEAL_SIZE { ; Reserve empty region for stack seal immediately after stack + } +#endif + +#if __RAM1_SIZE > 0 + RW_RAM1 __RAM1_BASE __RAM1_SIZE { + .ANY (+RW +ZI) + } +#endif + +#if __RAM2_SIZE > 0 + RW_RAM2 __RAM2_BASE __RAM2_SIZE { + .ANY (+RW +ZI) + } +#endif + +#if __RAM3_SIZE > 0 + RW_RAM3 __RAM3_BASE __RAM3_SIZE { + .ANY (+RW +ZI) + } +#endif +} + +#if __ROM1_SIZE > 0 +LR_ROM1 __ROM1_BASE __ROM1_SIZE { + ER_ROM1 +0 __ROM1_SIZE { + .ANY (+RO +XO) + } +} +#endif + +#if __ROM2_SIZE > 0 +LR_ROM2 __ROM2_BASE __ROM2_SIZE { + ER_ROM2 +0 __ROM2_SIZE { + .ANY (+RO +XO) + } +} +#endif + +#if __ROM3_SIZE > 0 +LR_ROM3 __ROM3_BASE __ROM3_SIZE { + ER_ROM3 +0 __ROM3_SIZE { + .ANY (+RO +XO) + } +} +#endif diff --git a/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/regions_RteTest_ARMCM0.h b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/regions_RteTest_ARMCM0.h new file mode 100644 index 000000000..3c4dc42f8 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/RTE/Device/RteTest_ARMCM0/regions_RteTest_ARMCM0.h @@ -0,0 +1,101 @@ +#ifndef REGIONS_RTETEST_ARMCM0_H +#define REGIONS_RTETEST_ARMCM0_H + + +//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- +//------ With VS Code: Open Preview for Configuration Wizard ------------------- + +// Auto-generated using information from packs +// Device Family Pack (DFP): ARM::RteTest_DFP@0.2.0 + +// ROM Configuration +// ======================= +// __ROM0 (is rx memory: IROM1 from DFP) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. Default: 0x00000000 +// Contains Startup and Vector Table +#define __ROM0_BASE 0x00000000 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. Default: 0x00040000 +#define __ROM0_SIZE 0x00040000 +// + +// __ROM1 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __ROM1_BASE 0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __ROM1_SIZE 0 +// + +// __ROM2 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __ROM2_BASE 0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __ROM2_SIZE 0 +// + +// __ROM3 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __ROM3_BASE 0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __ROM3_SIZE 0 +// + +// + +// RAM Configuration +// ======================= +// __RAM0 (is rw memory: IRAM1 from DFP) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. Default: 0x20000000 +// Contains uninitialized RAM, Stack, and Heap +#define __RAM0_BASE 0x20000000 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. Default: 0x00020000 +#define __RAM0_SIZE 0x00020000 +// + +// __RAM1 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __RAM1_BASE 0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __RAM1_SIZE 0 +// + +// __RAM2 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __RAM2_BASE 0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __RAM2_SIZE 0 +// + +// __RAM3 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __RAM3_BASE 0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __RAM3_SIZE 0 +// + +// + +// Stack / Heap Configuration +// Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +// Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +#define __STACK_SIZE 0x00000200 +#define __HEAP_SIZE 0x00000C00 +// + + +#endif /* REGIONS_RTETEST_ARMCM0_H */ diff --git a/tools/projmgr/test/data/TestSolution/simple/RTE/_Debug_TEST_TARGET/RTE_Components.h b/tools/projmgr/test/data/TestSolution/simple/RTE/_Debug_TEST_TARGET/RTE_Components.h new file mode 100644 index 000000000..43c756bad --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/RTE/_Debug_TEST_TARGET/RTE_Components.h @@ -0,0 +1,20 @@ +/* + * CSOLUTION generated file: DO NOT EDIT! + * Generated by: csolution version 2.8.0 + * + * Project: 'project.Debug+TEST_TARGET' + * Target: 'Debug+TEST_TARGET' + */ + +#ifndef RTE_COMPONENTS_H +#define RTE_COMPONENTS_H + + +/* + * Define the Device Header File: + */ +#define CMSIS_device_header "ARMCM0.h" + + + +#endif /* RTE_COMPONENTS_H */ diff --git a/tools/projmgr/test/data/TestSolution/simple/main.c b/tools/projmgr/test/data/TestSolution/simple/main.c new file mode 100644 index 000000000..7e06c94d0 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/main.c @@ -0,0 +1 @@ +/* Dummy */ diff --git a/tools/projmgr/test/data/TestSolution/simple/project.Debug+TEST_TARGET.cbuild.yml b/tools/projmgr/test/data/TestSolution/simple/project.Debug+TEST_TARGET.cbuild.yml new file mode 100644 index 000000000..96ce9a67c --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/project.Debug+TEST_TARGET.cbuild.yml @@ -0,0 +1,76 @@ +build: + generated-by: csolution version 2.8.0 + solution: test.csolution.yml + project: project.cproject.yml + context: project.Debug+TEST_TARGET + compiler: AC6 + device: ARM::RteTest_ARMCM0 + device-pack: ARM::RteTest_DFP@0.2.0 + device-books: + - name: http://infocenter.arm.com/help/topic/com.arm.doc.dui0497a/index.html + title: Cortex-M0 Device Generic Users Guide + dbgconf: + - file: RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf + version: 0.0.2 + processor: + fpu: off + core: Cortex-M0 + packs: + - pack: ARM::RteTest_DFP@0.2.0 + path: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0 + define: + - ARMCM0 + - _RTE_ + define-asm: + - ARMCM0 + - _RTE_ + add-path: + - RTE/_Debug_TEST_TARGET + - ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/ARMCM0/Include + add-path-asm: + - RTE/_Debug_TEST_TARGET + - ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/ARMCM0/Include + output-dirs: + intdir: tmp + outdir: out/project/TEST_TARGET/Debug + rtedir: RTE + output: + - type: elf + file: project.axf + components: + - component: ARM::RteTest:CORE@0.1.1 + condition: Cortex-M Device + from-pack: ARM::RteTest_DFP@0.2.0 + selected-by: CORE + implements: RteTest:CORE@1.1.2 + files: + - file: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Doc/html/index.html + category: doc + version: 0.1.1 + apis: + - api: RteTest:CORE@1.1.2 + from-pack: ARM::RteTest_DFP@0.2.0 + implemented-by: ARM::RteTest:CORE@0.1.1 + files: + - file: https://arm-software.github.io/CMSIS_5/Pack/html/pdsc_apis_pg.html + category: doc + version: 1.1.2 + linker: + script: RTE/Device/RteTest_ARMCM0/ac6_linker_script.sct.src + regions: RTE/Device/RteTest_ARMCM0/regions_RteTest_ARMCM0.h + groups: + - group: Sources + files: + - file: main.c + category: sourceC + constructed-files: + - file: RTE/_Debug_TEST_TARGET/RTE_Components.h + category: header + licenses: + - license: + license-agreement: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Doc/license.txt + packs: + - pack: ARM::RteTest_DFP@0.2.0 + components: + - component: ARM::RteTest:CORE@0.1.1 + - component: RteTest:CORE(API) diff --git a/tools/projmgr/test/data/TestSolution/simple/project.cproject.yml b/tools/projmgr/test/data/TestSolution/simple/project.cproject.yml new file mode 100644 index 000000000..3e7a69114 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/project.cproject.yml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + device: RteTest_ARMCM0 + components: + - component: CORE + - component: ARM::Device:Startup + groups: + - group: Sources + files: + - file: main.c diff --git a/tools/projmgr/test/data/TestSolution/simple/test+TEST_TARGET.cbuild-run.yml b/tools/projmgr/test/data/TestSolution/simple/test+TEST_TARGET.cbuild-run.yml new file mode 100644 index 000000000..50548e546 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/test+TEST_TARGET.cbuild-run.yml @@ -0,0 +1,102 @@ +cbuild-run: + generated-by: csolution version 2.8.0 + solution: test.csolution.yml + target-type: TEST_TARGET + compiler: AC6 + device: ARM::RteTest_ARMCM0 + device-pack: ARM::RteTest_DFP@0.2.0 + programming: + - algorithm: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/Flash/FAMILY.FLM + start: 0x00000000 + size: 0x00040000 + default: true + system-descriptions: + - file: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/SVD/ARMCM0.svd + type: svd + output: + - file: out/project/TEST_TARGET/Debug/project.axf + info: generate by project.Debug+TEST_TARGET + type: elf + system-resources: + memory: + - name: IROM1 + start: 0x00000000 + size: 0x00040000 + default: true + startup: true + from-pack: ARM::RteTest_DFP@0.2.0 + - name: IRAM1 + start: 0x20000000 + size: 0x00020000 + default: true + uninit: true + from-pack: ARM::RteTest_DFP@0.2.0 + debugger: + - name: + port: swd + clock: 10000000 + dbgconf: RTE/Device/RteTest_ARMCM0/ARMCM.dbgconf + debug-vars: + vars: | + __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY + __var TraceClk_Pin = 0x00040002; // PE2 + __var TraceD0_Pin = 0x00040003; // PE3 + __var TraceD1_Pin = 0x00040004; // PE4 + debug-sequences: + - name: DebugDeviceUnlock + blocks: + - execute: | + Sequence("CheckID"); + - name: DebugCoreStart + blocks: + - execute: | + // Replication of Standard Functionality + Write32(0xE000EDF0, 0xA05F0001); // Enable Core Debug via DHCSR + - info: DbgMCU registers + execute: | + // Device Specific Debug Setup + Write32(0x40021018, Read32(0x40021018) | 0x00400000); // Set RCC_APB2ENR.DBGMCUEN + - name: CheckID + blocks: + - execute: | + __var pidr1 = 0; + __var pidr2 = 0; + __var jep106id = 0; + __var ROMTableBase = 0; + + __ap = 0; // AHB-AP + + ROMTableBase = ReadAP(0xF8) & ~0x3; + + pidr1 = Read32(ROMTableBase + 0x0FE4); + pidr2 = Read32(ROMTableBase + 0x0FE8); + jep106id = ((pidr2 & 0x7) << 4 ) | ((pidr1 >> 4) & 0xF); + - if: jep106id != 0x20 + execute: | + Query(0, "Incorrect ID! Abort connection", 1); + Message(2, "Incorrect ID! Abort connection."); + - name: DebugPortStop + blocks: + - execute: | + __var connectionFlash = ( __connection & 0xF ) == 2 ; + __var FLASH_BASE = 0x40022000 ; + __var FLASH_CR = FLASH_BASE + 0x10 ; + __var OBL_LAUNCH_BIT = ( 1 << 13 ) ; + __var FLASH_CR_Value = 0 ; + __var DoDebugPortStop = 1 ; + __var DP_CTRL_STAT = 0x4 ; + __var DP_SELECT = 0x8 ; + - if: connectionFlash && DoDebugPortStop + execute: | + DoDebugPortStop = 0 ; + FLASH_CR_Value = Read32( FLASH_CR ) ; + __errorcontrol = 1 ; + // write OBL_LAUNCH bit (causes a reset) + Write32( FLASH_CR, FLASH_CR_Value | ( OBL_LAUNCH_BIT ) ) ; + __errorcontrol = 0 ; + - if: DoDebugPortStop + execute: | + // Switch to DP Register Bank 0 + WriteDP(DP_SELECT, 0x00000000); + // Power Down Debug port + WriteDP(DP_CTRL_STAT, 0x00000000); diff --git a/tools/projmgr/test/data/TestSolution/simple/test.cbuild-idx.yml b/tools/projmgr/test/data/TestSolution/simple/test.cbuild-idx.yml new file mode 100644 index 000000000..6aae918b5 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/test.cbuild-idx.yml @@ -0,0 +1,20 @@ +build-idx: + generated-by: csolution version 2.8.0 + csolution: test.csolution.yml + tmpdir: tmp + cprojects: + - cproject: project.cproject.yml + cbuilds: + - cbuild: project.Debug+TEST_TARGET.cbuild.yml + project: project + configuration: .Debug+TEST_TARGET + errors: true + messages: + errors: + - "no component was found with identifier 'ARM::Device:Startup'\n did you mean 'Device:Startup&RteTest Startup'?" + - processing context 'project.Debug+TEST_TARGET' failed + warnings: + - "project.cproject.yml - 'device: Dname' is deprecated at this level and accepted in *.csolution.yml only" + info: + - test.cbuild-pack.yml - file is already up-to-date + - project.Debug+TEST_TARGET.cbuild.yml - file is already up-to-date diff --git a/tools/projmgr/test/data/TestSolution/simple/test.cbuild-pack.yml b/tools/projmgr/test/data/TestSolution/simple/test.cbuild-pack.yml new file mode 100644 index 000000000..20081b017 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/test.cbuild-pack.yml @@ -0,0 +1,5 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::RteTest_DFP@0.2.0 + selected-by-pack: + - ARM::RteTest_DFP diff --git a/tools/projmgr/test/data/TestSolution/simple/test.cbuild-set.yml b/tools/projmgr/test/data/TestSolution/simple/test.cbuild-set.yml new file mode 100644 index 000000000..9505a9941 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/test.cbuild-set.yml @@ -0,0 +1,4 @@ +cbuild-set: + generated-by: vscode-cmsis-csolution version 1.50.1-24-gaf492fc + contexts: + - context: project.Debug+TEST_TARGET diff --git a/tools/projmgr/test/data/TestSolution/simple/test.csolution.yml b/tools/projmgr/test/data/TestSolution/simple/test.csolution.yml new file mode 100644 index 000000000..12d068192 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/simple/test.csolution.yml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: TEST_TARGET + + build-types: + - type: Debug + compiler: AC6 + + packs: + - pack: ARM::RteTest_DFP + + projects: + - project: ./project.cproject.yml + diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp new file mode 100644 index 000000000..7e45c02c1 --- /dev/null +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020-2025 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ProjMgr.h" +#include "ProjMgrTestEnv.h" +#include "ProjMgrRpcServer.h" +#include "ProjMgrRpcServerData.h" + + +#include "ProjMgrLogger.h" + + +using namespace std; + +class ProjMgrRpcTests : public ProjMgr, public ::testing::Test { +protected: + ProjMgrRpcTests() {} + virtual ~ProjMgrRpcTests() {} +}; + +TEST_F(ProjMgrRpcTests, Load_Solution) { + + + +} + +// end of ProjMgrRpcTests.cpp diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index 7e73b6c12..eb94b47a0 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -4011,11 +4011,25 @@ TEST_F(ProjMgrUnitTests, Convert_ValidationResults_Dependencies) { argv[5] = (char*)"-c"; map testData = { - {"conflict+CM0", "warning csolution: dependency validation for context 'conflict+CM0' failed:\nCONFLICT RteTest:ApiExclusive(API)\n ARM::RteTest:ApiExclusive:S1\n ARM::RteTest:ApiExclusive:S2" }, - {"incompatible+CM0", "warning csolution: dependency validation for context 'incompatible+CM0' failed:\nINCOMPATIBLE ARM::RteTest:Check:Incompatible@0.9.9\n deny RteTest:Dependency:Incompatible_component" }, - {"incompatible-variant+CM0", "warning csolution: dependency validation for context 'incompatible-variant+CM0' failed:\nINCOMPATIBLE_VARIANT ARM::RteTest:Check:IncompatibleVariant@0.9.9\n require RteTest:Dependency:Variant&Compatible" }, - {"missing+CM0", "warning csolution: dependency validation for context 'missing+CM0' failed:\nMISSING ARM::RteTest:Check:Missing@0.9.9\n require RteTest:Dependency:Missing" }, - {"selectable+CM0", "warning csolution: dependency validation for context 'selectable+CM0' failed:\nSELECTABLE ARM::Device:Startup&RteTest Startup@2.0.3\n require RteTest:CORE" } + {"conflict+CM0", "warning csolution: dependency validation for context 'conflict+CM0' failed:\n\ +CONFLICT RteTest:ApiExclusive@1.0.0\n\ + ARM::RteTest:ApiExclusive:S1\n\ + ARM::RteTest:ApiExclusive:S2" }, + {"incompatible+CM0", "warning csolution: dependency validation for context 'incompatible+CM0' failed:\n\ +INCOMPATIBLE ARM::RteTest:Check:Incompatible@0.9.9\n\ + deny RteTest:Dependency:Incompatible_component\n\ + ARM::RteTest:Dependency:Incompatible_component" }, + {"incompatible-variant+CM0", "warning csolution: dependency validation for context 'incompatible-variant+CM0' failed:\n\ +INCOMPATIBLE_VARIANT ARM::RteTest:Check:IncompatibleVariant@0.9.9\n\ + require RteTest:Dependency:Variant&Compatible\n\ + ARM::RteTest:Dependency:Variant" }, + {"missing+CM0", "warning csolution: dependency validation for context 'missing+CM0' failed:\n\ +MISSING ARM::RteTest:Check:Missing@0.9.9\n\ + require RteTest:Dependency:Missing" }, + {"selectable+CM0", "warning csolution: dependency validation for context 'selectable+CM0' failed:\n\ +SELECTABLE ARM::Device:Startup&RteTest Startup@2.0.3\n\ + require RteTest:CORE\n\ + ARM::RteTest:CORE" } }; for (const auto& [context, expected] : testData) { diff --git a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp index f448308c3..7a5eb04b4 100644 --- a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp @@ -528,8 +528,10 @@ TEST_F(ProjMgrWorkerUnitTests, ProcessDependencies) { EXPECT_FALSE(ValidateContext(context)); ASSERT_EQ(expected.size(), context.validationResults.size()); map> dependenciesMap; - for (const auto& [result, component, dependencies, _] : context.validationResults) { - dependenciesMap[component] = dependencies; + for (const auto& validation : context.validationResults) { + for (const auto& condition : validation.conditions) { + dependenciesMap[validation.id].insert(condition.expression); + } } for (const auto& [expectedComponent, expectedDependencies] : expected) { EXPECT_TRUE(dependenciesMap.find(expectedComponent) != dependenciesMap.end());