Did you know that YAKINDU Statechart Tools Professional Edition brings great additional features to the Open Edition? The newest addition is the headless code generation. In this article, I'll show you, how to integrate it into your favorite build tools.
Using the headless compiler means invoking the supplied launcher script ‘scc’, short for Statechart Compiler. This script resides in the installation folder of your YAKINDU Statechart Tools installation. In case you are using the Open Edition of YAKINDU Statechart Tools, you will need to install the headless code generator from the Professional Edition’s update site, free of charge. The process is explained in the documentation.
Working with scc is greatly simplified when its location is part of your PATH variable. You can either add the location of your YAKINDU SCT (short for YAKINDU Statechart Tools) installation to it manually, or, on Mac/Linux, you could also create a symlink to the scc script in /usr/bin or similar. The latter variant is more elegant and you will not need to modify the path variable on every login. All further examples assume you somehow added scc to your path.
Boring setup stuff aside, let’s get to the exciting part and generate some code together!
To get an overview of scc’s command line options, you can invoke it with -h to get a list, as you might be used to:
# scc -h
Launching /home/rbeckmann/Downloads/yakindu-sctpro/SCT...
OpenJDK 64-Bit Server VM warning: ignoring option PermSize=256m; support was removed in 8.0
--------------------------------------------------------
YAKINDU Statechart Tools Headless Generator ((c) by itemis AG)
Visit http://www.statecharts.org
--------------------------------------------------------
usage: scc [-d <path>] [-h] [-m <path(s)>]
-d,--baseDir <path> Relative/absolute path to the working directory that contains your statechart projects. If not set the current directory is used.
-h Shows help content.
-m,--model <path(s)> A list of comma separated relative/absolute paths to model(s) used during execution. If not set the runtime value of baseDir is used.
ls
defaultSM2.sgen defaultSM.sct defaultSM.sgen main.c
Invoking ‘scc’ will generate both sgen files.
If you only want to execute one of them, the call would look like this:
scc -m defaultSM2.sgen,defaultSM.sct
That’s it, basically. Now let’s explore how to integrate code generation with scc into your build tools.
With Makefiles, you are able to define your compile targets and its dependencies. Every time a dependency is changed, the next run of Make will recompile said target. Using this infrastructure it is pretty simple to generate code from statecharts whenever needed.
Consider the following very basic example project:
The build of this project can be viewed as a dependency tree. To build the executable, we would need to link the object files main.o and defaultSM.o. To create these, we need to compile main.c and defaultSM.c. To have defaultSM.c we need to generate the statechart from defaultSM.sct and defaultSM.sgen – that’s it.
The makefile looks like this:
.PHONY: all
.PHONY: clean
all: main
main: src-gen/DefaultSM.o main.o
gcc -o main src-gen/DefaultSM.o main.o
clean:
rm -rf src src-gen *.o main
main.o: main.c
gcc -Wall -c main.c
src-gen/DefaultSM.o: src-gen/DefaultSM.c
gcc -Wall -c src-gen/DefaultSM.c -o src-gen/DefaultSM.o
src-gen/DefaultSM.c: defaultSM.sct defaultSM.sgen
scc
It’s a good practice to name the default target ‘all’, but you could remove it.
The line main: src-gen/DefaultSM.o main.o states that the file main (the final executable, no file ending is needed on Linux) depends on both these object files. The order is important here, because otherwise the compiler would fail, stating that the header files also generated from the statechart are not available when compiling main.c – you could work around this but this is an easy solution.
Both these dependencies are reinterpreted as targets, which have other dependencies. This is standard make stuff. The most interesting entry is this:
src-gen/DefaultSM.c: defaultSM.sct defaultSM.sgen
scc
This says “to produce src-gen/DefaultSM.c, which depends on defaultSM.sct and defaultSM.sgen, call ‘scc’". Calling scc without arguments is possible here, because the Makefile is located in the project and scc will just generate everything in there.
Adding both the *.sct and the *.sgen file has the additional benefit that a change in either of them results in a re-generation of the statechart sources – for example when you change the options in the *.sgen file.
The clean target removes all object files and the folders src and src-gen, both generated by YAKINDU Statechart Tools. Pay attention when you have sources in ‘src’ as well.
Maven allows you to automatically build and deploy your Java projects. Its operation is mainly revolving around the so-called “build lifecycle” and a number of plugins. One of them is the exec-maven-plugin, which allows the user to execute arbitrary tools.
This time, the project structure is a tad more complicated:
The Greeter-class imports the statemachine sources and adds “Hello World!” to a string everytime the statemachine cycles until it is finalized.
For Maven, instead of a Makefile, there is the pom.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-maven</artifactId>
<packaging>jar</packaging>
<version>0.1.0</version>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>Generate</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>scc</executable>
</configuration>
</plugin>
</plugins>
</build>
</project>
As you can see, it is pretty big. If you have never worked with Maven before, you will likely be overwhelmed. The interesting part is the plugin in the bottom, it specifies which plugin will be used and what it should do – namely, execute scc. Another interesting detail is the binding to a phase inside of the execution block: Maven executes generate-resources before the compile phase, thus the needed sources will be there when we need them.
Apache Ant is another build tool specialized for building Java projects. It uses an xml as well, but one that is a bit less talkative. The exact same project can be built with Ant using the following xml:
<project>
<target name="clean">
<delete dir="build"/>
</target>
<target name="generate">
<exec executable="scc"/>
</target>
<target name="compile" depends="generate">
<mkdir dir="build/classes"/>
<javac srcdir="src/main/java" destdir="build/classes"/>
</target>
<target name="jar" depends="compile">
<mkdir dir="build/jar"/>
<jar destfile="build/jar/HelloWorld.jar" basedir="build/classes">
<manifest>
<attribute name="Main-Class" value="hello.HelloWorld"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java jar="build/jar/HelloWorld.jar" fork="true"/>
</target>
</project>
The important bit is the part with “generate” and that the step “compile” depends on it.
While both Maven and Ant rely on a xml file to specify the build process, Gradle uses a DSL based on Groovy. This allows Gradle to have the shortest build configuration file of the three Java build tools mentioned here. Also, keep in mind that Gradle isn’t only a Java build tool, but can be used to build projects such as C/C++ and Android, as well. This script, named build.gradle, is located in the project root. It will allow you to build the project from the Ant and Maven example.
apply plugin: 'java'
apply plugin: 'application'
mainClassName = 'hello.HelloWorld'
jar {
baseName = 'sct-gradle-example'
version = '0.1.0'
manifest {
attributes 'Main-Class': 'hello.HelloWorld'
}
}
task generateStatechart(type:Exec) {
commandLine 'scc'
}
compileJava.dependsOn(generateStatechart)
Just like with Ant and Maven, the important part is the task specification (generateStatechart) and defining that compiling the Java depends on that task.
As you can see, using the headless code generator in your favorite build tools is quite easy, no matter if you are building embedded software or a huge enterprise project in Java. If you have got the Professional Edition of Statechart Tools, you can of course use the Deep C Integration in headless mode as well. Either way, try it out!