diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index fd71ff66d..ad55896c8 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -124,6 +124,9 @@ struct BoardItem { std::string vendor; std::string name; std::string revision; + bool operator==(const BoardItem& b) const { + return name == b.name && revision == b.revision; + } }; /** @@ -287,6 +290,7 @@ struct GdbServerItem { struct EnvironmentItem { std::string load; std::string folder; + bool operator==(const EnvironmentItem& b) const { return true; } }; /** @@ -315,6 +319,10 @@ struct ExampleItem { std::vector categories; std::vector keywords; std::string pack; + bool operator==(const ExampleItem& e) const { + return name == e.name && version == e.version && description == e.description && + boards == e.boards && environments == e.environments; + } }; /** @@ -428,9 +436,9 @@ struct ContextItem { RteProject* rteActiveProject = nullptr; RteTarget* rteActiveTarget = nullptr; RteModel* rteFilteredModel = nullptr; - RteItem* rteComponents; - RteDeviceItem* rteDevice; - RteBoard* rteBoard; + RteItem* rteComponents = nullptr; + RteDeviceItem* rteDevice = nullptr; + RteBoard* rteBoard = nullptr; TranslationControl controls; TargetItem targetItem; DirectoriesItem directories; @@ -525,6 +533,7 @@ enum class BoardOrDevice { Both, None, SkipDevice, + SkipProcessor, }; /** @@ -812,6 +821,13 @@ class ProjMgrWorker { */ bool InitializeModel(void); + /** + * @brief initialize rte target for a given context + * @param context reference + * @return true if executed successfully + */ + bool InitializeTarget(ContextItem& context); + /** * @brief load all relevant packs * @return true if executed successfully @@ -994,6 +1010,29 @@ class ProjMgrWorker { */ void AddImageOnlyContext(); + /** + * @brief process device and/or board + * @param context item + * @param scope: process board, device or both + * @return true if there is no error + */ + bool ProcessDevice(ContextItem& context, BoardOrDevice process = BoardOrDevice::Both); + + /** + * @brief collect examples + * @param context item + * @param environments filter + * @return vector of example items + */ + std::vector CollectExamples(const ContextItem& context, const StrVec& filter); + + /** + * @brief collect templates + * @param context item + * @return vector of template items + */ + std::vector CollectTemplates(const ContextItem& context); + /** * @brief clear worker members for reloading a solution * @return true if there is no error @@ -1073,11 +1112,9 @@ class ProjMgrWorker { bool CheckContextFilters(const TypeFilter& typeFilter, const ContextItem& context); bool GetTypeContent(ContextItem& context); bool GetProjectSetup(ContextItem& context); - bool InitializeTarget(ContextItem& context); bool ProcessPrecedences(ContextItem& context, BoardOrDevice process = BoardOrDevice::None, bool rerun = false); bool ProcessPrecedence(StringCollection& item); bool ProcessCompilerPrecedence(StringCollection& item, bool acceptRedefinition = false); - bool ProcessDevice(ContextItem& context, BoardOrDevice process = BoardOrDevice::Both); bool ProcessDevicePrecedence(StringCollection& item); bool ProcessBoardPrecedence(StringCollection& item); bool ProcessToolchain(ContextItem& context); @@ -1171,10 +1208,10 @@ class ProjMgrWorker { bool IsCreatedByExecute(const std::string file, const std::string dir); bool CollectAllRequiredPdscFiles(); bool ParseTargetSetContextSelection(); - std::vector CollectExamples(ContextItem& context); - std::vector GetCompatibleBoards(ContextItem& context); + std::vector GetCompatibleBoards(const ContextItem& context); bool IsBoardListCompatible(const ContextItem& context, const std::vector compatibleBoards, const Collection& boards); - std::vector CollectTemplates(ContextItem& context); + bool IsEnvironmentCompatible(const std::string& environment, const StrVec& filter); + bool HasCompatibleEnvironment(const Collection& environments, const StrVec& filter); }; #endif // PROJMGRWORKER_H diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp index 87b1bdf3b..548f09f66 100644 --- a/tools/projmgr/src/ProjMgrRpcServer.cpp +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -94,6 +94,7 @@ class RpcHandler : public RpcMethods { 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; + RpcArgs::DraftProjectsInfo GetDraftProjects(const RpcArgs::DraftProjectsFilter& filter) override; protected: enum Exception @@ -584,4 +585,92 @@ RpcArgs::LogMessages RpcHandler::GetLogMessages(void) { return messages; } +RpcArgs::DraftProjectsInfo RpcHandler::GetDraftProjects(const RpcArgs::DraftProjectsFilter& filter) { + RpcArgs::DraftProjectsInfo applications; + applications.success = false; + + if (!m_packsLoaded) { + applications.message = "Packs must be loaded before retrieving draft projects"; + return applications; + } + + // initialize context and target attributes with board and device + ContextItem context; + m_worker.InitializeTarget(context); + if (filter.board.has_value() || filter.device.has_value()) { + context.board = filter.board.has_value() ? filter.board.value() : ""; + context.device = filter.device.has_value() ? filter.device.value() : ""; + if (!m_worker.ProcessDevice(context, BoardOrDevice::SkipProcessor)) { + applications.message = "Board or device processing failed"; + return applications; + } + m_worker.SetTargetAttributes(context, context.targetAttributes); + } + + // collect examples, optionally filtered for 'environments' + vector examples, refApps; + const auto& environments = filter.environments.has_value() ? filter.environments.value() : StrVec(); + const auto& collectedExamples = m_worker.CollectExamples(context, environments); + for (const auto& example : collectedExamples) { + RpcArgs::ExampleProject e; + e.name = example.name; + e.pack = example.pack; + e.doc = example.doc; + e.description = example.description; + if (!example.version.empty()) { + e.version = example.version; + } + if (!example.archive.empty()) { + e.archive = example.archive; + } + for (const auto& [name, environment] : example.environments) { + RpcArgs::ExampleEnvironment env; + env.name = name; + env.file = environment.load; + env.folder = environment.folder; + e.environments.push_back(env); + } + if (!example.components.empty()) { + e.components = example.components; + } + if (!example.categories.empty()) { + e.categories = example.categories; + } + if (!example.keywords.empty()) { + e.keywords = example.keywords; + } + // classify the example as ref-app if it does not specify boards + auto& ref = example.boards.empty() ? refApps : examples; + ref.push_back(e); + } + if (!examples.empty()) { + applications.examples = examples; + } + if (!refApps.empty()) { + applications.refApps = refApps; + } + + // collect templates + vector templates; + const auto& csolutionTemplates = m_worker.CollectTemplates(context); + for (const auto& csolutionTemplate : csolutionTemplates) { + RpcArgs::SolutionTemplate t; + t.name = csolutionTemplate.name; + t.pack = csolutionTemplate.pack; + t.description = csolutionTemplate.description; + t.file = csolutionTemplate.file; + t.folder = csolutionTemplate.path; + if (!csolutionTemplate.copyTo.empty()) { + t.copyTo = csolutionTemplate.copyTo; + } + templates.push_back(t); + } + if (!templates.empty()) { + applications.templates = templates; + } + + applications.success = true; + return applications; +} + // end of ProkMgrRpcServer.cpp diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 1d312583c..8b3cfcca6 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -1413,7 +1413,7 @@ bool ProjMgrWorker::ProcessDevice(ContextItem& context, BoardOrDevice process) { if (!deviceItem.pname.empty()) { ProjMgrLogger::Get().Error("processor name '" + deviceItem.pname + "' was not found", context.name); return false; - } else if (!HasVarDefineError()) { + } else if (!HasVarDefineError() && process != BoardOrDevice::SkipProcessor) { string msg = "one of the following processors must be specified:"; const auto& processors = matchedDevice->GetProcessors(); for (const auto& p : processors) { @@ -4187,11 +4187,11 @@ bool ProjMgrWorker::ListDependencies(vector& dependencies, const string& return true; } -vector ProjMgrWorker::GetCompatibleBoards(ContextItem& context) { +vector ProjMgrWorker::GetCompatibleBoards(const ContextItem& context) { vector compatibleBoards; - if (!context.board.empty()) { + if (context.rteBoard) { compatibleBoards.push_back(context.rteBoard); - } else if (!context.device.empty()) { + } else if (context.rteDevice) { context.rteFilteredModel->GetCompatibleBoards(compatibleBoards, context.rteDevice); } return compatibleBoards; @@ -4224,11 +4224,23 @@ bool ProjMgrWorker::IsBoardListCompatible(const ContextItem& context, const vect return false; } -std::vector ProjMgrWorker::CollectExamples(ContextItem& context) { +bool ProjMgrWorker::IsEnvironmentCompatible(const std::string& environment, const StrVec& filter) { + return (filter.empty() || find(filter.begin(), filter.end(), environment) != filter.end()); +} + +bool ProjMgrWorker::HasCompatibleEnvironment(const Collection& environments, const StrVec& filter) { + for (const auto& environment : environments) { + if (IsEnvironmentCompatible(environment->GetName(), filter)) { + return true; + } + } + return false; +} + +std::vector ProjMgrWorker::CollectExamples(const ContextItem& context, const StrVec& filter) { std::vector examples; vector rteExamples; - const auto& packs = context.rteFilteredModel->GetPackages(); - for (const auto& [_, pack] : packs) { + for (const auto& pack : m_loadedPacks) { const RteItem* packExamples = pack->GetExamples(); if (packExamples) { const Collection items = packExamples->GetChildren(); @@ -4246,6 +4258,11 @@ std::vector ProjMgrWorker::CollectExamples(ContextItem& context) { if (!IsBoardListCompatible(context, compatibleBoards, boards)) { continue; } + Collection environments; + environments = rteExample->GetChildrenByTag("environment", environments); + if (!HasCompatibleEnvironment(environments, filter)) { + continue; + } ExampleItem example; example.name = rteExample->GetName(); example.description = rteExample->GetDescription(); @@ -4262,17 +4279,18 @@ std::vector ProjMgrWorker::CollectExamples(ContextItem& context) { for (const auto& board : boards) { example.boards.push_back(BoardItem{ board->GetVendorString(), board->GetName() }); } - const auto& version = rteExample->GetVersionString(); + const auto& version = rteExample->GetAttribute("version"); if (!version.empty()) { example.version = version; } - Collection environments; - environments = rteExample->GetChildrenByTag("environment", environments); for (const auto& item : environments) { + const auto& name = item->GetName(); + if (!IsEnvironmentCompatible(name, filter)) { + continue; + } string load = item->GetAttribute("load"); RteFsUtils::NormalizePath(load, folder); - const auto& name = item->GetName(); example.environments[name].load = load; example.environments[name].folder = item->GetFolderString(); RteFsUtils::NormalizePath(example.environments[name].folder, folder); @@ -4296,7 +4314,9 @@ std::vector ProjMgrWorker::CollectExamples(ContextItem& context) { example.keywords.push_back(item->GetText()); } - examples.push_back(example); + if (find(examples.begin(), examples.end(), example) == examples.end()) { + examples.push_back(example); + } } return examples; } @@ -4316,7 +4336,7 @@ bool ProjMgrWorker::ListExamples(vector& examples, const string& filter) return false; } - const auto& collectedExamples = CollectExamples(context); + const auto& collectedExamples = CollectExamples(context, StrVec()); for (const auto& exampleItem : collectedExamples) { if (!filter.empty() && exampleItem.name.find(filter) == string::npos) { @@ -4348,7 +4368,7 @@ bool ProjMgrWorker::ListExamples(vector& examples, const string& filter) return true; } -std::vector ProjMgrWorker::CollectTemplates(ContextItem& context) { +std::vector ProjMgrWorker::CollectTemplates(const ContextItem& context) { std::vector templates; const auto& rteTemplates = context.rteFilteredModel->GetProjectDescriptors(); for (const auto& rteTemplate : rteTemplates) { diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp index a5c4014e2..22d3f94cd 100644 --- a/tools/projmgr/test/src/ProjMgrRpcTests.cpp +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -631,5 +631,72 @@ TEST_F(ProjMgrRpcTests, RpcGetUsedItems) { EXPECT_TRUE(components[1]["options"]["explicitVendor"]); } +TEST_F(ProjMgrRpcTests, RpcGetDraftProjects) { + // filter 'board' + auto requests = + FormatRequest(1, "LoadPacks") + + FormatRequest(2, "GetDraftProjects", json{{ "filter", {{ "board", "RteTest Dummy board" }}}}); + auto responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[1]["result"]["success"]); + auto examples = responses[1]["result"]["examples"]; + auto templates = responses[1]["result"]["templates"]; + EXPECT_EQ(2, examples.size()); + EXPECT_EQ(1, templates.size()); + EXPECT_EQ(examples[0]["name"], "PreInclude"); + EXPECT_EQ(examples[1]["name"], "PreIncludeEnvFolder"); + EXPECT_EQ(templates[0]["name"], "Board3"); + + // filter 'device' + requests = + FormatRequest(1, "LoadPacks") + + FormatRequest(2, "GetDraftProjects", json{{ "filter", {{ "device", "RteTest_ARMCM0_Dual" }}}}); + responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[1]["result"]["success"]); + examples = responses[1]["result"]["examples"]; + templates = responses[1]["result"]["templates"]; + EXPECT_EQ(2, examples.size()); + EXPECT_EQ(3, templates.size()); + EXPECT_EQ(examples[0]["name"], "PreInclude"); + EXPECT_EQ(examples[1]["name"], "PreIncludeEnvFolder"); + EXPECT_EQ(templates[0]["name"], "Board1Template"); + EXPECT_EQ(templates[1]["name"], "Board2"); + EXPECT_EQ(templates[2]["name"], "Board3"); + + // filter 'environment' + requests = + FormatRequest(1, "LoadPacks") + + FormatRequest(2, "GetDraftProjects", json{{ "filter", {{ "environments", { "csolution" }}}}}); + responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[1]["result"].contains("examples")); + templates = responses[1]["result"]["templates"]; + EXPECT_EQ(3, templates.size()); + EXPECT_EQ(templates[0]["name"], "Board1Template"); + EXPECT_EQ(templates[1]["name"], "Board2"); + EXPECT_EQ(templates[2]["name"], "Board3"); + + // empty filter + requests = + FormatRequest(1, "LoadPacks") + + FormatRequest(2, "GetDraftProjects", json{{ "filter", json::object() }}); + responses = RunRpcMethods(requests); + examples = responses[1]["result"]["examples"]; + templates = responses[1]["result"]["templates"]; + EXPECT_EQ(2, examples.size()); + EXPECT_EQ(3, templates.size()); + + // unknown board + requests = + FormatRequest(1, "LoadPacks") + + FormatRequest(2, "GetDraftProjects", json{{ "filter", {{ "board", "UNKNOWN" }}}}); + responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[1]["result"]["success"]); + EXPECT_EQ(responses[1]["result"]["message"], "Board or device processing failed"); + + // without loading packs + requests = FormatRequest(1, "GetDraftProjects", json{{ "filter", json::object() }}); + responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[0]["result"]["success"]); + EXPECT_EQ(responses[0]["result"]["message"], "Packs must be loaded before retrieving draft projects"); +} // end of ProjMgrRpcTests.cpp