From c5728d582275306ffdc1befa72f397518745b0ff Mon Sep 17 00:00:00 2001 From: grasci <86058054+grasci-arm@users.noreply.github.com> Date: Mon, 9 Oct 2023 12:54:43 +0100 Subject: [PATCH] [projmgr] Generate *.cbuild-set.yml listing selected contexts --- tools/projmgr/include/ProjMgrParser.h | 23 ++++ tools/projmgr/include/ProjMgrWorker.h | 9 +- tools/projmgr/include/ProjMgrYamlEmitter.h | 7 + tools/projmgr/include/ProjMgrYamlParser.h | 9 ++ .../include/ProjMgrYamlSchemaChecker.h | 3 +- tools/projmgr/schemas/cbuildset.schema.json | 13 ++ tools/projmgr/schemas/common.schema.json | 10 ++ tools/projmgr/src/ProjMgr.cpp | 13 +- tools/projmgr/src/ProjMgrParser.cpp | 9 ++ tools/projmgr/src/ProjMgrWorker.cpp | 30 ++++- tools/projmgr/src/ProjMgrYamlEmitter.cpp | 48 ++++++- tools/projmgr/src/ProjMgrYamlParser.cpp | 45 +++++++ .../projmgr/src/ProjMgrYamlSchemaChecker.cpp | 3 + tools/projmgr/test/CMakeLists.txt | 2 +- .../invalid_keys_test.cbuild-set.yml | 11 ++ .../TestSolution/invalid_test.cbuild-set.yml | 12 ++ .../cbuild/all_contexts_test.cbuild-set.yml | 7 + .../all_contexts_test_with_AC6.cbuild-set.yml | 8 ++ .../specific_contexts_test.cbuild-set.yml | 6 + ...exts_test_without_toolchain.cbuild-set.yml | 5 + tools/projmgr/test/src/ProjMgrUnitTests.cpp | 126 ++++++++++++++++++ .../test/src/ProjMgrWorkerUnitTests.cpp | 4 + .../test/src/ProjMgrYamlParserUnitTest.cpp | 44 ++++++ 23 files changed, 436 insertions(+), 11 deletions(-) create mode 100644 tools/projmgr/schemas/cbuildset.schema.json create mode 100644 tools/projmgr/test/data/TestSolution/invalid_keys_test.cbuild-set.yml create mode 100644 tools/projmgr/test/data/TestSolution/invalid_test.cbuild-set.yml create mode 100644 tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test.cbuild-set.yml create mode 100644 tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test_with_AC6.cbuild-set.yml create mode 100644 tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test.cbuild-set.yml create mode 100644 tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test_without_toolchain.cbuild-set.yml create mode 100644 tools/projmgr/test/src/ProjMgrYamlParserUnitTest.cpp diff --git a/tools/projmgr/include/ProjMgrParser.h b/tools/projmgr/include/ProjMgrParser.h index 08cf8bc9c..460166707 100644 --- a/tools/projmgr/include/ProjMgrParser.h +++ b/tools/projmgr/include/ProjMgrParser.h @@ -402,6 +402,17 @@ struct ClayerItem { GeneratorsItem generators; }; +/** + * @brief cbuildset item containing + * list of contexts, + * toochain used, +*/ +struct CbuildSetItem { + std::string generatedBy; + std::vector contexts; + std::string compiler; +}; + /** * @brief projmgr parser class for public interfacing */ @@ -448,6 +459,12 @@ class ProjMgrParser { */ bool ParseGenericClayer(const std::string& input, bool checkSchema); + /** + * @brief parse cbuild set file + * @param input path to *.cbuild-set.yml file + */ + bool ParseCbuildSet(const std::string& input); + /** * @brief get cdefault * @return cdefault item @@ -478,9 +495,15 @@ class ProjMgrParser { */ std::map& GetGenericClayers(void); + /** + * @brief get cbuildset + * @return cbuildset item + */ + CbuildSetItem& GetCbuildSetItem(void); protected: CdefaultItem m_cdefault; CsolutionItem m_csolution; + CbuildSetItem m_cbuildSet; std::map m_cprojects; std::map m_clayers; std::map m_genericClayers; diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index 0b0a287f7..32fe5ca72 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -574,7 +574,8 @@ class ProjMgrWorker { */ bool ParseContextSelection( const std::vector& contextSelection, - const std::string& contextReplace = RteUtils::EMPTY_STRING); + const std::string& contextReplace = RteUtils::EMPTY_STRING, + const bool checkCbuildSet = false); /** * @brief check if context is selected @@ -605,6 +606,12 @@ class ProjMgrWorker { */ void PrintMissingFilters(void); + /** + * @brief get selected toolchain + * @return string selected toolchain + */ + std::string GetSelectedToochain(void); + protected: ProjMgrParser* m_parser = nullptr; ProjMgrKernel* m_kernel = nullptr; diff --git a/tools/projmgr/include/ProjMgrYamlEmitter.h b/tools/projmgr/include/ProjMgrYamlEmitter.h index da0c48882..00c604e6f 100644 --- a/tools/projmgr/include/ProjMgrYamlEmitter.h +++ b/tools/projmgr/include/ProjMgrYamlEmitter.h @@ -39,6 +39,13 @@ class ProjMgrYamlEmitter { * @return true if executed successfully */ static bool GenerateCbuild(ContextItem* context, const RteGenerator* generator = nullptr); + + /** + * @brief generate cbuild set file + * @param contexts vector with pointers to contexts + * @return true if executed successfully + */ + static bool GenerateCbuildSet(ProjMgrParser& parser, const std::vector contexts, const std::string& selectedCompiler); }; #endif // PROJMGRYAMLEMITTER_H diff --git a/tools/projmgr/include/ProjMgrYamlParser.h b/tools/projmgr/include/ProjMgrYamlParser.h index 41be751f8..08131b51e 100644 --- a/tools/projmgr/include/ProjMgrYamlParser.h +++ b/tools/projmgr/include/ProjMgrYamlParser.h @@ -26,6 +26,7 @@ static constexpr const char* YAML_BUILDTYPES = "build-types"; static constexpr const char* YAML_CATEGORY = "category"; static constexpr const char* YAML_CBUILDS = "cbuilds"; static constexpr const char* YAML_CBUILD = "cbuild"; +static constexpr const char* YAML_CBUILD_SET = "cbuild-set"; static constexpr const char* YAML_CDEFAULT = "cdefault"; static constexpr const char* YAML_CLAYERS = "clayers"; static constexpr const char* YAML_CLAYER = "clayer"; @@ -178,6 +179,13 @@ class ProjMgrYamlParser { bool ParseClayer(const std::string& input, std::map& clayers, bool checkSchema); + /** + * @brief parse cbuild-set + * @param input path to cbuild-set.yml file + * @param reference to store parsed cbuildset item + * @return true if executed successfully + */ + bool ParseCbuildSet(const std::string& input, CbuildSetItem& cbuildSet); protected: void ParseMisc(const YAML::Node& parent, std::vector& misc); void ParseDefine(const YAML::Node& parent, std::vector& define); @@ -211,6 +219,7 @@ class ProjMgrYamlParser { bool ValidateCsolution(const std::string& input, const YAML::Node& root); bool ValidateCproject(const std::string& input, const YAML::Node& root); bool ValidateClayer(const std::string& input, const YAML::Node& root); + bool ValidateCbuildSet(const std::string& input, const YAML::Node& root); bool ValidateKeys(const std::string& input, const YAML::Node& parent, const std::set& keys); bool ValidateSequence(const std::string& input, const YAML::Node& parent, const std::string& seqKey); bool ValidateMapping(const std::string& input, const YAML::Node& parent, const std::string& seqKey); diff --git a/tools/projmgr/include/ProjMgrYamlSchemaChecker.h b/tools/projmgr/include/ProjMgrYamlSchemaChecker.h index 8b4f3acdf..266d861f5 100644 --- a/tools/projmgr/include/ProjMgrYamlSchemaChecker.h +++ b/tools/projmgr/include/ProjMgrYamlSchemaChecker.h @@ -20,7 +20,8 @@ class ProjMgrYamlSchemaChecker { SOLUTION, PROJECT, LAYER, - BUILD + BUILD, + BUILDSET }; /** * @brief class constructor diff --git a/tools/projmgr/schemas/cbuildset.schema.json b/tools/projmgr/schemas/cbuildset.schema.json new file mode 100644 index 000000000..f3c86aeb6 --- /dev/null +++ b/tools/projmgr/schemas/cbuildset.schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/schemas/projmgr/2.1.0/tools/projmgr/schemas/cbuild-set.schema.json", + "title": "CMSIS cbuild-set", + "description": "defines a selected set of build configuration", + "version": "2.1.0", + "properties": { + "cbuild-set": { + "$ref": "./common.schema.json#/definitions/BuildSetDescType" + } + }, + "required": [ "cbuild-set" ] +} diff --git a/tools/projmgr/schemas/common.schema.json b/tools/projmgr/schemas/common.schema.json index f567b0806..31b03f613 100644 --- a/tools/projmgr/schemas/common.schema.json +++ b/tools/projmgr/schemas/common.schema.json @@ -1036,6 +1036,16 @@ "CreatedInfoType": { "type": "string", "pattern": "^.*@\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$" + }, + "BuildSetDescType": { + "type": "object", + "properties": { + "generated-by": { "type": "string", "description": "Tool name along with version information used to generate this application" }, + "contexts": { "$ref": "#/definitions/ArrayOfStrings", "description": "List of fully specified contexts" }, + "compiler": { "type": "string", "description": "Selection of compiler used" } + }, + "additionalProperties": false, + "required": ["generated-by", "contexts"] } } } diff --git a/tools/projmgr/src/ProjMgr.cpp b/tools/projmgr/src/ProjMgr.cpp index 935084cf6..e47c9d933 100644 --- a/tools/projmgr/src/ProjMgr.cpp +++ b/tools/projmgr/src/ProjMgr.cpp @@ -438,8 +438,10 @@ bool ProjMgr::RunConfigure(bool printConfig) { if (!PopulateContexts()) { return false; } + + bool checkCbuildSet = ((m_context.size() > 0) || (!m_selectedToolchain.empty()) ? false : true); // Parse context selection - if (!m_worker.ParseContextSelection(m_context, m_contextReplacement)) { + if (!m_worker.ParseContextSelection(m_context, m_contextReplacement, checkCbuildSet)) { return false; } // Get context pointers @@ -469,6 +471,7 @@ bool ProjMgr::RunConfigure(bool printConfig) { m_processedContexts.push_back(&contextItem); } } + m_selectedToolchain = m_worker.GetSelectedToochain(); // Print warnings for missing filters m_worker.PrintMissingFilters(); if (m_verbose) { @@ -491,6 +494,13 @@ bool ProjMgr::RunConfigure(bool printConfig) { } } + // Generate cbuild-set file + if (!m_processedContexts.empty()) { + if (!m_emitter.GenerateCbuildSet(m_parser, m_processedContexts, m_selectedToolchain)) { + return false; + } + } + return !error; } @@ -526,6 +536,7 @@ bool ProjMgr::RunConvert(void) { return false; } } + return !error; } diff --git a/tools/projmgr/src/ProjMgrParser.cpp b/tools/projmgr/src/ProjMgrParser.cpp index 3c58cb468..c633f7050 100644 --- a/tools/projmgr/src/ProjMgrParser.cpp +++ b/tools/projmgr/src/ProjMgrParser.cpp @@ -49,6 +49,11 @@ bool ProjMgrParser::ParseGenericClayer(const string& input, bool checkSchema) { return ProjMgrYamlParser().ParseClayer(input, m_genericClayers, checkSchema); } +bool ProjMgrParser::ParseCbuildSet(const string& input) { + // Parse cbuild-set file + return ProjMgrYamlParser().ParseCbuildSet(input, m_cbuildSet); +} + CdefaultItem& ProjMgrParser::GetCdefault(void) { return m_cdefault; } @@ -68,3 +73,7 @@ map& ProjMgrParser::GetClayers(void) { map& ProjMgrParser::GetGenericClayers(void) { return m_genericClayers; } + +CbuildSetItem& ProjMgrParser::GetCbuildSetItem(void) { + return m_cbuildSet; +} diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 697694aa8..764b02011 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -3511,15 +3511,29 @@ bool ProjMgrWorker::ProcessSequencesRelatives(ContextItem& context, BuildType& b return true; } -bool ProjMgrWorker::ParseContextSelection(const vector& contextSelection, const string& contextReplace) { +bool ProjMgrWorker::ParseContextSelection(const vector& contextSelection, + const string& contextReplace, const bool checkCbuildSet) { vector contexts; ListContexts(contexts); - const auto& filterError = ProjMgrUtils::GetSelectedContexts(m_selectedContexts, contexts, contextSelection); - if (filterError) { - ProjMgrLogger::Error(filterError.m_errMsg); - return false; - } + auto csolutionItem = m_parser->GetCsolution(); + string cbuildSetFile = csolutionItem.directory + "/" + csolutionItem.name + ".cbuild-set.yml"; + if (checkCbuildSet && RteFsUtils::Exists(cbuildSetFile)) { + if (!m_parser->ParseCbuildSet(cbuildSetFile)) { + return false; + } + const auto& cbuildSetItem = m_parser->GetCbuildSetItem(); + m_selectedContexts = cbuildSetItem.contexts; + m_selectedToolchain = cbuildSetItem.compiler; + } + else { + const auto& filterError = ProjMgrUtils::GetSelectedContexts( + m_selectedContexts, contexts, contextSelection); + if (filterError) { + ProjMgrLogger::Error(filterError.m_errMsg); + return false; + } + } auto selectedContexts = m_selectedContexts; if (contextReplace != RteUtils::EMPTY_STRING) { const auto& replaceError = ProjMgrUtils::ReplaceContexts(m_selectedContexts, contexts, contextReplace); @@ -3923,3 +3937,7 @@ bool ProjMgrWorker::ListConfigFiles(vector& configFiles) { } return true; } + +std::string ProjMgrWorker::GetSelectedToochain(void) { + return m_selectedToolchain; +} diff --git a/tools/projmgr/src/ProjMgrYamlEmitter.cpp b/tools/projmgr/src/ProjMgrYamlEmitter.cpp index ce3e91383..5e824d9b5 100644 --- a/tools/projmgr/src/ProjMgrYamlEmitter.cpp +++ b/tools/projmgr/src/ProjMgrYamlEmitter.cpp @@ -42,6 +42,7 @@ class ProjMgrYamlBase { class ProjMgrYamlCbuild : public ProjMgrYamlBase { private: friend class ProjMgrYamlEmitter; + ProjMgrYamlCbuild(YAML::Node node, const vector processedContexts, const string& selectedCompiler); ProjMgrYamlCbuild(YAML::Node node, const ContextItem* context, const RteGenerator* generator); void SetContextNode(YAML::Node node, const ContextItem* context); void SetComponentsNode(YAML::Node node, const ContextItem* context); @@ -134,6 +135,22 @@ ProjMgrYamlCbuild::ProjMgrYamlCbuild(YAML::Node node, const ContextItem* context } } +ProjMgrYamlCbuild::ProjMgrYamlCbuild(YAML::Node node, + const vector processedContexts, const string& selectedCompiler) +{ + vector contexts; + for (const auto& context : processedContexts) { + if (context) { + contexts.push_back(context->name); + } + } + SetNodeValue(node[YAML_GENERATED_BY], ORIGINAL_FILENAME + string(" version ") + VERSION_STRING); + SetNodeValue(node[YAML_CONTEXTS], contexts); + if (!selectedCompiler.empty()) { + SetNodeValue(node[YAML_COMPILER], selectedCompiler); + } +} + void ProjMgrYamlCbuild::SetContextNode(YAML::Node contextNode, const ContextItem* context) { SetNodeValue(contextNode[YAML_GENERATED_BY], ORIGINAL_FILENAME + string(" version ") + VERSION_STRING); @@ -414,7 +431,6 @@ void ProjMgrYamlCbuild::SetLicenseInfoNode(YAML::Node node, const ContextItem* c } } - void ProjMgrYamlCbuild::SetControlsNode(YAML::Node node, const ContextItem* context, const BuildType& controls) { SetNodeValue(node[YAML_OPTIMIZE], controls.optimize); SetNodeValue(node[YAML_DEBUG], controls.debug); @@ -621,3 +637,33 @@ bool ProjMgrYamlEmitter::GenerateCbuild(ContextItem* context, const RteGenerator return cbuild.WriteFile(rootNode, filename); } + +bool ProjMgrYamlEmitter::GenerateCbuildSet(ProjMgrParser& parser, + const std::vector contexts, const string& selectedCompiler) +{ + const string& filename = parser.GetCsolution().directory + "/" + + parser.GetCsolution().name + ".cbuild-set.yml"; + + YAML::Node rootNode; + ProjMgrYamlCbuild cbuild(rootNode[YAML_CBUILD_SET], contexts, selectedCompiler); + + // Compare yaml contents + if (!cbuild.CompareFile(filename, rootNode)) { + ofstream fileStream(filename); + if (!fileStream) { + ProjMgrLogger::Error(filename, "file cannot be written"); + return false; + } + YAML::Emitter emitter; + emitter << rootNode; + fileStream << emitter.c_str(); + fileStream << endl; + fileStream << flush; + fileStream.close(); + ProjMgrLogger::Info(filename, "file generated successfully"); + } + else { + ProjMgrLogger::Info(filename, "file is already up-to-date"); + } + return true; +} diff --git a/tools/projmgr/src/ProjMgrYamlParser.cpp b/tools/projmgr/src/ProjMgrYamlParser.cpp index 488dd7984..25656947d 100644 --- a/tools/projmgr/src/ProjMgrYamlParser.cpp +++ b/tools/projmgr/src/ProjMgrYamlParser.cpp @@ -225,6 +225,31 @@ bool ProjMgrYamlParser::ParseClayer(const string& input, return true; } +bool ProjMgrYamlParser::ParseCbuildSet(const string& input, CbuildSetItem& cbuildSet) { + // Validate file schema + if (!ProjMgrYamlSchemaChecker().Validate( + input, ProjMgrYamlSchemaChecker::FileType::BUILDSET)) { + return false; + } + + try { + const YAML::Node& root = YAML::LoadFile(input); + if (!ValidateCbuildSet(input, root)) { + return false; + } + + const YAML::Node& cbuildSetNode = root[YAML_CBUILD_SET]; + ParseString(cbuildSetNode, YAML_GENERATED_BY, cbuildSet.generatedBy); + ParseVector(cbuildSetNode, YAML_CONTEXTS, cbuildSet.contexts); + ParseString(cbuildSetNode, YAML_COMPILER, cbuildSet.compiler); + } + catch (YAML::Exception& e) { + ProjMgrLogger::Error(input, e.mark.line + 1, e.mark.column + 1, e.msg); + return false; + } + return true; +} + // EnsurePortability checks the presence of backslash, case inconsistency and absolute path // It clears the string 'value' when it is an absolute path void ProjMgrYamlParser::EnsurePortability(const string& file, const YAML::Mark& mark, const string& key, string& value, bool checkExist) { @@ -863,6 +888,12 @@ const set layerKeys = { YAML_GENERATORS, }; +const set cbuildSetKeys = { + YAML_GENERATED_BY, + YAML_CONTEXTS, + YAML_COMPILER, +}; + const set targetTypeKeys = { YAML_TYPE, YAML_DEVICE, @@ -1109,6 +1140,20 @@ bool ProjMgrYamlParser::ValidateClayer(const string& input, const YAML::Node& ro return true; } +bool ProjMgrYamlParser::ValidateCbuildSet(const string& input, const YAML::Node& root) { + const set rootKeys = { + YAML_CBUILD_SET, + }; + if (!ValidateKeys(input, root, rootKeys)) { + return false; + } + const YAML::Node& contextSetNode = root[YAML_CBUILD_SET]; + if (!ValidateKeys(input, contextSetNode, cbuildSetKeys)) { + return false; + } + return true; +} + bool ProjMgrYamlParser::ValidateKeys(const string& input, const YAML::Node& parent, const set& keys) { bool valid = true; for (const auto& item : parent) { diff --git a/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp b/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp index 793aa544c..55b4af05e 100644 --- a/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp +++ b/tools/projmgr/src/ProjMgrYamlSchemaChecker.cpp @@ -74,6 +74,9 @@ bool ProjMgrYamlSchemaChecker::GetSchemaFile(string& schemaFile, const ProjMgrYa case ProjMgrYamlSchemaChecker::FileType::BUILD: schemaFileName = "cbuild.schema.json"; break; + case ProjMgrYamlSchemaChecker::FileType::BUILDSET: + schemaFileName = "cbuildset.schema.json"; + break; default: ProjMgrLogger::Error("Unknown file type"); return false; diff --git a/tools/projmgr/test/CMakeLists.txt b/tools/projmgr/test/CMakeLists.txt index 119a4daab..0bce14755 100644 --- a/tools/projmgr/test/CMakeLists.txt +++ b/tools/projmgr/test/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(ProjMgrUnitTests src/ProjMgrUnitTests.cpp src/ProjMgrTestEnv.cpp src/ProjMgrWorkerUnitTests.cpp src/ProjMgrGeneratorUnitTests.cpp src/ProjMgrSchemaCheckerUnitTests.cpp src/ProjMgrUtilsUnitTests.cpp - src/ProjMgrTestEnv.h) + src/ProjMgrYamlParserUnitTest.cpp src/ProjMgrTestEnv.h) set_property(TARGET ProjMgrUnitTests PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") diff --git a/tools/projmgr/test/data/TestSolution/invalid_keys_test.cbuild-set.yml b/tools/projmgr/test/data/TestSolution/invalid_keys_test.cbuild-set.yml new file mode 100644 index 000000000..74486d1b3 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/invalid_keys_test.cbuild-set.yml @@ -0,0 +1,11 @@ +cbuild-set: + generated-by: csolution version 2.1.0 + + # invalid data + output: test1 + contexts: + - test2.Debug+CM0 + - test2.Debug+CM3 + - test1.Debug+CM0 + - test1.Release+CM0 + diff --git a/tools/projmgr/test/data/TestSolution/invalid_test.cbuild-set.yml b/tools/projmgr/test/data/TestSolution/invalid_test.cbuild-set.yml new file mode 100644 index 000000000..61a46cfa2 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/invalid_test.cbuild-set.yml @@ -0,0 +1,12 @@ +cbuild-set: + generated-by: csolution version 2.1.0 + + # invalid entry + toolchain: test + + contexts: + - test2.Debug+CM0 + - test2.Debug+CM3 + - test1.Debug+CM0 + - test1.Release+CM0 + compiler: AC6 diff --git a/tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test.cbuild-set.yml b/tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test.cbuild-set.yml new file mode 100644 index 000000000..e8b742849 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test.cbuild-set.yml @@ -0,0 +1,7 @@ +cbuild-set: + generated-by: csolution version 2.1.0 + contexts: + - test2.Debug+CM0 + - test2.Debug+CM3 + - test1.Debug+CM0 + - test1.Release+CM0 diff --git a/tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test_with_AC6.cbuild-set.yml b/tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test_with_AC6.cbuild-set.yml new file mode 100644 index 000000000..e969f1fd0 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ref/cbuild/all_contexts_test_with_AC6.cbuild-set.yml @@ -0,0 +1,8 @@ +cbuild-set: + generated-by: csolution version 2.1.0 + contexts: + - test2.Debug+CM0 + - test2.Debug+CM3 + - test1.Debug+CM0 + - test1.Release+CM0 + compiler: AC6 diff --git a/tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test.cbuild-set.yml b/tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test.cbuild-set.yml new file mode 100644 index 000000000..82867385f --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test.cbuild-set.yml @@ -0,0 +1,6 @@ +cbuild-set: + generated-by: csolution version 2.1.0 + contexts: + - test2.Debug+CM0 + - test1.Debug+CM0 + compiler: GCC diff --git a/tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test_without_toolchain.cbuild-set.yml b/tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test_without_toolchain.cbuild-set.yml new file mode 100644 index 000000000..2c1d04966 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ref/cbuild/specific_contexts_test_without_toolchain.cbuild-set.yml @@ -0,0 +1,5 @@ +cbuild-set: + generated-by: csolution version 2.1.0 + contexts: + - test2.Debug+CM0 + - test1.Debug+CM0 diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index e50eb21ff..3ef28afa4 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -47,8 +47,20 @@ class ProjMgrUnitTests : public ProjMgr, public ::testing::Test { EXPECT_EQ(tree1, tree2); } + void RemoveCbuildSetFile(const string& csolutionFile) { + string fileName = fs::path(csolutionFile).filename().generic_string(); + fileName = RteUtils::RemoveSuffixByString(fileName, ".csolution."); + const string& cbuildSetFile = fs::path(csolutionFile).parent_path().generic_string() + + "/" + fileName + ".cbuild-set.yml"; + if (RteFsUtils::Exists(cbuildSetFile)) { + RteFsUtils::RemoveFile(cbuildSetFile); + } + } + string UpdateTestSolutionFile(const string& projectFilePath) { string csolutionFile = testinput_folder + "/TestSolution/test_validate_project.csolution.yml"; + RemoveCbuildSetFile(csolutionFile); + YAML::Node root = YAML::LoadFile(csolutionFile); root["solution"]["projects"][0]["project"] = projectFilePath; std::ofstream fout(csolutionFile); @@ -607,6 +619,8 @@ TEST_F(ProjMgrUnitTests, RunProjMgrSolutionNonExistentContext) { char* argv[8]; // convert --solution solution.yml const string& csolution = testinput_folder + "/TestSolution/test.csolution.yml"; + RemoveCbuildSetFile(csolution); + argv[1] = (char*)"convert"; argv[2] = (char*)"--solution"; argv[3] = (char*)csolution.c_str(); @@ -2800,6 +2814,8 @@ TEST_F(ProjMgrUnitTests, ListComponents_MultiplePackSelection) { TEST_F(ProjMgrUnitTests, Convert_ValidationResults_Dependencies) { char* argv[6]; const string& csolution = testinput_folder + "/Validation/dependencies.csolution.yml"; + RemoveCbuildSetFile(csolution); + argv[1] = (char*)"convert"; argv[2] = (char*)"--solution"; argv[3] = (char*)csolution.c_str(); @@ -2814,6 +2830,7 @@ TEST_F(ProjMgrUnitTests, Convert_ValidationResults_Dependencies) { }; for (const auto& [context, expected] : testData) { + RemoveCbuildSetFile(csolution); StdStreamRedirect streamRedirect; argv[5] = (char*)context.c_str(); EXPECT_EQ(0, RunProjMgr(6, argv, 0)); @@ -3165,26 +3182,31 @@ TEST_F(ProjMgrUnitTests, SelectToolchains) { argv[6] = (char*)"-t"; argv[7] = (char*)"AC6@6.20.0"; + RemoveCbuildSetFile(csolution); EXPECT_EQ(0, RunProjMgr(8, argv, envp)); ProjMgrTestEnv::CompareFile(testoutput_folder + "/toolchain.Debug+Target.cprj", testinput_folder + "/TestSolution/ref/toolchains/toolchain.Debug+Target.cprj.ac6_6_20_0"); argv[7] = (char*)"AC6@6.16.1"; + RemoveCbuildSetFile(csolution); EXPECT_EQ(0, RunProjMgr(8, argv, envp)); ProjMgrTestEnv::CompareFile(testoutput_folder + "/toolchain.Debug+Target.cprj", testinput_folder + "/TestSolution/ref/toolchains/toolchain.Debug+Target.cprj.ac6_6_16_1"); argv[7] = (char*)"AC6@6.6.5"; + RemoveCbuildSetFile(csolution); EXPECT_EQ(0, RunProjMgr(8, argv, envp)); ProjMgrTestEnv::CompareFile(testoutput_folder + "/toolchain.Debug+Target.cprj", testinput_folder + "/TestSolution/ref/toolchains/toolchain.Debug+Target.cprj.ac6_6_6_5"); argv[7] = (char*)"GCC"; + RemoveCbuildSetFile(csolution); EXPECT_EQ(0, RunProjMgr(8, argv, envp)); ProjMgrTestEnv::CompareFile(testoutput_folder + "/toolchain.Debug+Target.cprj", testinput_folder + "/TestSolution/ref/toolchains/toolchain.Debug+Target.cprj.gcc"); argv[7] = (char*)"IAR"; + RemoveCbuildSetFile(csolution); EXPECT_EQ(0, RunProjMgr(8, argv, envp)); ProjMgrTestEnv::CompareFile(testoutput_folder + "/toolchain.Debug+Target.cprj", testinput_folder + "/TestSolution/ref/toolchains/toolchain.Debug+Target.cprj.iar"); @@ -3226,6 +3248,8 @@ TEST_F(ProjMgrUnitTests, ToolchainRedefinition) { TEST_F(ProjMgrUnitTests, RunProjMgr_LinkerOptions) { char* argv[7]; const string& csolution = testinput_folder + "/TestSolution/LinkerOptions/linker.csolution.yml"; + RemoveCbuildSetFile(csolution); + argv[1] = (char*)"convert"; argv[2] = (char*)csolution.c_str(); argv[3] = (char*)"-c"; @@ -3250,6 +3274,8 @@ TEST_F(ProjMgrUnitTests, RunProjMgr_LinkerOptions) { TEST_F(ProjMgrUnitTests, RunProjMgr_LinkerPriority) { char* argv[7]; const string& csolution = testinput_folder + "/TestSolution/LinkerOptions/linker.csolution.yml"; + RemoveCbuildSetFile(csolution); + argv[1] = (char*)"convert"; argv[2] = (char*)csolution.c_str(); argv[3] = (char*)"-c"; @@ -3269,6 +3295,8 @@ TEST_F(ProjMgrUnitTests, RunProjMgr_LinkerOptions_Redefinition) { char* argv[7]; StdStreamRedirect streamRedirect; const string& csolution = testinput_folder + "/TestSolution/LinkerOptions/linker.csolution.yml"; + RemoveCbuildSetFile(csolution); + argv[1] = (char*)"convert"; argv[2] = (char*)csolution.c_str(); argv[3] = (char*)"-c"; @@ -3428,6 +3456,8 @@ TEST_F(ProjMgrUnitTests, RunProjMgr_UpdateRte) { StdStreamRedirect streamRedirect; string csolutionFile = testinput_folder + "/TestSolution/test.csolution.yml"; + RemoveCbuildSetFile(csolutionFile); + char* argv[9]; argv[0] = (char*)""; argv[1] = (char*)"update-rte"; @@ -3448,6 +3478,7 @@ info csolution: config files for each component:\n\ - .*/TestSolution/TestProject1/RTE/Device/RteTest_ARMCM0/startup_ARMCM0.c \\(base@1.1.1\\) \\(update@2.0.3\\)\n\ - .*/TestSolution/TestProject1/RTE/Device/RteTest_ARMCM0/system_ARMCM0.c \\(base@1.0.0\\)\n\ .*/test.cbuild-idx.yml - info csolution: file generated successfully\n\ +.*/test.cbuild-set.yml - info csolution: file generated successfully\n\ "; auto outStr = streamRedirect.GetOutString(); @@ -3792,6 +3823,8 @@ TEST_F(ProjMgrUnitTests, RunProjMgrSolution_context_replacement) { StdStreamRedirect streamRedirect; // convert --solution solution.yml const string& csolution = testinput_folder + "/TestSolution/test.csolution.yml"; + RemoveCbuildSetFile(csolution); + argv[1] = (char*)"convert"; argv[2] = (char*)"--solution"; argv[3] = (char*)csolution.c_str(); @@ -3855,3 +3888,96 @@ warning csolution: compiler 'Ac6' is not supported\n\ auto errStr = streamRedirect.GetErrorString(); EXPECT_TRUE(errStr.find(expected) != string::npos); } + +TEST_F(ProjMgrUnitTests, RunProjMgrSolution_cbuildset_file) { + char* argv[12]; + + const string& csolutionDir = testinput_folder + "/TestSolution"; + const string& csolution = csolutionDir + "/test.csolution.yml"; + const string& cbuildSetFile = csolutionDir + "/test.cbuild-set.yml"; + const string& outputDir = testoutput_folder + "/cbuildset"; + + auto CleanUp = [&]() { + // Clean residual (if any) + if (RteFsUtils::Exists(outputDir)) { + RteFsUtils::RemoveDir(outputDir); + } + if (RteFsUtils::Exists(cbuildSetFile)) { + RteFsUtils::RemoveFile(cbuildSetFile); + } + }; + + // Test 1: Run with only specified contexts and no cbuild-set file + CleanUp(); + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-c"; + argv[5] = (char*)"test2.Debug+CM0"; + argv[6] = (char*)"-c"; + argv[7] = (char*)"test1.Debug+CM0"; + argv[8] = (char*)"-o"; + argv[9] = (char*)outputDir.c_str(); + argv[10] = (char*)"-t"; + argv[11] = (char*)"GCC"; + EXPECT_EQ(0, RunProjMgr(12, argv, 0)); + + // Check cbuild-set file + EXPECT_TRUE(RteFsUtils::Exists(cbuildSetFile)); + ProjMgrTestEnv::CompareFile(cbuildSetFile, testinput_folder + "/TestSolution/ref/cbuild/specific_contexts_test.cbuild-set.yml"); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM0.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test1.Debug+CM0.cbuild.yml")); + EXPECT_FALSE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM3.cbuild.yml")); + EXPECT_FALSE(RteFsUtils::Exists(outputDir + "/test1.Release+CM0.cbuild.yml")); + + // Test 2: Run without contexts specified (with existing cbuild-set file) + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)outputDir.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check cbuild-set file + EXPECT_TRUE(RteFsUtils::Exists(cbuildSetFile)); + ProjMgrTestEnv::CompareFile(cbuildSetFile, testinput_folder + "/TestSolution/ref/cbuild/specific_contexts_test.cbuild-set.yml"); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM0.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test1.Debug+CM0.cbuild.yml")); + EXPECT_FALSE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM3.cbuild.yml")); + EXPECT_FALSE(RteFsUtils::Exists(outputDir + "/test1.Release+CM0.cbuild.yml")); + + // Test 3: Run without contexts specified (without existing cbuild-set file) + CleanUp(); + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)outputDir.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + + // Check cbuild-set file + EXPECT_TRUE(RteFsUtils::Exists(cbuildSetFile)); + ProjMgrTestEnv::CompareFile(cbuildSetFile, testinput_folder + "/TestSolution/ref/cbuild/all_contexts_test.cbuild-set.yml"); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM0.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test1.Debug+CM0.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM3.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test1.Release+CM0.cbuild.yml")); + + // Test 4: Run with only toolchain specified + argv[1] = (char*)"convert"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-o"; + argv[5] = (char*)outputDir.c_str(); + argv[6] = (char*)"-t"; + argv[7] = (char*)"AC6"; + EXPECT_EQ(0, RunProjMgr(8, argv, 0)); + + // Check cbuild-set file + EXPECT_TRUE(RteFsUtils::Exists(cbuildSetFile)); + ProjMgrTestEnv::CompareFile(cbuildSetFile, testinput_folder + "/TestSolution/ref/cbuild/all_contexts_test_with_AC6.cbuild-set.yml"); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM0.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test1.Debug+CM0.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test2.Debug+CM3.cbuild.yml")); + EXPECT_TRUE(RteFsUtils::Exists(outputDir + "/test1.Release+CM0.cbuild.yml")); +} diff --git a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp index c023d04ba..20f358268 100644 --- a/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp @@ -566,6 +566,8 @@ TEST_F(ProjMgrWorkerUnitTests, LoadFilteredPack_1) { TEST_F(ProjMgrWorkerUnitTests, LoadFilteredPack_2) { CsolutionItem csolution; SetCsolutionPacks(&csolution, { "ARM" }, "Test"); + ProjMgrParser parser; + SetParser(&parser); // get list of available packs vector availablePacks; @@ -588,6 +590,8 @@ TEST_F(ProjMgrWorkerUnitTests, LoadFilteredPack_3) { TEST_F(ProjMgrWorkerUnitTests, LoadFilteredPack_4) { CsolutionItem csolution; SetCsolutionPacks(&csolution, { "ARM::*" }, "Test"); + ProjMgrParser parser; + SetParser(&parser); // get list of available packs vector availablePacks; diff --git a/tools/projmgr/test/src/ProjMgrYamlParserUnitTest.cpp b/tools/projmgr/test/src/ProjMgrYamlParserUnitTest.cpp new file mode 100644 index 000000000..518463302 --- /dev/null +++ b/tools/projmgr/test/src/ProjMgrYamlParserUnitTest.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ProjMgr.h" +#include "ProjMgrTestEnv.h" +#include "ProjMgrYamlParser.h" + +#include "gtest/gtest.h" + +using namespace std; + +class ProjMgrYamlParserUnitTests : public ProjMgrYamlParser, public ::testing::Test { +protected: + ProjMgrYamlParserUnitTests() {} + virtual ~ProjMgrYamlParserUnitTests() {} +}; + +TEST_F(ProjMgrYamlParserUnitTests, ParseCbuildSet) { + string cbuildSetFile = testinput_folder + "/TestSolution/invalid_test.cbuild-set.yml"; + CbuildSetItem buildSetItem; + EXPECT_FALSE(ParseCbuildSet(cbuildSetFile, buildSetItem)); + + cbuildSetFile = testinput_folder + "/TestSolution/ref/cbuild/specific_contexts_test.cbuild-set.yml"; + EXPECT_TRUE(ParseCbuildSet(cbuildSetFile, buildSetItem)); + EXPECT_EQ(buildSetItem.contexts.size(), 2); + EXPECT_EQ(buildSetItem.contexts[0], "test2.Debug+CM0"); + EXPECT_EQ(buildSetItem.contexts[1], "test1.Debug+CM0"); + EXPECT_EQ(buildSetItem.compiler, "GCC"); + + EXPECT_FALSE(ParseCbuildSet("unkownfile.cbuild-set.yml", buildSetItem)); +} + +TEST_F(ProjMgrYamlParserUnitTests, ValidateCbuildSet) { + string cbuildSetFile = testinput_folder + "/TestSolution/invalid_keys_test.cbuild-set.yml"; + YAML::Node root = YAML::LoadFile(cbuildSetFile); + EXPECT_FALSE(ValidateCbuildSet(cbuildSetFile, root)); + + YAML::Node invalidRoot; + invalidRoot["processor"] = "invalid"; + EXPECT_FALSE(ValidateCbuildSet(cbuildSetFile, invalidRoot)); +}