diff --git a/tools/projmgr/include/ProjMgr.h b/tools/projmgr/include/ProjMgr.h index 2318b288b..2164b4109 100644 --- a/tools/projmgr/include/ProjMgr.h +++ b/tools/projmgr/include/ProjMgr.h @@ -70,6 +70,14 @@ class ProjMgr { */ bool LoadSolution(const std::string& csolution, const std::string& activeTargetSet); + /** + * @brief setup contexts + * @param path to .csolution.yml file + * @param active target set in the format [@] + * @return processing status + */ + bool SetupContexts(const std::string& csolution, const std::string& activeTargetSet); + /** * @brief convert solution and generate yml files * @param path to .csolution.yml file diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index a2d9c9a75..8ed1d1df6 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -367,6 +367,42 @@ struct DebuggerType { CustomItem custom; }; +/** + * @brief settings type containing + * connection set +*/ +struct SettingsType { + std::string set; +}; + +/** + * @brief layer variable type containing + * variable name + * clayer path + * brief description + * list of connection sets + * directory path that contains the layer (from PDSC) + * clayer path relative to directory path (from PDSC) + * proposed destination directory for the layer (from PDSC) +*/ +struct LayerVariable { + std::string name; + std::string clayer; + std::string description; + std::vector settings; + std::string path; + std::string file; + std::string copyTo; +}; + +/** + * @brief variables configuration type containing + * list of layer variables +*/ +struct VariablesConfiguration { + std::vector variables; +}; + /** * @brief project context item containing * pointer to csolution, @@ -431,6 +467,7 @@ struct DebuggerType { * image only flag * west options * west on flag + * layer variables configurations */ struct ContextItem { CdefaultItem* cdefault = nullptr; @@ -502,6 +539,7 @@ struct ContextItem { bool imageOnly = false; WestDesc west; bool westOn = false; + std::vector variablesConfigurations; }; /** @@ -1051,6 +1089,18 @@ class ProjMgrWorker { */ bool IsLibOnly(const std::vector& contexts); + /** + * @brief get processed contexts + * @return reference to vector with context items + */ + const std::vector& GetProcessedContexts(void); + + /** + * @brief elaborate possible variables configurations according to compatible layers + * @return true if there are configurations available + */ + bool ElaborateVariablesConfigurations(); + /** * @brief clear worker members for reloading a solution * @return true if there is no error @@ -1094,6 +1144,7 @@ class ProjMgrWorker { std::map m_contexts; std::map> m_contextErrMap; std::vector m_selectedContexts; + std::vector m_processedContexts; std::string m_outputDir; std::string m_packRoot; std::string m_compilerRoot; diff --git a/tools/projmgr/src/ProjMgr.cpp b/tools/projmgr/src/ProjMgr.cpp index ac726c814..fd2502c99 100644 --- a/tools/projmgr/src/ProjMgr.cpp +++ b/tools/projmgr/src/ProjMgr.cpp @@ -1104,22 +1104,11 @@ bool ProjMgr::RunListLayers(void) { // Step4: Run only when --update-idx flag is used // Update the cbuild-idx.yml file with layer information if (m_updateIdx) { - map* contexts = nullptr; - m_worker.GetContexts(contexts); - - // Check if contexts were properly retrieved - if (contexts == nullptr) { - return false; - } - // Generate Cbuild index - m_processedContexts.clear(); - for (auto& contextName : m_worker.GetSelectedContexts()) { - auto& contextItem = (*contexts)[contextName]; - m_processedContexts.push_back(&contextItem); - } - if (!m_processedContexts.empty()) { - if (!m_emitter.GenerateCbuildIndex(m_processedContexts, + const auto& processedContexts = m_worker.GetProcessedContexts(); + if (!processedContexts.empty()) { + m_worker.ElaborateVariablesConfigurations(); + if (!m_emitter.GenerateCbuildIndex(processedContexts, m_failedContext, map())) { return false; } @@ -1301,10 +1290,11 @@ void ProjMgr::InitSolution(const std::string& csolution, const std::string& acti m_contextSet = true; } else { m_activeTargetSet = activeTargetSet; + m_contextSet = false; } } -bool ProjMgr::LoadSolution(const std::string& csolution, const std::string& activeTargetSet) { +bool ProjMgr::SetupContexts(const std::string& csolution, const std::string& activeTargetSet) { InitSolution(csolution, activeTargetSet, false); @@ -1314,6 +1304,14 @@ bool ProjMgr::LoadSolution(const std::string& csolution, const std::string& acti if (!ParseAndValidateContexts()) { return false; } + return true; +} + +bool ProjMgr::LoadSolution(const std::string& csolution, const std::string& activeTargetSet) { + + if (!SetupContexts(csolution, activeTargetSet)) { + return false; + } if (!ProcessContexts()) { return false; } diff --git a/tools/projmgr/src/ProjMgrCbuildIdx.cpp b/tools/projmgr/src/ProjMgrCbuildIdx.cpp index 5b0dc938e..098026048 100644 --- a/tools/projmgr/src/ProjMgrCbuildIdx.cpp +++ b/tools/projmgr/src/ProjMgrCbuildIdx.cpp @@ -22,7 +22,7 @@ class ProjMgrCbuildIdx : public ProjMgrCbuildBase { const map& executes); private: void SetExecutesNode(YAML::Node node, const map& executes, const string& base, const string& ref); - void SetVariablesNode(YAML::Node node, ProjMgrParser* parser, const ContextItem* context, const map>>& layerTypes); + void SetVariablesNode(YAML::Node node, ProjMgrParser* parser, const ContextItem* context, const VariablesConfiguration& configuration); }; ProjMgrCbuildIdx::ProjMgrCbuildIdx(YAML::Node node, @@ -64,37 +64,17 @@ ProjMgrCbuildIdx::ProjMgrCbuildIdx(YAML::Node node, } // Collect layer connection info specific to each target - if (context->validConnections.size() > 0) { + if (!context->variablesConfigurations.empty()) { configTargets.push_back(context->type.target); - map>>> configurations; - int index = 0; - for (const auto& combination : context->validConnections) { - index++; - for (const auto& item : combination) { - if (item.type.empty()) - continue; - for (const auto& [type, _] : context->compatibleLayers) { - // add all required layer types, including discarded optionals - configurations[index][type].insert({}); - } - for (const auto& connect : item.connections) { - configurations[index][item.type][item.filename].insert(connect); - } - } - } - - // Process connection info and generate nodes - if (configurations.size() > 0) { - YAML::Node targetTypeNode; - SetNodeValue(targetTypeNode[YAML_TARGETTYPE], context->type.target); - for (const auto& [index, types] : configurations) { - YAML::Node configurationsNode; - configurationsNode[YAML_CONFIGURATION] = YAML::Null; - SetVariablesNode(configurationsNode[YAML_VARIABLES], parser, context, types); - targetTypeNode[YAML_TARGET_CONFIGURATIONS].push_back(configurationsNode); - } - node[YAML_CONFIGURATIONS].push_back(targetTypeNode); + YAML::Node targetTypeNode; + SetNodeValue(targetTypeNode[YAML_TARGETTYPE], context->type.target); + for (const auto& configuration : context->variablesConfigurations) { + YAML::Node configurationsNode; + configurationsNode[YAML_CONFIGURATION] = YAML::Null; + SetVariablesNode(configurationsNode[YAML_VARIABLES], parser, context, configuration); + targetTypeNode[YAML_TARGET_CONFIGURATIONS].push_back(configurationsNode); } + node[YAML_CONFIGURATIONS].push_back(targetTypeNode); } } @@ -206,53 +186,22 @@ ProjMgrCbuildIdx::ProjMgrCbuildIdx(YAML::Node node, } void ProjMgrCbuildIdx::SetVariablesNode(YAML::Node node, ProjMgrParser* parser, const ContextItem* context, - const map>>& layerTypes) { - for (const auto& [type, filenames] : layerTypes) { - if (type.empty()) { - continue; - } + const VariablesConfiguration& configuration) { + for (const auto& variable : configuration.variables) { YAML::Node layerNode; - string layerFile; - const string layerId = context->layerVariables.find(type) != context->layerVariables.end() ? - context->layerVariables.at(type) : type + "-Layer"; - if (filenames.empty()) { - layerNode[layerId] = ""; + if (variable.clayer.empty()) { + layerNode[variable.name] = ""; } - for (const auto& [filename, options] : filenames) { - string packRoot = ProjMgrKernel::Get()->GetCmsisPackRoot(); - layerFile = filename; - RteFsUtils::NormalizePath(layerFile); - layerFile = RteFsUtils::MakePathCanonical(layerFile); - // Replace with ${CMSIS_PACK_ROOT} or $SolutionDir()$ depending on the detected path - size_t index = layerFile.find(packRoot); - if (index != string::npos) { - layerFile.replace(index, packRoot.length(), "${CMSIS_PACK_ROOT}"); - } - else { - string relPath = RteFsUtils::RelativePath(filename, context->csolution->directory); - if (!relPath.empty()) { - layerFile = "$" + string(RteConstants::AS_SOLUTION_DIR_BR) + "$/" + RteFsUtils::LexicallyNormal(relPath); - } - } - SetNodeValue(layerNode[layerId], layerFile); - if (parser->GetGenericClayers().find(filename) != parser->GetGenericClayers().end()) { - const auto& clayer = parser->GetGenericClayers().at(filename); - SetNodeValue(layerNode[YAML_DESCRIPTION], clayer.description); - } - for (const auto& connect : options) { - if (!connect->set.empty()) { - YAML::Node setNode; - SetNodeValue(setNode[YAML_SET], connect->set + " (" + connect->connect + (connect->info.empty() ? "" : " - " + connect->info) + ")"); - layerNode[YAML_SETTINGS].push_back(setNode); - } - } - if (context->packLayers.find(filename) != context->packLayers.end()) { - const auto& clayer = context->packLayers.at(filename); - SetNodeValue(layerNode[YAML_PATH], clayer->GetOriginalAbsolutePath(clayer->GetPathString())); - SetNodeValue(layerNode[YAML_FILE], clayer->GetFileString()); - SetNodeValue(layerNode[YAML_COPY_TO], clayer->GetCopyToString()); - } + SetNodeValue(layerNode[variable.name], variable.clayer); + SetNodeValue(layerNode[YAML_DESCRIPTION], variable.description); + for (const auto& setting : variable.settings) { + YAML::Node setNode; + SetNodeValue(setNode[YAML_SET], setting.set); + layerNode[YAML_SETTINGS].push_back(setNode); } + SetNodeValue(layerNode[YAML_PATH], variable.path); + SetNodeValue(layerNode[YAML_FILE], variable.file); + SetNodeValue(layerNode[YAML_COPY_TO], variable.copyTo); node.push_back(layerNode); } } diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp index ca2b2e65f..cc690d181 100644 --- a/tools/projmgr/src/ProjMgrRpcServer.cpp +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -97,6 +97,7 @@ class RpcHandler : public RpcMethods { RpcArgs::LogMessages GetLogMessages(void) override; RpcArgs::DraftProjectsInfo GetDraftProjects(const RpcArgs::DraftProjectsFilter& filter) override; RpcArgs::ConvertSolutionResult ConvertSolution(const string& solution, const string& activeTarget, const bool& updateRte) override; + RpcArgs::DiscoverLayersInfo DiscoverLayers(const string& solution, const string& activeTarget) override; protected: enum Exception @@ -126,6 +127,7 @@ class RpcHandler : public RpcMethods { RteTarget* GetActiveTarget(const string& context) const; RteComponentAggregate* GetComponentAggregate(const string& context, const string& id) const; bool SelectVariantOrVersion(const string& context, const string& id, const string& value, bool bVariant); + bool CheckSolutionArg(string& solution, optional& message) const; }; bool ProjMgrRpcServer::Run(void) { @@ -169,6 +171,19 @@ bool ProjMgrRpcServer::Run(void) { return true; } +bool RpcHandler::CheckSolutionArg(string& solution, optional& message) const { + if (!regex_match(solution, regex(".*\\.csolution\\.(yml|yaml)"))) { + message = solution + " is not a *.csolution.yml file"; + return false; + } + solution = RteFsUtils::MakePathCanonical(solution); + if (!RteFsUtils::Exists(solution)) { + message = solution + " file does not exist"; + return false; + } + 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"); @@ -690,9 +705,8 @@ RpcArgs::DraftProjectsInfo RpcHandler::GetDraftProjects(const RpcArgs::DraftProj RpcArgs::ConvertSolutionResult RpcHandler::ConvertSolution(const string& solution, const string& activeTarget, const bool& updateRte) { RpcArgs::ConvertSolutionResult result = {{ false }}; - const auto csolutionFile = RteFsUtils::MakePathCanonical(solution); - if (!regex_match(csolutionFile, regex(".*\\.csolution\\.(yml|yaml)"))) { - result.message = solution + " is not a *.csolution.yml file"; + string csolutionFile = solution; + if (!CheckSolutionArg(csolutionFile, result.message)) { return result; } if (!m_manager.RunConvert(csolutionFile, activeTarget, updateRte) || !ProjMgrLogger::Get().GetErrors().empty()) { @@ -712,4 +726,67 @@ RpcArgs::ConvertSolutionResult RpcHandler::ConvertSolution(const string& solutio return result; } +RpcArgs::DiscoverLayersInfo RpcHandler::DiscoverLayers(const string& solution, const string& activeTarget) { + RpcArgs::DiscoverLayersInfo result = {{ false }}; + string csolutionFile = solution; + if (!CheckSolutionArg(csolutionFile, result.message)) { + return result; + } + if (!m_manager.SetupContexts(csolutionFile, activeTarget)) { + result.message = "Setup of solution contexts failed"; + return result; + } + m_worker.SetUpCommand(true); + StrVec layers; + StrSet fails; + if (!m_worker.ListLayers(layers, "", fails) || !m_worker.ElaborateVariablesConfigurations()) { + result.message = "No compatible software layer found. Review required connections of the project"; + return result; + } else { + // retrieve valid configurations + vector vcVec; + const auto& processedContexts = m_worker.GetProcessedContexts(); + for (const auto& context : processedContexts) { + if (!context->variablesConfigurations.empty()) { + for (const auto& configuration : context->variablesConfigurations) { + RpcArgs::VariablesConfiguration vc; + vector lvVec; + for (const auto& variable : configuration.variables) { + RpcArgs::LayerVariable lv = { variable.name, variable.clayer }; + vector settings; + for (const auto& s : variable.settings) { + if (!s.set.empty()) { + settings.push_back(RpcArgs::SettingsType{s.set}); + } + } + if (!settings.empty()) { + lv.settings = settings; + } + if (!variable.path.empty()) { + lv.path = variable.path; + } + if (!variable.file.empty()) { + lv.file = variable.file; + } + if (!variable.copyTo.empty()) { + lv.copyTo = variable.copyTo; + } + lvVec.push_back(lv); + } + vc.variables = lvVec; + vcVec.push_back(vc); + } + // break after first project context with possible variable configurations + // solutions with undefined layer variables over multiple projects are currently not supported + break; + } + } + if (!vcVec.empty()) { + result.configurations = vcVec; + } + result.success = true; + return result; + } +} + // end of ProkMgrRpcServer.cpp diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index da9ad4011..aafe456a2 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -5858,3 +5858,93 @@ bool ProjMgrWorker::IsLibOnly(const std::vector& contexts) { } return true; } + +const vector& ProjMgrWorker::GetProcessedContexts(void) { + m_processedContexts.clear(); + for (auto& contextName : m_selectedContexts) { + auto& contextItem = m_contexts[contextName]; + m_processedContexts.push_back(&contextItem); + } + return m_processedContexts; +} + +bool ProjMgrWorker::ElaborateVariablesConfigurations() { + bool configurationFound = false; + const auto& processedContexts = GetProcessedContexts(); + for (const auto& context : processedContexts) { + // collect layer connection info specific to each context + if (context->validConnections.size() > 0) { + map>>> configurations; + int index = 0; + for (const auto& combination : context->validConnections) { + index++; + for (const auto& item : combination) { + if (item.type.empty()) + continue; + for (const auto& [type, _] : context->compatibleLayers) { + // add all required layer types, including discarded optionals + configurations[index][type].insert({}); + } + for (const auto& connect : item.connections) { + configurations[index][item.type][item.filename].insert(connect); + } + } + } + // process connection info + if (configurations.size() > 0) { + for (const auto& [index, types] : configurations) { + VariablesConfiguration configuration; + for (const auto& [type, filenames] : types) { + if (type.empty()) { + continue; + } + LayerVariable variable; + variable.name = context->layerVariables.find(type) != context->layerVariables.end() ? + context->layerVariables.at(type) : type + "-Layer"; + if (filenames.empty()) { + configuration.variables.push_back(variable); + } else { + for (const auto& [filename, options] : filenames) { + string packRoot = ProjMgrKernel::Get()->GetCmsisPackRoot(); + variable.clayer = filename; + RteFsUtils::NormalizePath(variable.clayer); + variable.clayer = RteFsUtils::MakePathCanonical(variable.clayer); + // Replace with ${CMSIS_PACK_ROOT} or $SolutionDir()$ depending on the detected path + size_t index = variable.clayer.find(packRoot); + if (index != string::npos) { + variable.clayer.replace(index, packRoot.length(), "${CMSIS_PACK_ROOT}"); + } else { + string relPath = RteFsUtils::RelativePath(filename, context->csolution->directory); + if (!relPath.empty()) { + variable.clayer = "$" + string(RteConstants::AS_SOLUTION_DIR_BR) + "$/" + RteFsUtils::LexicallyNormal(relPath); + } + } + if (m_parser->GetGenericClayers().find(filename) != m_parser->GetGenericClayers().end()) { + const auto& clayer = m_parser->GetGenericClayers().at(filename); + variable.description = clayer.description; + } + for (const auto& connect : options) { + if (!connect->set.empty()) { + SettingsType settings; + settings.set = connect->set + " (" + connect->connect + (connect->info.empty() ? "" : " - " + connect->info) + ")"; + variable.settings.push_back(settings); + } + } + if (context->packLayers.find(filename) != context->packLayers.end()) { + const auto& clayer = context->packLayers.at(filename); + variable.path = clayer->GetOriginalAbsolutePath(clayer->GetPathString()); + variable.file = clayer->GetFileString(); + variable.copyTo = clayer->GetCopyToString(); + } + configuration.variables.push_back(variable); + } + } + } + context->variablesConfigurations.push_back(configuration); + configurationFound = true; + } + } + } + } + return configurationFound; +} diff --git a/tools/projmgr/test/data/TestLayers/ref/rpc-discover-layers.json b/tools/projmgr/test/data/TestLayers/ref/rpc-discover-layers.json new file mode 100644 index 000000000..e5f6aace0 --- /dev/null +++ b/tools/projmgr/test/data/TestLayers/ref/rpc-discover-layers.json @@ -0,0 +1,149 @@ +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "configurations": [ + { + "variables": [ + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config1.clayer.yml", + "copy-to": "path/to/config1", + "file": "config1.clayer.yml", + "name": "Config1-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0/Layers", + "settings": [ + { + "set": "set1.select1 (connect A - set 1 select 1)" + }, + { + "set": "set2.select1 (connect C - set 2 select 1)" + } + ] + }, + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config2.clayer.yml", + "copy-to": "path/to/config2", + "file": "Layers/config2.clayer.yml", + "name": "Config2-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0", + "settings": [ + { + "set": "set1.select1 (connect F - set 1 select 1)" + } + ] + }, + { + "clayer": "", + "name": "Incompatible-Layer" + } + ] + }, + { + "variables": [ + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config1.clayer.yml", + "copy-to": "path/to/config1", + "file": "config1.clayer.yml", + "name": "Config1-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0/Layers", + "settings": [ + { + "set": "set1.select1 (connect A - set 1 select 1)" + }, + { + "set": "set2.select1 (connect C - set 2 select 1)" + } + ] + }, + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config3.clayer.yml", + "copy-to": "path/to/config3", + "file": "Layers/config3.clayer.yml", + "name": "Config2-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0", + "settings": [ + { + "set": "set3.select1 (connect F - set 3 select 1)" + } + ] + }, + { + "clayer": "", + "name": "Incompatible-Layer" + } + ] + }, + { + "variables": [ + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config1.clayer.yml", + "copy-to": "path/to/config1", + "file": "config1.clayer.yml", + "name": "Config1-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0/Layers", + "settings": [ + { + "set": "set1.select2 (connect B - set 1 select 2)" + }, + { + "set": "set2.select2 (connect D - set 2 select 2)" + } + ] + }, + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config2.clayer.yml", + "copy-to": "path/to/config2", + "file": "Layers/config2.clayer.yml", + "name": "Config2-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0", + "settings": [ + { + "set": "set1.select2 (connect G - set 1 select 2)" + } + ] + }, + { + "clayer": "", + "name": "Incompatible-Layer" + } + ] + }, + { + "variables": [ + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config1.clayer.yml", + "copy-to": "path/to/config1", + "file": "config1.clayer.yml", + "name": "Config1-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0/Layers", + "settings": [ + { + "set": "set1.select2 (connect B - set 1 select 2)" + }, + { + "set": "set2.select2 (connect D - set 2 select 2)" + } + ] + }, + { + "clayer": "${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Layers/config3.clayer.yml", + "copy-to": "path/to/config3", + "file": "Layers/config3.clayer.yml", + "name": "Config2-Layer", + "path": "${DEVTOOLS(packs)}/ARM/RteTest_DFP/0.2.0", + "settings": [ + { + "set": "set3.select2 (connect G - set 3 select 2)" + } + ] + }, + { + "clayer": "", + "name": "Incompatible-Layer" + } + ] + } + ], + "success": true + } +} diff --git a/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp b/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp index 0ec8872d6..804feffd9 100644 --- a/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp @@ -90,14 +90,7 @@ TEST_F(ProjMgrGeneratorUnitTests, GenFiles) { const string generatorInputFile = testinput_folder + "/TestSolution/tmp/TestProject3_1.Debug+TypeA.cbuild-gen.yml"; const string generatedGPDSC = testinput_folder + "/TestSolution/TestProject3_1/gendir/RteTestGen_ARMCM0/RteTest.gpdsc"; - auto stripAbsoluteFunc = [](const std::string& in) { - std::string str = in; - RteUtils::ReplaceAll(str, testinput_folder, "${DEVTOOLS(data)}"); - RteUtils::ReplaceAll(str, testcmsispack_folder, "${DEVTOOLS(packs)}"); - return str; - }; - - ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/ref/TestProject3_1.Debug+TypeA.cbuild-gen.yml", generatorInputFile, stripAbsoluteFunc); + ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/ref/TestProject3_1.Debug+TypeA.cbuild-gen.yml", generatorInputFile, ProjMgrTestEnv::StripAbsoluteFunc); EXPECT_EQ(true, std::filesystem::exists(generatorInputFile)); EXPECT_EQ(true, std::filesystem::exists(generatedGPDSC)); @@ -190,14 +183,7 @@ TEST_F(ProjMgrGeneratorUnitTests, DryRun) { EXPECT_EQ(0, ProjMgr::RunProjMgr(7, argv, envp)); - auto stripAbsoluteFunc = [](const std::string& in) { - std::string str = in; - RteUtils::ReplaceAll(str, testinput_folder, "${DEVTOOLS(data)}"); - RteUtils::ReplaceAll(str, testcmsispack_folder, "${DEVTOOLS(packs)}"); - return str; - }; - - ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/ref/TestProject3_1.Debug+TypeA.cbuild-gen.yml", generatorInputFile, stripAbsoluteFunc); + ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/ref/TestProject3_1.Debug+TypeA.cbuild-gen.yml", generatorInputFile, ProjMgrTestEnv::StripAbsoluteFunc); EXPECT_EQ(true, std::filesystem::exists(generatorInputFile)); EXPECT_EQ(false, std::filesystem::exists(rteDir + "/Device")); diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp index 1e2d9231a..ccf29541c 100644 --- a/tools/projmgr/test/src/ProjMgrRpcTests.cpp +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -31,6 +31,7 @@ class ProjMgrRpcTests : public ProjMgr, public ::testing::Test { const string& activeTarget = RteUtils::EMPTY_STRING, const vector& contextList = RteUtils::EMPTY_STRING_VECTOR ); + bool CompareRpcResponse(const json& response, const string& ref); }; @@ -69,6 +70,13 @@ string ProjMgrRpcTests::CreateLoadRequests(const string& solution, const string& return FormatRequest(1, "LoadPacks") + loadSolutionRequest; } +bool ProjMgrRpcTests::CompareRpcResponse(const json& response, const string& ref) { + ifstream fileRef(ref); + json jsonRef; + fileRef >> jsonRef; + return ProjMgrTestEnv::StripAbsoluteFunc(response.dump()) == jsonRef.dump(); +} + vector ProjMgrRpcTests::RunRpcMethods(const string& strIn) { StdStreamRedirect streamRedirect; streamRedirect.SetInString(strIn); @@ -797,4 +805,30 @@ TEST_F(ProjMgrRpcTests, RpcConvertSolution) { EXPECT_FALSE(responses[0]["result"]["success"]); EXPECT_EQ(responses[0]["result"]["undefinedLayers"][0], "NotDefined"); } + +TEST_F(ProjMgrRpcTests, RpcDiscoverLayers) { + auto csolutionPath = testinput_folder + "/TestLayers/config.csolution.yml"; + auto requests = FormatRequest(1, "DiscoverLayers", + json({ { "solution", csolutionPath }, { "activeTarget", "" } })); + auto responses = RunRpcMethods(requests); + + // valid configurations: compare response with golden reference + EXPECT_TRUE(CompareRpcResponse(responses[0], testinput_folder + "/TestLayers/ref/rpc-discover-layers.json")); + + // unknown active target set + csolutionPath = testinput_folder + "/TestLayers/config.csolution.yml"; + requests = FormatRequest(1, "DiscoverLayers", + json({ { "solution", csolutionPath }, { "activeTarget", "unknown" } })); + responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[0]["result"]["success"]); + EXPECT_EQ(responses[0]["result"]["message"], "Setup of solution contexts failed"); + + // no compatible layer combination + csolutionPath = testinput_folder + "/TestLayers/variables-notdefined.csolution.yml"; + requests = FormatRequest(1, "DiscoverLayers", + json({ { "solution", csolutionPath }, { "activeTarget", "" } })); + responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[0]["result"]["success"]); + EXPECT_EQ(responses[0]["result"]["message"], "No compatible software layer found. Review required connections of the project"); +} // end of ProjMgrRpcTests.cpp diff --git a/tools/projmgr/test/src/ProjMgrTestEnv.cpp b/tools/projmgr/test/src/ProjMgrTestEnv.cpp index 8283da05e..6c1d189e0 100644 --- a/tools/projmgr/test/src/ProjMgrTestEnv.cpp +++ b/tools/projmgr/test/src/ProjMgrTestEnv.cpp @@ -163,6 +163,13 @@ void ProjMgrTestEnv::TearDown() { CleanUpEnvp(); } +std::string ProjMgrTestEnv::StripAbsoluteFunc(const std::string& in) { + std::string str = in; + RteUtils::ReplaceAll(str, testinput_folder, "${DEVTOOLS(data)}"); + RteUtils::ReplaceAll(str, testcmsispack_folder, "${DEVTOOLS(packs)}"); + return str; +}; + void ProjMgrTestEnv::CompareFile(const string& file1, const string& file2, LineReplaceFunc_t file2LineReplaceFunc) { ifstream f1, f2; string l1, l2; diff --git a/tools/projmgr/test/src/ProjMgrTestEnv.h b/tools/projmgr/test/src/ProjMgrTestEnv.h index 380d1c3d8..29d82e833 100644 --- a/tools/projmgr/test/src/ProjMgrTestEnv.h +++ b/tools/projmgr/test/src/ProjMgrTestEnv.h @@ -60,6 +60,7 @@ class ProjMgrTestEnv : public ::testing::Environment { public: void SetUp() override; void TearDown() override; + static std::string StripAbsoluteFunc(const std::string& in); static void CompareFile(const std::string& file1, const std::string& file2, LineReplaceFunc_t file2LineReplaceFunc = nullptr); static const std::string& GetCmsisPackRoot(); static std::map GetEffectivePdscFiles(bool bLatestsOnly = false); diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index 12920492f..48a386a7b 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -1726,13 +1726,8 @@ TEST_F(ProjMgrUnitTests, ListLayersConfigurations_update_idx_pack_layer) { EXPECT_EQ(0, RunProjMgr(6, argv, m_envp)); EXPECT_TRUE(regex_match(streamRedirect.GetOutString(), regex(expectedOutStr))); - auto stripAbsoluteFunc = [](const std::string& in) { - std::string str = in; - RteUtils::ReplaceAll(str, testcmsispack_folder, "${DEVTOOLS(packs)}"); - return str; - }; ProjMgrTestEnv::CompareFile(testinput_folder + "/TestLayers/ref/config.cbuild-idx.yml", - testinput_folder + "/TestLayers/config.cbuild-idx.yml", stripAbsoluteFunc); + testinput_folder + "/TestLayers/config.cbuild-idx.yml", ProjMgrTestEnv::StripAbsoluteFunc); EXPECT_TRUE(ProjMgrYamlSchemaChecker().Validate(testinput_folder + "/TestLayers/config.cbuild-idx.yml")); } @@ -5422,39 +5417,32 @@ TEST_F(ProjMgrUnitTests, ExternalGenerator) { argv[6] = (char*)"core0.Debug+MultiCore"; EXPECT_EQ(0, RunProjMgr(7, argv, m_envp)); - auto stripAbsoluteFunc = [](const std::string& in) { - std::string str = in; - RteUtils::ReplaceAll(str, testinput_folder, "${DEVTOOLS(data)}"); - RteUtils::ReplaceAll(str, testcmsispack_folder, "${DEVTOOLS(packs)}"); - return str; - }; - ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/MultiCore/extgen.cbuild-gen-idx.yml", - testinput_folder + "/ExternalGenerator/tmp/extgen.cbuild-gen-idx.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/extgen.cbuild-gen-idx.yml", ProjMgrTestEnv::StripAbsoluteFunc); ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/MultiCore/core0.Debug+MultiCore.cbuild-gen.yml", - testinput_folder + "/ExternalGenerator/tmp/core0.Debug+MultiCore.cbuild-gen.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/core0.Debug+MultiCore.cbuild-gen.yml", ProjMgrTestEnv::StripAbsoluteFunc); ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/MultiCore/core1.Debug+MultiCore.cbuild-gen.yml", - testinput_folder + "/ExternalGenerator/tmp/core1.Debug+MultiCore.cbuild-gen.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/core1.Debug+MultiCore.cbuild-gen.yml", ProjMgrTestEnv::StripAbsoluteFunc); // run single-core argv[6] = (char*)"single-core.Debug+CM0"; EXPECT_EQ(0, RunProjMgr(7, argv, m_envp)); ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/SingleCore/extgen.cbuild-gen-idx.yml", - testinput_folder + "/ExternalGenerator/tmp/extgen.cbuild-gen-idx.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/extgen.cbuild-gen-idx.yml", ProjMgrTestEnv::StripAbsoluteFunc); ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/SingleCore/single-core.Debug+CM0.cbuild-gen.yml", - testinput_folder + "/ExternalGenerator/tmp/single-core.Debug+CM0.cbuild-gen.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/single-core.Debug+CM0.cbuild-gen.yml", ProjMgrTestEnv::StripAbsoluteFunc); // run trustzone argv[6] = (char*)"ns.Debug+CM0"; EXPECT_EQ(0, RunProjMgr(7, argv, m_envp)); ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/TrustZone/extgen.cbuild-gen-idx.yml", - testinput_folder + "/ExternalGenerator/tmp/extgen.cbuild-gen-idx.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/extgen.cbuild-gen-idx.yml", ProjMgrTestEnv::StripAbsoluteFunc); ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/TrustZone/ns.Debug+CM0.cbuild-gen.yml", - testinput_folder + "/ExternalGenerator/tmp/ns.Debug+CM0.cbuild-gen.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/ns.Debug+CM0.cbuild-gen.yml", ProjMgrTestEnv::StripAbsoluteFunc); ProjMgrTestEnv::CompareFile(testinput_folder + "/ExternalGenerator/ref/TrustZone/s.Debug+CM0.cbuild-gen.yml", - testinput_folder + "/ExternalGenerator/tmp/s.Debug+CM0.cbuild-gen.yml", stripAbsoluteFunc); + testinput_folder + "/ExternalGenerator/tmp/s.Debug+CM0.cbuild-gen.yml", ProjMgrTestEnv::StripAbsoluteFunc); // convert single core argv[2] = (char*)"convert";