diff --git a/.github/workflows/nightlydeploy.yml b/.github/workflows/nightlydeploy.yml index 953ee83037a..e6b76fbbb25 100644 --- a/.github/workflows/nightlydeploy.yml +++ b/.github/workflows/nightlydeploy.yml @@ -9,8 +9,11 @@ permissions: contents: write id-token: write +# The documentation generation for varconds and proof script commands +# extracts `///` markdown javadocs via therapi, which requires JDK 23+. +# `--release=21` is still set! env: - JAVA_VERSION: 21 + JAVA_VERSION: 25 jobs: @@ -29,7 +32,7 @@ jobs: uses: gradle/actions/setup-gradle@v6 - name: Build with Gradle - run: ./gradlew --parallel assemble javadoc alldoc + run: ./gradlew --parallel assemble javadoc alldoc :key.core:generateVarcondDoc :key.core:generateScriptDoc - name: Upload ShadowJars (fat and slim) uses: actions/upload-artifact@v7 @@ -40,6 +43,14 @@ jobs: path: "*/build/libs/*-exe.jar" retention-days: 1 + - name: Upload varcond and proof script documentation + uses: actions/upload-artifact@v7 + with: + name: varcond-script-docs + path: "key.core/build/docs/keydocs/*" + retention-days: 30 + compression-level: '9' + - name: Package run: tar cvf key-javadoc.tar.xz build/docs/javadoc diff --git a/build.gradle b/build.gradle index 93b792ef77c..c89c5127e6a 100644 --- a/build.gradle +++ b/build.gradle @@ -83,7 +83,6 @@ subprojects { implementation(libs.slf4j.api) testImplementation(libs.logback.classic) - compileOnly(libs.jspecify) testCompileOnly(libs.jspecify) compileOnly(libs.checkerframework.qual) @@ -316,7 +315,6 @@ subprojects { afterEvaluate { description = project.description assert (project.description != null && project.description != "") : "Description of modules are required at maven-central" - print(project.description) } url = 'https://key-project.org/' diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7ddff7e2730..8038c479dfe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,6 +25,7 @@ stringtemplate = "4.3.4" javapoet = "1.13.0" javaparser = "3.28.0-K13.6" truth = "1.4.5" +therapi = "0.13.0" # UI and CLI flatlaf = "3.7.2" @@ -35,6 +36,7 @@ docking-frames = "1.1.3p1" # External tools scala-isabelle = "0.4.5" + [libraries] # SLF4J and logging slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } @@ -43,6 +45,10 @@ logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "lo # JSpecify (nullability annotations) jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" } +#Extracting Javadoc +therapi-processor = { module = "com.github.therapi:therapi-runtime-javadoc-scribe", version.ref = "therapi" } +therapi-runtime = { module = "com.github.therapi:therapi-runtime-javadoc", version.ref = "therapi" } + # EISOP Checker Framework checkerframework-qual = { module = "io.github.eisop:checker-qual", version.ref = "eisop" } checkerframework-util = { module = "io.github.eisop:checker-util", version.ref = "eisop" } diff --git a/key.core/build.gradle b/key.core/build.gradle index c6584ca35ab..f12b7330bac 100644 --- a/key.core/build.gradle +++ b/key.core/build.gradle @@ -24,6 +24,9 @@ dependencies { testImplementation(libs.truth) testImplementation(project(":key.core")) + annotationProcessor(libs.therapi.processor) + api(libs.therapi.runtime) + // https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml testImplementation(libs.jackson.dataformat.yaml) @@ -32,7 +35,7 @@ dependencies { } tasks.withType(JavaCompile).configureEach { - options.compilerArgs << "-Ajavadoc.packages=de.uka.ilkd.key.scripts" + options.compilerArgs << "-Ajavadoc.packages=de.uka.ilkd.key.scripts,de.uka.ilkd.key.rule.conditions" } // The target directory for JavaCC (parser generation) @@ -67,6 +70,38 @@ tasks.register('generateSMTListings') { } } +// Generates the markdown documentation of all variable conditions (varconds), +// see de.uka.ilkd.key.doc.VarcondDoc. +tasks.register('generateVarcondDoc', JavaExec) { + group = 'documentation' + description = 'Generates the markdown documentation of all variable conditions (varconds).' + classpath = sourceSets.main.runtimeClasspath + mainClass = 'de.uka.ilkd.key.doc.VarcondDoc' + def mdFile = layout.buildDirectory.file("docs/keydocs/varconds.md") + def jsonFile = layout.buildDirectory.file("docs/keydocs/varconds.json") + outputs.file(mdFile) + outputs.file(jsonFile) + doFirst { + args = [mdFile.get().asFile.absolutePath, jsonFile.get().asFile.absolutePath ] + } +} + +// Generates the markdown documentation of all proof script commands, +// see de.uka.ilkd.key.doc.ScriptDoc. +tasks.register('generateScriptDoc', JavaExec) { + group = 'documentation' + description = 'Generates the markdown documentation of all proof script commands.' + classpath = sourceSets.main.runtimeClasspath + mainClass = 'de.uka.ilkd.key.doc.ScriptDoc' + def mdFile = layout.buildDirectory.file("docs/keydocs/script-commands.md") + def jsonFile = layout.buildDirectory.file("docs/keydocs/script-commands.json") + outputs.file(mdFile) + outputs.file(jsonFile) + doFirst { + args = [mdFile.get().asFile.absolutePath, jsonFile.get().asFile.absolutePath ] + } +} + tasks.register('generateSolverPropsList') { def pack = "de/uka/ilkd/key/smt/solvertypes" def resourcesDir = "${project.projectDir}/src/main/resources" diff --git a/key.core/src/main/java/de/uka/ilkd/key/doc/AbstractDocGenerator.java b/key.core/src/main/java/de/uka/ilkd/key/doc/AbstractDocGenerator.java new file mode 100644 index 00000000000..f73fbbbcb1b --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/doc/AbstractDocGenerator.java @@ -0,0 +1,202 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2, + * or (at your option) any later version. + * SPDX-License-Identifier: GPL-2.0-or-later */ +package de.uka.ilkd.key.doc; + +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +import org.jspecify.annotations.Nullable; + +/** + * Base class for the documentation generators of KeY's taclet language constructs, e.g., for the + * variable conditions ({@link VarcondDoc}) and the proof script commands ({@link ScriptDoc}). It + * provides the shared handling of the output target and the post-processing of javadoc text. + * + * @author Alexander Weigl + * @version 1 (23.08.26) + */ +public abstract class AbstractDocGenerator { + + /// Runs this generator. If a command line argument is given, the markdown output is written + /// to that file together with a JSON dump ([#generateJsonData()]) next to it; otherwise the + /// markdown is printed to stdout. + /// + /// @param mdFile an optional single argument with the path of the output file + /// @param jsonFile + /// @throws IOException if the output file cannot be written + protected void run(String mdFile, String jsonFile) throws IOException { + Path mdPath = Path.of(mdFile); + if (mdPath.getParent() != null) { + Files.createDirectories(mdPath.getParent()); + } + + try (var out = new PrintStream(Files.newOutputStream(mdPath), false)) { + generateDocumentation(out); + } + + + Object data = generateJsonData(); + + if (data != null) { + Path jsonPath = Paths.get(jsonFile); + if (jsonPath.getParent() != null) { + Files.createDirectories(jsonPath.getParent()); + } + try (var out = new PrintWriter(Files.newOutputStream(jsonPath), false)) { + JsonWriter.toJson(data, out); + } + } + } + + /// Writes the documentation of this generator in markdown format. + protected abstract void generateDocumentation(PrintStream out); + + /// Returns the documentation data of this generator in a structured form, which is serialized + /// into a JSON dump next to the markdown file. Returns `null` if this generator does not + /// support a JSON dump. + /// + /// @return the structured documentation data, or `null` + protected @Nullable Object generateJsonData() { + return null; + } + + /// Converts a javadoc comment into markdown by replacing common HTML tags and inline tags + /// with their markdown equivalents. + /// + /// @param it the javadoc comment, may be null + /// @return the comment as markdown + protected static String cleanJavadoc(@Nullable String it) { + if (it == null) { + it = ""; + } + return it.replace("", "`") + .replace("", "\n") + .replace("