diff --git a/test/local_packs/S/SomeVendor.RteTest.pdsc b/test/local_packs/S/SomeVendor.RteTest.pdsc
index 904c1552d..754388acc 100644
--- a/test/local_packs/S/SomeVendor.RteTest.pdsc
+++ b/test/local_packs/S/SomeVendor.RteTest.pdsc
@@ -30,6 +30,61 @@
+
+
+ Test bundle one
+ https://arm-software.github.io/CMSIS_5/Pack/html/pdsc_components_pg.html#Component_Bundle
+
+ Bundle One, component G0, version differs from bundle
+
+
+
+
+
+
+ Test bundle two
+ https://arm-software.github.io/CMSIS_5/Pack/html/pdsc_components_pg.html#element_bundle
+
+ Bundle Two, component G, version differs from bundle
+
+
+
+
+
+
+ Component G without bundle, but the same class and group as in bundle
+
+
+
+
+
+
+ Test bundle one
+ https://arm-software.github.io/CMSIS_5/Pack/html/pdsc_components_pg.html#Component_Bundle
+
+ Bundle One, component G0, version differs from bundle
+
+
+
+
+
+
+ Test bundle two
+ https://arm-software.github.io/CMSIS_5/Pack/html/pdsc_components_pg.html#element_bundle
+
+ Bundle Two, component G, version differs from bundle
+
+
+
+
+
+
+ Component G without bundle, but the same class and group as in bundle
+
+
+
+
+
diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp
index f94624b5c..f92121a7f 100644
--- a/tools/projmgr/src/ProjMgrWorker.cpp
+++ b/tools/projmgr/src/ProjMgrWorker.cpp
@@ -1911,6 +1911,7 @@ bool ProjMgrWorker::ProcessComponents(ContextItem& context) {
}
map> processedComponents;
+ map>> processedBundles;
for (auto& [item, layer] : context.componentRequirements) {
if (item.component.empty()) {
continue;
@@ -1937,10 +1938,20 @@ bool ProjMgrWorker::ProcessComponents(ContextItem& context) {
return pair.first.compare(aggCompId) == 0;
});
if (itr != processedComponents.end()) {
- // multiple variant of the same component found
+ // multiple variants of the same component found
error = true;
}
+ string bundleId = matchedComponentInstance->GetCbundleName();
+ if (!bundleId.empty()) {
+ string classId = matchedComponentInstance->GetCclassName();
+ processedBundles[classId][bundleId].push_back(item.component);
+ if (processedBundles[classId].size() > 1) {
+ // multiple bundles of the same class found (without considering Cvendor)
+ error = true;
+ }
+ }
+
// Set layer's rtePath attribute
if (!layer.empty()) {
error_code ec;
@@ -2022,13 +2033,34 @@ bool ProjMgrWorker::ProcessComponents(ContextItem& context) {
// check if same component variants are specified multiple times
for (const auto& pair : processedComponents) {
if (pair.second.size() > 1) {
- string errMsg = "multiple variants of the same component are specified:";
+ string errMsg = "multiple Cvariants of the same component are mutually exclusive:";
for (const auto& comp : pair.second) {
errMsg += ("\n - " + comp);
}
ProjMgrLogger::Get().Error(errMsg, context.name);
}
}
+ // check if different bundles are specified for the same class
+ string errMsg;
+ bool hasConflict = false;
+ for (const auto& [classId, bundleMap] : processedBundles) {
+ if (bundleMap.size() > 1) {
+ if (!hasConflict) {
+ errMsg = "components in the same Cclass must belong to the same Cbundle.";
+ hasConflict = true;
+ }
+ errMsg += "\nConflicting Cbundle in Cclass '" + classId + "':";
+ for (const auto& [bundleId, components] : bundleMap) {
+ errMsg += "\n - '" + bundleId + "' component(s):";
+ for (const auto& comp : components) {
+ errMsg += "\n - " + comp;
+ }
+ }
+ }
+ }
+ if (hasConflict) {
+ ProjMgrLogger::Get().Error(errMsg, context.name);
+ }
}
// Add required components into RTE
diff --git a/tools/projmgr/test/data/TestSolution/MultiBundleComponent/TestProject1/testProject1.cproject.yml b/tools/projmgr/test/data/TestSolution/MultiBundleComponent/TestProject1/testProject1.cproject.yml
new file mode 100644
index 000000000..6e1a5be49
--- /dev/null
+++ b/tools/projmgr/test/data/TestSolution/MultiBundleComponent/TestProject1/testProject1.cproject.yml
@@ -0,0 +1,8 @@
+project:
+ components:
+ - component: Startup
+ - component: RteTest:CORE
+ - component: ARM::RteTestBundle:G0
+ - component: ARM::RteTestBundle&BundleOne:G0
+ - component: SomeVendor::RteTestBundle:G0
+ - component: SomeVendor::RteTestBundle&BundleOne:G0
diff --git a/tools/projmgr/test/data/TestSolution/MultiBundleComponent/TestProject2/testProject2.cproject.yml b/tools/projmgr/test/data/TestSolution/MultiBundleComponent/TestProject2/testProject2.cproject.yml
new file mode 100644
index 000000000..41f30489a
--- /dev/null
+++ b/tools/projmgr/test/data/TestSolution/MultiBundleComponent/TestProject2/testProject2.cproject.yml
@@ -0,0 +1,11 @@
+project:
+ components:
+ - component: Startup
+ - component: CORE
+ # Cclass RteTestBundle
+ - component: ARM::RteTestBundle&BundleOne:G0
+ - component: ARM::RteTestBundle&BundleOne:G1
+ - component: SomeVendor::RteTestBundle&BundleTwo:G0
+ # Cclass RteTestBundle2
+ - component: SomeVendor::RteTestBundle2&BundleOne:G0
+ - component: SomeVendor::RteTestBundle2&BundleTwo:G0
diff --git a/tools/projmgr/test/data/TestSolution/MultiBundleComponent/multiBundleComponent.csolution.yml b/tools/projmgr/test/data/TestSolution/MultiBundleComponent/multiBundleComponent.csolution.yml
new file mode 100644
index 000000000..7db5e2aee
--- /dev/null
+++ b/tools/projmgr/test/data/TestSolution/MultiBundleComponent/multiBundleComponent.csolution.yml
@@ -0,0 +1,17 @@
+solution:
+ target-types:
+ - type: CM0
+ device: RteTest_ARMCM0
+
+ build-types:
+ - type: Debug
+ compiler: AC6
+
+ packs:
+ - pack: ARM::RteTest
+ - pack: ARM::RteTest_DFP@0.2.0
+ - pack: SomeVendor::RteTest@0.0.1
+
+ projects:
+ - project: ./TestProject1/testProject1.cproject.yml
+ - project: ./TestProject2/testProject2.cproject.yml
diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp
index 3217ff372..e5500a04d 100644
--- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp
+++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp
@@ -6458,7 +6458,37 @@ TEST_F(ProjMgrUnitTests, RunProjMgr_MultiVariantComponent) {
auto errMsg = streamRedirect.GetErrorString();
EXPECT_NE(string::npos,
- errMsg.find("multiple variants of the same component are specified:\n - Device:Test variant\n - Device:Test variant&Variant name"));
+ errMsg.find("multiple Cvariants of the same component are mutually exclusive:\n - Device:Test variant\n - Device:Test variant&Variant name"));
+}
+
+TEST_F(ProjMgrUnitTests, RunProjMgr_MultiBundleComponent) {
+ StdStreamRedirect streamRedirect;
+ char* argv[8];
+ string csolutionFile = testinput_folder + "/TestSolution/MultiBundleComponent/multiBundleComponent.csolution.yml";
+ argv[1] = (char*)"convert";
+ argv[2] = (char*)"--solution";
+ argv[3] = (char*)csolutionFile.c_str();
+ argv[4] = (char*)"-o";
+ argv[5] = (char*)testoutput_folder.c_str();
+ argv[6] = (char*)"--context";
+
+ argv[7] = (char*)"testProject1";
+ EXPECT_EQ(0, RunProjMgr(8, argv, m_envp));
+ auto errMsg = streamRedirect.GetErrorString();
+ EXPECT_EQ(string::npos, errMsg.find("components from multiple bundles are specified:"));
+ streamRedirect.ClearStringStreams();
+
+ argv[7] = (char*)"testProject2";
+ EXPECT_EQ(1, RunProjMgr(8, argv, m_envp));
+ errMsg = streamRedirect.GetErrorString();
+ EXPECT_NE(string::npos, errMsg.find("components in the same Cclass must belong to the same Cbundle."));
+ EXPECT_NE(string::npos, errMsg.find("Conflicting Cbundle in Cclass 'RteTestBundle':"));
+ EXPECT_NE(string::npos, errMsg.find(" - 'BundleOne' component(s):"));
+ EXPECT_NE(string::npos, errMsg.find(" - ARM::RteTestBundle&BundleOne:G0"));
+ EXPECT_NE(string::npos, errMsg.find("- ARM::RteTestBundle&BundleOne:G1"));
+ EXPECT_NE(string::npos, errMsg.find(" - 'BundleTwo' component(s):"));
+ EXPECT_NE(string::npos, errMsg.find(" - SomeVendor::RteTestBundle&BundleTwo:G0"));
+ EXPECT_NE(string::npos, errMsg.find("Conflicting Cbundle in Cclass 'RteTestBundle2':"));
}
TEST_F(ProjMgrUnitTests, RunProjMgr_ListPacks_ContextSet) {
@@ -7400,37 +7430,26 @@ TEST_F(ProjMgrUnitTests, TargetSetDependencies) {
}
TEST_F(ProjMgrUnitTests, DuplicateComponents) {
- StdStreamRedirect streamRedirect;
-
- const string csolution = testinput_folder + "/TestSolution/DuplicateComponents/duplicateComponents.csolution.yml";
-
- char* argv[9];
- argv[1] = (char*)"convert";
- argv[2] = (char*)"--solution";
- argv[3] = (char*)csolution.c_str();
- argv[4] = (char*)"-o";
- argv[5] = (char*)testoutput_folder.c_str();
- argv[6] = (char*)"--context";
- argv[7] = (char*)"duplicateComponents_cproject";
- argv[8] = (char*)"--no-check-schema";
-
- EXPECT_EQ(1, RunProjMgr(8, argv, m_envp));
- auto errStr = streamRedirect.GetErrorString();
- EXPECT_NE(string::npos, errStr.find("error csolution: conflict: component 'RteTest:CORE' is listed multiple times"));
-
- argv[7] = (char*)"duplicateComponents_clayer";
- EXPECT_EQ(1, RunProjMgr(8, argv, m_envp));
- errStr = streamRedirect.GetErrorString();
- EXPECT_NE(string::npos, errStr.find("error csolution: conflict: component 'RteTest:CORE' is listed multiple times"));
-
- // Run with "--no-check-schema"
- argv[7] = (char*)"duplicateComponents_cproject";
- EXPECT_EQ(1, RunProjMgr(9, argv, m_envp));
- errStr = streamRedirect.GetErrorString();
- EXPECT_NE(string::npos, errStr.find("error csolution: conflict: component 'RteTest:CORE' is listed multiple times"));
-
- argv[7] = (char*)"duplicateComponents_clayer";
- EXPECT_EQ(1, RunProjMgr(9, argv, m_envp));
- errStr = streamRedirect.GetErrorString();
- EXPECT_NE(string::npos, errStr.find("error csolution: conflict: component 'RteTest:CORE' is listed multiple times"));
+ StdStreamRedirect streamRedirect;
+ const string csolution = testinput_folder + "/TestSolution/DuplicateComponents/duplicateComponents.csolution.yml";
+ char* argv[9];
+ argv[1] = (char*)"convert";
+ argv[2] = (char*)"--solution";
+ argv[3] = (char*)csolution.c_str();
+ argv[4] = (char*)"-o";
+ argv[5] = (char*)testoutput_folder.c_str();
+ argv[6] = (char*)"--context";
+ argv[8] = (char*)"--no-check-schema";
+
+ const char* contexts[] = { "duplicateComponents_cproject", "duplicateComponents_clayer" };
+ int argCounts[] = { 8, 9 }; // without/with --no-check-schema
+ for (int argCount : argCounts) {
+ for (const char* context : contexts) {
+ argv[7] = (char*)context;
+ EXPECT_EQ(1, RunProjMgr(argCount, argv, m_envp));
+ auto errStr = streamRedirect.GetErrorString();
+ EXPECT_NE(string::npos, errStr.find("error csolution: conflict: component 'RteTest:CORE' is listed multiple times"));
+ streamRedirect.ClearStringStreams();
+ }
+ }
}