blob: 92acd92c5359eaa35cee31ada2bf8a6101f269f3 [file] [log] [blame]
/*****************************************************************************
* 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'''
}
}