Bug 571655 - [Robotics] Existing components not included as dependencies in system assembly projects

- Calculate full list of components, i.e. also those defined in other packages if a system is present

- Use full list of components for package.xml, use lists of components defined in current package as
  well as full list for CMakeLists

Change-Id: Ia1849325fa622133ec29a2aa4e22fcc14ac5554f
Signed-off-by: Ansgar Radermacher <ansgar.radermacher@cea.fr>
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend
index 128136f..111108b 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (c) 2018 CEA LIST.
+ * Copyright (c) 2018, 2021 CEA LIST.
  * 
  * 
  * All rights reserved. This program and the accompanying materials
@@ -10,6 +10,7 @@
  * Contributors:
  *  Ansgar Radermacher  ansgar.radermacher@cea.fr
  *  Matteo MORELLI      matteo.morelli@cea.fr - Bug #566899
+ *  Ansgar Radermacher  ansgar.radermacher@cea.fr - Bug #571655
  * 
  *****************************************************************************/
 
@@ -39,6 +40,7 @@
 import org.eclipse.papyrus.uml.tools.utils.PackageUtil
 import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil
 import org.eclipse.uml2.uml.Package
+import org.eclipse.uml2.uml.Class
 
 import static extension org.eclipse.papyrus.robotics.codegen.common.utils.PackageTools.*
 import static extension org.eclipse.papyrus.robotics.core.utils.InstanceUtils.*
