diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 2b0ba38..94c08ea 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -4,29 +4,36 @@ on: [ push, pull_request, workflow_dispatch ] jobs: build-bld-project: - runs-on: ubuntu-latest - strategy: matrix: - java-version: [ 17, 20 ] + java-version: [ 17, 21, 24 ] + kotlin-version: [ 1.9.25, 2.0.21, 2.1.20 ] + os: [ ubuntu-latest, windows-latest, macos-latest ] + + runs-on: ${{ matrix.os }} steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: ${{ matrix.java-version }} - - name: Grant execute permission for bld - run: chmod +x bld - - - name: Download the dependencies + - name: Download dependencies [examples] + working-directory: examples run: ./bld download - - name: Run tests with bld - run: ./bld compile test + - name: Compile and run [examples] + working-directory: examples + run: ./bld compile run + + - name: Download dependencies + run: ./bld download + + - name: Run tests + run: ./bld compile test \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index f6122cd..508f6a5 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -30,14 +30,14 @@ jobs: steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: 17 - name: Build Javadocs @@ -47,11 +47,11 @@ jobs: uses: actions/configure-pages@v3 - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: # Upload generated Javadocs repository - path: 'build/javadoc/' + path: "build/javadoc/" - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 \ No newline at end of file + uses: actions/deploy-pages@v4 diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/copyright/Apache_License.xml b/.idea/copyright/Apache_License.xml index 15687f4..4446c15 100644 --- a/.idea/copyright/Apache_License.xml +++ b/.idea/copyright/Apache_License.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 0000000..81220b4 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.idea/intellij-javadocs-4.0.1.xml b/.idea/intellij-javadocs-4.0.1.xml new file mode 100644 index 0000000..90d3347 --- /dev/null +++ b/.idea/intellij-javadocs-4.0.1.xml @@ -0,0 +1,204 @@ + + + + + UPDATE + false + true + + FIELD + METHOD + TYPE + + + PUBLIC + PROTECTED + DEFAULT + + + + + + ^.*(public|protected|private)*.+interface\s+\w+.* + /**\n + * The interface ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + ^.*(public|protected|private)*.+enum\s+\w+.* + /**\n + * The enum ${name}.\n + */ + + + ^.*(public|protected|private)*.+class\s+\w+.* + /**\n + * The type ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + .+ + /**\n + * The type ${name}.\n + */ + + + + + .+ + /**\n + * Instantiates a new ${name}.\n +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*\s*.*(\w(\s*<.+>)*)+\s+get\w+\s*\(.*\).+ + /**\n + * Gets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*(public|protected|private)*\s*.*(void|\w(\s*<.+>)*)+\s+set\w+\s*\(.*\).+ + /**\n + * Sets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+ + /**\n + * The entry point of application.\n + + <#if element.parameterList.parameters?has_content> + *\n +</#if> + * @param ${element.parameterList.parameters[0].name} the input arguments\n +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + .+ + /**\n + * ${name}<#if isNotVoid> ${return}</#if>.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${return}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*.+static.*(\w\s\w)+.+ + /**\n + * The constant ${element.getName()}.\n + */ + + + ^.*(public|protected|private)*.*(\w\s\w)+.+ + /**\n + <#if element.parent.isInterface()> + * The constant ${element.getName()}.\n +<#else> + * The ${name}.\n +</#if> */ + + + .+ + /**\n + <#if element.parent.isEnum()> + *${name} ${typeName}.\n +<#else> + * The ${name}.\n +</#if>*/ + + + + + \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index cf75013..153a060 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,11 +2,12 @@ - + - + + @@ -14,4 +15,4 @@ - \ No newline at end of file + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/.idea/libraries/compile.xml +++ b/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/.idea/libraries/runtime.xml +++ b/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/.idea/libraries/test.xml +++ b/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index cf5fb1d..d85343f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + @@ -8,11 +9,18 @@ + \ No newline at end of file + diff --git a/examples/.idea/libraries/compile.xml b/examples/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/examples/.idea/libraries/compile.xml +++ b/examples/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/runtime.xml b/examples/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/examples/.idea/libraries/runtime.xml +++ b/examples/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/test.xml b/examples/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/examples/.idea/libraries/test.xml +++ b/examples/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.vscode/launch.json b/examples/.vscode/launch.json index dc9c983..0368ce2 100644 --- a/examples/.vscode/launch.json +++ b/examples/.vscode/launch.json @@ -6,19 +6,6 @@ "name": "Run Main", "request": "launch", "mainClass": "com.example.SampleMain" - }, - { - "type": "java", - "name": "Run Tests", - "request": "launch", - "mainClass": "org.junit.platform.console.ConsoleLauncher", - "args": [ - "--details=verbose", - "--scan-classpath", - "--disable-banner", - "--disable-ansi-colors", - "--exclude-engine=junit-platform-suite", - "--exclude-engine=junit-vintage"] } ] } diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json index 5ae2eaa..ba429d0 100644 --- a/examples/.vscode/settings.json +++ b/examples/.vscode/settings.json @@ -3,13 +3,13 @@ "src/main/java", "src/main/resources", "src/test/java", - "src/bld/java" + "src/test/resources", + "src/bld/java", + "src/bld/resources" ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}bld-1.7.0.jar", - "lib/compile/*.jar", - "lib/runtime/*.jar", - "lib/test/*.jar" + "${HOME}/.bld/dist/bld-2.2.1.jar", + "lib/**/*.jar" ] } diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..ede07c3 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +# Compile and Run Example + +```console +./bld compile run +``` diff --git a/examples/lib/bld/bld-wrapper.jar b/examples/lib/bld/bld-wrapper.jar index 9b4ee52..3842a62 100644 Binary files a/examples/lib/bld/bld-wrapper.jar and b/examples/lib/bld/bld-wrapper.jar differ diff --git a/examples/lib/bld/bld-wrapper.properties b/examples/lib/bld/bld-wrapper.properties index 19f286f..bc257d2 100644 --- a/examples/lib/bld/bld-wrapper.properties +++ b/examples/lib/bld/bld-wrapper.properties @@ -1,6 +1,6 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.2 -bld.repositories=MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= -bld.version=1.7.5 +bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.1 +bld.repositories=MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES +bld.version=2.2.1 diff --git a/examples/my_app_version.txt b/examples/my_app_version.txt new file mode 100644 index 0000000..3d4c223 --- /dev/null +++ b/examples/my_app_version.txt @@ -0,0 +1,30 @@ +package {{v packageName/}}; + +import java.util.Date; + +public final class {{v className/}} implements Comparable<{{v className/}}> { + public static final String PROJECT = "{{v project/}}"; + public static final Date BUILD_DATE = new Date({{v epoch/}}L); + public static final int MAJOR = {{v major/}}; + public static final int MINOR = {{v minor/}}; + public static final int REVISION = {{v revision/}}; + public static final String QUALIFIER = "{{v qualifier/}}"; + public static final String VERSION = "{{v version/}}"; + + private {{v className/}}() { + // no-op + } + + @Override + public int compareTo({{v className/}} other) { + if (MAJOR != other.MAJOR) { + return Integer.compare(MAJOR, other.MAJOR); + } else if (MINOR != other.MINOR) { + return Integer.compare(MINOR, other.MINOR); + } else if (REVISION != other.REVISION) { + return Integer.compare(REVISION, other.REVISION); + } else { + return QUALIFIER.compareTo(other.QUALIFIER); + } + } +} \ No newline at end of file diff --git a/examples/src/bld/java/com/example/SampleBuild.java b/examples/src/bld/java/com/example/SampleBuild.java index f3fae67..c9303a6 100644 --- a/examples/src/bld/java/com/example/SampleBuild.java +++ b/examples/src/bld/java/com/example/SampleBuild.java @@ -4,7 +4,6 @@ import rife.bld.BuildCommand; import rife.bld.Project; import rife.bld.extension.GeneratedVersionOperation; -import java.io.File; import java.util.List; import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; @@ -13,10 +12,6 @@ import static rife.bld.dependencies.Scope.test; /** * Example build. - * - *
    - *
  • ./bld compile run
  • - *
