diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 37a1c8716456b8ada6bd3b7c2afc011f59565f20..71fba4155eb9ee80538e42b1746c79a1f19b0ede 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,11 +2,10 @@ variables:
   GIT_SUBMODULE_STRATEGY: recursive
 
 stages:
-  - build
   - test
   - ragdoc_build
   - ragdoc_view
-  - publish
+  - deploy
 
 before_script:
   - export GRADLE_USER_HOME=`pwd`/.gradle
@@ -16,24 +15,46 @@ cache:
     - .gradle/wrapper
     - .gradle/caches
 
-build:
-  image: openjdk:8
-  stage: build
+## Hidden jobs, base configurations
+.test:
+  image: openjdk:11
+  stage: test
   script:
-    - ./gradlew --console=plain --no-daemon assemble
+    - ./gradlew --console=plain --no-daemon test
   artifacts:
+    reports:
+      junit: build/test-results/test/**/TEST-*.xml
     paths:
       - "src/gen"
     expire_in: 1 week
 
-test:
-  image: openjdk:8
-  stage: test
+.publish_dev:
+  image: openjdk:11
+  stage: deploy
   script:
-    - ./gradlew --console=plain --no-daemon test
-  artifacts:
-    reports:
-      junit: build/test-results/test/**/TEST-*.xml
+    - "./gradlew setDevVersionForCI"
+    - "./gradlew publish"
+
+## Real jobs
+test8:
+  extends: .test
+  image: "openjdk:8"
+
+test11:
+  extends: .test
+  image: "openjdk:11"
+
+publish_dev8:
+  extends: .publish_dev
+  image: "openjdk:8"
+  needs:
+    - test8
+
+publish_dev11:
+  extends: .publish_dev
+  image: "openjdk:11"
+  needs:
+    - test11
 
 ragdoc_build:
   image:
@@ -41,7 +62,7 @@ ragdoc_build:
     entrypoint: [""]
   stage: ragdoc_build
   needs:
-    - build
+    - test8
   script:
     - JAVA_FILES=$(find src/ -name '*.java')
     - /ragdoc-builder/start-builder.sh -excludeGenerated -d data/ $JAVA_FILES
@@ -72,10 +93,10 @@ ragdoc_view:
 
 pages:
   image: python:3.8-buster
-  stage: publish
+  stage: deploy
   needs:
     - ragdoc_view
-    - test
+    - test8
   before_script:
     - pip install -U mkdocs mkdocs-macros-plugin mkdocs-git-revision-date-localized-plugin
   script:
diff --git a/build-template.gradle b/build-template.gradle
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/build.gradle b/build.gradle
index 9687674fc55502fca335ea12c00561833b3679b7..32a4835d54d57e38279258795fd60930c5c75499 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,9 +1,11 @@
 plugins {
+    id 'com.github.ben-manes.versions' version '0.42.0'
     id 'java-library'
     id 'application'
-    id 'org.jastadd'
+    id 'org.jastadd' version "${jastaddgradle_version}"
     id 'java'
     id 'idea'
+    id 'maven-publish'
     id 'java-test-fixtures'
 }
 
@@ -11,14 +13,23 @@ ext {
     mainClassName = 'org.jastadd.relast.compiler.RelastSourceToSourceCompiler'
 }
 
+group = 'org.jastadd'
+
 // set the main class name for `gradle run`
 application.mainClassName = "${mainClassName}"
 
-sourceCompatibility = 1.8
-targetCompatibility = 1.8
-
 repositories {
     mavenCentral()
+    maven {
+        name 'gitlab-maven'
+        url 'https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven'
+    }
+}
+
+configurations {
+    grammar2uml
+    relast
+    jss
 }
 
 sourceSets {
@@ -27,56 +38,65 @@ sourceSets {
             srcDir "src/gen/java"
         }
     }
+    main {
+        compileClasspath += sourceSets.model.output
+        resources {
+            srcDir "src/main/jastadd"
+        }
+    }
+    test {
+        runtimeClasspath += sourceSets.model.output
+    }
 }
 
