From a46ed2c012bf070ab956c10ec035b32e16e60091 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 15 Jun 2024 16:36:35 -0700 Subject: [PATCH] Initial commit (split from bld-kotlin) --- .github/workflows/bld.yml | 50 ++ .github/workflows/pages.yml | 57 ++ .gitignore | 57 ++ .idea/.gitignore | 3 + .idea/app.iml | 28 + .idea/bld.iml | 14 + .idea/copyright/Apache_License.xml | 6 + .idea/copyright/profiles_settings.xml | 3 + .idea/inspectionProfiles/Project_Default.xml | 8 + .idea/intellij-javadocs-4.0.1.xml | 204 ++++++ .idea/libraries/bld.xml | 20 + .idea/libraries/compile.xml | 13 + .idea/libraries/runtime.xml | 14 + .idea/libraries/test.xml | 14 + .idea/misc.xml | 9 + .idea/modules.xml | 9 + .idea/runConfigurations/Run Tests.xml | 9 + .idea/vcs.xml | 6 + .vscode/launch.json | 18 + .vscode/settings.json | 15 + LICENSE.txt | 177 +++++ README.md | 80 ++ bld | 2 + bld.bat | 4 + config/pmd.xml | 109 +++ examples/.gitignore | 55 ++ examples/.idea/.gitignore | 3 + examples/.idea/.name | 1 + examples/.idea/app.iml | 29 + examples/.idea/bld.iml | 15 + .../inspectionProfiles/Project_Default.xml | 8 + examples/.idea/intellij-javadocs-4.0.1.xml | 141 ++++ examples/.idea/kotlinc.xml | 6 + .../.idea/libraries/KotlinJavaRuntime.xml | 23 + examples/.idea/libraries/bld.xml | 18 + examples/.idea/libraries/compile.xml | 13 + examples/.idea/libraries/runtime.xml | 14 + examples/.idea/libraries/test.xml | 14 + examples/.idea/misc.xml | 27 + examples/.idea/modules.xml | 9 + .../.idea/runConfigurations/Run Tests.xml | 9 + examples/.idea/vcs.xml | 6 + examples/.vscode/launch.json | 15 + examples/.vscode/settings.json | 15 + examples/README.md | 27 + examples/bld | 2 + examples/bld.bat | 4 + examples/lib/bld/bld-wrapper.jar | Bin 0 -> 27319 bytes examples/lib/bld/bld-wrapper.properties | 8 + .../bld/java/com/example/ExampleBuild.java | 116 +++ .../src/main/kotlin/com/example/Example.kt | 28 + .../test/kotlin/com/example/ExampleTest.kt | 11 + lib/bld/bld-wrapper.jar | Bin 0 -> 27319 bytes lib/bld/bld-wrapper.properties | 8 + scripts/checkcliargs.sh | 15 + scripts/cliargs.sh | 13 + .../bld/extension/DokkaOperationBuild.java | 114 +++ .../rife/bld/extension/DokkaOperation.java | 644 +++++++++++++++++ .../bld/extension/dokka/AnalysisPlatform.java | 27 + .../extension/dokka/DocumentedVisibility.java | 27 + .../bld/extension/dokka/LoggingLevel.java | 27 + .../bld/extension/dokka/OutputFormat.java | 27 + .../rife/bld/extension/dokka/SourceSet.java | 682 ++++++++++++++++++ .../bld/extension/DokkaOperationTest.java | 126 ++++ .../rife/bld/extension/SourceSetTest.java | 135 ++++ src/test/resources/dokka-args.txt | 16 + src/test/resources/dokka-sourceset-args.txt | 21 + 67 files changed, 3428 insertions(+) create mode 100644 .github/workflows/bld.yml create mode 100644 .github/workflows/pages.yml create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/app.iml create mode 100644 .idea/bld.iml create mode 100644 .idea/copyright/Apache_License.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/intellij-javadocs-4.0.1.xml create mode 100644 .idea/libraries/bld.xml create mode 100644 .idea/libraries/compile.xml create mode 100644 .idea/libraries/runtime.xml create mode 100644 .idea/libraries/test.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations/Run Tests.xml create mode 100644 .idea/vcs.xml create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100755 bld create mode 100644 bld.bat create mode 100644 config/pmd.xml create mode 100644 examples/.gitignore create mode 100644 examples/.idea/.gitignore create mode 100644 examples/.idea/.name create mode 100644 examples/.idea/app.iml create mode 100644 examples/.idea/bld.iml create mode 100644 examples/.idea/inspectionProfiles/Project_Default.xml create mode 100644 examples/.idea/intellij-javadocs-4.0.1.xml create mode 100644 examples/.idea/kotlinc.xml create mode 100644 examples/.idea/libraries/KotlinJavaRuntime.xml create mode 100644 examples/.idea/libraries/bld.xml create mode 100644 examples/.idea/libraries/compile.xml create mode 100644 examples/.idea/libraries/runtime.xml create mode 100644 examples/.idea/libraries/test.xml create mode 100644 examples/.idea/misc.xml create mode 100644 examples/.idea/modules.xml create mode 100644 examples/.idea/runConfigurations/Run Tests.xml create mode 100644 examples/.idea/vcs.xml create mode 100644 examples/.vscode/launch.json create mode 100644 examples/.vscode/settings.json create mode 100644 examples/README.md create mode 100755 examples/bld create mode 100644 examples/bld.bat create mode 100644 examples/lib/bld/bld-wrapper.jar create mode 100644 examples/lib/bld/bld-wrapper.properties create mode 100644 examples/src/bld/java/com/example/ExampleBuild.java create mode 100644 examples/src/main/kotlin/com/example/Example.kt create mode 100644 examples/src/test/kotlin/com/example/ExampleTest.kt create mode 100644 lib/bld/bld-wrapper.jar create mode 100644 lib/bld/bld-wrapper.properties create mode 100755 scripts/checkcliargs.sh create mode 100755 scripts/cliargs.sh create mode 100644 src/bld/java/rife/bld/extension/DokkaOperationBuild.java create mode 100644 src/main/java/rife/bld/extension/DokkaOperation.java create mode 100644 src/main/java/rife/bld/extension/dokka/AnalysisPlatform.java create mode 100644 src/main/java/rife/bld/extension/dokka/DocumentedVisibility.java create mode 100644 src/main/java/rife/bld/extension/dokka/LoggingLevel.java create mode 100644 src/main/java/rife/bld/extension/dokka/OutputFormat.java create mode 100644 src/main/java/rife/bld/extension/dokka/SourceSet.java create mode 100644 src/test/java/rife/bld/extension/DokkaOperationTest.java create mode 100644 src/test/java/rife/bld/extension/SourceSetTest.java create mode 100644 src/test/resources/dokka-args.txt create mode 100644 src/test/resources/dokka-sourceset-args.txt diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml new file mode 100644 index 0000000..d897fcc --- /dev/null +++ b/.github/workflows/bld.yml @@ -0,0 +1,50 @@ +name: bld-ci + +on: [push, pull_request, workflow_dispatch] + +jobs: + build-bld-project: + runs-on: ubuntu-latest + + strategy: + matrix: + java-version: [17, 21, 22] + + steps: + - name: Checkout source repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@v4 + with: + distribution: "zulu" + java-version: ${{ matrix.java-version }} + + - name: Download the examples dependencies + working-directory: examples + run: | + chmod +x bld + ./bld download + + - name: Run examples tests + working-directory: examples + run: ./bld compile test + + - name: Build examples documentation + working-directory: examples + run: | + ./bld javadoc + ./bld dokka-html + ./bld dokka-gfm + ./bld dokka-jekyll + + - name: Grant execute permission for bld + run: chmod +x bld + + - name: Download the dependencies + run: ./bld download + + - name: Run tests + run: ./bld compile test diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..e191f6d --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,57 @@ +name: javadocs-pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + runs-on: ubuntu-latest + + steps: + - name: Checkout source repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: "zulu" + java-version: 17 + + - name: Build Javadocs + run: ./bld download clean javadoc + + - name: Setup Pages + uses: actions/configure-pages@v3 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload generated Javadocs repository + path: "build/javadoc/" + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea86fe8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +.gradle +.DS_Store +build +lib/bld/** +!lib/bld/bld-wrapper.jar +!lib/bld/bld-wrapper.properties +lib/compile/ +lib/runtime/ +lib/standalone/ +lib/test/ + +# IDEA ignores + +# User-specific +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Editor-based Rest Client +.idea/httpRequests + +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/app.iml b/.idea/app.iml new file mode 100644 index 0000000..6c0f6d7 --- /dev/null +++ b/.idea/app.iml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/bld.iml b/.idea/bld.iml new file mode 100644 index 0000000..e63e11e --- /dev/null +++ b/.idea/bld.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/Apache_License.xml b/.idea/copyright/Apache_License.xml new file mode 100644 index 0000000..ade80da --- /dev/null +++ b/.idea/copyright/Apache_License.xml @@ -0,0 +1,6 @@ + + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..f2907f1 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..1e01b48 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.idea/intellij-javadocs-4.0.1.xml b/.idea/intellij-javadocs-4.0.1.xml new file mode 100644 index 0000000..fbb9478 --- /dev/null +++ b/.idea/intellij-javadocs-4.0.1.xml @@ -0,0 +1,204 @@ + + + + + UPDATE + false + true + + FIELD + TYPE + METHOD + + + DEFAULT + PUBLIC + PROTECTED + + + + + + ^.*(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 new file mode 100644 index 0000000..cde67cb --- /dev/null +++ b/.idea/libraries/bld.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml new file mode 100644 index 0000000..9bd86aa --- /dev/null +++ b/.idea/libraries/compile.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml new file mode 100644 index 0000000..2ae5c4b --- /dev/null +++ b/.idea/libraries/runtime.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml new file mode 100644 index 0000000..b80486a --- /dev/null +++ b/.idea/libraries/test.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..cdabcf2 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..55adcb9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run Tests.xml b/.idea/runConfigurations/Run Tests.xml new file mode 100644 index 0000000..057a90e --- /dev/null +++ b/.idea/runConfigurations/Run Tests.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2d63b46 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "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/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d136e4d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "java.project.sourcePaths": [ + "src/main/java", + "src/main/resources", + "src/test/java", + "src/test/resources", + "src/bld/java", + "src/bld/resources" + ], + "java.configuration.updateBuildConfiguration": "automatic", + "java.project.referencedLibraries": [ + "${HOME}/.bld/dist/bld-1.9.1.jar", + "lib/**/*.jar" + ] +} diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..49cc83d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000..1afff74 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# [bld](https://rife2.com/bld) Extension to Generate API Documentation with [Dokka](https://github.com/Kotlin/dokka) for [Kotlin](https://kotlinlang.org/) + +[![License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![Java](https://img.shields.io/badge/java-17%2B-blue)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) +[![bld](https://img.shields.io/badge/1.9.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Release](https://flat.badgen.net/maven/v/metadata-url/repo.rife2.com/releases/com/uwyn/rife2/bld-dokka/maven-metadata.xml?color=blue)](https://repo.rife2.com/#/releases/com/uwyn/rife2/bld-dokka) +[![Snapshot](https://flat.badgen.net/maven/v/metadata-url/repo.rife2.com/snapshots/com/uwyn/rife2/bld-dokka/maven-metadata.xml?label=snapshot)](https://repo.rife2.com/#/snapshots/com/uwyn/rife2/bld-dokka) +[![GitHub CI](https://github.com/rife2/bld-dokka/actions/workflows/bld.yml/badge.svg)](https://github.com/rife2/bld-dokka/actions/workflows/bld.yml) + +To install, please refer to the [extensions](https://github.com/rife2/bld/wiki/Extensions) and [support](https://github.com/rife2/bld/wiki/Kotlin-Support) +documentation. + +## Generate API Documentation + +To generate a project's documentation in various formats: + +```java +@BuildCommand(value = "dokka-gfm", summary = "Generates documentation in GitHub flavored markdown format") +public void dokkaGfm() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .loggingLevel(LoggingLevel.INFO) + // Create build/dokka/gfm + .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "gfm").toFile()) + .outputFormat(OutputFormat.MARKDOWN) + .execute(); +} + +@BuildCommand(value = "dokka-html", summary = "Generates documentation in HTML format") +public void dokkaHtml() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .loggingLevel(LoggingLevel.INFO) + // Create build/dokka/html + .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "html").toFile()) + .outputFormat(OutputFormat.HTML) + .execute(); +} + +@BuildCommand(value = "dokka-jekyll", summary = "Generates documentation in Jekyll flavored markdown format") +public void dokkaJekyll() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .loggingLevel(LoggingLevel.INFO) + // Create build/dokka/jekyll + .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "jekkyl").toFile()) + .outputFormat(OutputFormat.JEKYLL) + .execute(); +} + +@BuildCommand(summary = "Generates Javadoc for the project") +@Override +public void javadoc() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .failOnWarning(true) + .loggingLevel(LoggingLevel.INFO) + // Create build/javadoc + .outputDir(new File(buildDirectory(), "javadoc")) + .outputFormat(OutputFormat.JAVADOC) + .execute(); +} +``` + +```console +./bld javadoc +./bld dokka-html +./bld dokka-gfm +./bld dokka-jekyll +``` + +- [View Examples Project](https://github.com/rife2/bld-dokka/tree/main/examples/) + +Please check the [Dokka Operation documentation](https://rife2.github.io/bld-dokka/rife/bld/extension/DokkaOperation.html#method-summary) +for all available configuration options. + +## Template Project + +There is also a [Kotlin Template Project](https://github.com/rife2/kotlin-bld-example) with support for Dokka and the +[Detekt](https://github.com/rife2/bld-detekt) extensions. diff --git a/bld b/bld new file mode 100755 index 0000000..3df448d --- /dev/null +++ b/bld @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build rife.bld.extension.DokkaOperationBuild "$@" \ No newline at end of file diff --git a/bld.bat b/bld.bat new file mode 100644 index 0000000..392477b --- /dev/null +++ b/bld.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build rife.bld.extension.DokkaOperationBuild %* \ No newline at end of file diff --git a/config/pmd.xml b/config/pmd.xml new file mode 100644 index 0000000..3d3203c --- /dev/null +++ b/config/pmd.xml @@ -0,0 +1,109 @@ + + + Erik's Ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..a2805aa --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,55 @@ +.gradle +.DS_Store +build +lib/bld/** +!lib/bld/bld-wrapper.jar +!lib/bld/bld-wrapper.properties +lib/compile/ +lib/runtime/ +lib/standalone/ +lib/test/ + +# IDEA ignores + +# User-specific +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Editor-based Rest Client +.idea/httpRequests \ No newline at end of file diff --git a/examples/.idea/.gitignore b/examples/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/examples/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/examples/.idea/.name b/examples/.idea/.name new file mode 100644 index 0000000..106baae --- /dev/null +++ b/examples/.idea/.name @@ -0,0 +1 @@ +bld-dokka-examples \ No newline at end of file diff --git a/examples/.idea/app.iml b/examples/.idea/app.iml new file mode 100644 index 0000000..56afe0c --- /dev/null +++ b/examples/.idea/app.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/bld.iml b/examples/.idea/bld.iml new file mode 100644 index 0000000..2eaac76 --- /dev/null +++ b/examples/.idea/bld.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/inspectionProfiles/Project_Default.xml b/examples/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..1e01b48 --- /dev/null +++ b/examples/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/examples/.idea/intellij-javadocs-4.0.1.xml b/examples/.idea/intellij-javadocs-4.0.1.xml new file mode 100644 index 0000000..a9eb719 --- /dev/null +++ b/examples/.idea/intellij-javadocs-4.0.1.xml @@ -0,0 +1,141 @@ + + + + + UPDATE + false + true + + METHOD + FIELD + TYPE + + + PROTECTED + DEFAULT + PUBLIC + + + + + + ^.*(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/examples/.idea/kotlinc.xml b/examples/.idea/kotlinc.xml new file mode 100644 index 0000000..0dd4b35 --- /dev/null +++ b/examples/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/examples/.idea/libraries/KotlinJavaRuntime.xml b/examples/.idea/libraries/KotlinJavaRuntime.xml new file mode 100644 index 0000000..215c35d --- /dev/null +++ b/examples/.idea/libraries/KotlinJavaRuntime.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/libraries/bld.xml b/examples/.idea/libraries/bld.xml new file mode 100644 index 0000000..a2969be --- /dev/null +++ b/examples/.idea/libraries/bld.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/.idea/libraries/compile.xml b/examples/.idea/libraries/compile.xml new file mode 100644 index 0000000..9bd86aa --- /dev/null +++ b/examples/.idea/libraries/compile.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/libraries/runtime.xml b/examples/.idea/libraries/runtime.xml new file mode 100644 index 0000000..2ae5c4b --- /dev/null +++ b/examples/.idea/libraries/runtime.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/libraries/test.xml b/examples/.idea/libraries/test.xml new file mode 100644 index 0000000..b80486a --- /dev/null +++ b/examples/.idea/libraries/test.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/misc.xml b/examples/.idea/misc.xml new file mode 100644 index 0000000..78b0f96 --- /dev/null +++ b/examples/.idea/misc.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/modules.xml b/examples/.idea/modules.xml new file mode 100644 index 0000000..55adcb9 --- /dev/null +++ b/examples/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/runConfigurations/Run Tests.xml b/examples/.idea/runConfigurations/Run Tests.xml new file mode 100644 index 0000000..2b503e5 --- /dev/null +++ b/examples/.idea/runConfigurations/Run Tests.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/examples/.idea/vcs.xml b/examples/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/examples/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/.vscode/launch.json b/examples/.vscode/launch.json new file mode 100644 index 0000000..6fe9188 --- /dev/null +++ b/examples/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Run Tests", + "request": "launch", + "mainClass": "com.example.ExampleBuild", + "args": [ + "compile", + "test" + ] + } + ] +} \ No newline at end of file diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json new file mode 100644 index 0000000..d136e4d --- /dev/null +++ b/examples/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "java.project.sourcePaths": [ + "src/main/java", + "src/main/resources", + "src/test/java", + "src/test/resources", + "src/bld/java", + "src/bld/resources" + ], + "java.configuration.updateBuildConfiguration": "automatic", + "java.project.referencedLibraries": [ + "${HOME}/.bld/dist/bld-1.9.1.jar", + "lib/**/*.jar" + ] +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..5570f01 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,27 @@ + +## Compile the Example + +```console +./bld compile +``` + +## Run the Example + +```console +./bld run +``` + +## Run the Tests + +```console +./bld test +``` + +## Build the documentation with [Dokka](https://github.com/Kotlin/dokka) + +```console +./bld javadoc +./bld dokka-html +./bld dokka-gfm +./bld dokka-jekyll +``` diff --git a/examples/bld b/examples/bld new file mode 100755 index 0000000..80d2986 --- /dev/null +++ b/examples/bld @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build com.example.ExampleBuild "$@" \ No newline at end of file diff --git a/examples/bld.bat b/examples/bld.bat new file mode 100644 index 0000000..084bb72 --- /dev/null +++ b/examples/bld.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build com.example.ExampleBuild %* \ No newline at end of file diff --git a/examples/lib/bld/bld-wrapper.jar b/examples/lib/bld/bld-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..0bfc526f16abc52e5fc1af85df2d99b021f2d72d GIT binary patch literal 27319 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 HhJg4lY-eF& literal 0 HcmV?d00001 diff --git a/examples/lib/bld/bld-wrapper.properties b/examples/lib/bld/bld-wrapper.properties new file mode 100644 index 0000000..d3f0a91 --- /dev/null +++ b/examples/lib/bld/bld-wrapper.properties @@ -0,0 +1,8 @@ +bld.downloadExtensionJavadoc=false +bld.downloadExtensionSources=true +bld.downloadLocation= +bld.extension-dokka=com.uwyn.rife2:bld-dokka:1.0.0-SNAPSHOT +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.0-SNAPSHOT +bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES +bld.sourceDirectories= +bld.version=1.9.1 diff --git a/examples/src/bld/java/com/example/ExampleBuild.java b/examples/src/bld/java/com/example/ExampleBuild.java new file mode 100644 index 0000000..04d3104 --- /dev/null +++ b/examples/src/bld/java/com/example/ExampleBuild.java @@ -0,0 +1,116 @@ +package com.example; + +import rife.bld.BuildCommand; +import rife.bld.Project; +import rife.bld.extension.CompileKotlinOperation; +import rife.bld.extension.DokkaOperation; +import rife.bld.extension.dokka.LoggingLevel; +import rife.bld.extension.dokka.OutputFormat; +import rife.bld.operations.exceptions.ExitStatusException; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static rife.bld.dependencies.Repository.*; +import static rife.bld.dependencies.Scope.compile; +import static rife.bld.dependencies.Scope.test; + +public class ExampleBuild extends Project { + public ExampleBuild() { + pkg = "com.example"; + name = "Example"; + mainClass = "com.example.Example"; + version = version(0, 1, 0); + + javaRelease = 17; + + downloadSources = true; + autoDownloadPurge = true; + + repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES); + + final var kotlin = version(2, 0, 0); + scope(compile) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)); + scope(test) + .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))); + + // Include the Kotlin source directory when creating or publishing sources Java Archives + jarSourcesOperation().sourceDirectories(new File(srcMainDirectory(), "kotlin")); + } + + public static void main(String[] args) { + // Enable detailed logging for the Kotlin extension +// 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); + + new ExampleBuild().start(args); + } + + @BuildCommand(summary = "Compiles the Kotlin project") + @Override + public void compile() throws IOException { + new CompileKotlinOperation() + .fromProject(this) + .execute(); + } + + @BuildCommand(value = "dokka-gfm", summary = "Generates documentation in GitHub flavored markdown format") + public void dokkaGfm() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .loggingLevel(LoggingLevel.INFO) + // Create build/dokka/gfm + .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "gfm").toFile()) + .outputFormat(OutputFormat.MARKDOWN) + .execute(); + } + + @BuildCommand(value = "dokka-html", summary = "Generates documentation in HTML format") + public void dokkaHtml() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .loggingLevel(LoggingLevel.INFO) + // Create build/dokka/html + .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "html").toFile()) + .outputFormat(OutputFormat.HTML) + .execute(); + } + + @BuildCommand(value = "dokka-jekyll", summary = "Generates documentation in Jekyll flavored markdown format") + public void dokkaJekyll() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .loggingLevel(LoggingLevel.INFO) + // Create build/dokka/jekyll + .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "jekkyl").toFile()) + .outputFormat(OutputFormat.JEKYLL) + .execute(); + } + + @BuildCommand(summary = "Generates Javadoc for the project") + @Override + public void javadoc() throws ExitStatusException, IOException, InterruptedException { + new DokkaOperation() + .fromProject(this) + .failOnWarning(true) + .loggingLevel(LoggingLevel.INFO) + // Create build/javadoc + .outputDir(new File(buildDirectory(), "javadoc")) + .outputFormat(OutputFormat.JAVADOC) + .execute(); + } +} diff --git a/examples/src/main/kotlin/com/example/Example.kt b/examples/src/main/kotlin/com/example/Example.kt new file mode 100644 index 0000000..4055391 --- /dev/null +++ b/examples/src/main/kotlin/com/example/Example.kt @@ -0,0 +1,28 @@ +package com.example + +/** + * Example class. + */ +class Example { + /** + * Message property. + */ + val message: String + /** + * Returns the message property. + */ + get() = "Hello World!" + + /** + * Companion object. + */ + companion object { + /** + * Main function. + */ + @JvmStatic + fun main(args: Array) { + println(Example().message) + } + } +} \ No newline at end of file diff --git a/examples/src/test/kotlin/com/example/ExampleTest.kt b/examples/src/test/kotlin/com/example/ExampleTest.kt new file mode 100644 index 0000000..6d8cf65 --- /dev/null +++ b/examples/src/test/kotlin/com/example/ExampleTest.kt @@ -0,0 +1,11 @@ +package com.example + +import kotlin.test.Test +import kotlin.test.assertEquals + +class ExampleTest { + @Test + fun verifyHello() { + assertEquals("Hello World!", Example().message) + } +} \ No newline at end of file diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..902333e6903601a3a5df9347730d4dd420d94e44 GIT binary patch literal 27319 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 HhJg4l@rYlr literal 0 HcmV?d00001 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties new file mode 100644 index 0000000..aea9055 --- /dev/null +++ b/lib/bld/bld-wrapper.properties @@ -0,0 +1,8 @@ +bld.downloadExtensionJavadoc=false +bld.downloadExtensionSources=true +bld.downloadLocation= +bld.extension-exec=com.uwyn.rife2:bld-exec:1.0.0 +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.0.1 +bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_RELEASES +bld.sourceDirectories= +bld.version=1.9.1 diff --git a/scripts/checkcliargs.sh b/scripts/checkcliargs.sh new file mode 100755 index 0000000..c2995bb --- /dev/null +++ b/scripts/checkcliargs.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +main=org.jetbrains.dokka.MainKt +new=/tmp/checkcliargs-new +old=/tmp/checkcliargs-old + +java -cp "lib/compile/*" $main -h >$new +java -cp "examples/lib/bld/*" $main -h >$old + +diff $old $new + +java -cp "lib/compile/*" $main -sourceSet -h >$new +java -cp "examples/lib/bld/*" $main -sourceSet -h >$old + +diff $old $new diff --git a/scripts/cliargs.sh b/scripts/cliargs.sh new file mode 100755 index 0000000..0fd649a --- /dev/null +++ b/scripts/cliargs.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +main=org.jetbrains.dokka.MainKt + +java -cp "lib/compile/*" $main -h |\ +grep " -" |\ +sed -e "s/^ -/-/" -e "s/ \[.*//" -e "s/ ->.*//" -e '/help/d' |\ +sort > "src/test/resources/dokka-args.txt" + +java -cp "lib/compile/*" $main -sourceSet -h |\ +grep " -" |\ +sed -e "s/^ -/-/" -e "s/ \[.*//" -e "s/ ->.*//" -e '/help/d' -e '/includeNonPublic/d' |\ +sort > "src/test/resources/dokka-sourceset-args.txt" diff --git a/src/bld/java/rife/bld/extension/DokkaOperationBuild.java b/src/bld/java/rife/bld/extension/DokkaOperationBuild.java new file mode 100644 index 0000000..9f1fc8e --- /dev/null +++ b/src/bld/java/rife/bld/extension/DokkaOperationBuild.java @@ -0,0 +1,114 @@ +/* + * Copyright 2023-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension; + +import rife.bld.BuildCommand; +import rife.bld.Project; +import rife.bld.publish.PublishDeveloper; +import rife.bld.publish.PublishLicense; +import rife.bld.publish.PublishScm; + +import java.util.List; + +import static rife.bld.dependencies.Repository.*; +import static rife.bld.dependencies.Scope.compile; +import static rife.bld.dependencies.Scope.test; +import static rife.bld.operations.JavadocOptions.DocLinkOption.NO_MISSING; + +public class DokkaOperationBuild extends Project { + public DokkaOperationBuild() { + pkg = "rife.bld.extension"; + name = "bld-dokka"; + version = version(1, 0, 0, "SNAPSHOT"); + + javaRelease = 17; + + downloadSources = true; + autoDownloadPurge = true; + repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES); + + var dokka = version(1, 9, 20); + scope(compile) + .include(dependency("org.jetbrains.dokka", "dokka-cli", dokka)) + .include(dependency("org.jetbrains.dokka", "dokka-base", dokka)) + .include(dependency("org.jetbrains.dokka", "analysis-kotlin-descriptors", dokka)) + .include(dependency("org.jetbrains.dokka", "javadoc-plugin", dokka)) + .include(dependency("org.jetbrains.dokka", "gfm-plugin", dokka)) + .include(dependency("org.jetbrains.dokka", "jekyll-plugin", dokka)) + .include(dependency("com.uwyn.rife2", "bld", version(1, 9, 1))); + scope(test) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))) + .include(dependency("org.assertj", "assertj-core", version(3, 26, 0))); + + javadocOperation() + .javadocOptions() + .author() + .docLint(NO_MISSING) + .link("https://rife2.github.io/bld/"); + + publishOperation() + .repository(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2")) + .info() + .groupId("com.uwyn.rife2") + .artifactId(name) + .description("bld Dokka Extension") + .url("https://github.com/rife2/bld-dokka") + .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/bld-dokka.git") + .developerConnection("scm:git:git@github.com:rife2/bld-dokka.git") + .url("https://github.com/rife2/bld-dokka") + ) + .signKey(property("sign.key")) + .signPassphrase(property("sign.passphrase")); + } + + public static void main(String[] args) { + new DokkaOperationBuild().start(args); + } + + @BuildCommand(summary = "Runs PMD analysis") + public void pmd() { + new PmdOperation() + .fromProject(this) + .failOnViolation(true) + .ruleSets("config/pmd.xml") + .execute(); + } + + @Override + public void test() throws Exception { + new ExecOperation() + .fromProject(this) + .command("scripts/cliargs.sh") + .execute(); + super.test(); + } +} diff --git a/src/main/java/rife/bld/extension/DokkaOperation.java b/src/main/java/rife/bld/extension/DokkaOperation.java new file mode 100644 index 0000000..c8a4184 --- /dev/null +++ b/src/main/java/rife/bld/extension/DokkaOperation.java @@ -0,0 +1,644 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension; + +import rife.bld.BaseProject; +import rife.bld.extension.dokka.LoggingLevel; +import rife.bld.extension.dokka.OutputFormat; +import rife.bld.extension.dokka.SourceSet; +import rife.bld.operations.AbstractProcessOperation; +import rife.tools.StringUtils; + +import java.io.File; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Builds documentation (javadoc, HTML, etc.) using Dokka. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +@SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes") +public class DokkaOperation extends AbstractProcessOperation { + private final static String GFM_PLUGIN_REGEXP = + "^.*(dokka-base|analysis-kotlin-descriptors|gfm-plugin|freemarker).*\\.jar$"; + private final static String HTML_PLUGIN_REGEXP = + "^.*(dokka-base|analysis-kotlin-descriptors|kotlinx-html-jvm|freemarker).*\\.jar$"; + private final static String JAVADOC_PLUGIN_REGEXP = + "^.*(dokka-base|analysis-kotlin-descriptors|javadoc-plugin|kotlin-as-java-plugin|korte-jvm).*\\.jar$"; + private final static String JEKYLL_PLUGIN_REGEXP = + "^.*(dokka-base|analysis-kotlin-descriptors|jekyll-plugin|gfm-plugin|freemarker).*\\.jar$"; + private final Logger LOGGER = Logger.getLogger(DokkaOperation.class.getName()); + private final Map globalLinks_ = new ConcurrentHashMap<>(); + private final Collection globalPackageOptions_ = new ArrayList<>(); + private final Collection globalSrcLinks_ = new ArrayList<>(); + private final Collection includes_ = new ArrayList<>(); + private final Collection pluginsClasspath_ = new ArrayList<>(); + private final Map pluginsConfiguration_ = new ConcurrentHashMap<>(); + private boolean delayTemplateSubstitution_; + private boolean failOnWarning_; + private File json; + private LoggingLevel loggingLevel_; + private String moduleName_; + private String moduleVersion_; + private boolean noSuppressObviousFunctions_; + private boolean offlineMode_; + private File outputDir_; + private BaseProject project_; + private SourceSet sourceSet_; + private boolean suppressInheritedMembers_; + + // Encodes to JSON adding braces as needed + private static String encodeJson(final String json) { + var sb = new StringBuilder(json); + if (!json.startsWith("{") || !json.endsWith("}")) { + sb.insert(0, "{").append('}'); + } + return StringUtils.encodeJson(sb.toString()); + } + + /** + * Returns the list of JARs contained in a given directory. + * + * @param directory the directory + * @param regex the regular expression to match + * @return the list of JARs + */ + public static List getJarList(File directory, String regex) { + var jars = new ArrayList(); + + if (directory.isDirectory()) { + var files = directory.listFiles(); + if (files != null) { + for (var f : files) { + if (!f.getName().endsWith("-sources.jar") && (!f.getName().endsWith("-javadoc.jar")) && + f.getName().matches(regex)) { + jars.add(f.getAbsolutePath()); + } + } + } + } + + return jars; + } + + /** + * Determines if the given string is not blank. + * + * @param s the string + * @return {@code true} if not blank, {@code false} otherwise. + */ + public static boolean isNotBlank(String s) { + return s != null && !s.isBlank(); + } + + /** + * Sets the delay substitution of some elements. + *

