| /***************************************************************************** |
| * Copyright (c) 2019 CEA LIST. |
| * |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * Contributors: |
| * Ansgar Radermacher ansgar.radermacher@cea.fr |
| * |
| *****************************************************************************/ |
| |
| package org.eclipse.papyrus.robotics.ros2.codegen.utils |
| |
| import java.io.File |
| import java.util.ArrayList |
| import java.util.List |
| import org.eclipse.cdt.core.CCProjectNature |
| import org.eclipse.cdt.core.model.CoreModel |
| import org.eclipse.cdt.core.settings.model.CIncludePathEntry |
| import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry |
| import org.eclipse.cdt.core.settings.model.ICSettingEntry |
| import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager |
| import org.eclipse.cdt.newmake.core.IMakeBuilderInfo |
| import org.eclipse.core.resources.IProject |
| import org.eclipse.core.runtime.CoreException |
| import org.eclipse.core.runtime.NullProgressMonitor |
| import org.eclipse.emf.common.util.UniqueEList |
| import org.eclipse.papyrus.designer.languages.common.extensionpoints.LanguageProjectSupport |
| import org.eclipse.papyrus.designer.languages.cpp.codegen.utils.CppClassUtils |
| import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.External |
| import org.eclipse.papyrus.designer.transformation.base.utils.ProjectManagement |
| import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException |
| import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext |
| import org.eclipse.papyrus.robotics.ros2.codegen.RosTransformations |
| import org.eclipse.papyrus.robotics.ros2.codegen.component.CodeSkeleton |
| import org.eclipse.papyrus.robotics.ros2.codegen.component.RoboticsCppCreator |
| import org.eclipse.papyrus.robotics.ros2.preferences.Ros2PreferenceUtils |
| import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil |
| import org.eclipse.uml2.uml.Class |
| import org.eclipse.uml2.uml.Package |
| |
| /** |
| * get or create a CDT project with a given name |
| */ |
| class ProjectTools { |
| /** |
| * get a CDT project with a given name. If the CDT project does not |
| * exist, it will be created. If the project exists, but is not a C++ |
| * CDT project, it will be converted to CDT. |
| * The project will be configured for a colcon build. |
| */ |
| static def IProject getProject(String projectName) { |
| var genProject = ProjectManagement.getNamedProject(projectName) |
| if (genProject !== null && genProject.exists() && genProject.getNature(CCProjectNature.CC_NATURE_ID) === null) { |
| // not a CDT project, force conversion into C++ |
| genProject = null |
| } |
| |
| if ((genProject === null) || !genProject.exists()) { |
| val projectSupport = LanguageProjectSupport.getProjectSupport("C++") |
| genProject = projectSupport.createProject(projectName, TransformationContext.current.modelRoot) |
| if (genProject !== null && !genProject.exists()) { |
| throw new RuntimeException(String.format( |
| "project does not exist" |
| )) |
| } |
| if (genProject !== null) { |
| configureCDT(genProject, projectName.toLowerCase) |
| } else { |
| throw new TransformationException(RosTransformations.USER_CANEL) |
| } |
| } |
| return genProject |
| } |
| |
| /** |
| * Basic configuration of a CDT project for colcon |
| */ |
| static def configureCDT(IProject project, String pkgName) { |
| |
| val mngr = CoreModel.getDefault().getProjectDescriptionManager() |
| var cdesc = mngr.getProjectDescription(project, true) |
| |
| try { |
| // loop over all configurations |
| for (configDescr : cdesc.getConfigurations()) { |
| |
| val main = ManagedBuildManager.getConfigurationForDescription(configDescr) |
| |
| main.setBuildCommand("colcon") |
| val builder = main.getBuilder() |
| builder.setUseDefaultBuildCmd(false) |
| builder.setBuildPath("${workspace_loc:}") |
| builder.setBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_INCREMENTAL, String.format( |
| "build %s %s %s", |
| Ros2PreferenceUtils.colconOptions, |
| Ros2PreferenceUtils.colconPackageOptions, |
| pkgName |
| )) |
| // managedBuild must be set to false, otherwise build path is not taken into account |
| // but once set to false, we can't change include directories via the UI - but |
| // they are still taken into account |
| main.setManagedBuildOn(false) |
| } |
| mngr.setProjectDescription(project, cdesc, true, null) |
| ManagedBuildManager.saveBuildInfo(project, true) |
| } catch (CoreException ce) { |
| throw new RuntimeException(ce.getMessage()) |
| } |
| } |
| |
| /** |
| * Configure the include paths of a CDT project |
| */ |
| static def configureIncludes(IProject project, List<String> depPkgList) { |
| |
| val mngr = CoreModel.getDefault().getProjectDescriptionManager() |
| var cdesc = mngr.getProjectDescription(project, true) |
| |
| if (cdesc !== null) { |
| // cdesc may be null if project setup is incomplete during test |
| try { |
| // loop over all configurations |
| for (configDescr : cdesc.getConfigurations()) { |
| |
| val folderDescription = configDescr.getRootFolderDescription() |
| val languageSettings = folderDescription.getLanguageSettings() |
| |
| val icIncludePaths = new ArrayList<ICLanguageSettingEntry> |
| icIncludePaths.add(new CIncludePathEntry("${workspace_loc:/${ProjName}/src-gen}", 0)) |
| icIncludePaths.add(new CIncludePathEntry("/opt/ros/${ROS_DISTRO}/include", 0)) |
| for (depPkg : depPkgList) { |
| icIncludePaths.add(new CIncludePathEntry(getIncludePath(depPkg), 0)) |
| } |
| |
| // now set include path and preprocessor code |
| for (lang : languageSettings) { |
| for (String ext : lang.getSourceExtensions()) { |
| if (ext.equals("cpp")) { |
| lang.setSettingEntries(ICSettingEntry.INCLUDE_PATH, icIncludePaths) |
| } |
| } |
| } |
| } |
| mngr.setProjectDescription(project, cdesc, true, null) |
| ManagedBuildManager.saveBuildInfo(project, true) |
| } catch (CoreException ce) { |
| throw new RuntimeException(ce.getMessage()) |
| } |
| } |
| } |
| |
| /** |
| * |
| */ |
| static def genCode(IProject project, Class component) { |
| val skeleton = component.nearestPackage.getMember(component.name + CodeSkeleton.POSTFIX) |
| val codeGen = new RoboticsCppCreator(project, "src-skel/", skeleton); |
| val packagesToGenerate = new UniqueEList<Package>(); |
| packagesToGenerate.add(component.nearestPackage); |
| for (incCl : CppClassUtils.includedClassifiers(component)) { |
| if (!StereotypeUtil.isApplied(incCl, External)) { |
| packagesToGenerate.add(incCl.nearestPackage); |
| } |
| } |
| for (pkg : packagesToGenerate) { |
| codeGen.createPackageableElement(pkg, new NullProgressMonitor) |
| } |
| } |
| |
| /** |
| * defaults to workspace path |
| */ |
| static def getIncludePath(String pkgName) { |
| val prefixPath = Ros2PreferenceUtils.standardMessagePath |
| for (pathEntry : prefixPath.split(":")) { |
| val testPath = '''«pathEntry»/include/«pkgName»''' |
| if (new File(testPath).exists()) { |
| return testPath |
| } |
| } |
| return '''${WorkspaceDirPath}/install/«pkgName»/include''' |
| } |
| } |