*/ public class SampleBuild extends Project { public SampleBuild() { @@ -25,11 +20,15 @@ public class SampleBuild extends Project { mainClass = "com.example.SampleMain"; version = version(1, 0, 1, "rc1"); + javaRelease = 17; + + autoDownloadPurge = true; downloadSources = true; + repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES); scope(test) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 0))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 0))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))); } public static void main(String[] args) { @@ -47,7 +46,8 @@ public class SampleBuild extends Project { new GeneratedVersionOperation() .fromProject(this) // .projectName("My App") -// .classTemplate(new File(workDirectory, "myversion.txt")) +// .classTemplate("my_app_version.txt") +// .classTemplate("version.txt") .execute(); } -} \ No newline at end of file +} diff --git a/examples/src/main/java/com/example/GeneratedVersion.java b/examples/src/main/java/com/example/GeneratedVersion.java index 35c8d67..64ecb97 100644 --- a/examples/src/main/java/com/example/GeneratedVersion.java +++ b/examples/src/main/java/com/example/GeneratedVersion.java @@ -1,4 +1,4 @@ -/* +/** * This file is automatically generated. * Do not modify! -- ALL CHANGES WILL BE ERASED! */ @@ -12,7 +12,7 @@ import java.util.Date; */ public final class GeneratedVersion { public static final String PROJECT = "Sample"; - public static final Date BUILD_DATE = new Date(1698009833587L); + public static final Date BUILD_DATE = new Date(1736843939053L); public static final int MAJOR = 1; public static final int MINOR = 0; public static final int REVISION = 1; diff --git a/examples/myversion.txt b/examples/version.txt similarity index 100% rename from examples/myversion.txt rename to examples/version.txt diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 9b4ee52..e26a2ae 100644 Binary files a/lib/bld/bld-wrapper.jar and b/lib/bld/bld-wrapper.jar differ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 46c4a1d..42fad1d 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,7 +1,6 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-pmd=com.uwyn.rife2:bld-pmd:0.9.3 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 -bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= -bld.version=1.7.5 +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.2 +bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES +bld.version=2.2.1 diff --git a/src/bld/java/rife/bld/extension/GeneratedVersionOperationBuild.java b/src/bld/java/rife/bld/extension/GeneratedVersionOperationBuild.java index e48ec08..d355d07 100644 --- a/src/bld/java/rife/bld/extension/GeneratedVersionOperationBuild.java +++ b/src/bld/java/rife/bld/extension/GeneratedVersionOperationBuild.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import rife.bld.publish.PublishDeveloper; import rife.bld.publish.PublishLicense; import rife.bld.publish.PublishScm; -import java.io.IOException; import java.util.List; import static rife.bld.dependencies.Repository.*; @@ -35,25 +34,28 @@ public class GeneratedVersionOperationBuild extends Project { public GeneratedVersionOperationBuild() { pkg = "rife.bld.extension"; name = "GeneratedVersionOperation"; - version = version(0, 9, 2); + version = version(1, 0, 1); javaRelease = 17; + downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES); + + repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES, RIFE2_SNAPSHOTS); scope(compile) - .include(dependency("com.uwyn.rife2", "bld", version(1, 7, 5))); + .include(dependency("com.uwyn.rife2", "bld", version(2, 2, 1))); scope(test) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 0))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 0))) - .include(dependency("org.assertj", "assertj-core", version(3, 24, 2))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))) + .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))); precompileOperation() .templateTypes(TXT); javadocOperation() .javadocOptions() + .author() .docLint(NO_MISSING) .link("https://rife2.github.io/bld/") .link("https://rife2.github.io/rife2/") @@ -61,18 +63,27 @@ public class GeneratedVersionOperationBuild extends Project { publishOperation() .repositories(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2")) + .repository(repository("github")) .info() .groupId("com.uwyn.rife2") .artifactId("bld-generated-version") .description("bld Extension to Generate Project Version Data") .url("https://github.com/rife2/generated-version") - .developer(new PublishDeveloper().id("ethauvin").name("Erik C. Thauvin").email("erik@thauvin.net") - .url("https://erik.thauvin.net/")) - .license(new PublishLicense().name("The Apache License, Version 2.0") - .url("http://www.apache.org/licenses/LICENSE-2.0.txt")) - .scm(new PublishScm().connection("scm:git:https://github.com/rife2/generated-version.git") + .developer(new PublishDeveloper() + .id("ethauvin") + .name("Erik C. Thauvin") + .email("erik@thauvin.net") + .url("https://erik.thauvin.net/") + ) + .license(new PublishLicense() + .name("The Apache License, Version 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0.txt") + ) + .scm(new PublishScm() + .connection("scm:git:https://github.com/rife2/generated-version.git") .developerConnection("scm:git:git@github.com:rife2/generated-version.git") - .url("https://github.com/rife2/generated-version")) + .url("https://github.com/rife2/generated-version") + ) .signKey(property("sign.key")) .signPassphrase(property("sign.passphrase")); } @@ -81,13 +92,6 @@ public class GeneratedVersionOperationBuild extends Project { new GeneratedVersionOperationBuild().start(args); } - @BuildCommand(summary = "Generates JaCoCo Reports") - public void jacoco() throws IOException { - new JacocoReportOperation() - .fromProject(this) - .execute(); - } - @BuildCommand(summary = "Runs PMD analysis") public void pmd() throws Exception { new PmdOperation() diff --git a/src/main/java/rife/bld/extension/GeneratedVersion.java b/src/main/java/rife/bld/extension/GeneratedVersion.java index 8da66e3..9ae40fb 100644 --- a/src/main/java/rife/bld/extension/GeneratedVersion.java +++ b/src/main/java/rife/bld/extension/GeneratedVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,16 @@ package rife.bld.extension; import rife.bld.BaseProject; +import rife.resources.ResourceFinderClasspath; +import rife.resources.ResourceFinderDirectories; +import rife.resources.ResourceFinderGroup; +import rife.template.Template; +import rife.template.TemplateFactory; +import rife.tools.FileUtils; import java.io.File; +import java.io.IOException; +import java.nio.file.Path; /** * GeneratedVersion data class. @@ -26,96 +34,251 @@ import java.io.File; * @author Erik C. Thauvin * @since 1.0 */ -@SuppressWarnings("PMD.DataClass") public class GeneratedVersion { - private File classFile; - private String className; - private String packageName; - private BaseProject project; - private String projectName; - private File template; + private static final String CLASSNAME = "className"; + private static final String EPOCH = "epoch"; + private static final String MAJOR = "major"; + private static final String MINOR = "minor"; + private static final String PACKAGE_NAME = "packageName"; + private static final String PROJECT = "project"; + private static final String QUALIFIER = "qualifier"; + private static final String REVISION = "revision"; + private static final String VERSION = "version"; + private File classFile_; + private String className_ = "GeneratedVersion"; + private File directory_; + private String extension_ = ".java"; + private String packageName_; + private String projectName_; + private BaseProject project_; + private File template_; /** - * Returns the class file. + * Builds the template based on the {@link GeneratedVersion} data. + * + * @return the template */ - public File getClassFile() { - return classFile; - } + public Template buildTemplate() { + Template template; + var version = project_.version(); + TemplateFactory.TXT.resetClassLoader(); + if (template_ == null) { + var group = new ResourceFinderGroup().add(ResourceFinderClasspath.instance()); + template = TemplateFactory.TXT.setResourceFinder(group).get("default_generated_version"); + } else { + File parent; + if (template_.getParentFile() != null) { + parent = template_.getParentFile(); + } else { + parent = new File(template_.getAbsolutePath()).getParentFile(); + } + var group = new ResourceFinderGroup().add(new ResourceFinderDirectories(parent)); + template = TemplateFactory.TXT.setResourceFinder(group).get(template_.getName()); + } - /** - * Sets the class file. - */ - public void setClassFile(File classFile) { - this.classFile = classFile; - } + if (packageName_ == null) { + packageName_ = project_.pkg(); + } - /** - * Returns the class name. - */ - public String getClassName() { - return className; - } + if (template.hasValueId(PACKAGE_NAME)) { + template.setValue(PACKAGE_NAME, packageName_); + } - /** - * Sets the class name. - */ - public void setClassName(String className) { - this.className = className; - } + if (template.hasValueId(CLASSNAME)) { + template.setValue(CLASSNAME, className_); + } - /** - * Returns the package name. - */ - public String getPackageName() { - return packageName; - } + if (template.hasValueId(PROJECT)) { + if (projectName_ == null) { + projectName_ = project_.name(); + } + template.setValue(PROJECT, projectName_); + } - /** - * Sets the package name. - */ - public void setPackageName(String packageName) { - this.packageName = packageName; - } + if (template.hasValueId(EPOCH)) { + template.setValue(EPOCH, System.currentTimeMillis()); + } - /** - * Returns the project. - */ - public BaseProject getProject() { - return project; - } + if (template.hasValueId(VERSION)) { + template.setValue(VERSION, version.toString()); + } - /** - * Sets the project. - */ - public void setProject(BaseProject project) { - this.project = project; - } + if (template.hasValueId(MAJOR)) { + template.setValue(MAJOR, version.majorInt()); + } - /** - * Returns the project name. - */ - public String getProjectName() { - return projectName; - } + if (template.hasValueId(MINOR)) { + template.setValue(MINOR, version.minorInt()); + } - /** - * Sets the project name. - */ - public void setProjectName(String projectName) { - this.projectName = projectName; - } + if (template.hasValueId(REVISION)) { + template.setValue(REVISION, version.revisionInt()); + } + + if (template.hasValueId(QUALIFIER)) { + template.setValue(QUALIFIER, version.qualifier()); + } - /** - * Returns the template. - */ - public File getTemplate() { return template; } + /** + * Returns the class file. + * + * @return the class file + */ + public File getClassFile() { + return classFile_; + } + + /** + * Returns the class name. + * + * @return the class name + */ + public String getClassName() { + return className_; + } + + /** + * Returns the destination directory. + * + * @return the destination directory + */ + public File getDirectory() { + return directory_; + } + + /** + * Returns the file extension. + * + * @return the file extension + */ + public String getExtension() { + return extension_; + } + + /** + * Returns the package name. + * + * @return the package name + */ + public String getPackageName() { + return packageName_; + } + + /** + * Returns the project. + * + * @return the project + */ + public BaseProject getProject() { + return project_; + } + + /** + * Returns the project name. + * + * @return the project name + */ + public String getProjectName() { + return projectName_; + } + + /** + * Returns the template. + * + * @return the template + */ + public File getTemplate() { + return template_; + } + + /** + * Sets the class name. + * + * @param className the class name + */ + public void setClassName(String className) { + this.className_ = className; + } + + /** + * Set the destination directory. + * + * @param directory the destination directory + */ + public void setDirectory(File directory) { + this.directory_ = directory; + } + + /** + * Sets the file extension. (Default is: {@code .java}) + * + * @param extension the file extension + */ + public void setExtension(String extension) { + this.extension_ = extension; + } + + /** + * Sets the package name. + * + * @param packageName the package name + */ + public void setPackageName(String packageName) { + this.packageName_ = packageName; + } + + /** + * Sets the project. + * + * @param project the project + */ + public void setProject(BaseProject project) { + this.project_ = project; + } + + /** + * Sets the project name. + * + * @param projectName the project name + */ + public void setProjectName(String projectName) { + this.projectName_ = projectName; + } + /** * Sets the template file. + * + * @param template the template */ public void setTemplate(File template) { - this.template = template; + this.template_ = template; + } + + /** + * Writes the project version class in the given directory. + */ + public void writeTemplate(Template template) throws IOException { + if (packageName_ != null) { + classFile_ = Path.of(directory_.getAbsolutePath(), packageName_.replace(".", File.separator), + className_ + extension_).toFile(); + } else { + classFile_ = new File(directory_, className_ + ".java"); + } + + if (!classFile_.getParentFile().exists()) { + var dirs = classFile_.getParentFile().mkdirs(); + if (!dirs && !classFile_.getParentFile().exists()) { + throw new IOException("Could not create project package directories: " + classFile_.getParent()); + } + } + + try { + FileUtils.writeString(template.getContent(), classFile_); + } catch (IOException e) { + throw new IOException("Unable to write the version class file: " + e.getMessage(), e); + } } } diff --git a/src/main/java/rife/bld/extension/GeneratedVersionOperation.java b/src/main/java/rife/bld/extension/GeneratedVersionOperation.java index c528f1a..6a5e065 100644 --- a/src/main/java/rife/bld/extension/GeneratedVersionOperation.java +++ b/src/main/java/rife/bld/extension/GeneratedVersionOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +19,10 @@ package rife.bld.extension; import rife.bld.BaseProject; import rife.bld.operations.AbstractOperation; -import rife.resources.ResourceFinderDirectories; -import rife.template.Template; -import rife.template.TemplateConfig; -import rife.template.TemplateFactory; -import rife.tools.FileUtils; +import rife.bld.operations.exceptions.ExitStatusException; import java.io.File; -import java.io.IOException; import java.nio.file.Path; -import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,163 +33,162 @@ import java.util.logging.Logger; * @since 1.0 */ public class GeneratedVersionOperation extends AbstractOperation { - private static final String CLASSNAME = "className"; - private static final String EPOCH = "epoch"; private static final Logger LOGGER = Logger.getLogger(GeneratedVersionOperation.class.getName()); - private static final String MAJOR = "major"; - private static final String MINOR = "minor"; - private static final String PACKAGE_NAME = "packageName"; - private static final String PROJECT = "project"; - private static final String QUALIFIER = "qualifier"; - private static final String REVISION = "revision"; - private static final String VERSION = "version"; - private final GeneratedVersion generatedVersion = new GeneratedVersion(); - - /** - * Builds the template based on the {@link GeneratedVersion} data. - */ - public static Template buildTemplate(GeneratedVersion gv) { - Template template; - var version = gv.getProject().version(); - if (gv.getTemplate() == null) { - template = TemplateFactory.TXT.get("version.txt"); - } else { - var files = new ResourceFinderDirectories(gv.getTemplate().getParentFile()); - template = new TemplateFactory(TemplateConfig.TXT, "txtFiles", TemplateFactory.TXT) - .setResourceFinder(files).get(gv.getTemplate().getName()); - } - - if (gv.getPackageName() == null) { - gv.setPackageName(gv.getProject().pkg()); - } - - if (template.hasValueId(PACKAGE_NAME)) { - template.setValue(PACKAGE_NAME, gv.getPackageName()); - } - - gv.setClassName(Objects.requireNonNullElse(gv.getClassName(), "GeneratedVersion")); - if (template.hasValueId(CLASSNAME)) { - template.setValue(CLASSNAME, gv.getClassName()); - } - - if (template.hasValueId(PROJECT)) { - if (gv.getProjectName() == null) { - gv.setProjectName(gv.getProject().name()); - } - template.setValue(PROJECT, gv.getProjectName()); - } - - if (template.hasValueId(EPOCH)) { - template.setValue(EPOCH, System.currentTimeMillis()); - } - - if (template.hasValueId(VERSION)) { - template.setValue(VERSION, version.toString()); - } - - if (template.hasValueId(MAJOR)) { - template.setValue(MAJOR, version.majorInt()); - } - - if (template.hasValueId(MINOR)) { - template.setValue(MINOR, version.minorInt()); - } - - if (template.hasValueId(REVISION)) { - template.setValue(REVISION, version.revisionInt()); - } - - if (template.hasValueId(QUALIFIER)) { - template.setValue(QUALIFIER, version.qualifier()); - } - - return template; - } - - /** - * Writes the project version class in the given directory. - */ - public static void writeTemplate(Template template, File directory, GeneratedVersion gv) { - if (gv.getPackageName() != null) { - gv.setClassFile(Path.of(directory.getAbsolutePath(), - gv.getPackageName().replace(".", File.separator), gv.getClassName() + ".java").toFile()); - } else { - gv.setClassFile(Path.of(directory.getAbsolutePath(), gv.getClassName() + ".java").toFile()); - } - - if (!gv.getClassFile().getParentFile().exists()) { - var mkdirs = gv.getClassFile().getParentFile().mkdirs(); - if (!mkdirs && !gv.getClassFile().getParentFile().exists() && LOGGER.isLoggable(Level.SEVERE)) { - LOGGER.log(Level.SEVERE, "Could not create project package directories: {0}", - gv.getClassFile().getParent()); - } - } - - try { - var updated = gv.getClassFile().exists(); - FileUtils.writeString(template.getContent(), gv.getClassFile()); - if (LOGGER.isLoggable(Level.INFO)) { - LOGGER.log(Level.INFO, "Generated version class has been {0} to {1}: {2}", - new String[]{updated ? "updated" : "created", gv.getProject().version().toString(), - gv.getClassFile().toString()}); - } - } catch (IOException e) { - if (LOGGER.isLoggable(Level.SEVERE)) { - LOGGER.log(Level.SEVERE, "Unable to write the version class file.", e); - } - } - } + private final GeneratedVersion generatedVersion_ = new GeneratedVersion(); /** * Sets the class name. + * + * @param className the class name + * @return this operation instance */ public GeneratedVersionOperation className(String className) { - generatedVersion.setClassName(className); + generatedVersion_.setClassName(className); return this; } /** * Sets the class template path. + * + * @param template the template path + * @return this operation instance */ public GeneratedVersionOperation classTemplate(File template) { - generatedVersion.setTemplate(template); + generatedVersion_.setTemplate(template); return this; } + /** + * Sets the class template path. + * + * @param template the template path + * @return this operation instance + */ + public GeneratedVersionOperation classTemplate(String template) { + return classTemplate(new File(template)); + } + + /** + * Sets the class template path. + * + * @param template the template path + * @return this operation instance + */ + public GeneratedVersionOperation classTemplate(Path template) { + return classTemplate(template.toFile()); + } + + /** + * Sets the destination directory. + * + * @param directory the destination directory + * @return this operation instance + */ + public GeneratedVersionOperation directory(File directory) { + generatedVersion_.setDirectory(directory); + return this; + } + + /** + * Sets the destination directory. + * + * @param directory the destination directory + * @return this operation instance + */ + public GeneratedVersionOperation directory(String directory) { + return directory(new File(directory)); + } + + /** + * Sets the destination directory. + * + * @param directory the destination directory + * @return this operation instance + */ + public GeneratedVersionOperation directory(Path directory) { + return directory(directory.toFile()); + } + /** * Generates a version data class for this project. */ @Override - public void execute() { - if (generatedVersion.getProject() == null && LOGGER.isLoggable(Level.SEVERE)) { - LOGGER.severe("A project must be specified."); + @SuppressWarnings("PMD.PreserveStackTrace") + public void execute() throws Exception { + if (generatedVersion_.getProject() == null) { + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("A project must be specified."); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); + } else { + try { + var template = generatedVersion_.buildTemplate(); + generatedVersion_.writeTemplate(template); + if (LOGGER.isLoggable(Level.INFO) && !silent()) { + LOGGER.log(Level.INFO, "Generated version ({0}) class saved to: file://{1}", + new String[]{generatedVersion_.getProject().version().toString(), + generatedVersion_.getClassFile().toURI().getPath()}); + } + } catch (Exception e) { + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe(e.getMessage()); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); + } } - - var template = buildTemplate(generatedVersion); - writeTemplate(template, generatedVersion.getProject().srcMainJavaDirectory(), generatedVersion); } /** - * Configure the operation from a {@link BaseProject}. + * Sets the file extension. (Default is: {@code .java}) + * + * @param extension the file extension + * @return this operation instance */ - public GeneratedVersionOperation fromProject(BaseProject project) { - generatedVersion.setProject(project); + public GeneratedVersionOperation extension(String extension) { + generatedVersion_.setExtension(extension); return this; } + /** + * Configure the operation from a {@link BaseProject}. + * + * @param project the project + * @return this operation instance + */ + public GeneratedVersionOperation fromProject(BaseProject project) { + generatedVersion_.setProject(project); + generatedVersion_.setDirectory(project.srcMainJavaDirectory()); + return this; + } + + /** + * Retrieves the generated version instance. + * + * @return the generated version + */ + public GeneratedVersion generatedVersion() { + return generatedVersion_; + } + /** * Sets the package name. + * + * @param packageName the package name + * @return this operation instance */ public GeneratedVersionOperation packageName(String packageName) { - generatedVersion.setPackageName(packageName); + generatedVersion_.setPackageName(packageName); return this; } /** * Sets the project name. + * + * @param projectName the project name + * @return this operation instance */ public GeneratedVersionOperation projectName(String projectName) { - generatedVersion.setProjectName(projectName); + generatedVersion_.setProjectName(projectName); return this; } -} \ No newline at end of file +} diff --git a/src/main/resources/templates/version.txt b/src/main/resources/templates/default_generated_version.txt similarity index 99% rename from src/main/resources/templates/version.txt rename to src/main/resources/templates/default_generated_version.txt index 19d932c..0e30456 100644 --- a/src/main/resources/templates/version.txt +++ b/src/main/resources/templates/default_generated_version.txt @@ -1,4 +1,4 @@ -/* +/** * This file is automatically generated. * Do not modify! -- ALL CHANGES WILL BE ERASED! */ diff --git a/src/test/java/rife/bld/extension/GeneratedVersionTest.java b/src/test/java/rife/bld/extension/GeneratedVersionTest.java index 689e0d5..32e3508 100644 --- a/src/test/java/rife/bld/extension/GeneratedVersionTest.java +++ b/src/test/java/rife/bld/extension/GeneratedVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,23 @@ package rife.bld.extension; +import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import rife.bld.BaseProject; import rife.bld.Project; +import rife.bld.blueprints.BaseProjectBlueprint; import rife.bld.dependencies.VersionNumber; import rife.tools.FileUtils; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; import static org.assertj.core.api.Assertions.assertThat; @@ -53,6 +60,41 @@ class GeneratedVersionTest { } }; + @BeforeAll + static void beforeAll() { + var level = Level.ALL; + var logger = Logger.getLogger("rife.bld.extension"); + var consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(level); + logger.addHandler(consoleHandler); + logger.setLevel(level); + logger.setUseParentHandlers(false); + } + + /** + * Compares two strings by removing all line separators and whitespace. + * + * @param text1 The first text to compare + * @param text2 The second text to compare + * @return true if the texts are equivalent when line separators are ignored, false otherwise + */ + static boolean compareTextIgnoringLineSeparators(String text1, String text2) { + // Handle null cases + if (text1 == null && text2 == null) { + return true; + } + if (text1 == null || text2 == null) { + return false; + } + + // Remove all line separators and whitespace + var cleanedText1 = text1.replaceAll("\\r?\\n|\\r|\\s", ""); + var cleanedText2 = text2.replaceAll("\\r?\\n|\\r|\\s", ""); + + // Compare the cleaned strings + return cleanedText1.equals(cleanedText2); + } + static void deleteOnExit(File folder) { folder.deleteOnExit(); for (var f : Objects.requireNonNull(folder.listFiles())) { @@ -68,30 +110,43 @@ class GeneratedVersionTest { void testBuildCustomTemplate() { var gv = new GeneratedVersion(); gv.setProject(PROJECT); - gv.setTemplate(new File(gv.getProject().srcTestResourcesDirectory().getAbsolutePath(), - "version_test.txt")); + gv.setTemplate(new File(gv.getProject().srcTestResourcesDirectory().getAbsolutePath(), "version_test.txt")); gv.setPackageName("com.example.my"); gv.setProjectName("My App"); gv.setClassName("MyVersion"); - var t = GeneratedVersionOperation.buildTemplate(gv); - - assertThat(t.getContent()).contains("package com.example.my;").contains("class MyVersion") - .contains("MAJOR = 2").contains("MINOR = 1").contains("REVISION = 3").contains("QUALIFIER = \"\"") - .contains("private MyVersion").contains("PROJECT = \"My App\""); + var t = gv.buildTemplate(); + var v = gv.getProject().version(); + assertThat(v).extracting("majorInt", "minorInt", "revisionInt").as("version") + .containsExactly(2, 1, 3); + assertThat(compareTextIgnoringLineSeparators(t.getContent(), + String.format("package %s;" + + "public final class %s {" + + " public static final int PROJECT = \"%s\";" + + " public static final int MAJOR = %d;" + + " public static final int MINOR = %d;" + + " public static final int REVISION = %d;" + + " public static final String QUALIFIER = \"\";" + + " private MyVersion() {" + + " // no-op" + + " }" + + "}", gv.getPackageName(), gv.getClassName(), gv.getProjectName(), v.majorInt(), + v.minorInt(), v.revisionInt()))).as("template").isTrue(); } @Test void testBuildTemplate() { var gv = new GeneratedVersion(); gv.setProject(PROJECT); - var t = GeneratedVersionOperation.buildTemplate(gv); + var t = gv.buildTemplate(); assertThat(t).isNotNull(); - assertThat(gv.getProject()).isEqualTo(PROJECT); - assertThat(gv.getPackageName()).isEqualTo(PROJECT.pkg()); - assertThat(gv.getProjectName()).isEqualTo(PROJECT.name()); + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(gv.getProject()).isEqualTo(PROJECT); + softly.assertThat(gv.getPackageName()).isEqualTo(PROJECT.pkg()); + softly.assertThat(gv.getProjectName()).isEqualTo(PROJECT.name()); + } assertThat(t.getContent()).contains("package com.example;").contains("class GeneratedVersion") .contains("PROJECT = \"MyExample\";").contains("MAJOR = 2").contains("MINOR = 1") @@ -100,19 +155,107 @@ class GeneratedVersionTest { } @Test - void testWriteTemplate() throws IOException { - var gv = new GeneratedVersion(); - gv.setProject(PROJECT); - var t = GeneratedVersionOperation.buildTemplate(gv); + void testDirectories() { + var foo = new File("foo"); + var bar = new File("bar"); - var tmpDir = Files.createTempDirectory("bldGeneratedVersion").toFile(); + var op = new GeneratedVersionOperation().directory(foo); + assertThat(op.generatedVersion().getDirectory()).as("as file").isEqualTo(foo); - GeneratedVersionOperation.writeTemplate(t, tmpDir, gv); + op = op.directory(bar.toPath()); + assertThat(op.generatedVersion().getDirectory()).as("as path").isEqualTo(bar); - assertThat(gv.getClassFile()).exists(); + op = op.directory("foo"); + assertThat(op.generatedVersion().getDirectory()).as("as string").isEqualTo(foo); + } + + @Test + void testExample() throws Exception { + var tmpDir = Files.createTempDirectory("bld-generated-version-example-").toFile(); + tmpDir.deleteOnExit(); + + new GeneratedVersionOperation() + .fromProject(new BaseProjectBlueprint(new File("examples"), "com.example", "Example", "Example")) + .directory(tmpDir.getAbsolutePath()) + //.classTemplate(new File("examples", "my_app_version.txt")) + .classTemplate(new File("examples", "version.txt")) + .execute(); deleteOnExit(tmpDir); + var template = Path.of(tmpDir.getAbsolutePath(), "com", "example", "GeneratedVersion.java"); + assertThat(template).exists(); + + var content = Files.readString(template); + assertThat(content).contains("class GeneratedVersion").contains("PROJECT = \"Example\";") + .contains("MAJOR = 0").contains("MINOR = 0").contains("REVISION = 1").contains("QUALIFIER = \"\"") + .doesNotContain("ERASED!"); // only in default template + } + + @Test + void testExecute() throws Exception { + var tmpDir = Files.createTempDirectory("bld-generated-version-execute-").toFile(); + tmpDir.deleteOnExit(); + + new GeneratedVersionOperation() + .fromProject(PROJECT) + .directory(tmpDir.getAbsolutePath()) + .extension(".java") + .classTemplate("src/test/resources/foo/version_test.txt") + .packageName("") + .className("MyVersion") + .execute(); + + deleteOnExit(tmpDir); + + var template = new File(tmpDir, "MyVersion.java"); + assertThat(template).exists(); + + var content = Files.readString(template.toPath()); + assertThat(content).contains("class MyVersion") + .contains("PROJECT = \"MyExample\";").contains("MAJOR = 2").contains("MINOR = 1") + .contains("REVISION = 3").contains("QUALIFIER = \"\"").contains("private MyVersion") + .doesNotContain("package"); + } + + @Test + void testGeneratedVersion() { + var gv = new GeneratedVersion(); + gv.setProject(PROJECT); + gv.setTemplate(new File(gv.getProject().srcTestResourcesDirectory().getAbsolutePath(), "version_test.txt")); + gv.setPackageName("com.example.cool"); + gv.setProjectName("Cool App"); + gv.setClassName("CoolVersion"); + gv.setDirectory(new File("build")); + gv.setExtension(".java"); + + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(gv.getProject()).as("project").isEqualTo(PROJECT); + softly.assertThat(gv.getTemplate()).as("template").exists(); + softly.assertThat(gv.getPackageName()).as("package name").isEqualTo("com.example.cool"); + softly.assertThat(gv.getProjectName()).as("project name").isEqualTo("Cool App"); + softly.assertThat(gv.getClassName()).as("class name").isEqualTo("CoolVersion"); + softly.assertThat(gv.getExtension()).as("extension").isEqualTo(".java"); + softly.assertThat(gv.getDirectory()).as("directory").isDirectory(); + } + } + + @Test + void testWriteTemplate() throws IOException { + var tmpDir = Files.createTempDirectory("bld-generated-version-write-").toFile(); + tmpDir.deleteOnExit(); + + var gv = new GeneratedVersion(); + gv.setProject(PROJECT); + gv.setDirectory(tmpDir); + + var t = gv.buildTemplate(); + gv.writeTemplate(t); + + deleteOnExit(tmpDir); + + assertThat(gv.getClassFile()).exists(); + var versionClass = FileUtils.readString(gv.getClassFile()); assertThat(versionClass).contains("package com.example;").contains("class GeneratedVersion") .contains("MAJOR = 2").contains("MINOR = 1").contains("REVISION = 3") diff --git a/src/test/resources/foo/version_test.txt b/src/test/resources/foo/version_test.txt new file mode 100644 index 0000000..06787e6 --- /dev/null +++ b/src/test/resources/foo/version_test.txt @@ -0,0 +1,11 @@ +public final class {{v className/}} { + public static final int PROJECT = "{{v project/}}"; + public static final int MAJOR = {{v major/}}; + public static final int MINOR = {{v minor/}}; + public static final int REVISION = {{v revision/}}; + public static final String QUALIFIER = "{{v qualifier/}}"; + + private {{v className/}}() { + // no-op + } +}