From 6402853d4f21d6a57a105886eb6eeb1e09ebf4bd Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Mon, 16 Mar 2026 11:25:38 +0100 Subject: [PATCH] [devtools] Handle automatic rebase of config files (#1444) * [devtools] Handle automatic rebase of config files * Address review comments --- libs/rtefsutils/include/RteFsUtils.h | 11 +++- libs/rtefsutils/src/RteFsUtils.cpp | 12 +++- libs/rtefsutils/test/src/RteFsUtilsTest.cpp | 21 ++++++- libs/rtemodel/src/RteProject.cpp | 15 ++++- libs/rtemodel/test/src/RteModelTest.cpp | 7 ++- tools/projmgr/include/ProjMgrUtils.h | 9 +-- tools/projmgr/src/ProjMgrUtils.cpp | 15 +---- tools/projmgr/src/ProjMgrYamlEmitter.cpp | 6 +- .../Device/RteTest_ARMCM3/startup_ARMCM3.c | 1 + .../startup_ARMCM3.c.base@1.0.0 | 63 +++++++++++++++++++ .../ConfigFilesUpdate/config.csolution.yml | 3 +- .../startup_ARMCM0.c.base@2.0.2 | 2 +- tools/projmgr/test/src/ProjMgrUnitTests.cpp | 15 +++++ .../test/src/ProjMgrUtilsUnitTests.cpp | 10 +-- 14 files changed, 145 insertions(+), 45 deletions(-) create mode 100644 tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c create mode 100644 tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c.base@1.0.0 diff --git a/libs/rtefsutils/include/RteFsUtils.h b/libs/rtefsutils/include/RteFsUtils.h index d6dedb52d..f8e2e4cf1 100644 --- a/libs/rtefsutils/include/RteFsUtils.h +++ b/libs/rtefsutils/include/RteFsUtils.h @@ -8,7 +8,7 @@ */ /******************************************************************************/ /* - * Copyright (c) 2020-2024 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -84,6 +84,15 @@ class RteFsUtils * @return true if copying file is processed */ static bool CopyMergeFile(const std::string& src, const std::string& dst, int nInstance, bool backup); + + /** + * @brief compare files content with normalized line endings + * @param fileName1 name of first file to be compared + * @param fileName2 name of second file to be compared + * @return true if files are equal + */ + static bool CmpFiles(const std::string& fileName1, const std::string& fileName2); + /** * @brief compare file content with given string 'buffer' * @param fileName name of file to be compared diff --git a/libs/rtefsutils/src/RteFsUtils.cpp b/libs/rtefsutils/src/RteFsUtils.cpp index 6e9e0f2f8..0a49d982e 100644 --- a/libs/rtefsutils/src/RteFsUtils.cpp +++ b/libs/rtefsutils/src/RteFsUtils.cpp @@ -6,7 +6,7 @@ */ /******************************************************************************/ /* - * Copyright (c) 2020-2021 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -104,6 +104,16 @@ bool RteFsUtils::ReadFile(const string& fileName, string& buffer) { return true; } +bool RteFsUtils::CmpFiles(const string& fileName1, const string& fileName2) { + + // Open files and read content + string fileBuffer1, fileBuffer2; + if (!ReadFile(fileName1, fileBuffer1) || !ReadFile(fileName2, fileBuffer2)) { + return false; + } + // Compare buffer contents with normalized line endings + return RteUtils::EnsureLf(fileBuffer1) == RteUtils::EnsureLf(fileBuffer2); +} bool RteFsUtils::CmpFileMem(const string& fileName, const string& buffer) { diff --git a/libs/rtefsutils/test/src/RteFsUtilsTest.cpp b/libs/rtefsutils/test/src/RteFsUtilsTest.cpp index 0d69797ef..1277c6411 100644 --- a/libs/rtefsutils/test/src/RteFsUtilsTest.cpp +++ b/libs/rtefsutils/test/src/RteFsUtilsTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -165,6 +165,25 @@ TEST_F(RteFsUtilsTest, BackupFile) { RteFsUtils::RemoveFile(filenameRegular); } +TEST_F(RteFsUtilsTest, CmpFiles) { + // Test files with same content + RteFsUtils::CreateTextFile(filenameRegular, bufferFoo); + RteFsUtils::CreateTextFile(filenameRegularCopy, bufferFoo); + EXPECT_TRUE(RteFsUtils::CmpFiles(filenameRegular, filenameRegularCopy)); + + // Test files with different content + RteFsUtils::CreateTextFile(filenameRegularCopy, bufferBar); + EXPECT_FALSE(RteFsUtils::CmpFiles(filenameRegular, filenameRegularCopy)); + + // Test missing file2 + RteFsUtils::RemoveFile(filenameRegularCopy); + EXPECT_FALSE(RteFsUtils::CmpFiles(filenameRegular, filenameRegularCopy)); + + // Test missing file1 + RteFsUtils::RemoveFile(filenameRegular); + EXPECT_FALSE(RteFsUtils::CmpFiles(filenameRegular, filenameRegularCopy)); +} + TEST_F(RteFsUtilsTest, CmpFileMem) { bool ret; diff --git a/libs/rtemodel/src/RteProject.cpp b/libs/rtemodel/src/RteProject.cpp index a53570298..8f6d86f54 100644 --- a/libs/rtemodel/src/RteProject.cpp +++ b/libs/rtemodel/src/RteProject.cpp @@ -6,7 +6,7 @@ */ /******************************************************************************/ /* - * Copyright (c) 2020-2025 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -696,8 +696,17 @@ void RteProject::UpdateConfigFileBackups(RteFileInstance* fi, RteItem* f) // copy current file if version differs string updateFile = RteUtils::AppendFileUpdateVersion(absPath, updateVersion); if (!baseFile.empty() && baseVersion != updateVersion) { // only copy update if base exists - RteFsUtils::CopyMergeFile(src, updateFile, fi->GetInstanceIndex(), false); - RteFsUtils::SetFileReadOnly(updateFile, true); + if (RteFsUtils::CmpFiles(baseFile, src)) { + // if base and update contents are equal, rebase it + const string rebaseFile = RteUtils::AppendFileBaseVersion(absPath, updateVersion); + RteFsUtils::MoveFileExAutoRetry(baseFile, rebaseFile); + fi->SetAttribute("version", updateVersion.c_str()); + baseFile = rebaseFile; + updateFile.clear(); + } else { + RteFsUtils::CopyMergeFile(src, updateFile, fi->GetInstanceIndex(), false); + RteFsUtils::SetFileReadOnly(updateFile, true); + } } else { updateFile.clear(); // no need in that } diff --git a/libs/rtemodel/test/src/RteModelTest.cpp b/libs/rtemodel/test/src/RteModelTest.cpp index 9d9e2d4d6..83f617623 100644 --- a/libs/rtemodel/test/src/RteModelTest.cpp +++ b/libs/rtemodel/test/src/RteModelTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -714,10 +714,11 @@ TEST_F(RteModelPrjTest, LoadCprjConfigVer) { const string deviceDir = rteDir + "Device/RteTest_ARMCM3/"; EXPECT_TRUE(RteFsUtils::Exists(deviceDir + "ARMCM3_ac6.sct")); - EXPECT_TRUE(RteFsUtils::Exists(deviceDir + "ARMCM3_ac6.sct.base@1.0.0")); + // content of update file is equal to the base file, rebase the version to 1.2.0 + EXPECT_TRUE(RteFsUtils::Exists(deviceDir + "ARMCM3_ac6.sct.base@1.2.0")); // check if file version is taken from base file (project contains "5.5.5") RteFileInstance* fi = loadedCprjProject->GetFileInstance("CONFIG_FOLDER/Device/RteTest_ARMCM3/ARMCM3_ac6.sct"); - EXPECT_TRUE(fi && fi->GetVersionString() == "1.0.0"); + EXPECT_TRUE(fi && fi->GetVersionString() == "1.2.0"); EXPECT_TRUE(RteFsUtils::Exists(deviceDir + "startup_ARMCM3.c.base@2.0.3")); EXPECT_TRUE(RteFsUtils::Exists(deviceDir + "system_ARMCM3.c.base@1.0.1")); diff --git a/tools/projmgr/include/ProjMgrUtils.h b/tools/projmgr/include/ProjMgrUtils.h index de430adc1..dc121e131 100644 --- a/tools/projmgr/include/ProjMgrUtils.h +++ b/tools/projmgr/include/ProjMgrUtils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -314,13 +314,6 @@ class ProjMgrUtils { */ static const std::string FileTypeFromExtension(const std::string& file); - /** - * @brief normalize line endings for consistent string comparison - * @param input string - * @return output normalized string - */ - static const std::string NormalizeLineEndings(const std::string& in); - /** * @brief convert board string to west board string * @param board string diff --git a/tools/projmgr/src/ProjMgrUtils.cpp b/tools/projmgr/src/ProjMgrUtils.cpp index 2ae6ad586..4b39f7507 100644 --- a/tools/projmgr/src/ProjMgrUtils.cpp +++ b/tools/projmgr/src/ProjMgrUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -486,19 +486,6 @@ const string ProjMgrUtils::FileTypeFromExtension(const string& file) { return RteUtils::EMPTY_STRING; } -const std::string ProjMgrUtils::NormalizeLineEndings(const std::string& in) { - string out = in; - size_t pos = 0; - while ((pos = out.find("\r\n", pos)) != std::string::npos) { - out.replace(pos++, 2, "\n"); - } - pos = 0; - while ((pos = out.find("\r", pos)) != std::string::npos) { - out.replace(pos, 1, "\n"); - } - return out; -} - const std::string ProjMgrUtils::GetWestBoard(const std::string& board) { string westBoard = RteUtils::StripSuffix(RteUtils::StripPrefix(board, "::")); std::transform(westBoard.begin(), westBoard.end(), westBoard.begin(), diff --git a/tools/projmgr/src/ProjMgrYamlEmitter.cpp b/tools/projmgr/src/ProjMgrYamlEmitter.cpp index 3c20ff202..d28d056a5 100644 --- a/tools/projmgr/src/ProjMgrYamlEmitter.cpp +++ b/tools/projmgr/src/ProjMgrYamlEmitter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2024 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -94,8 +94,8 @@ bool ProjMgrYamlEmitter::CompareFile(const string& filename, const YAML::Node& r } YAML::Emitter emitter; const auto& outBuffer = string((emitter << rootNode).c_str()) + '\n'; - return ProjMgrUtils::NormalizeLineEndings(inBuffer) == - ProjMgrUtils::NormalizeLineEndings(outBuffer); + return RteUtils::EnsureLf(inBuffer) == + RteUtils::EnsureLf(outBuffer); } bool ProjMgrYamlEmitter::CompareNodes(const YAML::Node& lhs, const YAML::Node& rhs) { diff --git a/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c b/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c new file mode 100644 index 000000000..c3580fb7c --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c @@ -0,0 +1 @@ +/* Dummy file */ diff --git a/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c.base@1.0.0 b/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c.base@1.0.0 new file mode 100644 index 000000000..881bf92ed --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c.base@1.0.0 @@ -0,0 +1,63 @@ +/****************************************************************************** + * @file startup_ARMCM3.c + * @brief CMSIS-Core(M) Device Startup File for a Cortex-M3 Device + * @version V2.0.3 + * @date 31. March 2020 + ******************************************************************************/ +/* + * Copyright (c) 2009-2020 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined (ARMCM3) + #include "ARMCM3.h" +#else + #error device not specified! +#endif + + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmissing-noreturn" +#endif + +/*---------------------------------------------------------------------------- + Reset Handler called on controller reset + *----------------------------------------------------------------------------*/ +void Reset_Handler(void) +{ +} + +/*---------------------------------------------------------------------------- + Hard Fault Handler + *----------------------------------------------------------------------------*/ +void HardFault_Handler(void) +{ + while(1); +} + +/*---------------------------------------------------------------------------- + Default Handler for Exceptions / Interrupts + *----------------------------------------------------------------------------*/ +void Default_Handler(void) +{ + while(1); +} + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang diagnostic pop +#endif + diff --git a/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/config.csolution.yml b/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/config.csolution.yml index ac541d37c..c537171c7 100644 --- a/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/config.csolution.yml +++ b/tools/projmgr/test/data/TestSolution/ConfigFilesUpdate/config.csolution.yml @@ -7,8 +7,9 @@ solution: - type: Patch - type: Minor - type: Major - - type: BaseUnknown + - type: BaseUnknown - type: Missing + - type: Rebase target-types: - type: RteTest_ARMCM3 diff --git a/tools/projmgr/test/data/TestSolution/TestBaseUpdate/RTE/Device/RteTest_ARMCM0/startup_ARMCM0.c.base@2.0.2 b/tools/projmgr/test/data/TestSolution/TestBaseUpdate/RTE/Device/RteTest_ARMCM0/startup_ARMCM0.c.base@2.0.2 index cf5319abc..2cfd6a878 100644 --- a/tools/projmgr/test/data/TestSolution/TestBaseUpdate/RTE/Device/RteTest_ARMCM0/startup_ARMCM0.c.base@2.0.2 +++ b/tools/projmgr/test/data/TestSolution/TestBaseUpdate/RTE/Device/RteTest_ARMCM0/startup_ARMCM0.c.base@2.0.2 @@ -1,7 +1,7 @@ /****************************************************************************** * @file startup_ARMCM0.c * @brief CMSIS-Core(M) Device Startup File for a Cortex-M0 Device - * @version V2.0.3 + * @version V2.0.2 * @date 31. March 2020 ******************************************************************************/ /* diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index 7e115ce52..a1f709829 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -6627,6 +6627,21 @@ TEST_F(ProjMgrUnitTests, ConfigFilesUpdate) { } } +TEST_F(ProjMgrUnitTests, ConfigFilesUpdateRebase) { + StdStreamRedirect streamRedirect; + char* argv[6]; + const string& csolution = testinput_folder + "/TestSolution/ConfigFilesUpdate/config.csolution.yml"; + argv[1] = (char*)"convert"; + argv[2] = (char*)csolution.c_str(); + argv[3] = (char*)"-c"; + argv[4] = (char*)".Rebase"; + EXPECT_EQ(0, RunProjMgr(5, argv, m_envp)); + EXPECT_TRUE(RteFsUtils::Exists(testinput_folder + "/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c.base@2.0.3")); + EXPECT_FALSE(RteFsUtils::Exists(testinput_folder + "/TestSolution/ConfigFilesUpdate/Rebase/Device/RteTest_ARMCM3/startup_ARMCM3.c.base@1.0.0")); + auto errStr = streamRedirect.GetErrorString(); + EXPECT_FALSE(regex_search(errStr, regex("startup_ARMCM3"))); +} + TEST_F(ProjMgrUnitTests, RegionsFileGeneration) { char* argv[3]; const string& csolution = testinput_folder + "/TestMemoryRegions/regions.csolution.yml"; diff --git a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp index faa25b857..29b47030d 100644 --- a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -531,11 +531,3 @@ TEST_F(ProjMgrUtilsUnitTests, ULLToHex) { EXPECT_EQ("0xDEADBEEF", ProjMgrUtils::ULLToHex(3735928559)); EXPECT_EQ("0xFFFFFFFF", ProjMgrUtils::ULLToHex(4294967295)); } - -TEST_F(ProjMgrUtilsUnitTests, NormalizeLineEndings) { - EXPECT_EQ("abc\ndef\n", ProjMgrUtils::NormalizeLineEndings("abc\r\ndef\r\n")); - EXPECT_EQ("abc\ndef\n", ProjMgrUtils::NormalizeLineEndings("abc\rdef\r")); - EXPECT_EQ("abc\ndef\n", ProjMgrUtils::NormalizeLineEndings("abc\rdef\n")); - EXPECT_EQ("abc\ndef\n", ProjMgrUtils::NormalizeLineEndings("abc\ndef\r")); - EXPECT_EQ("abc\ndef\n", ProjMgrUtils::NormalizeLineEndings("abc\ndef\n")); -}