Commit 434a145a authored by Evgeny Kusmenko's avatar Evgeny Kusmenko

Merge branch 'RosAndAdaptable' into 'master'

CLI, Model updates, assembly of jar with dependencies

See merge request !4
parents 158b1fd9 2186a834
Pipeline #57458 passed with stage
in 2 minutes and 35 seconds
# EMAM2Middleware # EMAM2Middleware
## Problems
This generator needs versions of other EMAM libraries that are not yet distributed via Maven.
Workaround: Download these branches and build each via 'mvn clean install -U -s settings.xml'
* EmbeddedMontiArc/RosPort: https://github.com/EmbeddedMontiArc/EmbeddedMontiArc/tree/RosPort
* EmbeddedMontiArcMath/RosPort: https://github.com/EmbeddedMontiArc/EmbeddedMontiArcMath/tree/RosPort
* EMAM2RosMsg: https://github.com/EmbeddedMontiArc/EMAM2RosMsg
* EMAM2RosCpp: https://github.com/EmbeddedMontiArc/EMAM2RosCpp
* EMAM2Cpp/hellwig: https://github.com/EmbeddedMontiArc/EMAM2Cpp/tree/hellwig
or use this script:
```bash
#!/bin/bash
#fail if any of the commands fail
set -e
git clone https://github.com/EmbeddedMontiArc/EmbeddedMontiArc
git clone https://github.com/EmbeddedMontiArc/EmbeddedMontiArcMath
git clone https://github.com/EmbeddedMontiArc/EMAM2RosMsg
git clone https://github.com/EmbeddedMontiArc/EMAM2RosCpp
git clone https://github.com/EmbeddedMontiArc/EMAM2Cpp
cd EmbeddedMontiArc
git checkout RosPort
mvn clean install -U -s settings.xml
cd ..
cd EmbeddedMontiArcMath
git checkout RosPort
mvn clean install -U -s settings.xml
cd ..
cd EMAM2RosMsg
mvn clean install -U -s settings.xml
cd ..
cd EMAM2RosCpp
mvn clean install -U -s settings.xml
cd ..
cd EMAM2Cpp
git checkout hellwig
mvn clean install -U -s settings.xml
cd ..
```
## Usage ## Usage
### Defining the connection between a component and the middleware ### Defining the connection between a component and the middleware
The connection between middleware and the component is defined as tags on Ports in .tag files. The connection between middleware and the component is defined as tags on Ports in .tag files.
...@@ -65,9 +20,8 @@ Look at GenerationTest::testDistributedTargetGenerator. The component is defined ...@@ -65,9 +20,8 @@ Look at GenerationTest::testDistributedTargetGenerator. The component is defined
1. install needed software: 1. install needed software:
* ROS Kinetic(http://wiki.ros.org/kinetic/Installation) * ROS Kinetic(http://wiki.ros.org/kinetic/Installation)
* CMake(https://cmake.org/) * CMake(https://cmake.org/)
* Armadillo( www.arma.sourceforge.net) * Armadillo 8 or higher( www.arma.sourceforge.net)
* Ubuntu package: libarmadillo-dev * creating a copy of the library named armadillo.h might be necessary.
* creating a copy of the library named armadillo.h might be necessary. Ubuntu location: /usr/include/armadillo
1. source your ros environment(http://wiki.ros.org/ROS/Tutorials/InstallingandConfiguringROSEnvironment , 2.) 1. source your ros environment(http://wiki.ros.org/ROS/Tutorials/InstallingandConfiguringROSEnvironment , 2.)
1. a) run src/test/resources/TargetCompilation.sh from **this project's** root 1. a) run src/test/resources/TargetCompilation.sh from **this project's** root
1. or b) compile a single project by 1. or b) compile a single project by
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<groupId>de.monticore.lang.monticar</groupId> <groupId>de.monticore.lang.monticar</groupId>
<artifactId>embedded-montiarc-math-middleware-generator</artifactId> <artifactId>embedded-montiarc-math-middleware-generator</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.2-SNAPSHOT</version>
<!-- == PROJECT DEPENDENCIES ============================================= --> <!-- == PROJECT DEPENDENCIES ============================================= -->
...@@ -305,6 +305,30 @@ ...@@ -305,6 +305,30 @@
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>jar-with-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>de.monticore.lang.monticar.generator.middleware.DistributedTargetGeneratorCli</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<!-- Source Jar Configuration --> <!-- Source Jar Configuration -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
......
package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosToEmamTagSchema;
import de.monticore.lang.monticar.generator.middleware.impls.CPPGenImpl;
import de.monticore.lang.monticar.generator.middleware.impls.RosCppGenImpl;
import de.monticore.lang.monticar.generator.order.simulator.AbstractSymtab;
import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper;
import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log;
import org.apache.commons.cli.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* java -cp target/embedded-montiarc-math-middleware-generator-0.0.1-SNAPSHOT-jar-with-dependencies.jar \
* de.monticore.lang.monticar.generator.middleware.DistributedTargetGeneratorCli \
* --models-dir=src/test/resources/ \
* --root-model=tests.a.addComp \
* --output-dir=target/cli-test/src \
* --generators=cpp,roscpp
*/
public final class DistributedTargetGeneratorCli {
private static final Option OPTION_MODELS_PATH = Option.builder("m")
.longOpt("models-dir")
.desc("full path to directory with EMAM models")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_ROOT_MODEL = Option.builder("r")
.longOpt("root-model")
.desc("fully qualified name of the root model")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_OUTPUT_PATH = Option.builder("o")
.longOpt("output-dir")
.desc("full path to output directory for tests")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_GENERATORS = Option.builder("g")
.longOpt("generators")
.desc("identifiers for the generators that should be used")
.hasArg(true)
.required(true)
.numberOfArgs(10)
.valueSeparator(',')
.build();
public static final String GENERATOR_CPP = "cpp";
public static final String GENERATOR_ROSCPP = "roscpp";
private DistributedTargetGeneratorCli() {}
public static void main(String[] args) {
System.out.println(Arrays.toString(args));
Options options = getOptions();
CommandLineParser parser = new DefaultParser();
CommandLine cliArgs = parseArgs(options, parser, args);
if (cliArgs != null) {
runGenerator(cliArgs);
}
}
private static Options getOptions() {
Options options = new Options();
options.addOption(OPTION_MODELS_PATH);
options.addOption(OPTION_ROOT_MODEL);
options.addOption(OPTION_OUTPUT_PATH);
options.addOption(OPTION_GENERATORS);
return options;
}
private static Set<String> getGeneratorNames() {
HashSet<String> res = new HashSet<>();
res.add(GENERATOR_CPP);
res.add(GENERATOR_ROSCPP);
return res;
}
private static CommandLine parseArgs(Options options, CommandLineParser parser, String[] args) {
CommandLine cliArgs;
try {
cliArgs = parser.parse(options, args);
} catch (ParseException e) {
Log.error("0x9A1AC: Argument parsing exception", e);
return null;
}
return cliArgs;
}
private static void runGenerator(CommandLine cliArgs) {
String modelsDirPath = expandHomeDir(cliArgs.getOptionValue(OPTION_MODELS_PATH.getOpt()));
if(!Files.isDirectory(Paths.get(modelsDirPath))){
Log.error("0x6444B: Models dir does not exist: " + modelsDirPath);
return;
}
String outputPath = expandHomeDir(cliArgs.getOptionValue(OPTION_OUTPUT_PATH.getOpt()));
String rootModelName = cliArgs.getOptionValue(OPTION_ROOT_MODEL.getOpt());
Set<String> generators = Arrays.stream(cliArgs.getOptionValues(OPTION_GENERATORS.getOpt())).collect(Collectors.toSet());
TaggingResolver taggingResolver = AbstractSymtab.createSymTabAndTaggingResolver(modelsDirPath);
DistributedTargetGenerator generator = new DistributedTargetGenerator();
generator.setGenerationTargetPath(outputPath);
Set<String> validGenNames = getGeneratorNames();
generators.forEach(genName -> {
if (validGenNames.contains(genName)) {
Log.warn("Using generator " + genName);
} else {
Log.error("0xE28B6: Not a valid generator Name:" + genName +".");
}
});
ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.<ExpandedComponentInstanceSymbol>resolve(rootModelName, ExpandedComponentInstanceSymbol.KIND).orElse(null);
if (componentInstanceSymbol == null) {
Log.error("0x5FFAE: The given component cannot be resolved.");
return;
}
if (generators.contains(GENERATOR_CPP)) {
generator.add(new CPPGenImpl(), "cpp");
}
if (generators.contains(GENERATOR_ROSCPP)) {
generator.add(new RosCppGenImpl(), "roscpp");
RosToEmamTagSchema.registerTagTypes(taggingResolver);
TagHelper.resolveTags(taggingResolver, componentInstanceSymbol);
}
try {
generator.generate(componentInstanceSymbol, taggingResolver);
} catch (IOException e) {
Log.error("Error generating files", e);
}
}
private static String expandHomeDir(String path) {
if (path.startsWith("~")) {
return System.getProperty("user.home") + path.substring(1);
}
return path;
}
}
...@@ -51,6 +51,7 @@ public class MiddlewareGenerator extends CMakeGenerator { ...@@ -51,6 +51,7 @@ public class MiddlewareGenerator extends CMakeGenerator {
.filter(fn -> fn.matches("(\\w+(Adapter_))?(" + name + "\\.h)")) .filter(fn -> fn.matches("(\\w+(Adapter_))?(" + name + "\\.h)"))
.filter(fn -> !fn.startsWith("IAdapter")) .filter(fn -> !fn.startsWith("IAdapter"))
.map(fn -> "#include \"" + fn + "\"") .map(fn -> "#include \"" + fn + "\"")
.sorted()
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));
String addAdapters = filesNames.stream() String addAdapters = filesNames.stream()
...@@ -58,6 +59,7 @@ public class MiddlewareGenerator extends CMakeGenerator { ...@@ -58,6 +59,7 @@ public class MiddlewareGenerator extends CMakeGenerator {
.filter(fn -> !fn.startsWith("IAdapter")) .filter(fn -> !fn.startsWith("IAdapter"))
.map(fn -> fn.substring(0, fn.length() - 2)) .map(fn -> fn.substring(0, fn.length() - 2))
.map(fn -> " adapters.push_back(new " + fn + "());") .map(fn -> " adapters.push_back(new " + fn + "());")
.sorted()
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));
String initParams = componentInstanceSymbol.getArguments().stream() String initParams = componentInstanceSymbol.getArguments().stream()
...@@ -87,6 +89,7 @@ public class MiddlewareGenerator extends CMakeGenerator { ...@@ -87,6 +89,7 @@ public class MiddlewareGenerator extends CMakeGenerator {
.filter(fn -> fn.matches("(\\w+(Adapter_))?(" + name + "\\.h)")) .filter(fn -> fn.matches("(\\w+(Adapter_))?(" + name + "\\.h)"))
.filter(fn -> !fn.startsWith("IAdapter")) .filter(fn -> !fn.startsWith("IAdapter"))
.map(fn -> fn.substring(0, fn.length() - 2)) .map(fn -> fn.substring(0, fn.length() - 2))
.sorted()
.collect(Collectors.joining(" ")); .collect(Collectors.joining(" "));
String content = TemplateHelper.getCoordinatorCmakeListsTemplate() String content = TemplateHelper.getCoordinatorCmakeListsTemplate()
......
package de.monticore.lang.monticar.generator.middleware.helpers; package de.monticore.lang.monticar.generator.middleware.helpers;
import de.se_rwth.commons.logging.Log; import de.se_rwth.commons.logging.Log;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils;
import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
...@@ -16,12 +15,13 @@ public class TemplateHelper { ...@@ -16,12 +15,13 @@ public class TemplateHelper {
} }
String tmpStr = ""; String tmpStr = "";
String fullFileName = "src/main/resources/de/monticore/lang/monticar/generator/middleware/" + fileName; String resourceFileName = "/de/monticore/lang/monticar/generator/middleware/" + fileName;
try { try {
tmpStr = FileUtils.readFileToString(new File(fullFileName)); tmpStr = IOUtils.toString(TemplateHelper.class.getResourceAsStream(resourceFileName));
} catch (Exception e) { } catch (Exception e) {
//Not recoverable //Not recoverable
Log.error("Template file not found: " + fullFileName); Log.error("Template file not found: " + resourceFileName);
} }
cache.put(fileName, tmpStr); cache.put(fileName, tmpStr);
......
...@@ -9,6 +9,11 @@ import de.monticore.lang.tagging._symboltable.TaggingResolver; ...@@ -9,6 +9,11 @@ import de.monticore.lang.tagging._symboltable.TaggingResolver;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -34,6 +39,8 @@ public class CPPGenImpl implements GeneratorImpl { ...@@ -34,6 +39,8 @@ public class CPPGenImpl implements GeneratorImpl {
files.add(generatorCPP.generateFile(generateCMake(componentInstanceSymbol))); files.add(generatorCPP.generateFile(generateCMake(componentInstanceSymbol)));
files.addAll(generatorCPP.generateFiles(componentInstanceSymbol, taggingResolver)); files.addAll(generatorCPP.generateFiles(componentInstanceSymbol, taggingResolver));
fixKnownErrors(files);
return files; return files;
} }
...@@ -49,4 +56,21 @@ public class CPPGenImpl implements GeneratorImpl { ...@@ -49,4 +56,21 @@ public class CPPGenImpl implements GeneratorImpl {
cmake.setFileContent(TemplateHelper.getCmakeCppTemplate().replace("${compName}", name)); cmake.setFileContent(TemplateHelper.getCmakeCppTemplate().replace("${compName}", name));
return cmake; return cmake;
} }
private void fixKnownErrors(List<File> files) throws IOException {
for (File f : files) {
Path path = Paths.get(f.getAbsolutePath());
Charset charset = StandardCharsets.UTF_8;
String content = new String(Files.readAllBytes(path), charset);
content = content
//Matrix access problems
.replace("int curIndex = indexLookup(i);","int curIndex = indexLookup(i-1);")
.replace("-1-1", "-1") //not present in emam2cpp master
.replace("] ;","];")
;
Files.write(path, content.getBytes(charset));
}
}
} }
...@@ -4,6 +4,7 @@ import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.Expanded ...@@ -4,6 +4,7 @@ import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.Expanded
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol;
import de.monticore.lang.monticar.generator.roscpp.GeneratorRosCpp; import de.monticore.lang.monticar.generator.roscpp.GeneratorRosCpp;
import de.monticore.lang.tagging._symboltable.TaggingResolver; import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
...@@ -36,8 +37,13 @@ public class RosCppGenImpl implements GeneratorImpl { ...@@ -36,8 +37,13 @@ public class RosCppGenImpl implements GeneratorImpl {
@Override @Override
public boolean willAccept(ExpandedComponentInstanceSymbol componentInstanceSymbol) { public boolean willAccept(ExpandedComponentInstanceSymbol componentInstanceSymbol) {
return componentInstanceSymbol.getPortsList().stream() boolean result = componentInstanceSymbol.getPortsList().stream()
.filter(PortSymbol::isRosPort) .filter(PortSymbol::isRosPort)
.count() > 0; .count() > 0;
if(!result){
Log.warn("GeneratorRosCpp: No ROS Ports found! Ignoring component " + componentInstanceSymbol.getName());
}
return result;
} }
} }
package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.LogConfig;
import de.se_rwth.commons.logging.Finding;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.Assert.assertTrue;
public class CliTest{
private static final String VALID_MODELS_DIR_OPTION = "--models-dir=src/test/resources/";
private static final String VALID_ROOT_MODEL_OPTION = "--root-model=tests.a.addComp";
private static final String VALID_GENERATOR_CPP_OPTION = "--generators=cpp";
private static final String VALID_GENERATOR_ALL_OPTION = "--generators=cpp,roscpp";
private static final String INVALID_MODELS_DIR_OPTION = "--models-dir=src/invalid/resources/";
private static final String INVALID_ROOT_MODEL_OPTION = "--root-model=invalid.invalid.addComp";
private static final String INVALID_GENERATOR_OPTION = "--generators=invalid";
private static final String INVALID_GENERATOR_EMPTY_OPTION = "--generators=";
@BeforeClass
public static void initLog(){
LogConfig.init();
LogConfig.enableFailQuick(false);
}
@Before
public void clearFindings(){
LogConfig.getFindings().clear();
}
@AfterClass
public static void resetLog(){
LogConfig.getFindings().clear();
LogConfig.enableFailQuick(true);
}
@Test
public void testParserError(){
String[] args = {
"invalidParsingArg"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(LogConfig.getFindings().stream().map(Finding::getMsg).anyMatch(msg -> msg.contains("0x9A1AC")));
}
@Test
public void testNoGenerators(){
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
INVALID_GENERATOR_EMPTY_OPTION,
"--output-dir=target/cliTest/NoGenerators/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(LogConfig.getFindings().stream().map(Finding::getMsg).anyMatch(msg -> msg.contains("0xE28B6")));
}
@Test
public void testInvalidGenerator(){
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
INVALID_GENERATOR_OPTION,
"--output-dir=target/cliTest/InvalidGenerators/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(logContains("0xE28B6"));
}
@Test
public void testOneGenerator(){
String targetDir = "target/cliTest/OneGenerator/";
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_CPP_OPTION,
"--output-dir=" + targetDir};
DistributedTargetGeneratorCli.main(args);
String[] positiveFileNames = {
"CMakeLists.txt",
"tests_a_addComp/cpp/tests_a_addComp.h",
"tests_a_addComp/cpp/CMakeLists.txt",
"tests_a_addComp/coordinator/CMakeLists.txt",
"tests_a_addComp/coordinator/Coordinator_tests_a_addComp.cpp"
};
for (String positiveFileName : positiveFileNames) {
assertTrue(Files.exists(Paths.get(targetDir + positiveFileName)));
}
}
@Test
public void testAllGenerators(){
String targetDir = "target/cliTest/AllGenerators/";
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_ALL_OPTION,
"--output-dir=" + targetDir};
DistributedTargetGeneratorCli.main(args);
String[] positiveFileNames = {
"CMakeLists.txt",
"tests_a_addComp/cpp/tests_a_addComp.h",
"tests_a_addComp/cpp/CMakeLists.txt",
"tests_a_addComp/coordinator/CMakeLists.txt",
"tests_a_addComp/coordinator/Coordinator_tests_a_addComp.cpp",
"tests_a_addComp/roscpp/RosAdapter_tests_a_addComp.h",
"tests_a_addComp/roscpp/CMakeLists.txt",
};
for (String positiveFileName : positiveFileNames) {
assertTrue(Files.exists(Paths.get(targetDir + positiveFileName)));
}
}
@Test
public void testInvalidRootModelError(){
String[] args = {
VALID_MODELS_DIR_OPTION,
INVALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_CPP_OPTION,
"--output-dir=target/cliTest/InvalidRootModel/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(logContains("0x5FFAE"));
}
@Test
public void testInvalidModelsDirError(){
String[] args = {
INVALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_CPP_OPTION,
"--output-dir=target/cliTest/InvalidModelsDir/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(logContains("0x6444B"));
}
private boolean logContains(String errorCode){
return LogConfig.getFindings().stream().map(Finding::getMsg).anyMatch(msg -> msg.contains(errorCode));
}
}
package de.monticore.lang.monticar.generator.middleware; package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTComponent;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ComponentSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarcmath.cocos.EmbeddedMontiArcMathCoCos;