+ * Used in incremental builds of multimodule projects. + * + * @param delayTemplateSubstitution the delay + * @return this operation instance + */ + public DokkaOperation delayTemplateSubstitution(Boolean delayTemplateSubstitution) { + delayTemplateSubstitution_ = delayTemplateSubstitution; + return this; + } + + /** + * Part of the {@link #execute execute} operation, constructs the command list to use for building the process. + * + * @since 1.5 + */ + @Override + protected List executeConstructProcessCommandList() { + if (project_ == null) { + throw new IllegalArgumentException("A project must be specified."); + } + + final List args = new ArrayList<>(); + + // java + args.add(javaTool()); + + var cli = getJarList(project_.libBldDirectory(), "^.*dokka-cli.*\\.jar$"); + + if (cli.size() != 1) { + throw new RuntimeException("The dokka-cli JAR could not be found."); + } + + // -jar dokka-cli + args.add("-jar"); + args.add(cli.get(0)); + + // -pluginClasspath + if (!pluginsClasspath_.isEmpty()) { + args.add("-pluginsClasspath"); + args.add(String.join(";", pluginsClasspath_)); + } + + // -sourceSet + var sourceSetArgs = sourceSet_.args(); + if (sourceSetArgs.isEmpty()) { + throw new IllegalArgumentException("At least one sourceSet is required."); + } else { + args.add("-sourceSet"); + args.add(String.join(" ", sourceSet_.args())); + } + + // -outputDir + if (outputDir_ != null) { + if (!outputDir_.exists() && !outputDir_.mkdirs()) { + throw new RuntimeException("Could not create: " + outputDir_.getAbsolutePath()); + } + + args.add("-outputDir"); + args.add(outputDir_.getAbsolutePath()); + } + + // -delayTemplateSubstitution + if (delayTemplateSubstitution_) { + args.add("-delayTemplateSubstitution"); + } + + // -failOnWarning + if (failOnWarning_) { + args.add("-failOnWarning"); + } + + // -globalLinks_ + if (!globalLinks_.isEmpty()) { + args.add("-globalLinks"); + var links = new ArrayList(); + globalLinks_.forEach((k, v) -> + links.add(String.format("%s^%s", k, v))); + args.add(String.join("^^", links)); + } + + // -globalPackageOptions + if (!globalPackageOptions_.isEmpty()) { + args.add("-globalPackageOptions"); + args.add(String.join(";", globalPackageOptions_)); + } + + // -globalSrcLinks + if (!globalSrcLinks_.isEmpty()) { + args.add("-globalSrcLinks_"); + args.add(String.join(";", globalSrcLinks_)); + } + + // -includes + if (!includes_.isEmpty()) { + args.add("-includes"); + args.add(String.join(";", includes_)); + } + + // -loggingLevel + if (loggingLevel_ != null) { + args.add("-loggingLevel"); + args.add(loggingLevel_.name().toLowerCase()); + } + + // -moduleName + if (isNotBlank(moduleName_)) { + args.add("-moduleName"); + args.add(moduleName_); + } + + // -moduleVersion + if (isNotBlank(moduleVersion_)) { + args.add("-moduleVersion"); + args.add(moduleVersion_); + } + + // -noSuppressObviousFunctions + if (noSuppressObviousFunctions_) { + args.add("-noSuppressObviousFunctions"); + } + + // -offlineMode + if (offlineMode_) { + args.add("-offlineMode"); + } + + // -pluginConfiguration + if (!pluginsConfiguration_.isEmpty()) { + args.add("-pluginsConfiguration"); + var confs = new ArrayList(); + pluginsConfiguration_.forEach((k, v) -> + confs.add(String.format("%s=%s", encodeJson(k), encodeJson(v)))); + args.add(String.join("^^", confs)); + } + + // -suppressInheritedMembers + if (suppressInheritedMembers_) { + args.add("-suppressInheritedMembers"); + } + + // json + if (json != null) { + args.add(json.getAbsolutePath()); + } + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine(String.join(" ", args)); + } + + return args; + } + + /** + * Configures the operation from a {@link BaseProject}. + *

+ * Sets the {@link #sourceSet sourceSet}, {@link SourceSet#jdkVersion jdkVersion}, {@link #moduleName moduleName} + * and {@link SourceSet#classpath(String...) classpath} from the project. + * + * @param project the project to configure the operation from + */ + @Override + public DokkaOperation fromProject(BaseProject project) { + project_ = project; + sourceSet_ = new SourceSet() + .src(new File(project.srcMainDirectory(), "kotlin").getAbsolutePath()) + .classpath(project.compileClasspathJars()) + .classpath(project.providedClasspathJars()); + if (project.javaRelease() != null) { + sourceSet_ = sourceSet_.jdkVersion(project.javaRelease()); + } + moduleName_ = project.name(); + return this; + } + + /** + * Sets whether to fail documentation generation if Dokka has emitted a warning or an error. + *

+ * Whether to fail documentation generation if Dokka has emitted a warning or an error. The process waits until all + * errors and warnings have been emitted first. + *

+ * This setting works well with {@link SourceSet#reportUndocumented} + * + * @param failOnWarning {@code true} or {@code false} + * @return this operation instance + */ + public DokkaOperation failOnWarning(Boolean failOnWarning) { + failOnWarning_ = failOnWarning; + return this; + } + + /** + * Set the global external documentation links. + * + * @param url the external documentation URL + * @param packageListUrl the external documentation package list URL + * @return this operation instance + */ + public DokkaOperation globalLinks(String url, String packageListUrl) { + globalLinks_.put(url, packageListUrl); + return this; + } + + /** + * Set the global external documentation links. + * + * @param globalLinks the map of global links + * @return this operation instance + * @see #globalSrcLink(String...) #globalSrcLink(String...)#globalSrcLink(String...) + */ + public DokkaOperation globalLinks(Map globalLinks) { + globalLinks_.putAll(globalLinks); + return this; + } + + /** + * Sets the global list of package configurations. + *

+ * Using format: + *

    + *
  • matchingRegexp
  • + *
  • -deprecated
  • + *
  • -privateApi
  • + *
  • +warnUndocumented
  • + *
  • +suppress
  • + *
  • +visibility:PUBLIC
  • + *
  • ...
  • + *
+ * + * @param options ome pr more package configurations + * @return this operation instance + */ + public DokkaOperation globalPackageOptions(String... options) { + Collections.addAll(globalPackageOptions_, options); + return this; + } + + /** + * Sets the global list of package configurations. + *

+ * Using format: + *

    + *
  • matchingRegexp
  • + *
  • -deprecated
  • + *
  • -privateApi
  • + *
  • +warnUndocumented
  • + *
  • +suppress
  • + *
  • +visibility:PUBLIC
  • + *
  • ...
  • + *
+ * + * @param options the list of package configurations + * @return this operation instance + */ + public DokkaOperation globalPackageOptions(Collection options) { + globalPackageOptions_.addAll(options); + return this; + } + + /** + * Sets the global mapping between a source directory and a Web service for browsing the code. + * + * @param links one or more links mapping + * @return this operation instance + */ + public DokkaOperation globalSrcLink(String... links) { + Collections.addAll(globalSrcLinks_, links); + return this; + } + + /** + * Sets the global mapping between a source directory and a Web service for browsing the code. + * + * @param links the links mapping + * @return this operation instance + */ + public DokkaOperation globalSrcLink(Collection links) { + globalSrcLinks_.addAll(links); + return this; + } + + /** + * Sets the Markdown files that contain module and package documentation. + *

+ * The contents of specified files are parsed and embedded into documentation as module and package descriptions. + *

+ * This can be configured on per-package basis. + * + * @param files one or more files + * @return this operation instance + */ + public DokkaOperation includes(String... files) { + Collections.addAll(includes_, files); + return this; + } + + /** + * Sets the Markdown files that contain module and package documentation. + *

+ * The contents of specified files are parsed and embedded into documentation as module and package descriptions. + *

+ * This can be configured on per-package basis. + * + * @param files the list of files + * @return this operation instance + */ + public DokkaOperation includes(Collection files) { + includes_.addAll(files); + return this; + } + + /** + * JSON configuration file path. + * + * @param configuration the configuration file path + */ + public DokkaOperation json(File configuration) { + json = configuration; + return this; + } + + /** + * Sets the logging level. + * + * @param loggingLevel the logging level + * @return this operation instance + */ + public DokkaOperation loggingLevel(LoggingLevel loggingLevel) { + loggingLevel_ = loggingLevel; + return this; + } + + /** + * Sets the name of the project/module. Default is {@code root}. + *

+ * The display name used to refer to the module. It is used for the table of contents, navigation, logging, etc. + * + * @param moduleName the project/module name + * @return this operation instance + */ + public DokkaOperation moduleName(String moduleName) { + moduleName_ = moduleName; + return this; + } + + /** + * Set the documented version. + * + * @param version the version + * @return this operation instance + */ + public DokkaOperation moduleVersion(String version) { + moduleVersion_ = version; + return this; + } + + /** + * Sets whether to suppress obvious functions such as inherited from + * kotlin.Any and {@link java.lang.Object}. + *

+ * A function is considered to be obvious if it is: + *

    + *
  • Inherited from kotlin.Any, + * Kotlin.Enum, {@link java.lang.Object} + * or {@link java.lang.Enum}, such as {@code equals}, {@code hashCode}, {@code toString}. + *
  • Synthetic (generated by the compiler) and does not have any documentation, such as + * {@code dataClass.componentN} or {@code dataClass.copy}. + *
+ * + * @param noSuppressObviousFunctions {@code true} or {@code false} + * @return this operation instance + */ + public DokkaOperation noSuppressObviousFunctions(Boolean noSuppressObviousFunctions) { + noSuppressObviousFunctions_ = noSuppressObviousFunctions; + return this; + } + + /** + * Sets whether to resolve remote files/links over network. + *

+ * This includes package-lists used for generating external documentation links. For example, to make classes from + * the standard library clickable. + *

+ * Setting this to true can significantly speed up build times in certain cases, but can also worsen documentation + * quality and user experience. For example, by not resolving class/member links from your dependencies, including + * the standard library. + *

+ * Note: You can cache fetched files locally and provide them to Dokka as local paths. + * + * @param offlineMode the offline mode + * @return this operation instance + * @see SourceSet#externalDocumentationLinks(String, String) + */ + public DokkaOperation offlineMode(Boolean offlineMode) { + offlineMode_ = offlineMode; + return this; + } + + /** + * Sets the output directory path, {@code ./dokka} by default. + *

+ * The directory to where documentation is generated, regardless of output format. + * + * @param outputDir the output directory + * @return this operation instance + */ + public DokkaOperation outputDir(File outputDir) { + outputDir_ = outputDir; + return this; + } + + /** + * Sets the output directory path, {@code ./dokka} by default. + *

+ * The directory to where documentation is generated, regardless of output format. + * + * @param outputDir the output directory + * @return this operation instance + */ + public DokkaOperation outputDir(String outputDir) { + outputDir_ = new File(outputDir); + return this; + } + + /** + * Sets the Dokka {@link OutputFormat output format}. + * + * @param format The {@link OutputFormat output format} + * @return this operation instance + */ + public DokkaOperation outputFormat(OutputFormat format) { + pluginsClasspath_.clear(); + if (format.equals(OutputFormat.JAVADOC)) { + pluginsClasspath_.addAll(getJarList(project_.libBldDirectory(), + JAVADOC_PLUGIN_REGEXP)); + } else if (format.equals(OutputFormat.HTML)) { + pluginsClasspath_.addAll(getJarList(project_.libBldDirectory(), + HTML_PLUGIN_REGEXP)); + } else if (format.equals(OutputFormat.MARKDOWN)) { + pluginsClasspath_.addAll(getJarList(project_.libBldDirectory(), + GFM_PLUGIN_REGEXP)); + } else if (format.equals(OutputFormat.JEKYLL)) { + pluginsClasspath_.addAll(getJarList(project_.libBldDirectory(), + JEKYLL_PLUGIN_REGEXP)); + } + return this; + } + + /** + * Sets the configuration for Dokka plugins. + * + * @param name The fully-qualified plugin name + * @param jsonConfiguration The plugin JSON configuration + * @return this operation instance + */ + public DokkaOperation pluginConfigurations(String name, String jsonConfiguration) { + pluginsConfiguration_.put(name, jsonConfiguration); + return this; + } + + /** + * Sets the configuration for Dokka plugins. + * + * @param pluginConfiguratione the map of configurations + * @return this operation instance + * @see #pluginConfigurations(String, String) + */ + public DokkaOperation pluginConfigurations(Map pluginConfiguratione) { + pluginsConfiguration_.putAll(pluginConfiguratione); + return this; + } + + /** + * Sets the list of jars with Dokka plugins and their dependencies. + * + * @param jars one or more jars + * @return this operation instance + */ + public DokkaOperation pluginsClasspath(String... jars) { + Collections.addAll(pluginsClasspath_, jars); + return this; + } + + /** + * Sets the list of jars with Dokka plugins and their dependencies. + * + * @param jars the list of jars + * @return this operation instance + */ + public DokkaOperation pluginsClasspath(Collection jars) { + pluginsClasspath_.addAll(jars); + return this; + } + + /** + * Clears the list of Dokka plugins. + * + * @param clear set to clear the list + * @return this operation instance + */ + public DokkaOperation pluginsClasspath(boolean clear) { + if (clear) { + pluginsClasspath_.clear(); + } + return this; + } + + /** + * Sets the configurations for a source set. + *

+ * Individual and additional configuration of Kotlin source sets. + * + * @param sourceSet the source set configurations + * @return this operation instance + */ + public DokkaOperation sourceSet(SourceSet sourceSet) { + sourceSet_ = sourceSet; + return this; + } + + /** + * Sets whether to suppress inherited members that aren't explicitly overridden in a given class. + * + * @param suppressInheritedMembers {@code true} or {@code false} + * @return this operation instance + */ + public DokkaOperation suppressInheritedMembers(Boolean suppressInheritedMembers) { + suppressInheritedMembers_ = suppressInheritedMembers; + return this; + } +} diff --git a/src/main/java/rife/bld/extension/dokka/AnalysisPlatform.java b/src/main/java/rife/bld/extension/dokka/AnalysisPlatform.java new file mode 100644 index 0000000..c9fc8b4 --- /dev/null +++ b/src/main/java/rife/bld/extension/dokka/AnalysisPlatform.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension.dokka; + +/** + * Dokka's analysis platforms. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public enum AnalysisPlatform { + JVM, JS, NATIVE, COMMON, ANDROID +} diff --git a/src/main/java/rife/bld/extension/dokka/DocumentedVisibility.java b/src/main/java/rife/bld/extension/dokka/DocumentedVisibility.java new file mode 100644 index 0000000..c3fa2ab --- /dev/null +++ b/src/main/java/rife/bld/extension/dokka/DocumentedVisibility.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension.dokka; + +/** + * Dokka documented visibilities. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public enum DocumentedVisibility { + PUBLIC, PRIVATE, PROTECTED, INTERNAL, PACKAGE +} diff --git a/src/main/java/rife/bld/extension/dokka/LoggingLevel.java b/src/main/java/rife/bld/extension/dokka/LoggingLevel.java new file mode 100644 index 0000000..73ecac0 --- /dev/null +++ b/src/main/java/rife/bld/extension/dokka/LoggingLevel.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension.dokka; + +/** + * Dokka logging levels. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public enum LoggingLevel { + DEBUG, PROGRESS, INFO, WARN, ERROR +} diff --git a/src/main/java/rife/bld/extension/dokka/OutputFormat.java b/src/main/java/rife/bld/extension/dokka/OutputFormat.java new file mode 100644 index 0000000..9982aee --- /dev/null +++ b/src/main/java/rife/bld/extension/dokka/OutputFormat.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension.dokka; + +/** + * Dokka output formats. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public enum OutputFormat { + JAVADOC, JEKYLL, HTML, MARKDOWN +} diff --git a/src/main/java/rife/bld/extension/dokka/SourceSet.java b/src/main/java/rife/bld/extension/dokka/SourceSet.java new file mode 100644 index 0000000..0edf01c --- /dev/null +++ b/src/main/java/rife/bld/extension/dokka/SourceSet.java @@ -0,0 +1,682 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension.dokka; + +import rife.bld.extension.DokkaOperation; + +import java.io.File; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Configuration for a Dokka source set. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class SourceSet { + private static final String SEMICOLON = ";"; + private final Collection classpath_ = new ArrayList<>(); + private final Map dependentSourceSets_ = new ConcurrentHashMap<>(); + private final Collection documentedVisibilities_ = new ArrayList<>(); + private final Map externalDocumentationLinks_ = new ConcurrentHashMap<>(); + private final Collection includes_ = new ArrayList<>(); + private final Collection perPackageOptions_ = new ArrayList<>(); + private final Collection samples_ = new ArrayList<>(); + private final Map srcLinks_ = new ConcurrentHashMap<>(); + private final Collection src_ = new ArrayList<>(); + private final Collection suppressedFiles_ = new ArrayList<>(); + private AnalysisPlatform analysisPlatform_; + private String apiVersion_; + private String displayName_; + private String jdkVersion_; + private String languageVersion_; + private boolean noJdkLink_; + private boolean noSkipEmptyPackages_; + private boolean noStdlibLink_; + private boolean reportUndocumented_; + private boolean skipDeprecated_; + private String sourceSetName_; + + /** + * Sets the platform used for setting up analysis. Default is {@link AnalysisPlatform#JVM JVM} + *

+ * Platform to be used for setting up code analysis and {@code @sample} environment. + * + * @param analysisPlatform the analysis platform + * @return this operation instance + */ + public SourceSet analysisPlatform(AnalysisPlatform analysisPlatform) { + analysisPlatform_ = analysisPlatform; + return this; + } + + /** + * Sets the Kotlin API version used for setting up analysis and samples. + * + * @param apiVersion the api version + * @return this operation instance + */ + public SourceSet apiVersion(String apiVersion) { + apiVersion_ = apiVersion; + return this; + } + + /** + * Sets the Kotlin API version used for setting up analysis and samples. + * + * @param apiVersion the api version + * @return this operation instance + */ + public SourceSet apiVersion(int apiVersion) { + apiVersion_ = String.valueOf(apiVersion); + return this; + } + + /** + * Returns the formatted arguments. + * + * @return the arguments + */ + public List args() { + var args = new ArrayList(); + + // -analysisPlatform + if (analysisPlatform_ != null) { + args.add("-analysisPlatform"); + args.add(analysisPlatform_.name().toLowerCase()); + } + + // -apiVersion + if (apiVersion_ != null) { + args.add("-apiVersion"); + args.add(apiVersion_); + } + + // -classpath + if (!classpath_.isEmpty()) { + args.add("-classpath"); + args.add(String.join(SEMICOLON, classpath_)); + } + + // -dependentSourceSets + if (!dependentSourceSets_.isEmpty()) { + args.add("-dependentSourceSets"); + var deps = new ArrayList(); + dependentSourceSets_.forEach((k, v) -> deps.add(String.format("%s/%s", k, v))); + args.add(String.join(SEMICOLON, deps)); + } + + // -displayName + if (displayName_ != null) { + args.add("-displayName"); + args.add(displayName_); + } + + // -documentedVisibilities + if (!documentedVisibilities_.isEmpty()) { + args.add("-documentedVisibilities"); + var vis = new ArrayList(); + documentedVisibilities_.forEach(d -> vis.add(d.name().toLowerCase())); + args.add(String.join(SEMICOLON, vis)); + } + + // -externalDocumentationLinks + if (!externalDocumentationLinks_.isEmpty()) { + args.add("-externalDocumentationLinks"); + var links = new ArrayList(); + externalDocumentationLinks_.forEach((k, v) -> links.add(String.format("%s^%s", k, v))); + args.add(String.join("^^", links)); + } + + // -jdkVersion + if (jdkVersion_ != null) { + args.add("-jdkVersion"); + args.add(jdkVersion_); + } + + // -includes + if (!includes_.isEmpty()) { + args.add("-includes"); + args.add(String.join(SEMICOLON, includes_)); + } + + // -languageVersion + if (languageVersion_ != null) { + args.add("-languageVersion"); + args.add(languageVersion_); + } + + // -noJdkLink + if (noJdkLink_) { + args.add("-noJdkLink"); + args.add(String.valueOf(noJdkLink_)); + } + + // -noSkipEmptyPackages + if (noSkipEmptyPackages_) { + args.add("-noSkipEmptyPackages"); + args.add(String.valueOf(noSkipEmptyPackages_)); + } + + // -noStdlibLink + if (noStdlibLink_) { + args.add("-noStdlibLink"); + args.add(String.valueOf(noStdlibLink_)); + } + + // -reportUndocumented + if (reportUndocumented_) { + args.add("-reportUndocumented"); + args.add(String.valueOf(reportUndocumented_)); + } + + // -perPackageOptions + if (!perPackageOptions_.isEmpty()) { + args.add("-perPackageOptions"); + args.add(String.join(SEMICOLON, perPackageOptions_)); + } + + // -samples + if (!samples_.isEmpty()) { + args.add("-samples"); + args.add(String.join(SEMICOLON, samples_)); + } + + // -skipDeprecated + if (skipDeprecated_) { + args.add("-skipDeprecated"); + args.add(String.valueOf(skipDeprecated_)); + } + + // -src + if (!src_.isEmpty()) { + args.add("-src"); + args.add(String.join(SEMICOLON, src_)); + } + + // -srcLink + if (!srcLinks_.isEmpty()) { + args.add("-srcLink"); + var links = new ArrayList(); + srcLinks_.forEach((k, v) -> links.add(String.format("%s=%s", k, v))); + args.add(String.join(SEMICOLON, links)); + } + + // -sourceSetName + if (sourceSetName_ != null) { + args.add("-sourceSetName"); + args.add(sourceSetName_); + } + + // -suppressedFiles + if (!suppressedFiles_.isEmpty()) { + args.add("-suppressedFiles"); + args.add(String.join(SEMICOLON, suppressedFiles_)); + } + + return args; + } + + /** + * Sets classpath for analysis and interactive samples. + *

+ * This is useful if some types that come from dependencies are not resolved/picked up automatically. + *

+ * This option accepts both {@code .jar} and {@code .klib} files. + * + * @param files one or more file + * @return this operation instance + */ + public SourceSet classpath(String... files) { + Collections.addAll(classpath_, files); + return this; + } + + /** + * Sets classpath for analysis and interactive samples. + *

+ * This is useful if some types that come from dependencies are not resolved/picked up automatically. + *

+ * This option accepts both {@code .jar} and {@code .klib} files. + * + * @param files the list of files + * @return this operation instance + */ + public SourceSet classpath(Collection files) { + classpath_.addAll(files); + return this; + } + + /** + * Sets classpath for analysis and interactive samples. + *

+ * This is useful if some types that come from dependencies are not resolved/picked up automatically. + *

+ * This option accepts both {@code .jar} and {@code .klib} files. + * + * @param files the list of files + * @return this operation instance + */ + public SourceSet classpath(List files) { + files.forEach(it -> classpath_.add(it.getAbsolutePath())); + return this; + } + + /** + * Sets the names of dependent source sets. + * + * @param moduleName the module name + * @param sourceSetName the source set name + * @return this operation instance + */ + public SourceSet dependentSourceSets(String moduleName, String sourceSetName) { + dependentSourceSets_.put(moduleName, sourceSetName); + return this; + } + + /** + * Sets the names of dependent source sets. + * + * @param dependentSourceSets the map of dependent source set names + * @return this operation instance + * @see #dependentSourceSets(String, String) + */ + public SourceSet dependentSourceSets(Map dependentSourceSets) { + dependentSourceSets_.putAll(dependentSourceSets); + return this; + } + + /** + * Sets the display name of the source set, used both internally and externally. + *

+ * The name is used both externally (for example, the source set name is visible to documentation readers) and + * internally (for example, for logging messages of {@link #reportUndocumented reportUndocumented}). + *

+ * The platform name can be used if you don't have a better alternative. + * + * @param displayName the display name + * @return this operation instance + */ + public SourceSet displayName(String displayName) { + displayName_ = displayName; + return this; + } + + /** + * Sets visibilities to be documented. + *

+ * This can be used if you want to document protected/internal/private declarations, as well as if you want to + * exclude public declarations and only document internal API. + *

+ * This can be configured on per-package basis. + * + * @param visibilities one or more visibilities + * @return this operation instance + */ + public SourceSet documentedVisibilities(DocumentedVisibility... visibilities) { + documentedVisibilities_.addAll(Arrays.asList(visibilities)); + return this; + } + + /** + * Sets the external documentation links. + *

+ * A set of parameters for external documentation links that is applied only for this source set. + * + * @param url the external documentation URL + * @param packageListUrl the external documentation package list URL + * @return this operation instance + */ + public SourceSet externalDocumentationLinks(String url, String packageListUrl) { + externalDocumentationLinks_.put(url, packageListUrl); + return this; + } + + /** + * Sets the external documentation links. + *

+ * A set of parameters for external documentation links that is applied only for this source set. + * + * @param externalDocumentationLinks the map of external documentation links + * @return this operation instance + * @see #externalDocumentationLinks(String, String) + */ + public SourceSet externalDocumentationLinks(Map externalDocumentationLinks) { + externalDocumentationLinks_.putAll(externalDocumentationLinks); + return this; + } + + /** + * Sets the Markdown files that contain module and package documentation. + *

+ * A list of Markdown files that contain module and package documentation. + *

+ * The contents of the specified files are parsed and embedded into documentation as module and package + * descriptions. + * + * @param files one or more files + * @return this operation instance + */ + public SourceSet includes(String... files) { + Collections.addAll(includes_, files); + return this; + } + + /** + * Sets the Markdown files that contain module and package documentation. + *

+ * A list of Markdown files that contain module and package documentation. + *

+ * The contents of the specified files are parsed and embedded into documentation as module and package + * descriptions. + * + * @param files the list of files + * @return this operation instance + */ + public SourceSet includes(Collection files) { + includes_.addAll(files); + return this; + } + + /** + * Sets the version of JDK to use for linking to JDK Javadocs. + *

+ * The JDK version to use when generating external documentation links for Java types. + *

+ * For example, if you use {@link java.util.UUID} in some public declaration signature, and this option is set to 8, + * Dokka generates an external documentation link to JDK 8 Javadocs for it. + * + * @param jdkVersion the JDK version + * @return this operation instance + */ + public SourceSet jdkVersion(String jdkVersion) { + jdkVersion_ = jdkVersion; + return this; + } + + /** + * Sets the version of JDK to use for linking to JDK Javadocs. + *

+ * The JDK version to use when generating external documentation links for Java types. + *

+ * For example, if you use {@link java.util.UUID} in some public declaration signature, and this option is set to 8, + * Dokka generates an external documentation link to JDK 8 Javadocs for it. + * + * @param jdkVersion the JDK version + * @return this operation instance + */ + public SourceSet jdkVersion(int jdkVersion) { + jdkVersion_ = String.valueOf(jdkVersion); + return this; + } + + /** + * Sets the language version used for setting up analysis and samples. + * + * @param languageVersion the language version + * @return this operation instance + */ + public SourceSet languageVersion(String languageVersion) { + languageVersion_ = languageVersion; + return this; + } + + /** + * Sets the language version used for setting up analysis and samples. + * + * @param languageVersion the language version + * @return this operation instance + */ + public SourceSet languageVersion(int languageVersion) { + languageVersion_ = String.valueOf(languageVersion); + return this; + } + + /** + * Sets whether to generate links to JDK Javadocs. + *

+ * Whether to generate external documentation links to JDK's Javadocs. + *

+ * The version of JDK Javadocs is determined by the {@link #jdkVersion jdkVersion} option. + *

+ * Note: Links are generated when noJdkLink is set to false. + * + * @param noJdkLink {@code true} or {@code false} + * @return this operation instance + */ + public SourceSet noJdkLink(Boolean noJdkLink) { + noJdkLink_ = noJdkLink; + return this; + } + + /** + * Sets whether to create pages for empty packages. + *

+ * Whether to skip packages that contain no visible declarations after various filters have been applied. + * + * @param noSkipEmptyPackages {@code true} or {@code false} + * @return this operation instance + */ + public SourceSet noSkipEmptyPackages(boolean noSkipEmptyPackages) { + noSkipEmptyPackages_ = noSkipEmptyPackages; + return this; + } + + /** + * Sets whether to generate links to Standard library. + *

+ * Whether to generate external documentation links that lead to the API reference documentation of Kotlin's + * standard library. + *

+ * Note: Links are generated when noStdLibLink is set to {@code false}. + * + * @param noStdlibLink {@code true} or {@code false} + * @return this operation instance + */ + public SourceSet noStdlibLink(Boolean noStdlibLink) { + noStdlibLink_ = noStdlibLink; + return this; + } + + /** + * Set the list of package source set configuration. + *

+ * A set of parameters specific to matched packages within this source set. + *

+ * Using format: + *

    + *
  • matchingRegexp
  • + *
  • -deprecated
  • + *
  • -privateApi
  • + *
  • +warnUndocumented
  • + *
  • +suppress
  • + *
  • +visibility:PUBLIC
  • + *
  • ...
  • + *
+ * + * @param perPackageOptions the list of per package options + * @return this operation instance + */ + public SourceSet perPackageOptions(Collection perPackageOptions) { + perPackageOptions_.addAll(perPackageOptions); + return this; + } + + /** + * Set the list of package source set configuration. + *

+ * A set of parameters specific to matched packages within this source set. + *

+ * Using format: + *

    + *
  • matchingRegexp
  • + *
  • -deprecated
  • + *
  • -privateApi
  • + *
  • +warnUndocumented
  • + *
  • +suppress
  • + *
  • +visibility:PUBLIC
  • + *
  • ...
  • + *
+ * + * @param perPackageOptions the list of per package options + * @return this operation instance + */ + public SourceSet perPackageOptions(String... perPackageOptions) { + Collections.addAll(perPackageOptions_, perPackageOptions); + return this; + } + + /** + * Sets whether to report undocumented declarations. + *

+ * Whether to emit warnings about visible undocumented declarations, that is declarations without KDocs after they + * have been filtered by documentedVisibilities and other filters. + *

+ * This setting works well with {@link DokkaOperation#failOnWarning}. + *

+ * This can be configured on per-package basis. + * + * @param reportUndocumented {@code true} or {@code false} + * @return this operation instance + */ + public SourceSet reportUndocumented(Boolean reportUndocumented) { + reportUndocumented_ = reportUndocumented; + return this; + } + + /** + * Set the list of directories or files that contain sample functions. + *

+ * A list of directories or files that contain sample functions which are referenced via the {@code @sample} KDoc + * tag. + * + * @param samples the list of samples + * @return this operation instance + */ + public SourceSet samples(Collection samples) { + samples_.addAll(samples); + return this; + } + + /** + * Set the list of directories or files that contain sample functions. + *

+ * A list of directories or files that contain sample functions which are referenced via the {@code @sample} KDoc + * tag. + * + * @param samples nne or more samples + * @return this operation instance + */ + public SourceSet samples(String... samples) { + Collections.addAll(samples_, samples); + return this; + } + + /** + * Sets whether to skip deprecated declarations. + *

+ * Whether to document declarations annotated with {@code @Deprecated}. + *

+ * This can be configured on per-package basis. + * + * @param skipDeprecated {@code true} or {@code false} + * @return this operation instance + */ + public SourceSet skipDeprecated(boolean skipDeprecated) { + skipDeprecated_ = skipDeprecated; + return this; + } + + /** + * Sets the name of the source set. Default is {@code main}. + * + * @param sourceSetName the source set name. + * @return this operation instance + */ + public SourceSet sourceSetName(String sourceSetName) { + sourceSetName_ = sourceSetName; + return this; + } + + /** + * Sets the source code roots to be analyzed and documented. + *

+ * The source code roots to be analyzed and documented. Acceptable inputs are directories and individual + * {@code .kt} / {@code .java} files. + * + * @param src the list of source code roots + * @return this operation instance + */ + public SourceSet src(Collection src) { + src_.addAll(src); + return this; + } + + /** + * Sets the source code roots to be analyzed and documented. + *

+ * The source code roots to be analyzed and documented. Acceptable inputs are directories and individual + * {@code .kt} / {@code .java} files. + * + * @param src pne ore moe source code roots + * @return this operation instance + */ + public SourceSet src(String... src) { + Collections.addAll(src_, src); + return this; + } + + /** + * Sets the mapping between a source directory and a Web service for browsing the code. + * + * @param srcPath the source path + * @param remotePath the remote path + * @param lineSuffix the line suffix + * @return this operation instance + */ + public SourceSet srcLink(String srcPath, String remotePath, String lineSuffix) { + srcLinks_.put(srcPath, remotePath + lineSuffix); + return this; + } + + /** + * Sets the paths to files to be suppressed. + *

+ * The files to be suppressed when generating documentation. + * + * @param suppressedFiles the list of suppressed files + * @return this operation instance + */ + public SourceSet suppressedFiles(Collection suppressedFiles) { + suppressedFiles_.addAll(suppressedFiles); + return this; + } + + /** + * Sets the paths to files to be suppressed. + *

+ * The files to be suppressed when generating documentation. + * + * @param suppressedFiles one or moe suppressed files + * @return this operation instance + */ + public SourceSet suppressedFiles(String... suppressedFiles) { + suppressedFiles_.addAll(Arrays.asList(suppressedFiles)); + return this; + } +} diff --git a/src/test/java/rife/bld/extension/DokkaOperationTest.java b/src/test/java/rife/bld/extension/DokkaOperationTest.java new file mode 100644 index 0000000..4abf744 --- /dev/null +++ b/src/test/java/rife/bld/extension/DokkaOperationTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension; + +import org.junit.jupiter.api.Test; +import rife.bld.blueprints.BaseProjectBlueprint; +import rife.bld.extension.dokka.LoggingLevel; +import rife.bld.extension.dokka.OutputFormat; +import rife.bld.extension.dokka.SourceSet; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; + +class DokkaOperationTest { + @Test + @SuppressWarnings({"ExtractMethodRecommender", "PMD.AvoidDuplicateLiterals"}) + void executeConstructProcessCommandListTest() throws IOException { + var args = Files.readAllLines(Paths.get("src", "test", "resources", "dokka-args.txt")); + + assertThat(args).isNotEmpty(); + + var examples = new File("examples"); + var jsonConf = new File("config.json"); + var params = new DokkaOperation() + .delayTemplateSubstitution(true) + .failOnWarning(true) + .fromProject(new BaseProjectBlueprint(examples, "com.example", "Example")) + .globalLinks("s", "link") + .globalLinks(Map.of("s2", "link2")) + .globalPackageOptions("option1", "option2") + .globalPackageOptions(List.of("option3", "option4")) + .globalSrcLink("link1", "link2") + .globalSrcLink(List.of("link3", "link4")) + .includes("file1", "file2") + .includes(List.of("file3", "file4")) + .json(jsonConf) + .loggingLevel(LoggingLevel.DEBUG) + .moduleName("name") + .moduleVersion("1.0") + .noSuppressObviousFunctions(true) + .offlineMode(true) + .outputDir(new File(examples, "build")) + .outputFormat(OutputFormat.JAVADOC) + .pluginConfigurations("name", "{\"json\"}") + .pluginConfigurations(Map.of("{\"name2\"}", "json2", "name3}", "{json3")) + .pluginsClasspath("path1", "path2") + .pluginsClasspath(List.of("path3", "path4")) + .sourceSet(new SourceSet().classpath( + List.of( + new File("examples/foo.jar"), + new File("examples/bar.jar") + ))) + .suppressInheritedMembers(true) + .executeConstructProcessCommandList(); + + for (var p : args) { + var found = false; + for (var a : params) { + if (a.startsWith(p)) { + found = true; + break; + } + } + assertThat(found).as(p + " not found.").isTrue(); + } + + var path = examples.getAbsolutePath(); + var dokkaJar = "1.9.20.jar"; + var matches = List.of("java", + "-jar", path + "/lib/bld/dokka-cli-" + dokkaJar, + "-pluginsClasspath", path + "/lib/bld/dokka-base-" + dokkaJar + ';' + + path + "/lib/bld/analysis-kotlin-descriptors-" + dokkaJar + ';' + + path + "/lib/bld/javadoc-plugin-" + dokkaJar + ';' + + path + "/lib/bld/korte-jvm-4.0.10.jar;" + + path + "/lib/bld/kotlin-as-java-plugin-" + dokkaJar + ";path1;path2;path3;path4", + "-sourceSet", "-src " + path + "/src/main/kotlin" + " -classpath " + path + "/foo.jar;" + path + "/bar.jar", + "-outputDir", path + "/build", + "-delayTemplateSubstitution", + "-failOnWarning", + "-globalLinks", "s^link^^s2^link2", + "-globalPackageOptions", "option1;option2;option3;option4", + "-globalSrcLinks_", "link1;link2;link3;link4", + "-includes", "file1;file2;file3;file4", + "-loggingLevel", "debug", + "-moduleName", "name", + "-moduleVersion", "1.0", + "-noSuppressObviousFunctions", + "-offlineMode", + "-pluginsConfiguration", "{\\\"name2\\\"}={json2}^^{name}={\\\"json\\\"}^^{name3}}={{json3}", + "-suppressInheritedMembers", + jsonConf.getAbsolutePath()); + + assertThat(params).hasSize(matches.size()); + + IntStream.range(0, params.size()).forEach(i -> { + if (params.get(i).contains(".jar;")) { + var jars = params.get(i).split(";"); + Arrays.stream(jars).forEach(jar -> assertThat(matches.get(i)).as(matches.get(i)).contains(jar)); + } else { + assertThat(params.get(i)).as(params.get(i)).isEqualTo(matches.get(i)); + } + }); + } +} diff --git a/src/test/java/rife/bld/extension/SourceSetTest.java b/src/test/java/rife/bld/extension/SourceSetTest.java new file mode 100644 index 0000000..4453fe4 --- /dev/null +++ b/src/test/java/rife/bld/extension/SourceSetTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2023-Copyright $today.yearamp;#36;today.year 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rife.bld.extension; + +import org.junit.jupiter.api.Test; +import rife.bld.extension.dokka.AnalysisPlatform; +import rife.bld.extension.dokka.DocumentedVisibility; +import rife.bld.extension.dokka.SourceSet; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; + +class SourceSetTest { + @Test + void sourceSetCollectionsTest() { + var args = new SourceSet() + .classpath(List.of("path1", "path2")) + .dependentSourceSets(Map.of("set1", "set2", "set3", "set4")) + .externalDocumentationLinks(Map.of("link1", "link2", "link3", "link4")) + .perPackageOptions(List.of("option1", "option2")) + .samples(List.of("samples1", "samples1")) + .suppressedFiles(List.of("sup1", "sup2")) + .args(); + + var matches = List.of( + "-classpath", "path1;path2", + "-dependentSourceSets", "set1/set2;set3/set4", + "-externalDocumentationLinks", "link3^link4^^link1^link2", + "-perPackageOptions", "option1;option2", + "-samples", "samples1;samples1", + "-suppressedFiles", "sup1;sup2" + ); + + assertThat(args).hasSize(matches.size()); + + IntStream.range(0, args.size()).forEach(i -> assertThat(args.get(i)).isEqualTo(matches.get(i))); + } + + @Test + @SuppressWarnings("PMD.AvoidDuplicateLiterals") + void sourceSetTest() throws IOException { + var args = Files.readAllLines(Paths.get("src", "test", "resources", "dokka-sourceset-args.txt")); + + assertThat(args).isNotEmpty(); + + var sourceSet = new SourceSet() + .analysisPlatform(AnalysisPlatform.JVM) + .apiVersion("1.0") + .classpath("classpath1", "classpath2") + .dependentSourceSets("moduleName", "sourceSetName") + .displayName("name") + .documentedVisibilities(DocumentedVisibility.PACKAGE, DocumentedVisibility.PRIVATE) + .externalDocumentationLinks("url1", "packageListUrl1") + .externalDocumentationLinks("url2", "packageListUrl2") + .includes("includes1", "includes2") + .jdkVersion(18) + .languageVersion("2.0") + .noJdkLink(true) + .noSkipEmptyPackages(true) + .noStdlibLink(true) + .perPackageOptions("options1", "options2") + .reportUndocumented(true) + .samples("samples1", "sample2") + .skipDeprecated(true) + .sourceSetName("setName") + .src("src1", "src2") + .srcLink("path1", "remote1", "#suffix1") + .srcLink("path2", "remote2", "#suffix2") + .suppressedFiles("sup1", "sup2"); + + var params = sourceSet.args(); + + for (var p : args) { + var found = false; + for (var a : params) { + if (a.startsWith(p)) { + found = true; + break; + } + } + assertThat(found).as(p + " not found.").isTrue(); + } + + var matches = List.of( + "-analysisPlatform", "jvm", + "-apiVersion", "1.0", + "-classpath", "classpath1;classpath2", + "-dependentSourceSets", "moduleName/sourceSetName", + "-displayName", "name", + "-documentedVisibilities", "package;private", + "-externalDocumentationLinks", "url1^packageListUrl1^^url2^packageListUrl2", + "-jdkVersion", "18", + "-includes", "includes1;includes2", + "-languageVersion", "2.0", + "-noJdkLink", "true", + "-noSkipEmptyPackages", "true", + "-noStdlibLink", "true", + "-reportUndocumented", "true", + "-perPackageOptions", "options1;options2", + "-samples", "samples1;sample2", + "-skipDeprecated", "true", + "-src", "src1;src2", + "-srcLink", "path1=remote1#suffix1;path2=remote2#suffix2", + "-sourceSetName", "setName", + "-suppressedFiles", "sup1;sup2"); + + assertThat(params).hasSize(matches.size()); + + IntStream.range(0, params.size()).forEach(i -> assertThat(params.get(i)).isEqualTo(matches.get(i))); + + sourceSet.classpath(List.of("classpath1", "classpath2")); + + IntStream.range(0, params.size()).forEach(i -> assertThat(params.get(i)).isEqualTo(matches.get(i))); + } +} diff --git a/src/test/resources/dokka-args.txt b/src/test/resources/dokka-args.txt new file mode 100644 index 0000000..4c9f29c --- /dev/null +++ b/src/test/resources/dokka-args.txt @@ -0,0 +1,16 @@ +-delayTemplateSubstitution +-failOnWarning +-globalLinks +-globalPackageOptions +-globalSrcLink +-includes +-loggingLevel +-moduleName +-moduleVersion +-noSuppressObviousFunctions +-offlineMode +-outputDir +-pluginsClasspath +-pluginsConfiguration +-sourceSet +-suppressInheritedMembers diff --git a/src/test/resources/dokka-sourceset-args.txt b/src/test/resources/dokka-sourceset-args.txt new file mode 100644 index 0000000..0699eb7 --- /dev/null +++ b/src/test/resources/dokka-sourceset-args.txt @@ -0,0 +1,21 @@ +-analysisPlatform +-apiVersion +-classpath +-dependentSourceSets +-displayName +-documentedVisibilities +-externalDocumentationLinks +-includes +-jdkVersion +-languageVersion +-noJdkLink +-noSkipEmptyPackages +-noStdlibLink +-perPackageOptions +-reportUndocumented +-samples +-skipDeprecated +-sourceSetName +-src +-srcLink +-suppressedFiles