diff --git a/libs/rtemodel/include/RteGenerator.h b/libs/rtemodel/include/RteGenerator.h index f0ab8646e..35993e964 100644 --- a/libs/rtemodel/include/RteGenerator.h +++ b/libs/rtemodel/include/RteGenerator.h @@ -130,17 +130,19 @@ class RteGenerator : public RteItem * @brief get all arguments as vector for the given host type * @param target pointer to RteTarget * @param hostType host type, empty to match current host + * @param dryRun include dry-run arguments * @return vector of arguments consisting of switch and value in pairs */ - std::vector > GetExpandedArguments(RteTarget* target, const std::string& hostType = EMPTY_STRING) const; + std::vector > GetExpandedArguments(RteTarget* target, const std::string& hostType = EMPTY_STRING, bool dryRun = false) const; /** * @brief get full command line with arguments and expanded key sequences for specified target * @param target pointer to RteTarget * @param hostType host type, empty to match current host + * @param dryRun include dry-run arguments * @return expanded command line with arguments, properly quoted */ - std::string GetExpandedCommandLine(RteTarget* target, const std::string& hostType = EMPTY_STRING) const; + std::string GetExpandedCommandLine(RteTarget* target, const std::string& hostType = EMPTY_STRING, bool dryRun = false) const; /** * @brief get absolute path to gpdsc file for specified target @@ -208,6 +210,13 @@ class RteGenerator : public RteItem */ RteItem* CreateItem(const std::string& tag) override; + /** + * @brief check whether the generator can be run in dry-run mode + * @param hostType host type, empty to match current host + * @return true if the generator is dry-run capable, false otherwise + */ + bool IsDryRunCapable(const std::string& hostType = EMPTY_STRING) const; + protected: /** * @brief construct generator ID diff --git a/libs/rtemodel/src/RteGenerator.cpp b/libs/rtemodel/src/RteGenerator.cpp index 0b1bfcb21..093affab2 100644 --- a/libs/rtemodel/src/RteGenerator.cpp +++ b/libs/rtemodel/src/RteGenerator.cpp @@ -142,7 +142,7 @@ string RteGenerator::GetExecutable(RteTarget* target, const std::string& hostTyp } -vector > RteGenerator::GetExpandedArguments(RteTarget* target, const string& hostType) const +vector > RteGenerator::GetExpandedArguments(RteTarget* target, const string& hostType, bool dryRun) const { vector > args; RteItem* argsItem = GetArgumentsItem("exe"); @@ -150,15 +150,17 @@ vector > RteGenerator::GetExpandedArguments(RteTarget* targ for (auto arg : argsItem->GetChildren()) { if (arg->GetTag() != "argument" || !arg->MatchesHost(hostType)) continue; + if (!dryRun && arg->GetAttribute("mode") == "dry-run") + continue; args.push_back({arg->GetAttribute("switch"), ExpandString(arg->GetText())}); } } return args; } -string RteGenerator::GetExpandedCommandLine(RteTarget* target, const string& hostType) const +string RteGenerator::GetExpandedCommandLine(RteTarget* target, const string& hostType, bool dryRun) const { - const vector > args = GetExpandedArguments(target, hostType); + const vector > args = GetExpandedArguments(target, hostType, dryRun); string fullCmd = GetExecutable(target, hostType); for (size_t i = 0; i < args.size(); i++) { fullCmd += ' ' + RteUtils::AddQuotesIfSpace(args[i].first + args[i].second); @@ -265,6 +267,20 @@ RteItem* RteGenerator::CreateItem(const std::string& tag) } +bool RteGenerator::IsDryRunCapable(const std::string& hostType) const +{ + RteItem* argsItem = GetArgumentsItem("exe"); + if (argsItem) { + for (auto arg : argsItem->GetChildren()) { + if (arg->GetTag() != "argument" || !arg->MatchesHost(hostType)) + continue; + if (arg->GetAttribute("mode") == "dry-run") + return true; + } + } + return false; +} + RteGeneratorContainer::RteGeneratorContainer(RteItem* parent) : RteItem(parent) { diff --git a/libs/rtemodel/test/src/RteChkTest.cpp b/libs/rtemodel/test/src/RteChkTest.cpp index 8c610786b..ac1bb34f5 100644 --- a/libs/rtemodel/test/src/RteChkTest.cpp +++ b/libs/rtemodel/test/src/RteChkTest.cpp @@ -30,9 +30,9 @@ Generic: 1\n\ DFP: 3\n\ BSP: 1\n\ \n\ -Components: 51\n\ +Components: 52\n\ From generic packs: 31\n\ -From DFP: 20\n\ +From DFP: 21\n\ From BSP: 0\n\ \n\ Devices: 10\n\ @@ -51,7 +51,7 @@ completed\n"; int res = rteChk.RunCheckRte(); EXPECT_EQ(res, 0); EXPECT_EQ(rteChk.GetPackCount(), 5); - EXPECT_EQ(rteChk.GetComponentCount(), 51); + EXPECT_EQ(rteChk.GetComponentCount(), 52); EXPECT_EQ(rteChk.GetDeviceCount(), 10); EXPECT_EQ(rteChk.GetBoardCount(), 13); diff --git a/test/packs/ARM/RteTestGenerator/0.1.0/ARM.RteTestGenerator.pdsc b/test/packs/ARM/RteTestGenerator/0.1.0/ARM.RteTestGenerator.pdsc index a9fc97f31..96d0285e9 100644 --- a/test/packs/ARM/RteTestGenerator/0.1.0/ARM.RteTestGenerator.pdsc +++ b/test/packs/ARM/RteTestGenerator/0.1.0/ARM.RteTestGenerator.pdsc @@ -53,6 +53,7 @@ $S $G + --dry-run bar bar bar @@ -105,6 +106,29 @@ + + + RteTest Generator Description + + $PRTE/Device + + Generator/script.bat + Generator/script.sh + Generator/script.sh + + + $D + + #P + + $S + + $G + + + + + @@ -157,6 +181,9 @@ + + Configuration via RteTest script + Configuration via RteTest script diff --git a/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.bat b/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.bat index 239285984..9379b1386 100644 --- a/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.bat +++ b/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.bat @@ -3,7 +3,14 @@ echo %1 echo %2 echo %3 echo %4 +echo %5 -mkdir %1 -copy "%3\Templates\RteTest.gpdsc.template" "%1\RteTest.gpdsc" -copy "%3\Templates\RteTest_Generated_Component.c.template" "%1\RteTest_Generated_Component.c" +if "%5" == "--dry-run" ( + echo -----BEGIN GPDSC----- + type "%3\Templates\RteTest.gpdsc.template" + echo -----END GPDSC----- +) else ( + mkdir %1 + copy "%3\Templates\RteTest.gpdsc.template" "%1\RteTest.gpdsc" + copy "%3\Templates\RteTest_Generated_Component.c.template" "%1\RteTest_Generated_Component.c" +) diff --git a/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.sh b/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.sh index bbb8c7718..9025199ca 100755 --- a/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.sh +++ b/test/packs/ARM/RteTestGenerator/0.1.0/Generator/script.sh @@ -3,7 +3,14 @@ echo $1 echo $2 echo $3 echo $4 +echo $5 -mkdir $1 -cp "$3/Templates/RteTest.gpdsc.template" "$1/RteTest.gpdsc" -cp "$3/Templates/RteTest_Generated_Component.c.template" "$1/RteTest_Generated_Component.c" +if [ "$5" = "--dry-run" ]; then + echo "-----BEGIN GPDSC-----" + cat "$3/Templates/RteTest.gpdsc.template" + echo "-----END GPDSC-----" +else + mkdir $1 + cp "$3/Templates/RteTest.gpdsc.template" "$1/RteTest.gpdsc" + cp "$3/Templates/RteTest_Generated_Component.c.template" "$1/RteTest_Generated_Component.c" +fi diff --git a/tools/projmgr/include/ProjMgr.h b/tools/projmgr/include/ProjMgr.h index 6cec43867..a3ee00bb9 100644 --- a/tools/projmgr/include/ProjMgr.h +++ b/tools/projmgr/include/ProjMgr.h @@ -108,6 +108,7 @@ class ProjMgr { bool m_updateRteFiles; bool m_verbose; bool m_debug; + bool m_dryRun; bool m_ymlOrder; GroupNode m_files; std::vector m_processedContexts; diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index c6b64820c..9725b2672 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -481,6 +481,12 @@ class ProjMgrWorker { */ void SetDebug(bool debug); + /** + * @brief set dry-run mode + * @param boolean dryRun + */ + void SetDryRun(bool dryRun); + /** * @brief set load packs policy * @param reference to load packs policy @@ -562,6 +568,7 @@ class ProjMgrWorker { bool m_checkSchema; bool m_verbose; bool m_debug; + bool m_dryRun; bool LoadPacks(ContextItem& context); bool GetRequiredPdscFiles(ContextItem& context, const std::string& packRoot, std::set& errMsgs); diff --git a/tools/projmgr/src/ProjMgr.cpp b/tools/projmgr/src/ProjMgr.cpp index 504a7fc0b..9196e9f10 100644 --- a/tools/projmgr/src/ProjMgr.cpp +++ b/tools/projmgr/src/ProjMgr.cpp @@ -38,6 +38,7 @@ Commands:\n\ Options:\n\ -c, --context arg [...] Input context names [][.][+]\n\ -d, --debug Enable debug messages\n\ + -D, --dry-run Enable dry-run\n\ -e, --export arg Set suffix for exporting .cprj retaining only specified versions\n\ -f, --filter arg Filter words\n\ -g, --generator arg Code generator identifier\n\ @@ -130,6 +131,7 @@ int ProjMgr::RunProjMgr(int argc, char **argv, char** envp) { cxxopts::Option version("V,version", "Print version"); cxxopts::Option verbose("v,verbose", "Enable verbose messages", cxxopts::value()->default_value("false")); cxxopts::Option debug("d,debug", "Enable debug messages", cxxopts::value()->default_value("false")); + cxxopts::Option dryRun("D,dry-run", "Enable dry-run", cxxopts::value()->default_value("false")); cxxopts::Option exportSuffix("e,export", "Set suffix for exporting .cprj retaining only specified versions", cxxopts::value()); cxxopts::Option toolchain("t,toolchain","Selection of the toolchain used in the project optionally with version", cxxopts::value()); cxxopts::Option ymlOrder("yml-order", "Preserve order as specified in input yml", cxxopts::value()->default_value("false")); @@ -139,7 +141,7 @@ int ProjMgr::RunProjMgr(int argc, char **argv, char** envp) { // command, optional args, options {"update-rte", { false, {context, debug, load, schemaCheck, toolchain, verbose}}}, {"convert", { false, {context, debug, exportSuffix, load, schemaCheck, noUpdateRte, output, toolchain, verbose}}}, - {"run", { false, {context, debug, generator, load, schemaCheck, verbose}}}, + {"run", { false, {context, debug, generator, load, schemaCheck, verbose, dryRun}}}, {"list packs", { true, {context, debug, filter, load, missing, schemaCheck, toolchain, verbose}}}, {"list boards", { true, {context, debug, filter, load, schemaCheck, toolchain, verbose}}}, {"list devices", { true, {context, debug, filter, load, schemaCheck, toolchain, verbose}}}, @@ -157,7 +159,7 @@ int ProjMgr::RunProjMgr(int argc, char **argv, char** envp) { {"positional", "", cxxopts::value>()}, solution, context, filter, generator, load, clayerSearchPath, missing, schemaCheck, noUpdateRte, output, - help, version, verbose, debug, exportSuffix, toolchain, ymlOrder + help, version, verbose, debug, dryRun, exportSuffix, toolchain, ymlOrder }); options.parse_positional({ "positional" }); @@ -169,7 +171,9 @@ int ProjMgr::RunProjMgr(int argc, char **argv, char** envp) { manager.m_verbose = parseResult.count("v"); manager.m_worker.SetVerbose(manager.m_verbose); manager.m_debug = parseResult.count("d"); + manager.m_dryRun = parseResult.count("D"); manager.m_worker.SetDebug(manager.m_debug); + manager.m_worker.SetDryRun(manager.m_dryRun); manager.m_ymlOrder = parseResult.count("yml-order"); vector positionalArguments; diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 6e9ad31b9..b5bb7ab74 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -37,8 +37,13 @@ static const map> { "IAR", {ProjMgrUtils::IAR_ELF_SUFFIX , ProjMgrUtils::IAR_LIB_PREFIX , ProjMgrUtils::IAR_LIB_SUFFIX }}, }; -ProjMgrWorker::ProjMgrWorker(void) { - m_loadPacksPolicy = LoadPacksPolicy::DEFAULT; +ProjMgrWorker::ProjMgrWorker(void) : + m_loadPacksPolicy(LoadPacksPolicy::DEFAULT), + m_checkSchema(false), + m_verbose(false), + m_debug(false), + m_dryRun(false) +{ RteCondition::SetVerboseFlags(0); } @@ -206,6 +211,10 @@ void ProjMgrWorker::SetDebug(bool debug) { m_debug = debug; } +void ProjMgrWorker::SetDryRun(bool dryRun) { + m_dryRun = dryRun; +} + void ProjMgrWorker::SetLoadPacksPolicy(const LoadPacksPolicy& policy) { m_loadPacksPolicy = policy; } @@ -3190,7 +3199,11 @@ bool ProjMgrWorker::ExecuteGenerator(std::string& generatorId) { ProjMgrLogger::Error("generator file '" + generatorExe + "' cannot be executed, check permissions"); return false; } - const string generatorCommand = generator->GetExpandedCommandLine(context.rteActiveTarget); + if (m_dryRun && !generator->IsDryRunCapable(generatorExe)) { + ProjMgrLogger::Error("generator '" + generatorId + "' is not dry-run capable"); + return false; + } + const string generatorCommand = generator->GetExpandedCommandLine(context.rteActiveTarget, RteUtils::EMPTY_STRING, m_dryRun); error_code ec; const auto& workingDir = fs::current_path(ec); @@ -3638,4 +3651,4 @@ bool ProjMgrWorker::ListConfigFiles(vector& configFiles) { } } return true; -} \ No newline at end of file +} diff --git a/tools/projmgr/test/data/TestSolution/TestProject3_3/TestProject3_3.cproject.yml b/tools/projmgr/test/data/TestSolution/TestProject3_3/TestProject3_3.cproject.yml new file mode 100644 index 000000000..5c6ade70f --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/TestProject3_3/TestProject3_3.cproject.yml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + description: Project 3_3 + components: + - component: Startup + - component: CORE + - component: Device:RteTest Generated Component:RteTestNoDryRun diff --git a/tools/projmgr/test/data/TestSolution/gen_nodryrun.csolution.yml b/tools/projmgr/test/data/TestSolution/gen_nodryrun.csolution.yml new file mode 100644 index 000000000..4c2fedb2e --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/gen_nodryrun.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: TypeA + device: RteTestGen_ARMCM0 + + build-types: + - type: Debug + compiler: GCC@0.0.0 + + projects: + - project: ./TestProject3_3/TestProject3_3.cproject.yml + + output-dirs: + outdir: $Project$/outdir/ diff --git a/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp b/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp index 6bf12b8ba..5eefa1ea4 100644 --- a/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrGeneratorUnitTests.cpp @@ -6,6 +6,7 @@ #include "ProjMgr.h" #include "ProjMgrTestEnv.h" +#include "RteFsUtils.h" #include "gtest/gtest.h" #include #include @@ -98,3 +99,61 @@ TEST_F(ProjMgrGeneratorUnitTests, NoExeFiles) { // but not gpdsc EXPECT_FALSE(std::filesystem::exists(generatedGPDSC)); } + +TEST_F(ProjMgrGeneratorUnitTests, DryRunIncapableGenerator) { + char* argv[7]; + + StdStreamRedirect streamRedirect; + const string& csolution = testinput_folder + "/TestSolution/gen_nodryrun.csolution.yml"; + argv[1] = (char*)"run"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-g"; + argv[5] = (char*)"RteTestGeneratorNoDryRun"; + argv[6] = (char*)"--dry-run"; + + EXPECT_NE(0, ProjMgr::RunProjMgr(7, argv, 0)); + auto outStr = streamRedirect.GetErrorString(); + EXPECT_NE(string::npos, outStr.find("is not dry-run capable")); +} + +TEST_F(ProjMgrGeneratorUnitTests, DryRun) { + char* argv[7]; + + StdStreamRedirect streamRedirect; + const string& csolution = testinput_folder + "/TestSolution/genfiles.csolution.yml"; + argv[1] = (char*)"run"; + argv[2] = (char*)"--solution"; + argv[3] = (char*)csolution.c_str(); + argv[4] = (char*)"-g"; + argv[5] = (char*)"RteTestGeneratorIdentifier"; + argv[6] = (char*)"--dry-run"; + + const string generatorInputFile = testinput_folder + "/TestSolution/TestProject3_1/TestProject3_1.Debug+TypeA.cbuild.yml"; + const string targetGPDSC = testinput_folder + "/TestSolution/TestProject3_1/gendir/RteTestGen_ARMCM0/RteTest.gpdsc"; + + RteFsUtils::RemoveFile(targetGPDSC); + + EXPECT_EQ(0, ProjMgr::RunProjMgr(7, argv, 0)); + + ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/ref/TestProject3_1.Debug+TypeA.cbuild.yml", generatorInputFile); + + EXPECT_EQ(true, std::filesystem::exists(generatorInputFile)); + EXPECT_EQ(false, std::filesystem::exists(targetGPDSC)); + + // Expect that the GPDSC content was printed to stdout, enclosed within the begin and end marks + auto outStr = streamRedirect.GetOutString(); + string beginGpdscMark = "-----BEGIN GPDSC-----\n"; + string endGpdscMark = "-----END GPDSC-----\n"; + auto beginGpdscMarkIndex = outStr.find(beginGpdscMark); + auto endGpdscMarkIndex = outStr.find(endGpdscMark); + EXPECT_NE(string::npos, beginGpdscMarkIndex); + EXPECT_NE(string::npos, endGpdscMarkIndex); + auto gpdscContentIndex = beginGpdscMarkIndex + beginGpdscMark.size(); + auto contentLength = endGpdscMarkIndex - gpdscContentIndex; + string gpdscContent = outStr.substr(gpdscContentIndex, contentLength); + + // Check that the GPDSC content seems OK (the full reference GPDSC file is not easily available from the test for comparison) + EXPECT_EQ(0, gpdscContent.find("