-def versionFile = 'src/main/resources/preprocessor.properties'
-def versionProps = new Properties()
+File genSrc = file("src/gen/java")
+idea.module.generatedSourceDirs += genSrc
+
+def versionFile = 'src/main/resources/RelASTPreprocessorVersion.properties'
+def oldProps = new Properties()
 
 try {
-    file(versionFile).withInputStream { stream -> versionProps.load(stream) }
-    version = versionProps['version']
-} catch (e) {
+    file(versionFile).withInputStream { stream -> oldProps.load(stream) }
+    version = oldProps['version']
+} catch (ignored) {
     // this happens, if either the properties file is not present, or cannot be read from
-    throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e)
+    throw new GradleException("File ${versionFile} not found or unreadable. Aborting.")
 }
 
-task fatJar(type: Jar) {
-    group = "build"
-    archiveAppendix = "fatjar"
-    from sourceSets.main.output
-    from {
-        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
-    }
-    manifest {
-        attributes "Main-Class": "${mainClassName}"
+task newVersion() {
+    doFirst {
+        def props = new Properties()
+        props['version'] = value
+        props.store(file(versionFile).newWriter(), null)
     }
 }
 
-task modelJar(type: Jar) {
-    group = "build"
-    archiveAppendix = "model"
-    from sourceSets.model.output
+task printVersion() {
+    doLast {
+        println(version)
+    }
 }
 
-artifacts {
-    archives modelJar
-    archives fatJar
+task setDevVersionForCI() {
+    doFirst {
+        def props = new Properties()
+        props['version'] = version + "-$System.env.CI_PIPELINE_IID"
+        props.store(file(versionFile).newWriter(), null)
+    }
 }
 
 dependencies {
 
-    modelImplementation group: 'org.jastadd', name: 'jastadd', version: '2.3.5'
     modelImplementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
 
-    implementation files(modelJar.archiveFile.get())
+    grammar2uml group: 'de.tudresden.inf.st', name: 'grammar2uml', version: "${grammar2uml_version}"
+    relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
+
     api group: 'org.jastadd', name: 'jastadd', version: '2.3.5'
     api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
     implementation group: 'com.github.jknack', name: 'handlebars', version: '4.3.0'
     implementation group: 'org.yaml', name: 'snakeyaml', version: '1.27'
 
-    // test
-    testRuntimeClasspath files(modelJar.archiveFile.get())
-
     // test fixtures
     testFixturesApi group: 'org.slf4j', name: 'slf4j-jdk14', version: '1.7.30'
     testFixturesApi group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0'
@@ -110,8 +130,9 @@ def relastOutputFiles = [
 ]
 
 task relast(type: JavaExec) {
-    classpath = files("libs/relast.jar")
     group = 'Build'
+    classpath = configurations.relast
+    mainClass = 'org.jastadd.relast.compiler.Compiler'
 
     doFirst {
         delete relastOutputFiles
@@ -190,12 +211,34 @@ jastadd {
     jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
 }
 
-generateAst.dependsOn relast
+// publish gitlab project
+publishing {
+    publications {
+        maven(MavenPublication) {
+            artifactId = project.getName() + (JavaVersion.current() == JavaVersion.VERSION_1_8 ? '-java8' : '')
 
-clean.dependsOn(cleanGen)
+            from components.java
+        }
+    }
+    repositories {
+        maven {
+            url "https://git-st.inf.tu-dresden.de/api/v4/projects/$System.env.CI_PROJECT_ID/packages/maven"
+            // Uncomment the following lines to publish manually (and comment out the other credentials section)
+//            credentials(HttpHeaderCredentials) {
+//                name = "Private-Token"
+//                value = gitLabPrivateToken // the variable resides in ~/.gradle/gradle.properties
+//            }
+            credentials(HttpHeaderCredentials) {
+                name = 'Job-Token'
+                value = System.getenv('CI_JOB_TOKEN')
+            }
+            authentication {
+                header(HttpHeaderAuthentication)
+            }
+        }
 
-modelJar.dependsOn(generateAst, modelClasses)
-modelClasses.dependsOn(generateAst)
-compileJava.dependsOn(modelJar)
+    }
+}
 
-jar.dependsOn(modelJar)
+generateAst.dependsOn relast
+clean.dependsOn cleanGen
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000000000000000000000000000000000000..0e0cc086ffe4f79f86c59464b82fb53d4e99d4cb
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,6 @@
+relast_version = 0.4.0
+relast2uml_version = 0.3.7-59
+jupyter_version = 5.8.2
+assertj_version = 3.22.0
+grammar2uml_version = 0.2.1
+jastaddgradle_version = 1.14.5
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index f3d88b1c2faf2fc91d853cd5d4242b5547257070..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index be52383ef49cdf484098989f96738b3d82d7810d..41dfb87909a877d96c3af4adccce4c7a301b55a2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 2fe81a7d95e4f9ad2c9b2a046707d36ceb3980b3..4f906e0c811fc9e230eb44819f509cd0627f2600 100755
--- a/gradlew
+++ b/gradlew
@@ -82,6 +82,7 @@ esac
 
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 
+
 # Determine the Java command to use to start the JVM.
 if [ -n "$JAVA_HOME" ] ; then
     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -129,6 +130,7 @@ fi
 if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
     JAVACMD=`cygpath --unix "$JAVACMD"`
 
     # We build the pattern for arguments to be converted via cygpath
diff --git a/gradlew.bat b/gradlew.bat
index 24467a141f791695fc1009c78d913b2c849d1412..ac1b06f93825db68fb0c0b5150917f340eaa5d02 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
 @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
 
@@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
 
 echo.
 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -51,7 +54,7 @@ goto fail
 set JAVA_HOME=%JAVA_HOME:"=%
 set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
 
 echo.
 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -61,28 +64,14 @@ echo location of your Java installation.
 
 goto fail
 
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
 :execute
 @rem Setup the command line
 
 set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
 
+
 @rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
 
 :end
 @rem End local scope for the variables with windows NT shell
diff --git a/libs/relast.jar b/libs/relast.jar
deleted file mode 100644
index 9f1d60c7c99a1e35d9cf5558d5f329c5aa7ba66e..0000000000000000000000000000000000000000
Binary files a/libs/relast.jar and /dev/null differ
diff --git a/pages/docs/using.md b/pages/docs/using.md
index a42891831f635201bd179762e0fa9fc01ed91774..5377ecae0873e6eed048be75a5381e2cd4a5e001 100644
--- a/pages/docs/using.md
+++ b/pages/docs/using.md
@@ -44,8 +44,9 @@ def relastOutputFiles = [
 ]
 
 task relast(type: JavaExec) {
-    classpath = files("relast.preprocessor/libs/relast.jar")
     group = 'Build'
+    classpath = configurations.relast
+    mainClass = 'org.jastadd.relast.compiler.Compiler'
 
     doFirst {
         delete relastOutputFiles
diff --git a/settings.gradle b/settings.gradle
index 5f99c5a83ca1b73205acb1de3960ccc501af1cc4..2aeef63375455191b0c0e5fc1f30bb3196c81807 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,5 +1 @@
-pluginManagement {
-    plugins {
-        id 'org.jastadd' version '1.13.3'
-    }
-}
+rootProject.name = 'relast-preprocessor'
\ No newline at end of file
diff --git a/src/main/resources/RelASTPreprocessorVersion.properties b/src/main/resources/RelASTPreprocessorVersion.properties
new file mode 100644
index 0000000000000000000000000000000000000000..de55ab654e5845e918eedddeabe2aea9b9518b5c
--- /dev/null
+++ b/src/main/resources/RelASTPreprocessorVersion.properties
@@ -0,0 +1 @@
+version=0.1.0
diff --git a/src/main/resources/preprocessor.properties b/src/main/resources/preprocessor.properties
deleted file mode 100644
index fb55bf09edd40713c458341e14b57714f11924db..0000000000000000000000000000000000000000
--- a/src/main/resources/preprocessor.properties
+++ /dev/null
@@ -1 +0,0 @@
-version=1.0.0-pre-release