@@ -51,32 +53,51 @@
 	CreateSkillRelizationPackage skrPkgCreator
 
 	/**
-	 * Create code for a ROS2 package. The passed is used to
-	 * identify the ros2 package (BAD), must be determined
+	 * Create code for a ROS2 package. The passed pkg is used to
+	 * identify the ros2 package
+	 * @param pkg the passed ROS2 package
 	 */
 	def pkgTrafos(Package pkg) {
 		val project = ProjectTools.getProject(TransformationContext.initialSourceRoot.projectName)
 		if (project !== null) {
-			val compDefs = project.compDefs
+			/*
+			 * obtain the list of component definitions defined in the ROS2 package (from the project),
+			 * the list of components defined in the system and the concatenation of these (a system
+			 * does not necessarily instantiate all components defined within the package)
+			 */ 
+			val compDefsInPkg = project.compDefs
+			val compDefsInSys = new ArrayList<Class>();
+			// all component definitions
+			val compDefs = new ArrayList<Class>();
+			compDefs.addAll(compDefsInPkg)
 			val system = project.system
+			if (system !== null) {
+				for (part : system.compInstanceList) {
+					val type = part.type;
+					compDefsInSys.add(type as Class)
+					if (!compDefs.contains(type)) {
+						compDefs.add(type as Class)
+					}
+				} 
+			}
 			val fileAccess = FileSystemAccessFactory.create(project)
-			CreateCompCMakeLists.generate(fileAccess, pkg, compDefs, system)
-			CreateCompPackageXML.generate(fileAccess, pkg, compDefs, system)
-			ProjectTools.configureIncludes(project, MessageUtils.calcDependencies(compDefs))
+			CreateCompCMakeLists.generate(fileAccess, pkg, compDefs, compDefsInPkg, system)
+			CreateCompPackageXML.generate(fileAccess, pkg, compDefs, compDefsInPkg, system)
+			ProjectTools.configureIncludes(project, MessageUtils.calcDependencies(compDefsInSys))
 			if (system !== null) {
 				// generate launch before the component transformations
 				// since the latter remove ports
 				fileAccess.generateLaunch(system)
 			}
 			val ct = new ComponentTransformations(fileAccess, project);
-			for (compDef : compDefs) {
+			for (compDef : compDefsInPkg) {
 				ct.componentTrafo(compDef, msgPkgCreator)
 			}
 			if (!system.uniqueSkills.nullOrEmpty) {
 				skrPkgCreator = new CreateSkillRelizationPackage(system, system.componentList);
 				skrPkgCreator.createSkillRealizationPkg(pkg)
 			}
-			for (compDef : compDefs) {
+			for (compDef : compDefsInPkg) {
 				ct.componentCodegen(compDef, msgPkgCreator)
 			}
 			val cleanup = fileAccess as ICleanUntouchedTmp
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompCMakeLists.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompCMakeLists.xtend
index 28ccee2..fcc3a29 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompCMakeLists.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompCMakeLists.xtend
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (c) 2018 CEA LIST.
+ * Copyright (c) 2018, 2021 CEA LIST.
  *
  *
  * All rights reserved. This program and the accompanying materials
@@ -10,6 +10,7 @@
  * Contributors:
  *  Ansgar Radermacher  ansgar.radermacher@cea.fr
  *  Matteo MORELLI      matteo.morelli@cea.fr - Bug #566899
+ *  Ansgar Radermacher  ansgar.radermacher@cea.fr - Bug #571655
  *
  *****************************************************************************/
  
@@ -30,10 +31,10 @@
 import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.SkillUtils.*
 
 /**
- * Create CMakeLists file for a component definition
+ * Create CMakeLists file for a ROS2 package, containing components and eventually one system
  */
 class CreateCompCMakeLists {
-	static def createCMakeLists(Package pkg, List<Class> components, Class system) '''
+	static def createCMakeLists(Package pkg, List<Class> allComponents, List<Class> componentsInPkg, Class system) '''
 		cmake_minimum_required(VERSION 3.5.0)
 		project(«pkg.pkgName»)
 
@@ -47,8 +48,8 @@
 		endif()
 
 		find_package(ament_cmake REQUIRED)
-		«FOR msg_pkg : CreateCompPackageXML.calcDependencies(components)»
-			find_package(«msg_pkg» REQUIRED)
+		«FOR dep_pkg : CreateCompPackageXML.calcDependencies(pkg, allComponents, componentsInPkg)»
+			find_package(«dep_pkg» REQUIRED)
 		«ENDFOR»
 		«IF !system.uniqueSkills.nullOrEmpty»
 			find_package(«pkg.realizationPackageName» REQUIRED)
@@ -60,7 +61,7 @@
 			${PROJECT_SOURCE_DIR}/src-gen
 		)
 
-		«FOR component : components»
+		«FOR component : componentsInPkg»
 			«val compBaseFile = component.fileName»
 			«IF component.isRegistered»
 				add_library(«component.name»_comp SHARED
@@ -72,13 +73,13 @@
 				target_compile_definitions(«component.name»_comp
 					PRIVATE "«component.name»_shared_library")
 				ament_target_dependencies(«component.name»_comp
-					«component.msgDependencies»
+					«pkg.msgDependencies(component)»
 					«IF !system.uniqueSkills.nullOrEmpty»
 						«pkg.realizationPackageName»
 					«ENDIF»
 				)
 				rclcpp_components_register_nodes(«component.name»_comp "«pkg.pkgName»::«component.name»")
-				
+
 				add_executable(«component.name»_main src-gen/«compBaseFile»_main.cpp)
 				target_link_libraries(«component.name»_main «component.name»_comp)
 				ament_target_dependencies(«component.name»_main rclcpp)
@@ -92,7 +93,7 @@
 					ENDIF»
 				)
 				ament_target_dependencies(«component.name»
-					«component.msgDependencies»
+					«pkg.msgDependencies(component)»
 					«IF !system.uniqueSkills.nullOrEmpty»
 						«pkg.realizationPackageName»
 					«ENDIF»
@@ -104,7 +105,7 @@
 		# «ProtSection.protSection("dependencies")»
 		# «ProtSection.protSection»
 
-		«FOR component : components»
+		«FOR component : componentsInPkg»
 			install(TARGETS
 				«IF component.isRegistered»
 					«component.name»_main
@@ -126,14 +127,15 @@
 		ament_package()
 	'''
 
-	static def msgDependencies(Class component) '''
-		«FOR msg_pkg : CreateCompPackageXML.calcDependencies(Collections.singletonList(component))»
+	static def msgDependencies(Package model, Class component) '''
+		«val compList = Collections.singletonList(component)»
+		«FOR msg_pkg : CreateCompPackageXML.calcDependencies(model, compList, compList)»
 			«msg_pkg»
 		«ENDFOR»
 	'''
 
-	static def generate(IPFileSystemAccess fileAccess, Package pkg, List<Class> components, Class system) {
-		fileAccess.generateFile("CMakeLists.txt", createCMakeLists(pkg, components, system).toString)
+	static def generate(IPFileSystemAccess fileAccess, Package pkg, List<Class> allComponents, List<Class> componentsInPkg, Class system) {
+		fileAccess.generateFile("CMakeLists.txt", createCMakeLists(pkg, allComponents, componentsInPkg, system).toString)
 	}
 
 	static def fileName(NamedElement ne) {
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompPackageXML.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompPackageXML.xtend
index c233bf7..346d3e7 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompPackageXML.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/build/CreateCompPackageXML.xtend
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (c) 2018 CEA LIST.
+ * Copyright (c) 2018, 2021 CEA LIST.
  *
  *
  * All rights reserved. This program and the accompanying materials
@@ -28,12 +28,15 @@
 import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.PackageXMLUtils.*
 import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.SkillUtils.*
 
+/**
+ * Create PackageXML file for a ROS2 package, containing components and eventually one system
+ */
 class CreateCompPackageXML {
 
 	public static String USER = "USER"
-	
-	static def createPackageXML(Package model, List<Class> components, Class system) '''
-		«val entities = entityArray(components, system)»
+
+	static def createPackageXML(Package model, List<Class> allComponents, List<Class> componentsInPkg, Class system) '''
+		«val entities = entityArray(allComponents, system)»
 		<?xml version="1.0"?>
 		<package format="3">
 			<name>«model.pkgName»</name>
@@ -46,14 +49,14 @@
 
 			<buildtool_depend>ament_cmake</buildtool_depend>
 
-			«FOR dependency : calcDependencies(components)»
+			«FOR dependency : model.calcDependencies(allComponents, componentsInPkg)»
 				<build_depend>«dependency»</build_depend>
 			«ENDFOR»
 			«IF !system.uniqueSkills.nullOrEmpty»
 				<build_depend>«model.realizationPackageName»</build_depend>
 			«ENDIF»
 
-			«FOR dependency : calcDependencies(components)»
+			«FOR dependency : model.calcDependencies(allComponents, componentsInPkg)»
 				 <exec_depend>«dependency»</exec_depend>
 			«ENDFOR»
 			«IF !system.uniqueSkills.nullOrEmpty»
@@ -69,14 +72,22 @@
 		</package>
 	'''
 
-	static def calcDependencies(List<Class> components) {
-		val dependencies = MessageUtils.calcDependencies(components)
-		for (component : components) {
+	/**
+	 * Calculate the dependencies of a list of all components and those defined in
+	 * this package. The dependencies includes the message dependencies of the
+	 * components in this package and user-defined dependencies as well as the package
+	 * defining the component itself for all components.
+	 */
+	static def calcDependencies(Package model, List<Class> allComponents, List<Class> componentsInPkg) {
+		val dependencies = MessageUtils.calcDependencies(componentsInPkg)
+		for (component : allComponents) {
 			// add user-defined dependencies defined for a component package
 			for (pkgName : component.dependsPackage) {
-				if (!dependencies.contains(pkgName)) {
-					dependencies.add(pkgName)
-				}
+				dependencies.add(pkgName)
+			}
+			// add component package itself, if not part of the current package
+			if (component.model.pkgName != model.pkgName) {
+				dependencies.add(component.model.pkgName)
 			}
 		}
 		return dependencies
@@ -93,7 +104,7 @@
 		return entities
 	}
 
-	static def generate(IPFileSystemAccess fileAccess, Package pkg, List<Class> components, Class system) {
-		fileAccess.generateFile("package.xml", createPackageXML(pkg, components, system).toString)
+	static def generate(IPFileSystemAccess fileAccess, Package pkg, List<Class> allComponents, List<Class> componentsInPkg, Class system) {
+		fileAccess.generateFile("package.xml", createPackageXML(pkg, allComponents, componentsInPkg, system).toString)
 	}
 }
\ No newline at end of file
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/MessageUtils.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/MessageUtils.xtend
index b5271a3..5787ee1 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/MessageUtils.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/MessageUtils.xtend
@@ -335,10 +335,10 @@
 
 	/**
 	 * Return the list of package names that are required by a list of components
-	 * @param the component list
+	 * @param the component list (a unique list in order to avoid duplicates)
 	 */
 	def static List<String> calcDependencies(List<Class> components) {
-		val list = new ArrayList<String>
+		val list = new UniqueEList<String>
 		list.add("rclcpp")
 		if (components.isRegistered) {
 			list.add("rclcpp_components")
@@ -448,4 +448,4 @@
 		}
 		return availPackages
 	}
-}
+}
\ No newline at end of file