diff --git a/.gitignore b/.gitignore index 14314b412..29ce75ec3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,11 @@ **/src/site/apt/*.txt **/.externalToolBuilders/**/* **/.checkstyle + +# Gradle plugin +apache-rat-gradle-plugin/build/** +apache-rat-gradle-plugin/.idea +apache-rat-gradle-plugin/.gradle +# Gradle wrapper jar is downloaded and its checksum validated by apache-rat-gradle-plugin/gradle/gradlew-include.sh +apache-rat-gradle-plugin/gradle/wrapper/*.jar +apache-rat-gradle-plugin/gradle/wrapper/*.sha256 diff --git a/apache-rat-core/src/main/resources/org/apache/rat/html.xsl b/apache-rat-core/src/main/resources/org/apache/rat/html.xsl new file mode 100644 index 000000000..967ff522b --- /dev/null +++ b/apache-rat-core/src/main/resources/org/apache/rat/html.xsl @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + +

Rat Report

+

This HTML version (yes, it is!) is generated from the RAT xml reports using Saxon9B. All the outputs required are displayed below, similar to the .txt version. + This is obviously a work in progress; and a prettier, easier to read and manage version will be available soon

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 1: A snapshot summary of this rat report. +
Notes: Binaries: Archives: Standards:
Apache Licensed: Generated Documents:
Note: JavaDocs are generated and so license header is optionalNote: Generated files do not require license headers
Unknown Licenses - or files without a license. Unknown Licenses - or files without a license.
+
+
+

Unapproved Licenses:

+ + + +
+ + +
+
+ +

Archives:

+ + + + +
+
+
+ +

+ Files with Apache License headers will be marked AL
+ Binary files (which do not require AL headers) will be marked B
+ Compressed archives will be marked A
+ Notices, licenses etc will be marked N
+

+ + + + ! + + + + N + A + B + + !!!!! + + +
+ + +
+
+ +

Printing headers for files without AL header...

+ + + +

+ +
+
+
+ + + +
+ + +
+

Resource:

+ +
+
+ + + +

First few lines of non-compliant file

+

+ +

+
+

Other Info:

+
+ + + Header Type: +
+
+ + + License Family: +
+
+ + + License Approval: +
+
+ + + Type: +
+
+ + + +
diff --git a/apache-rat-gradle-plugin/README.md b/apache-rat-gradle-plugin/README.md new file mode 100644 index 000000000..d1cf45a7b --- /dev/null +++ b/apache-rat-gradle-plugin/README.md @@ -0,0 +1,141 @@ + + +# WIP instructions + +## Using the Gradle plugin (once RAT 1.0.0 is released) + +1. Add the following to your `settings.gradle.kts` or `settings.gradle` file. + ```kotlin + pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } + } + ``` +2. Add the following to your `build.gradle[.kts]` file. Adopt the version to the latest release + ```kotlin + plugins { + id("org.apache.rat") version "1.0.0" + } + ``` + + For Groovy (`build.gradle`) use: + ```groovy + plugins { + id "org.apache.rat" version "1.0.0" + } + ``` + +## IDEs + +### IntelliJ IDEA + +There's no special setup needed to develop the RAT Gradle plugin, except having to install the Maven projects +to your local Maven repository using `./mvnw install -DskipTests -T1C` before. + +IntelliJ asks you to "link" the Gradle project when you open the `build.gradle.kts` or `settings.gradle.kts` file. + +## Integration in a Gradle build (locally built / for development) + +1. `./mvnw install -DskipTests -T1C` +2. Alternative 1: Use the plugin via + `settings.gradle.kts`: + ```kotlin + pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + mavenLocal() + } + } + ``` + `build.gradle.kts`: + ```kotlin + plugins { + id("org.apache.rat") version "1.0.0-SNAPSHOT" + } + ``` +3. Or include the plugin as an included Gradle build: + `settings.gradle.kts`: + ```kotlin + includeBuild("/apache-rat-gradle-plugin") { + name = "apache-rat-gradle-plugin" + } + pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + mavenLocal() + } + } + ``` + `build.gradle.kts`: + ```kotlin + plugins { + id("org.apache.rat") + } + ``` + +## TODO + +* Documentation + * On website +* Release to Maven Central or Gradle plugin portal or both? +* Split task into rat check + 1 or more report tasks? (likely overengineering) + * Default output is XML, text, HTML +* Unify code of `GradleOption` + `MavenOption`, if and where necessary? +* Apache release process + * Are Gradle Plugin sources included in release source tarball? + * Is the RAT release process automated?? + +## Done + +* Add Gradle configurations to consume RAT reports in other Gradle tasks. +* Align code formatting with Creadur RAT. +* LICENSE/NOTICE file work needed? + * No changes needed +* Gradle plugin specific unit and integration tests +* Integrate into CI workflows +* Cleanup plugin's build file(s) +* Verify all artifacts ("special" Gradle plugin artifact) are built and deployed by Maven +* Integrate into Maven build +* Logging: + * Implement a `Log` instance that delegates to Gradle's SLF4J compatible logging and install via + `org.apache.rat.utils.DefaultLog.setInstance`. +* Use Gradle's worker approach to isolate parallel RAT runs. +* Allow usage of Rat native types (enums) in config options. + * Alternatively or in addition to `String`s? +* Use `RegularFileProperty` for options that reference files. +* Added `GRADLE` standard exclude collection +* `GradleGenerator`, `Gradle.tpl` + * Generation of `GradleOptions` in Gradle plugin build +* Gradle plugin extension + task +* Smoke-tested via Apache Polaris build + * `./mvnw install -DskipTests -T1C` in creadur-rat + * Checkout `rat-new-plugin` from `https://github.com/snazy/polaris/` + * Adopt file path to `apache-rat-gradle-plugin`in `settings.gradle.kts` (only for this WIP work!) + * Run `./gradlew :rat` + +## Later + +* Make Gradle task fully cachable by Gradle + * Requires Gradle task to know all files that are considered + * Caching support for properties + generated files is alrady implemented though diff --git a/apache-rat-gradle-plugin/build.gradle.kts b/apache-rat-gradle-plugin/build.gradle.kts new file mode 100644 index 000000000..1ebc773b1 --- /dev/null +++ b/apache-rat-gradle-plugin/build.gradle.kts @@ -0,0 +1,290 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +import groovy.util.Node +import groovy.util.NodeList +import groovy.xml.XmlParser +import org.jetbrains.gradle.ext.copyright +import org.jetbrains.gradle.ext.encodings +import org.jetbrains.gradle.ext.settings + +plugins { + idea + eclipse + id("com.diffplug.spotless") version "8.0.0" + id("org.jetbrains.gradle.plugin.idea-ext") version "1.3" + + `java-gradle-plugin` + `jvm-test-suite` +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Maven integration helper functions + +fun xmlFirstChild(node: Node, element: String): Node = (node.get(element) as NodeList)[0] as Node + +val rootPom: Node = XmlParser().parse(file("../pom.xml")) +val projectPom: Node = XmlParser().parse(file("pom.xml")) + +val rootPomDependencyManagement = xmlFirstChild(rootPom, "dependencyManagement") +val rootPomDependencies = xmlFirstChild(rootPomDependencyManagement, "dependencies") + +/** Get a `` node by groupId and artifactId. */ +fun pomDependency(pomDependencies: Node, groupId: String, artifactId: String): Node = + pomDependencies + .children() + .map { it as Node } + .first { dependency -> + xmlFirstChild(dependency, "groupId").text() == groupId + xmlFirstChild(dependency, "artifactId").text() == artifactId + } + +/** Get the managed dependency version from the root-pom by groupId and artifactId. */ +fun rootPomDependencyVersion(groupId: String, artifactId: String): String = + if (groupId == project.group.toString()) project.version.toString() + else xmlFirstChild(pomDependency(rootPomDependencies, groupId, artifactId), "version").text() + +/** Get the managed dependency as a GAV string from the root-pom by groupId and artifactId. */ +fun rootPomDependency(groupId: String, artifactId: String): String = + "$groupId:$artifactId:${rootPomDependencyVersion(groupId, artifactId)}" + +// //////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Plugin, dependencies, et al + +dependencies { + // Gradle plugin production dependencies. + // Declared in the `pom.xml` file to a have a single source of truth. + xmlFirstChild(projectPom, "dependencies") + .children() + .map { it as Node } + .forEach { dependency -> + val groupId = xmlFirstChild(dependency, "groupId").text() + val artifactId = xmlFirstChild(dependency, "artifactId").text() + val version = rootPomDependencyVersion(groupId, artifactId) + implementation("$groupId:$artifactId:$version") + } +} + +gradlePlugin { + plugins { + register("ratPlugin") { + id = project.group.toString() + implementationClass = "org.apache.rat.gradle.RatPlugin" + // The following information is only relevant for the Gradle Plugin portal + displayName = "Apache RAT Gradle Plugin" + description = "Apache Release Audit Tool Gradle Plugin" + tags.addAll("apache", "release", "audit", "RAT") + } + } +} + +// Build the javadoc and sources jars when assembling (assemble / build tasks). +java { + withJavadocJar() + withSourcesJar() +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////////////// +// RAT options code generation + +val generateGradleOptions by configurations.creating + +dependencies { + // Need apache-rat-tools jar to execute the contained `GradleGenerator` class. + generateGradleOptions("org.apache.rat:apache-rat-tools:$version") +} + +val generatedOptionsDir = project.layout.buildDirectory.dir("generated/sources/rat") + +sourceSets { main { java { srcDir(generatedOptionsDir) } } } + +val generateGradleOptionsSource by + tasks.registering(JavaExec::class) { + classpath(generateGradleOptions) + mainClass = "org.apache.rat.tools.GradleGenerator" + args = + listOf( + "org.apache.rat.gradle", + "RatOptions", + generatedOptionsDir.get().asFile.relativeTo(project.projectDir).toString(), + ) + inputs.file("../apache-rat-tools/src/main/resources/GradleConfiguration.tpl") + inputs.file("../apache-rat-tools/src/main/resources/GradleOptions.tpl") + inputs.file("../apache-rat-tools/src/main/resources/GradleTaskBase.tpl") + outputs.cacheIf { true } + outputs.dir(generatedOptionsDir) + actions.addFirst { generatedOptionsDir.get().asFile.deleteRecursively() } + } + +tasks.named("compileJava") { dependsOn(generateGradleOptionsSource) } + +tasks.withType(JavaCompile::class.java).configureEach { options.release = 8 } + +tasks.named("sourcesJar") { dependsOn(generateGradleOptionsSource) } + +testing { + suites { + withType { + useJUnitJupiter(rootPomDependencyVersion("org.junit", "junit-bom")) + + dependencies { + implementation(project()) + + implementation(platform(rootPomDependency("org.junit", "junit-bom"))) + implementation("org.junit.jupiter:junit-jupiter") + implementation(rootPomDependency("org.assertj", "assertj-core")) + } + + targets.configureEach { + testTask.configure { + systemProperty("file.encoding", "UTF-8") + systemProperty("user.language", "en") + systemProperty("user.country", "US") + systemProperty("user.variant", "") + } + } + } + } +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IDE helpers + +val ideName = "${project.name} ${rootProject.version.toString().replace("^([0-9.]+).*", "\\1")}" + +if (System.getProperty("idea.sync.active").toBoolean()) { + // There's no proper way to set the name of the IDEA project (when "just importing" or + // syncing the Gradle project) + val ideaDir = rootProject.layout.projectDirectory.dir(".idea") + ideaDir.asFile.mkdirs() + ideaDir.file(".name").asFile.writeText(ideName) +} + +eclipse { project { name = ideName } } + +if (System.getProperty("idea.sync.active").toBoolean()) { + idea { + module { + isDownloadJavadoc = false // was 'true', but didn't work + isDownloadSources = false // was 'true', but didn't work + inheritOutputDirs = true + + excludeDirs = + excludeDirs + + setOf( + projectDir.resolve("build-logic/.kotlin"), + projectDir.resolve("target"), + projectDir.resolve(".gradle"), + projectDir.resolve(".idea"), + ) + + allprojects.map { prj -> prj.layout.buildDirectory.asFile.get() } + } + + project.settings { + copyright { + useDefault = "ApacheLicense-v2" + profiles.create("ApacheLicense-v2") { + // strip trailing LF + val copyrightText = rootProject.file("codestyle/copyright-header.txt").readText() + notice = copyrightText + } + } + + encodings.encoding = "UTF-8" + encodings.properties.encoding = "UTF-8" + } + } +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Code style/formatting + +spotless { + java { + target("src/*/java/**/*.java") + googleJavaFormat() + importOrder("java", "javax", "org").apply { + forbidWildcardImports() + semanticSort() + // No equivalent for static imports, always at the top. + } + licenseHeaderFile(rootProject.file("codestyle/copyright-header-java.txt")) + endWithNewline() + toggleOffOn() + } + kotlinGradle { + ktfmt().googleStyle() + // licenseHeaderFile(rootProject.file("codestyle/copyright-header-java.txt"), "$") + target("*.gradle.kts") + } + format("xml") { + target("src/**/*.xml", "src/**/*.xsd") + targetExclude("codestyle/copyright-header.xml") + eclipseWtp(com.diffplug.spotless.extra.wtp.EclipseWtpFormatterStep.XML) + .configFile(rootProject.file("codestyle/org.eclipse.wst.xml.core.prefs")) + // getting the license-header delimiter right is a bit tricky. + // licenseHeaderFile(rootProject.file("codestyle/copyright-header.xml"), '<^[!?].*$') + } +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Misc build stuff + +// ensure jars conform to reproducible builds +// (https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives) +tasks.withType().configureEach { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + + dirPermissions { unix("755") } + filePermissions { + // do not force the "execute" bit in case the file _is_ executable + user.read = true + user.write = true + group.read = true + group.write = false + other.read = true + other.write = false + } +} + +tasks.withType().configureEach { + manifest { + attributes( + // Do not add any (more or less) dynamic information to jars, because that makes Gradle's + // caching way less efficient. Note that version and Git information are already added to jar + // manifests for release(-like) builds. + "Implementation-Title" to "Apache Creadur RAT™", + "Implementation-Vendor" to "Apache Software Foundation", + "Implementation-URL" to "https://creadur.apache.org/rat/", + ) + } + + from("..") { + include("LICENSE", "NOTICE") + into("META-INF") + } +} + +// Silence some (not so useful) javadoc warnings +tasks.withType().configureEach { + val opt = options as CoreJavadocOptions + // don't spam log w/ "warning: no @param/@return" + opt.addStringOption("Xdoclint:-reference", "-quiet") +} diff --git a/apache-rat-gradle-plugin/codestyle/copyright-header-hash.txt b/apache-rat-gradle-plugin/codestyle/copyright-header-hash.txt new file mode 100644 index 000000000..fe95886d5 --- /dev/null +++ b/apache-rat-gradle-plugin/codestyle/copyright-header-hash.txt @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# diff --git a/apache-rat-gradle-plugin/codestyle/copyright-header-java.txt b/apache-rat-gradle-plugin/codestyle/copyright-header-java.txt new file mode 100644 index 000000000..042f3ce1f --- /dev/null +++ b/apache-rat-gradle-plugin/codestyle/copyright-header-java.txt @@ -0,0 +1,18 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ diff --git a/apache-rat-gradle-plugin/codestyle/copyright-header.txt b/apache-rat-gradle-plugin/codestyle/copyright-header.txt new file mode 100644 index 000000000..90705e02e --- /dev/null +++ b/apache-rat-gradle-plugin/codestyle/copyright-header.txt @@ -0,0 +1,16 @@ +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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 + + http://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. \ No newline at end of file diff --git a/apache-rat-gradle-plugin/codestyle/copyright-header.xml b/apache-rat-gradle-plugin/codestyle/copyright-header.xml new file mode 100644 index 000000000..af24b9732 --- /dev/null +++ b/apache-rat-gradle-plugin/codestyle/copyright-header.xml @@ -0,0 +1,19 @@ + + diff --git a/apache-rat-gradle-plugin/codestyle/org.eclipse.wst.xml.core.prefs b/apache-rat-gradle-plugin/codestyle/org.eclipse.wst.xml.core.prefs new file mode 100644 index 000000000..4a9500ca2 --- /dev/null +++ b/apache-rat-gradle-plugin/codestyle/org.eclipse.wst.xml.core.prefs @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# +# + +eclipse.preferences.version=1 +formatCommentJoinLines=false +formatCommentText=false +indentationChar=space +indentationSize=2 +lineWidth=100 +spaceBeforeEmptyCloseTag=false diff --git a/apache-rat-gradle-plugin/gradle.properties b/apache-rat-gradle-plugin/gradle.properties new file mode 100644 index 000000000..c49137521 --- /dev/null +++ b/apache-rat-gradle-plugin/gradle.properties @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# + +# enable the Gradle build cache +org.gradle.caching=true +# enable Gradle parallel builds +org.gradle.parallel=true +# configure only necessary Gradle tasks +org.gradle.configureondemand=true +# explicitly disable the configuration cache +org.gradle.configuration-cache=false +#org.gradle.configuration-cache-problems=warn +# bump the Gradle daemon heap size (you can set bigger heap sizes as well) +#org.gradle.jvmargs=-Xms2g -Xmx4g -XX:MaxMetaspaceSize=768m diff --git a/apache-rat-gradle-plugin/gradle/README.md b/apache-rat-gradle-plugin/gradle/README.md new file mode 100644 index 000000000..7e201aaaa --- /dev/null +++ b/apache-rat-gradle-plugin/gradle/README.md @@ -0,0 +1,35 @@ + + +# No `gradle-wrapper.jar` in source tree (Gradle Wrapper in Apache projects) + +Apache policies prohibit committing `gradle-wrapper.jar` into the source tree. + +The `gradlew` and `gradlew.bat` scripts have been updated to call the `gradlew-include.sh`/`.ps1` script +in this directory to download and verify the Gradle Wrapper for the Gradle version defined in +`gradle/wrapper/gradle-wrapper.properties`. + +The `gradlew-include.*` scripts... +1. Determine (extract) the Gradle version from the `gradle/wrapper/gradle-wrapper.properties` file. +2. If the a checksum file does not exist, delete any existing `gradle-wrapper.jar`. +3. If the `gradle-wrapper.jar` exists, verify its checksum using a the (possibly) exiting checksum file. +4. If the checksum of the jar and the checksum in the file do not match, delete both the checksum and `gradle-wrapper.jar` files. +5. If the checksum file does not exist, download it from `https://services.gradle.org/distributions/gradle--wrapper.jar.sha256` +6. If the `gradle-wrapper.jar` does not exist, download it from `https://raw.githubusercontent.com/gradle/gradle/v/gradle/wrapper/gradle-wrapper.jar` + and verify the checksum of the downloaded jar. diff --git a/apache-rat-gradle-plugin/gradle/gradlew-include.ps1 b/apache-rat-gradle-plugin/gradle/gradlew-include.ps1 new file mode 100644 index 000000000..1c40c8bba --- /dev/null +++ b/apache-rat-gradle-plugin/gradle/gradlew-include.ps1 @@ -0,0 +1,137 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +# Binary, especially executable binary files, shall not be in the source tree of any Apache project. +# Gradle usually requires a `gradle-wrapper.jar` file to be present in the source tree. +# This script, included from `gradlew` downloads the gradle-wrapper.jar if necessary and verifies its integrity. +# The `gradle-wrapper.jar` and its checksum are downloaded from two well-known locations. + +# Extract the Gradle version from gradle-wrapper.properties using a regular expression. +$GradlePropertiesPath = Join-Path -Path $env:APP_HOME -ChildPath "gradle\wrapper\gradle-wrapper.properties" +if (-not (Test-Path $GradlePropertiesPath)) { + Write-Error "Gradle properties file not found: $GradlePropertiesPath" + exit 1 +} + +# Read the content and use a regex match to capture the version number. +# Bash regex: 's/^.*gradle-\([0-9.]*\)-[a-z]*.zip$/\1/' +# PowerShell equivalent: Match the distributionUrl line, then capture the version group. +$FileContent = Get-Content -Path $GradlePropertiesPath | Select-String -Pattern "distributionUrl=" -CaseSensitive +if ($FileContent -eq $null) { + Write-Error "Could not find 'distributionUrl' in $GradlePropertiesPath" + exit 1 +} + +$Match = [regex]::Match($FileContent.ToString(), 'gradle-([0-9.]+)-[a-z]*.zip$') +if (-not $Match.Success) { + Write-Error "Could not extract Gradle version from distributionUrl." + exit 1 +} +$GRADLE_DIST_VERSION = $Match.Groups[1].Value + +# Define file paths +$GRADLE_WRAPPER_SHA256 = Join-Path -Path $env:APP_HOME -ChildPath "gradle\wrapper\gradle-wrapper-$GRADLE_DIST_VERSION.jar.sha256" +$GRADLE_WRAPPER_JAR = Join-Path -Path $env:APP_HOME -ChildPath "gradle\wrapper\gradle-wrapper.jar" + +# Checksum Verification and Cleanup +# If the checksum file does not exist, delete the wrapper jar. +if (-not (Test-Path $GRADLE_WRAPPER_SHA256)) { + Remove-Item -Path $GRADLE_WRAPPER_JAR -Force -ErrorAction SilentlyContinue +} + +# This fixes an issue that Get-FileHash works fine "out of the box" on older systems, but fails on newer ones. +# More info via https://github.com/actions/runner-images/issues/225 +Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + +# If the wrapper jar exists, verify its checksum. +if (Test-Path $GRADLE_WRAPPER_JAR) { + try { + # Calculate the SHA256 hash of the existing wrapper JAR. + # Get-FileHash is the native PowerShell equivalent for sha256sum. + $JarHashObject = Get-FileHash -Path $GRADLE_WRAPPER_JAR -Algorithm SHA256 + $JAR_CHECKSUM = $JarHashObject.Hash.ToLower() # Hash is uppercase by default, convert to lowercase + + # Read the expected checksum from the file. + # Note: 'cat' is an alias for Get-Content in PowerShell. + $EXPECTED = (Get-Content -Path $GRADLE_WRAPPER_SHA256 -Raw).Trim().ToLower() + + # Compare checksums and delete files if they do not match. + if ($JAR_CHECKSUM -ne $EXPECTED) { + Write-Warning "Checksum mismatch. Deleting $GRADLE_WRAPPER_JAR and $GRADLE_WRAPPER_SHA256." + Remove-Item -Path $GRADLE_WRAPPER_JAR -Force -ErrorAction SilentlyContinue + Remove-Item -Path $GRADLE_WRAPPER_SHA256 -Force -ErrorAction SilentlyContinue + } + } catch { + # Handle cases where Get-Content or Get-FileHash might fail (e.g., file deleted during operation). + Write-Warning "Error during checksum verification: $($_.Exception.Message)" + Remove-Item -Path $GRADLE_WRAPPER_JAR -Force -ErrorAction SilentlyContinue + Remove-Item -Path $GRADLE_WRAPPER_SHA256 -Force -ErrorAction SilentlyContinue + } +} + +# Download Checksum File +# If the checksum file is missing, download it. +if (-not (Test-Path $GRADLE_WRAPPER_SHA256)) { + $Sha256DownloadUrl = "https://services.gradle.org/distributions/gradle-$GRADLE_DIST_VERSION-wrapper.jar.sha256" + Write-Host "Downloading SHA256 checksum from $Sha256DownloadUrl" + # Invoke-WebRequest is the native PowerShell equivalent for curl --location --output. + try { + Invoke-WebRequest -Uri $Sha256DownloadUrl -OutFile $GRADLE_WRAPPER_SHA256 -UseBasicParsing + } catch { + Write-Error "Failed to download SHA256 checksum: $($_.Exception.Message)" + exit 1 + } +} + +# Download Wrapper JAR and Final Verification +# If the wrapper jar is missing, download it. +if (-not (Test-Path $GRADLE_WRAPPER_JAR)) { + # The original script handles a case where the version might be like 'x.y' and needs 'x.y.0'. + # Bash sed: 's/^\([0-9]*[.][0-9]*\)$/\1.0/' + # PowerShell equivalent using regex replacement. + $GRADLE_VERSION = $GRADLE_DIST_VERSION + if ($GRADLE_DIST_VERSION -match '^\d+\.\d+$') { + $GRADLE_VERSION = "$GRADLE_DIST_VERSION.0" + } + + $JarDownloadUrl = "https://raw.githubusercontent.com/gradle/gradle/v$GRADLE_VERSION/gradle/wrapper/gradle-wrapper.jar" + Write-Host "Downloading wrapper JAR from $JarDownloadUrl" + + try { + Invoke-WebRequest -Uri $JarDownloadUrl -OutFile $GRADLE_WRAPPER_JAR -UseBasicParsing + } catch { + Write-Error "Failed to download wrapper JAR: $($_.Exception.Message)" + exit 1 + } + + # Verify the checksum of the newly downloaded JAR. + try { + $JarHashObject = Get-FileHash -Path $GRADLE_WRAPPER_JAR -Algorithm SHA256 + $JAR_CHECKSUM = $JarHashObject.Hash.ToLower() + $EXPECTED = (Get-Content -Path $GRADLE_WRAPPER_SHA256 -Raw).Trim().ToLower() + + if ($JAR_CHECKSUM -ne $EXPECTED) { + # Critical failure: downloaded file does not match its expected checksum. + Write-Error "Expected sha256 of the downloaded $GRADLE_WRAPPER_JAR does not match the downloaded sha256!" + exit 1 + } + } catch { + Write-Error "Error during final checksum verification: $($_.Exception.Message)" + exit 1 + } +} \ No newline at end of file diff --git a/apache-rat-gradle-plugin/gradle/gradlew-include.sh b/apache-rat-gradle-plugin/gradle/gradlew-include.sh new file mode 100644 index 000000000..0c4fe3fa3 --- /dev/null +++ b/apache-rat-gradle-plugin/gradle/gradlew-include.sh @@ -0,0 +1,67 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. + +# Binary, especially executable binary files, shall not be in the source tree of any Apache project. +# Gradle usually requires a `gradle-wrapper.jar` file to be present in the source tree. +# This script, included from `gradlew` downloads the gradle-wrapper.jar if necessary and verifies its integrity. +# The `gradle-wrapper.jar` and its checksum are downloaded from two well-known locations. + +# Extract the Gradle version from gradle-wrapper.properties. +GRADLE_DIST_VERSION="$(grep distributionUrl= "$APP_HOME/gradle/wrapper/gradle-wrapper.properties" | sed 's/^.*gradle-\([0-9.]*\)-[a-z]*.zip$/\1/')" +GRADLE_WRAPPER_SHA256="$APP_HOME/gradle/wrapper/gradle-wrapper-${GRADLE_DIST_VERSION}.jar.sha256" +GRADLE_WRAPPER_JAR="$APP_HOME/gradle/wrapper/gradle-wrapper.jar" +if [ -x "$(command -v sha256sum)" ] ; then + SHASUM="sha256sum" +else + if [ -x "$(command -v shasum)" ] ; then + SHASUM="shasum -a 256" + else + echo "Neither sha256sum nor shasum are available, install either." > /dev/stderr + exit 1 + fi +fi +if [ ! -e "${GRADLE_WRAPPER_SHA256}" ]; then + # Delete the wrapper jar, if the checksum file does not exist. + rm -f "${GRADLE_WRAPPER_JAR}" +fi +if [ -e "${GRADLE_WRAPPER_JAR}" ]; then + # Verify the wrapper jar, if it exists, delete wrapper jar and checksum file, if the checksums + # do not match. + JAR_CHECKSUM="$(${SHASUM} "${GRADLE_WRAPPER_JAR}" | cut -d\ -f1)" + EXPECTED="$(cat "${GRADLE_WRAPPER_SHA256}")" + if [ "${JAR_CHECKSUM}" != "${EXPECTED}" ]; then + rm -f "${GRADLE_WRAPPER_JAR}" "${GRADLE_WRAPPER_SHA256}" + fi +fi +if [ ! -e "${GRADLE_WRAPPER_SHA256}" ]; then + curl --location --output "${GRADLE_WRAPPER_SHA256}" https://services.gradle.org/distributions/gradle-${GRADLE_DIST_VERSION}-wrapper.jar.sha256 || exit 1 +fi +if [ ! -e "${GRADLE_WRAPPER_JAR}" ]; then + # The Gradle version extracted from the `distributionUrl` property does not contain ".0" patch + # versions. Need to append a ".0" in that case to download the wrapper jar. + GRADLE_VERSION="$(echo "$GRADLE_DIST_VERSION" | sed 's/^\([0-9]*[.][0-9]*\)$/\1.0/')" + curl --location --output "${GRADLE_WRAPPER_JAR}" https://raw.githubusercontent.com/gradle/gradle/v${GRADLE_VERSION}/gradle/wrapper/gradle-wrapper.jar || exit 1 + JAR_CHECKSUM="$(${SHASUM} "${GRADLE_WRAPPER_JAR}" | cut -d\ -f1)" + EXPECTED="$(cat "${GRADLE_WRAPPER_SHA256}")" + if [ "${JAR_CHECKSUM}" != "${EXPECTED}" ]; then + # If the (just downloaded) checksum and the downloaded wrapper jar do not match, something + # really bad is going on. + echo "Expected sha256 of the downloaded gradle-wrapper.jar does not match the downloaded sha256!" > /dev/stderr + exit 1 + fi +fi diff --git a/apache-rat-gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/apache-rat-gradle-plugin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..488b513b9 --- /dev/null +++ b/apache-rat-gradle-plugin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# + +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionSha256Sum=df67a32e86e3276d011735facb1535f64d0d88df84fa87521e90becc2d735444 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/apache-rat-gradle-plugin/gradlew b/apache-rat-gradle-plugin/gradlew new file mode 100755 index 000000000..829157be7 --- /dev/null +++ b/apache-rat-gradle-plugin/gradlew @@ -0,0 +1,250 @@ +#!/bin/sh + +# +# Copyright © 2015 the original 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +. ${APP_HOME}/gradle/gradlew-include.sh + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/apache-rat-gradle-plugin/gradlew.bat b/apache-rat-gradle-plugin/gradlew.bat new file mode 100644 index 000000000..2678d8eca --- /dev/null +++ b/apache-rat-gradle-plugin/gradlew.bat @@ -0,0 +1,95 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +powershell -file "%APP_HOME%\gradle\gradlew-include.ps1" + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/apache-rat-gradle-plugin/pom.xml b/apache-rat-gradle-plugin/pom.xml new file mode 100644 index 000000000..8b0984318 --- /dev/null +++ b/apache-rat-gradle-plugin/pom.xml @@ -0,0 +1,243 @@ + + + + 4.0.0 + + org.apache.rat + apache-rat-project + 0.17 + + + org.apache.rat.gradle.plugin + pom + Apache Creadur RAT::Gradle Plugin + Gradle plugin. + + build + ./gradlew + false + apache-rat-gradle-plugin + + + + org.apache.rat + apache-rat-core + + + + + + + org.apache.rat + apache-rat-plugin + + + .gradle/** + .idea/** + build/** + gradle/wrapper/gradle-wrapper.jar + gradle/wrapper/gradle-wrapper*.sha256 + src/test/resources/TestGradleIntegration/badLicenseFailsBuild/** + + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + + org.apache.rat + apache-rat-tools + ${project.version} + + + + + gradle + prepare-package + + ${gradle.executable} + + clean + ${gradle.task} + -Pdescription=${project.description} + -Dmaven.repo.local=${settings.localRepository} + -S + --stacktrace + + + ${settings.localRepository} + ${gradleJavaHome} + + + + exec + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + package + + attach-artifact + + + + + build/libs/${artifactPrefix}-${project.version}.jar + jar + + + build/libs/${artifactPrefix}-${project.version}-javadoc.jar + jar + javadoc + + + build/libs/${artifactPrefix}-${project.version}-sources.jar + jar + sources + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + true + true + warning + true + ../src/conf/checkstyle.xml + ../src/conf/checkstyle-suppressions.xml + + + + + check + + compile + + + + + + + + skipTests + + + skipTests + true + + + + assemble + + + + windows + + + windows + + + + gradlew.bat + + + + linux + + + unix + + + + + + org.codehaus.mojo + exec-maven-plugin + + + + org.apache.rat + apache-rat-tools + ${project.version} + + + + + + smoketest + install + + ./smoketest.sh + ${skipTests} + + + exec + + + + + + + + + gradleJavaHomeSet + + + env.GRADLE_JAVA_HOME + + + + ${env.GRADLE_JAVA_HOME} + + + + gradleJavaHomeNotSet + + + !env.GRADLE_JAVA_HOME + + + + ${env.JAVA_HOME} + + + + diff --git a/apache-rat-gradle-plugin/settings.gradle.kts b/apache-rat-gradle-plugin/settings.gradle.kts new file mode 100644 index 000000000..8a5762c0b --- /dev/null +++ b/apache-rat-gradle-plugin/settings.gradle.kts @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +import groovy.util.Node +import groovy.util.NodeList +import groovy.xml.XmlParser + +rootProject.name = "apache-rat-gradle-plugin" + +// Extract groupId + version from RAT root pom +val rootPom = XmlParser().parse(file("../pom.xml")) + +fun xmlFirstChildText(node: Node, element: String): String = + (node.get(element) as NodeList).text().trim() + +val rootPomVersion = xmlFirstChildText(rootPom, "version") +val rootPomGroup = xmlFirstChildText(rootPom, "groupId") + +val ideaActive = System.getProperty("idea.active").toBoolean() + +pluginManagement { + repositories { + mavenCentral() // prefer Maven Central, in case Gradle's repo has issues + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS + repositories { + // TODO move mavenLocal() under mavenCentral() once the 0.18 development branch is there + mavenLocal() + mavenCentral() + } +} + +gradle.beforeProject { + version = rootPomVersion + group = rootPomGroup +} diff --git a/apache-rat-gradle-plugin/smoketest.sh b/apache-rat-gradle-plugin/smoketest.sh new file mode 100755 index 000000000..4802abaf9 --- /dev/null +++ b/apache-rat-gradle-plugin/smoketest.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# + +# Script to verify that the Gradle plugin works from a local Maven install + +set -e + +cd "${0%/*}" + +temp_dir="$(mktemp --tmpdir --directory)" +function purge_temp_dir { + echo "Purging ${temp_dir}..." + rm -rf "${temp_dir}" +} +trap purge_temp_dir EXIT + +rat_version="$(cat ../pom.xml | grep '^ ' | head -1 | sed -e 's/.*\([^<]*\)<\/version>.*/\1/')" + +cat < "${temp_dir}/settings.gradle" +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + } +} +! + +cat < "${temp_dir}/build.gradle" +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +import org.apache.rat.config.exclusion.StandardCollection + +plugins { + id 'org.apache.rat' version '${rat_version}' +} + +rat { + // excludes standard file patterns (like .git) + inputExcludeStds.addAll( + StandardCollection.MAC) + + inputExcludes.addAll( + "gradle/wrapper/gradle-wrapper.jar", + "gradle/wrapper/gradle-wrapper-*.sha256" + ) +} +! + +cp -r gradle "${temp_dir}" +cp gradlew "${temp_dir}" + +(cd "$temp_dir" ; ./gradlew :rat) diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatExtension.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatExtension.java new file mode 100644 index 000000000..23294af33 --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatExtension.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle; + +/** + * Configurations set on this RAT extension are the defaults for the plugin defined {@code rat} task + * and all additionally registered {@link RatTask}s. + * + *

All properties are inherited from {@link RatOptions}. + * + * @see RatOptions + */ +public interface RatExtension extends RatOptions { + // empty comment for checkstyle +} diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatPlugin.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatPlugin.java new file mode 100644 index 000000000..b71b9453f --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatPlugin.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.UnknownTaskException; +import org.gradle.api.tasks.TaskProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Apache RAT Gradle plugin. + * + *

Apply this plugin to your project's root project. + * + *

Configurations should be configured on the {@code rat} extension of type {@link RatExtension}. + * + *

Registers the default {@code rat} task and lets the {@code check} task depend on it, if the + * {@code check} task exists at the time when this RAT plugin is applied. Task-specific properties, + * like the report output directory and/or report-specific output files, are configured on the task + * and not the extension. + * + *

If multiple RAT reports with different RAT configurations are needed, register a custom task + * of type {@link RatTask} and configure it accordingly. All {@link RatTask} RAT tasks inherit the + * configurations from the {@code rat} extension. + */ +@SuppressWarnings({"unused", "NullableProblems"}) +public abstract class RatPlugin implements Plugin { + /** SLF4j logger. */ + private static final Logger LOGGER = LoggerFactory.getLogger(RatPlugin.class); + + @Override + public void apply(final Project project) { + project.getExtensions().create("rat", RatExtension.class); + + TaskProvider ratTask = project.getTasks().register("rat", RatTask.class); + RatTask.setupRatTaskConfigurations(ratTask, project); + + try { + project.getTasks().named("check").configure(task -> task.dependsOn("rat")); + } catch (UnknownTaskException ignore) { + // Log at level 'INFO' to not spam people's build output. + LOGGER.info( + "The project '{}' does not have a 'check' task available when the Apache RAT Gradle plugin is applied. " + + "If another plugin registers a 'check' task, consider changing the order of the plugins in the 'plugins' block. " + + "If your build script registers a 'check' task later, consider configuring with with a 'dependsOn(\"rat\")'", + project.getParent()); + } + } +} diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatTask.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatTask.java new file mode 100644 index 000000000..794e9de95 --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatTask.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle; + +import static java.lang.String.format; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import javax.inject.Inject; + +import org.apache.rat.gradle.internal.RatWorkAction; +import org.apache.rat.gradle.internal.RatWorkParameters; +import org.gradle.api.DefaultTask; +import org.gradle.api.NamedDomainObjectProvider; +import org.gradle.api.Project; +import org.gradle.api.Transformer; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.workers.WorkQueue; +import org.gradle.workers.WorkerExecutor; +import org.jetbrains.annotations.NotNull; + +/** + * Apache RAT execution task. + * + *

Default property values are taken from the Gradle project's {@link RatExtension}, named {@code + * rat}. + * + *

Available properties are all properties available on the {@link RatTaskProperties}, which + * inherits its properties from {@link RatOptions}. + */ +public abstract class RatTask extends DefaultTask implements RatTaskProperties { + @SuppressWarnings("unused") + public RatTask() { + RatExtension extension = getExtension(); + + applyConventions(extension); + + getProjectBaseDir().convention(getProject().getLayout().getProjectDirectory()); + + getReportOutputDirectory() + .convention(getProject().getLayout().getBuildDirectory().dir("reports/" + getName())); + getRatTxtFile().convention(getReportOutputDirectory().file("rat-report.txt")); + getRatXmlFile().convention(getReportOutputDirectory().file("rat-report.xml")); + getRatHtmlFile().convention(getReportOutputDirectory().file("rat-report.html")); + + // Task outputs are produced for a particular task input. + // Task inputs consist of properties and files. + // Unfortunately, we do not have a view to the same RAT processed files + // _without_ fully configuring RAT. + // Therefore, the task outputs are never up to date, leading to + // unconditional task execution. + getOutputs().upToDateWhen(t -> false); + + setGroup("verification"); + setDescription("Run Apache Release Audit Tool (RAT)"); + } + + /** + * Sets up the Gradle {@link Configuration}s that allow consuming the generated report files. + * + *

    + *
  • {@code XmlReportElements}, consumable + *
  • {@code XmlReport}, resolvable, extends from the former + *
  • {@code AllReportsElements}, consumable + *
  • {@code AllReports}, resolvable, extends from the former + *
+ * + *

This function is always called for the {@code rat} task. When registering a custom task of + * type {@link RatTask}, this function can be used to set up the configurations for that task as + * well. + * + *

This function is not called from the constructor to ensure that the configurations are + * immediately available. + */ + public static void setupRatTaskConfigurations( + final TaskProvider ratTask, final Project project) { + String xmlReportElements = format("%sXmlReportElements", ratTask.getName()); + NamedDomainObjectProvider xmlReportElementsConfig = + project + .getConfigurations() + .register( + xmlReportElements, + c -> { + c.setDescription( + format( + "Consumable configuration containing the XML report file generated by the '%s' task.", + ratTask.getName())); + c.setCanBeResolved(false); + c.setCanBeConsumed(true); + c.outgoing( + configurationPublications -> + configurationPublications.artifact( + ratTask.flatMap(RatTask::getRatXmlFile))); + }); + + String allReportsElements = format("%sAllReportsElements", ratTask.getName()); + NamedDomainObjectProvider allReportsElementsConfig = + project + .getConfigurations() + .register( + allReportsElements, + c -> { + c.setDescription( + format( + "Consumable configuration containing all report files generated by the '%s' task.", + ratTask.getName())); + c.setCanBeResolved(false); + c.setCanBeConsumed(true); + c.outgoing( + configurationPublications -> { + for (Transformer reportFileFunction : + Arrays.>asList( + RatTask::getRatXmlFile, + RatTask::getRatTxtFile, + RatTask::getRatHtmlFile)) { + configurationPublications.artifact(ratTask.flatMap(reportFileFunction)); + } + }); + }); + + for (Function reportFileFunction : + Arrays.>asList( + RatTask::getRatXmlFile, RatTask::getRatTxtFile, RatTask::getRatHtmlFile)) { + project + .getArtifacts() + .add( + allReportsElements, + project.provider(() -> reportFileFunction.apply(ratTask.get())), + artifact -> artifact.builtBy(ratTask)); + } + + // Add resolvable configurations, extending the consumable '*Elements' configurations. + + String xmlReport = format("%sXmlReport", ratTask.getName()); + project + .getConfigurations() + .register( + xmlReport, + c -> { + c.setDescription( + format( + "Resolvable configuration containing the XML report file generated by the '%s' task, extends from the '%s' configuration.", + ratTask.getName(), xmlReportElementsConfig.getName())); + c.setCanBeResolved(true); + c.setCanBeConsumed(false); + c.extendsFrom(xmlReportElementsConfig.get()); + }); + + String allReports = format("%sAllReports", ratTask.getName()); + project + .getConfigurations() + .register( + allReports, + c -> { + c.setDescription( + format( + "Resolvable configuration containing the all report files generated by the '%s' task, extends from the '%s' configuration.", + ratTask.getName(), allReportsElementsConfig.getName())); + c.setCanBeResolved(true); + c.setCanBeConsumed(false); + c.extendsFrom(allReportsElementsConfig.get()); + }); + + // Finally, map the dependencies + DependencyHandler dependencies = project.getDependencies(); + Map xmlReportDependencyNotion = new HashMap<>(); + xmlReportDependencyNotion.put("path", project.getPath()); + xmlReportDependencyNotion.put("configuration", xmlReportElements); + dependencies.add(xmlReport, dependencies.project(xmlReportDependencyNotion)); + Map allReportsDependencyNotion = new HashMap<>(); + allReportsDependencyNotion.put("path", project.getPath()); + allReportsDependencyNotion.put("configuration", allReportsElements); + dependencies.add(allReports, dependencies.project(allReportsDependencyNotion)); + } + + private @NotNull RatExtension getExtension() { + return getProject().getExtensions().getByType(RatExtension.class); + } + + @Inject + protected abstract WorkerExecutor getWorkerExecutor(); + + @Internal + public abstract DirectoryProperty getProjectBaseDir(); + + @TaskAction + public void check() { + WorkQueue workQueue = getWorkerExecutor().classLoaderIsolation(); + + // Execute RAT in a classloader isolation worker. + // Some information is set using static fields in the RAT code base. + // Classloader isolation prevents potential "collisions" or races in the case of multiple + // Gradle projects (think: modules) running RAT at the same time / within the same build. + workQueue.submit(RatWorkAction.class, this::applyWorkParameters); + // Wait for RAT execution to finish. + workQueue.await(); + } + + void applyWorkParameters(final RatWorkParameters parameters) { + parameters.getProjectBaseDir().set(getProjectBaseDir()); + parameters.applyTaskPropertiesConventions(this); + parameters.applyConventions(this); + } +} diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatTaskProperties.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatTaskProperties.java new file mode 100644 index 000000000..97c03e24d --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/RatTaskProperties.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle; + +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.OutputFile; + +/** RAT task-specific properties. */ +public interface RatTaskProperties extends RatOptionsTaskBase { + + /** + * The default output directory for RAT reports. The default value is the project's build + * directory plus {@code reports} plus the task name, for example {@code build/reports/rat}. + */ + @OutputDirectory + DirectoryProperty getReportOutputDirectory(); + + /** + * The RAT XML report output file. Defaults to {@code rat-report.txt} in {@link + * #getReportOutputDirectory()}. + */ + @OutputFile + RegularFileProperty getRatXmlFile(); + + /** + * The RAT text report output file. Defaults to {@code rat-report.txt} in {@link + * #getReportOutputDirectory()}. + */ + @OutputFile + RegularFileProperty getRatTxtFile(); + + /** + * The RAT HTML report output file. Defaults to {@code rat-report.txt} in {@link + * #getReportOutputDirectory()}. + */ + @OutputFile + RegularFileProperty getRatHtmlFile(); + + default void applyTaskPropertiesConventions(RatTaskProperties from) { + getReportOutputDirectory().convention(from.getReportOutputDirectory()); + getRatTxtFile().convention(from.getRatTxtFile()); + getRatXmlFile().convention(from.getRatXmlFile()); + getRatHtmlFile().convention(from.getRatHtmlFile()); + } +} diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/RatWorkAction.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/RatWorkAction.java new file mode 100644 index 000000000..e9fb96781 --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/RatWorkAction.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle.internal; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.rat.OptionCollection; +import org.apache.rat.ReportConfiguration; +import org.apache.rat.Reporter; +import org.apache.rat.commandline.Arg; +import org.apache.rat.commandline.StyleSheets; +import org.apache.rat.document.DocumentName; +import org.apache.rat.document.FileDocument; +import org.apache.rat.gradle.RatTask; +import org.apache.rat.license.ILicense; +import org.apache.rat.license.LicenseSetFactory; +import org.apache.rat.report.claim.ClaimStatistic; +import org.apache.rat.utils.DefaultLog; +import org.apache.rat.walker.DirectoryWalker; +import org.gradle.api.GradleException; +import org.gradle.workers.WorkAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Actual RAT checks and report generation happens via this Gradle work action, which is called from + * {@link RatTask} using classloader isolation. + */ +public abstract class RatWorkAction implements WorkAction { + + /** SLF4j logger. */ + private static final Logger LOGGER = LoggerFactory.getLogger(RatWorkAction.class); + + @Override + public void execute() { + + deleteFiles(); + + DefaultLog.setInstance(new Slf4jLogBridge(LOGGER)); + + RatOptionsToConfiguration ratArguments = new RatOptionsToConfiguration(getParameters()); + + ReportConfiguration config = getConfiguration(ratArguments); + + logLicenses(config.getLicenses(LicenseSetFactory.LicenseFilter.ALL)); + try { + Reporter reporter = new Reporter(config); + + config.setStyleSheet(RatTask.class.getResource("/org/apache/rat/plain-rat.xsl")); + config.setOut(getParameters().getRatTxtFile().get().getAsFile()); + reporter.output(); + + config.setStyleSheet(RatTask.class.getResource("/org/apache/rat/html.xsl")); + config.setOut(getParameters().getRatHtmlFile().get().getAsFile()); + reporter.output(); + + config.setStyleSheet(RatTask.class.getResource("/org/apache/rat/xml.xsl")); + config.setOut(getParameters().getRatXmlFile().get().getAsFile()); + reporter.output(); + + StringWriter summaryWriter = new StringWriter(); + reporter.writeSummary(summaryWriter); + + check(reporter, config); + } catch (Exception e) { + throw new GradleException(e.getMessage(), e); + } + } + + private void deleteFiles() { + try { + Files.deleteIfExists(getParameters().getRatXmlFile().getAsFile().get().toPath()); + Files.deleteIfExists(getParameters().getRatTxtFile().getAsFile().get().toPath()); + Files.deleteIfExists(getParameters().getRatHtmlFile().getAsFile().get().toPath()); + + try (Stream pathStream = + Files.walk(getParameters().getReportOutputDirectory().getAsFile().get().toPath())) { + Iterator iter = + pathStream + .sorted(Comparator.comparingInt(p -> p.toString().length()).reversed()) + .iterator(); + while (iter.hasNext()) { + Files.deleteIfExists(iter.next()); + } + } + } catch (Exception e) { + throw new RuntimeException("Unable to delete RAT report directory or files", e); + } + } + + protected void logLicenses(final Collection licenses) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("The following {} licenses are activated:", licenses.size()); + for (ILicense license : licenses) { + LOGGER.debug("* {}", license); + } + } + } + + protected void check(final Reporter reporter, final ReportConfiguration config) throws Exception { + ClaimStatistic statistics = reporter.getClaimsStatistic(); + reporter.writeSummary(DefaultLog.getInstance().asWriter()); + if (config.getClaimValidator().hasErrors()) { + config.getClaimValidator().logIssues(statistics); + if (!config + .getClaimValidator() + .isValid( + ClaimStatistic.Counter.UNAPPROVED, + statistics.getCounter(ClaimStatistic.Counter.UNAPPROVED))) { + try { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + reporter.output(StyleSheets.UNAPPROVED_LICENSES.getStyleSheet(), () -> buffer); + LOGGER.error(new String(buffer.toByteArray(), StandardCharsets.UTF_8)); + } catch (Exception e) { + LOGGER.error("Unable to print the files with unapproved licenses to the console.", e); + } + } + + String msg = + String.format( + "Counter(s) %s exceeded minimum or maximum values.\nSee RAT reports:\n- '%s'\n- '%s'\n- '%s'", + String.join(", ", config.getClaimValidator().listIssues(statistics)), + getParameters().getRatHtmlFile().get().getAsFile().toURI(), + getParameters().getRatTxtFile().get().getAsFile().toURI(), + getParameters().getRatXmlFile().get().getAsFile().toURI()); + + throw new Exception(msg); + } + } + + // see org.apache.rat.mp.AbstractRatMojo.getConfiguration + protected ReportConfiguration getConfiguration(final RatOptionsToConfiguration ratArguments) { + try { + File basedir = getParameters().getProjectBaseDir().getAsFile().get(); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("RAT configuration options:"); + for (Map.Entry> entry : ratArguments.getArgsMap().entrySet()) { + LOGGER.debug(" * {} {}}", entry.getKey(), String.join(", ", entry.getValue())); + } + } + + boolean helpLicenses = !ratArguments.getValues(Arg.HELP_LICENSES).isEmpty(); + ratArguments.removeKey(Arg.HELP_LICENSES); + + ReportConfiguration config = + OptionCollection.parseCommands( + basedir, + ratArguments.args().toArray(new String[0]), + ignore -> LOGGER.warn("Help option not supported"), + true); + + DocumentName dirName = DocumentName.builder(basedir).build(); + config.addSource( + new DirectoryWalker( + new FileDocument(dirName, basedir, config.getDocumentExcluder(dirName)))); + + if (helpLicenses) { + Writer w = new OutputStreamWriter(System.out); + new org.apache.rat.help.Licenses(config, w).printHelp(); + } + return config; + } catch (Exception e) { + throw new GradleException("Failed to build RAT ReportConfiguration", e); + } + } +} diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/RatWorkParameters.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/RatWorkParameters.java new file mode 100644 index 000000000..a6b8b637a --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/RatWorkParameters.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle.internal; + +import org.apache.rat.gradle.RatOptionsTaskBase; +import org.apache.rat.gradle.RatTaskProperties; +import org.gradle.api.Project; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.workers.WorkParameters; + +/** + * RAT Gradle worker parameters. + * + *

Consists of all user-configurable properties ({@link RatTaskProperties} including {@link + * RatOptionsTaskBase}) plus the {@link Project#getProjectDir() project's base directory}. + */ +public interface RatWorkParameters extends WorkParameters, RatTaskProperties { + DirectoryProperty getProjectBaseDir(); +} diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/Slf4jLogBridge.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/Slf4jLogBridge.java new file mode 100644 index 000000000..c8d96ba5c --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/Slf4jLogBridge.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle.internal; + +import org.apache.rat.utils.Log; +import org.slf4j.Logger; + +/** RAT {@link Log} implementation backed by an SLF4J {@link Logger}. */ +public class Slf4jLogBridge implements Log { + /** The SLF4J logger to delegate to. */ + private final Logger logger; + + public Slf4jLogBridge(final Logger logger) { + this.logger = logger; + } + + @Override + public Level getLevel() { + // We don't know the "minimum" log level for an SLF4J logger, so we just return DEBUG. + return Level.DEBUG; + } + + @Override + public boolean isEnabled(final Level level) { + switch (level) { + case DEBUG: + return logger.isDebugEnabled(); + case INFO: + return logger.isInfoEnabled(); + case WARN: + return logger.isWarnEnabled(); + case ERROR: + return logger.isErrorEnabled(); + default: + throw new IllegalArgumentException("Unknown log level: " + level); + } + } + + @Override + public void log(final Level level, final String message) { + switch (level) { + case DEBUG: + logger.debug(message); + break; + case INFO: + logger.info(message); + break; + case WARN: + logger.warn(message); + break; + case ERROR: + logger.error(message); + break; + default: + throw new IllegalArgumentException("Unknown log level: " + level); + } + } +} diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/package-info.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/package-info.java new file mode 100644 index 000000000..f996ae159 --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/internal/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** + * Apache RAT Gradle plugin internal classes. + * + *

Do not use any of the types in this package in your Gradle build scripts or downstream + * projects. Any kind of (breaking) changes to the types may happen in an Apache RAT release, even + * patch releases. + */ +package org.apache.rat.gradle.internal; diff --git a/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/package-info.java b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/package-info.java new file mode 100644 index 000000000..152f333e3 --- /dev/null +++ b/apache-rat-gradle-plugin/src/main/java/org/apache/rat/gradle/package-info.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +/** Publicly available types of the Apache RAT Gradle plugin. */ +package org.apache.rat.gradle; diff --git a/apache-rat-gradle-plugin/src/test/java/org/apache/rat/gradle/TestConventions.java b/apache-rat-gradle-plugin/src/test/java/org/apache/rat/gradle/TestConventions.java new file mode 100644 index 000000000..4b7b1ae4b --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/java/org/apache/rat/gradle/TestConventions.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle; + +import java.io.File; +import java.util.Arrays; + +import org.apache.groovy.util.Maps; +import org.apache.rat.config.exclusion.StandardCollection; +import org.apache.rat.gradle.internal.RatWorkParameters; +import org.apache.rat.report.claim.ClaimStatistic; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; +import org.gradle.api.Project; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SoftAssertionsExtension.class) +public class TestConventions { + @InjectSoftAssertions SoftAssertions soft; + + /** + * Verify that the conventions mapping from the {@link RatExtension} to a {@code RatTask} down to + * the {@link RatWorkParameters} works. + * + *

Does not exercise all options/properties, but one of each type. + */ + @Test + public void conventionsMapping() { + Project project = ProjectBuilder.builder().build(); + project.getPluginManager().apply("org.apache.rat"); + + RatExtension extension = project.getExtensions().getByType(RatExtension.class); + + // Property (no arg options) + extension.getConfigurationNoDefaults().set(true); + extension.getDryRun().set(false); + + extension.getEditCopyright().set("my copyright"); + + extension.getCounterMins().put(ClaimStatistic.Counter.APPROVED, 42); + extension.getCounterMins().put(ClaimStatistic.Counter.UNAPPROVED, 666); + + extension.getInputIncludes().addAll("one", "two", "three"); + + extension + .getInputExcludeParsedScms() + .addAll(StandardCollection.GIT, StandardCollection.SUBVERSION); + + extension.getLicensesApprovedFile().set(project.file("approved.txt")); + + extension.getConfigs().from(project.file("my-config.xml"), project.file("my-other-config.xml")); + + TaskProvider customTaskProvider = + project.getTasks().register("customRat", RatTask.class); + RatTask customTask = customTaskProvider.get(); + + TaskProvider ratTaskProvider = project.getTasks().named("rat", RatTask.class); + ratTaskProvider.configure( + task -> { + task.getConfigurationNoDefaults().set(false); + task.getDryRun().set(true); + + task.getEditCopyright().set("other copyright"); + + task.getCounterMins().put(ClaimStatistic.Counter.APPROVED, 666); + task.getCounterMins().put(ClaimStatistic.Counter.UNAPPROVED, 42); + + task.getInputIncludes().set(Arrays.asList("1", "2", "3")); + + task.getInputExcludeParsedScms() + .set(Arrays.asList(StandardCollection.CVS, StandardCollection.MERCURIAL)); + + task.getLicensesApprovedFile().set(project.file("more-approved.txt")); + + // `.from()` is "additive", would need to "unset" the convention first to just have the + // files below. + // Example: `task.getConfigs().unsetConvention();` + task.getConfigs() + .from(project.file("more-config.xml"), project.file("more-other-config.xml")); + }); + + RatTask ratTask = ratTaskProvider.get(); + + // The 'customTask' has no properties on its own instance. + // All properties default to their convention, the extension object's properties. + + soft.assertThat(customTask.getConfigurationNoDefaults()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, true); + soft.assertThat(customTask.getDryRun()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, false); + soft.assertThat(customTask.getEditCopyright()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, "my copyright"); + soft.assertThat(customTask.getCounterMins()) + .extracting(MapProperty::isPresent, MapProperty::get) + .containsExactly( + true, + Maps.of(ClaimStatistic.Counter.APPROVED, 42, ClaimStatistic.Counter.UNAPPROVED, 666)); + soft.assertThat(customTask.getInputIncludes()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly(true, Arrays.asList("one", "two", "three")); + soft.assertThat(customTask.getInputExcludeParsedScms()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly( + true, Arrays.asList(StandardCollection.GIT, StandardCollection.SUBVERSION)); + soft.assertThat(customTask.getLicensesApprovedFile().getAsFile().get()) + .hasFileName("approved.txt"); + soft.assertThat(customTask.getConfigs().getFiles()) + .map(File::getName) + .containsExactlyInAnyOrder("my-config.xml", "my-other-config.xml"); + + // same assertions for the 'customTask' task + + RatWorkParameters customTaskWorkParameters = + project.getObjects().newInstance(RatWorkParameters.class); + customTask.applyWorkParameters(customTaskWorkParameters); + soft.assertThat(customTaskWorkParameters.getProjectBaseDir().getAsFile().get()) + .isEqualTo(project.getProjectDir()); + soft.assertThat( + customTaskWorkParameters + .getRatXmlFile() + .getAsFile() + .get() + .toString() + .replace('\\', '/')) + .endsWith("build/reports/customRat/rat-report.xml"); + soft.assertThat( + customTaskWorkParameters + .getRatTxtFile() + .getAsFile() + .get() + .toString() + .replace('\\', '/')) + .endsWith("build/reports/customRat/rat-report.txt"); + soft.assertThat( + customTaskWorkParameters + .getRatHtmlFile() + .getAsFile() + .get() + .toString() + .replace('\\', '/')) + .endsWith("build/reports/customRat/rat-report.html"); + + soft.assertThat(customTaskWorkParameters.getConfigurationNoDefaults()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, true); + soft.assertThat(customTaskWorkParameters.getDryRun()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, false); + soft.assertThat(customTaskWorkParameters.getEditCopyright()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, "my copyright"); + soft.assertThat(customTaskWorkParameters.getCounterMins()) + .extracting(MapProperty::isPresent, MapProperty::get) + .containsExactly( + true, + Maps.of(ClaimStatistic.Counter.APPROVED, 42, ClaimStatistic.Counter.UNAPPROVED, 666)); + soft.assertThat(customTaskWorkParameters.getInputIncludes()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly(true, Arrays.asList("one", "two", "three")); + soft.assertThat(customTaskWorkParameters.getInputExcludeParsedScms()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly( + true, Arrays.asList(StandardCollection.GIT, StandardCollection.SUBVERSION)); + soft.assertThat(customTaskWorkParameters.getLicensesApprovedFile().getAsFile().get()) + .hasFileName("approved.txt"); + soft.assertThat(customTaskWorkParameters.getConfigs().getFiles()) + .map(File::getName) + .containsExactlyInAnyOrder("my-config.xml", "my-other-config.xml"); + + // The 'ratTask' has custom property values. + + soft.assertThat(ratTask.getConfigurationNoDefaults()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, false); + soft.assertThat(ratTask.getDryRun()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, true); + soft.assertThat(ratTask.getEditCopyright()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, "other copyright"); + soft.assertThat(ratTask.getCounterMins()) + .extracting(MapProperty::isPresent, MapProperty::get) + .containsExactly( + true, + Maps.of(ClaimStatistic.Counter.APPROVED, 666, ClaimStatistic.Counter.UNAPPROVED, 42)); + soft.assertThat(ratTask.getInputIncludes()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly(true, Arrays.asList("1", "2", "3")); + soft.assertThat(ratTask.getInputExcludeParsedScms()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly(true, Arrays.asList(StandardCollection.CVS, StandardCollection.MERCURIAL)); + soft.assertThat(ratTask.getLicensesApprovedFile().getAsFile().get()) + .hasFileName("more-approved.txt"); + soft.assertThat(ratTask.getConfigs().getFiles()) + .map(File::getName) + .containsExactlyInAnyOrder( + "my-config.xml", "my-other-config.xml", + "more-config.xml", "more-other-config.xml"); + + // same assertions for the 'rat' task + + RatWorkParameters ratTaskWorkParameters = + project.getObjects().newInstance(RatWorkParameters.class); + ratTask.applyWorkParameters(ratTaskWorkParameters); + soft.assertThat(ratTask.getProjectBaseDir().getAsFile().get()) + .isEqualTo(project.getProjectDir()); + soft.assertThat(ratTask.getRatXmlFile().getAsFile().get().toString().replace('\\', '/')) + .endsWith("build/reports/rat/rat-report.xml"); + soft.assertThat(ratTask.getRatXmlFile().getAsFile().get().toString().replace('\\', '/')) + .endsWith("build/reports/rat/rat-report.xml"); + soft.assertThat(ratTask.getRatTxtFile().getAsFile().get().toString().replace('\\', '/')) + .endsWith("build/reports/rat/rat-report.txt"); + soft.assertThat(ratTask.getRatHtmlFile().getAsFile().get().toString().replace('\\', '/')) + .endsWith("build/reports/rat/rat-report.html"); + + soft.assertThat(ratTaskWorkParameters.getConfigurationNoDefaults()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, false); + soft.assertThat(ratTaskWorkParameters.getDryRun()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, true); + soft.assertThat(ratTaskWorkParameters.getEditCopyright()) + .extracting(Property::isPresent, Property::get) + .containsExactly(true, "other copyright"); + soft.assertThat(ratTaskWorkParameters.getCounterMins()) + .extracting(MapProperty::isPresent, MapProperty::get) + .containsExactly( + true, + Maps.of(ClaimStatistic.Counter.APPROVED, 666, ClaimStatistic.Counter.UNAPPROVED, 42)); + soft.assertThat(ratTaskWorkParameters.getInputIncludes()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly(true, Arrays.asList("1", "2", "3")); + soft.assertThat(ratTaskWorkParameters.getInputExcludeParsedScms()) + .extracting(ListProperty::isPresent, ListProperty::get) + .containsExactly(true, Arrays.asList(StandardCollection.CVS, StandardCollection.MERCURIAL)); + soft.assertThat(ratTaskWorkParameters.getLicensesApprovedFile().getAsFile().get()) + .hasFileName("more-approved.txt"); + soft.assertThat(ratTaskWorkParameters.getConfigs().getFiles()) + .map(File::getName) + .containsExactlyInAnyOrder( + "my-config.xml", "my-other-config.xml", + "more-config.xml", "more-other-config.xml"); + } +} diff --git a/apache-rat-gradle-plugin/src/test/java/org/apache/rat/gradle/TestGradleIntegration.java b/apache-rat-gradle-plugin/src/test/java/org/apache/rat/gradle/TestGradleIntegration.java new file mode 100644 index 000000000..6481de9ca --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/java/org/apache/rat/gradle/TestGradleIntegration.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.gradle; + +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Objects; + +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; +import org.gradle.testkit.runner.TaskOutcome; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; + +/** + * Gradle build integration tests. + * + *

Not many functional tests in this class, as integration tests are (by nature) rather slow. + * + *

Test build directories intentionally use Groovy instead of Kotlin-Script, because the latter + * requires some expensive preparation steps (Gradle API compilation for Kotlin). That test can be + * removed, as it is not strictly necessary. + * + *

The Gradle test-kit automatically "wires" the plugins in the current project to the test + * builds, a version reference is not needed, it is actually incorrect to specify one in the test + * builds. + */ +@ExtendWith(SoftAssertionsExtension.class) +public class TestGradleIntegration { + @InjectSoftAssertions SoftAssertions soft; + + @TempDir Path projectDir; + + @BeforeEach + void setup(TestInfo testInfo) throws Exception { + String testCaseDir = + format( + "/%s/%s", + testInfo.getTestClass().get().getSimpleName(), + testInfo.getTestMethod().get().getName()); + Path templateDir = + Paths.get( + Objects.requireNonNull( + TestGradleIntegration.class.getResource(testCaseDir), + "Test case Gradle project resource directory " + testCaseDir + " not found") + .toURI()); + + Files.walkFileTree( + templateDir, + new FileVisitor() { + @Override + public @NotNull FileVisitResult preVisitDirectory( + Path dir, @NotNull BasicFileAttributes attrs) throws IOException { + Path relativePath = templateDir.relativize(dir); + Path target = projectDir.resolve(relativePath); + if (!Files.isDirectory(target)) { + Files.createDirectory(target); + } + return FileVisitResult.CONTINUE; + } + + @Override + public @NotNull FileVisitResult visitFile(Path file, @NotNull BasicFileAttributes attrs) + throws IOException { + Path relativePath = templateDir.relativize(file); + Path target = projectDir.resolve(relativePath); + Files.copy(file, target); + return FileVisitResult.CONTINUE; + } + + @Override + public @NotNull FileVisitResult visitFileFailed(Path file, @NotNull IOException exc) + throws IOException { + return FileVisitResult.TERMINATE; + } + + @Override + public @NotNull FileVisitResult postVisitDirectory(Path dir, @Nullable IOException exc) + throws IOException { + return FileVisitResult.CONTINUE; + } + }); + } + + @Test + public void defaultSettings() { + assertThat(ratOutcome(standardGradleRunner().build())) + .matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.html")).isNotEmptyFile(); + } + + /** Verify that the {@code rat} task is run as a dependency of the {@code check} task. */ + @Test + public void withJava() { + assertThat( + ratOutcome( + createGradleRunner("--build-cache", "--info", "--stacktrace", "check").build())) + .matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.html")).isNotEmptyFile(); + } + + /** + * Verify that the {@code rat} task's XML report can be consumed by another task in the same + * Gradle project. + * + *

Dependency chain is: + * + *

    + *
  • Task {@code other} declares a task-input from the {@code ratXmlReport} Gradle + * configuration. + *
  • The {@code ratXmlReport} Gradle configuration is configured with a single artifact for + * the XML report file, built by the {@code rat} task. + *
+ * + *

Executing the task {@code other} therefore depends on the {@code rat} task, and the XML + * report file must be available. + */ + @Test + public void consumeXmlReportInCustomTask() { + BuildResult buildResult = + createGradleRunner("--build-cache", "--info", "--stacktrace", "other").build(); + assertThat(ratOutcome(buildResult)).matches(TestGradleIntegration::isSuccess); + assertThat(buildResult.task(":other").getOutcome()).matches(TestGradleIntegration::isSuccess); + + // We run the 'other' task, which has no "direct" task dependency (aka dependsOn()) to rat. + // The 'rat' task has to be run. + + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.html")).isNotEmptyFile(); + } + + /** Verify that the {@code rat} task's XML report can be used as a "copy source". */ + @Test + public void consumeXmlReportInCopyTask() { + BuildResult buildResult = + createGradleRunner("--build-cache", "--info", "--stacktrace", "copyXml").build(); + assertThat(ratOutcome(buildResult)).matches(TestGradleIntegration::isSuccess); + assertThat(buildResult.task(":copyXml").getOutcome()).matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.html")).isNotEmptyFile(); + + soft.assertThat(projectDir.resolve("build/copied/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/copied/rat-report.txt")).doesNotExist(); + soft.assertThat(projectDir.resolve("build/copied/rat-report.html")).doesNotExist(); + } + + /** Verify that the {@code rat} task's XML report can be used as a "copy source". */ + @Test + public void consumeAllReportsInCopyTask() { + BuildResult buildResult = + createGradleRunner("--build-cache", "--info", "--stacktrace", "copyAll").build(); + assertThat(ratOutcome(buildResult)).matches(TestGradleIntegration::isSuccess); + assertThat(buildResult.task(":copyAll").getOutcome()).matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.html")).isNotEmptyFile(); + + soft.assertThat(projectDir.resolve("build/copied/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/copied/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/copied/rat-report.html")).isNotEmptyFile(); + } + + /** Verify that the {@code rat} task's XML report can be used as a "copy source". */ + @Test + public void consumeXmlReportInOtherProject() { + BuildResult buildResult = + createGradleRunner("--build-cache", "--info", "--stacktrace", ":a:copyXml").build(); + assertThat(ratOutcome(buildResult)).matches(TestGradleIntegration::isSuccess); + assertThat(buildResult.task(":a:copyXml").getOutcome()) + .matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.html")).isNotEmptyFile(); + + soft.assertThat(projectDir.resolve("a/build/copied/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("a/build/copied/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("a/build/copied/rat-report.html")).isNotEmptyFile(); + } + + @Test + public void kotlinDefaultSettings() { + assertThat(ratOutcome(standardGradleRunner().build())) + .matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("build/reports/rat/rat-report.html")).isNotEmptyFile(); + } + + @Test + public void customOutputDirectory() { + assertThat(ratOutcome(standardGradleRunner().build())) + .matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat")).doesNotExist(); + + soft.assertThat(projectDir.resolve("custom/output/rat-report.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("custom/output/rat-report.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("custom/output/rat-report.html")).isNotEmptyFile(); + } + + @Test + public void customOutputFiles() { + assertThat(ratOutcome(standardGradleRunner().build())) + .matches(TestGradleIntegration::isSuccess); + + soft.assertThat(projectDir.resolve("build/reports/rat")).doesNotExist(); + + // Not great that this directory is created, but it doesn't hurt at all. + // This assertion isn't really needed and can be changed or removed when necessary. + soft.assertThat(projectDir.resolve("custom")).isEmptyDirectory(); + + soft.assertThat(projectDir.resolve("tech/that.xml")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("my/output.txt")).isNotEmptyFile(); + soft.assertThat(projectDir.resolve("my/output.txt")).isNotEmptyFile(); + } + + @Test + public void badLicenseFailsBuild() { + assertThat(ratOutcome(standardGradleRunner().buildAndFail())).isSameAs(TaskOutcome.FAILED); + } + + private static boolean isSuccess(TaskOutcome outcome) { + // All these outcomes represent a successful task execution. + // SUCCESS -> task successfully executed + // UP_TO_DATE -> task inputs match the inputs of a previous successful execution, output is + // up-to-date + // FROM_CACHE -> task output was loaded from the build cache using the inputs of the task + // (Note: the latter two are currently impossible, see RatTask constructor) + return outcome == TaskOutcome.SUCCESS + || outcome == TaskOutcome.UP_TO_DATE + || outcome == TaskOutcome.FROM_CACHE; + } + + private static TaskOutcome ratOutcome(BuildResult result) { + return result.task(":rat").getOutcome(); + } + + private GradleRunner standardGradleRunner(String... args) { + return createGradleRunner("--build-cache", "--info", "--stacktrace", "rat"); + } + + private GradleRunner createGradleRunner(String... args) { + return GradleRunner.create() + .withPluginClasspath() + .withProjectDir(projectDir.toFile()) + .withArguments(args) + .withDebug(true) + .forwardOutput(); + } +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/badLicenseFailsBuild/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/badLicenseFailsBuild/build.gradle new file mode 100644 index 000000000..b5b2744a9 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/badLicenseFailsBuild/build.gradle @@ -0,0 +1,3 @@ +plugins { + id 'org.apache.rat' +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/badLicenseFailsBuild/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/badLicenseFailsBuild/settings.gradle new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/badLicenseFailsBuild/settings.gradle @@ -0,0 +1 @@ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeAllReportsInCopyTask/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeAllReportsInCopyTask/build.gradle new file mode 100644 index 000000000..9284e7d6e --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeAllReportsInCopyTask/build.gradle @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +plugins { + id 'org.apache.rat' +} + +tasks.register('copyAll', Copy) { + it.destinationDir = project.layout.buildDirectory.dir('copied').get().asFile + it.from(configurations.ratAllReports) +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeAllReportsInCopyTask/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeAllReportsInCopyTask/settings.gradle new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeAllReportsInCopyTask/settings.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCopyTask/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCopyTask/build.gradle new file mode 100644 index 000000000..00e554743 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCopyTask/build.gradle @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +plugins { + id 'org.apache.rat' +} + +tasks.register('copyXml', Copy) { + it.destinationDir = project.layout.buildDirectory.dir('copied').get().asFile + it.from(configurations.ratXmlReport) +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCopyTask/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCopyTask/settings.gradle new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCopyTask/settings.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCustomTask/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCustomTask/build.gradle new file mode 100644 index 000000000..85a5c45b7 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCustomTask/build.gradle @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +plugins { + id 'org.apache.rat' +} + +tasks.register('other') { + inputs.files(configurations.named('ratXmlReport')) +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCustomTask/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCustomTask/settings.gradle new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInCustomTask/settings.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/a/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/a/build.gradle new file mode 100644 index 000000000..1792dd42a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/a/build.gradle @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +var resolvableConfig = configurations.register('resolvableConfig') { + canBeConsumed = false + canBeResolved = true +} + +dependencies { + add('resolvableConfig', project(['path': ':', 'configuration': 'ratAllReportsElements'])) +} + +tasks.register('copyXml', Copy) { + it.destinationDir = project.layout.buildDirectory.dir('copied').get().asFile + it.from(resolvableConfig) +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/build.gradle new file mode 100644 index 000000000..2cffc86c6 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/build.gradle @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +plugins { + id 'org.apache.rat' +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/settings.gradle new file mode 100644 index 000000000..3a4fa693d --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/consumeXmlReportInOtherProject/settings.gradle @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +include('a') diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputDirectory/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputDirectory/build.gradle new file mode 100644 index 000000000..8f9dc0fe4 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputDirectory/build.gradle @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +import org.apache.rat.gradle.RatTask + +plugins { + id 'org.apache.rat' +} + +tasks.named('rat', RatTask) { + reportOutputDirectory = file('custom/output') +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputDirectory/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputDirectory/settings.gradle new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputDirectory/settings.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputFiles/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputFiles/build.gradle new file mode 100644 index 000000000..1c88ef69e --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputFiles/build.gradle @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +import org.apache.rat.gradle.RatTask + +plugins { + id 'org.apache.rat' +} + +tasks.named('rat', RatTask) { + reportOutputDirectory = file('custom/output') + ratTxtFile = file('my/output.txt') + ratHtmlFile = file('some/index.html') + ratXmlFile = file('tech/that.xml') +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputFiles/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputFiles/settings.gradle new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/customOutputFiles/settings.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/defaultSettings/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/defaultSettings/build.gradle new file mode 100644 index 000000000..2cffc86c6 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/defaultSettings/build.gradle @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +plugins { + id 'org.apache.rat' +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/defaultSettings/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/defaultSettings/settings.gradle new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/defaultSettings/settings.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/kotlinDefaultSettings/build.gradle.kts b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/kotlinDefaultSettings/build.gradle.kts new file mode 100644 index 000000000..8b693a926 --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/kotlinDefaultSettings/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +plugins { + id("org.apache.rat") +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/kotlinDefaultSettings/settings.gradle.kts b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/kotlinDefaultSettings/settings.gradle.kts new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/kotlinDefaultSettings/settings.gradle.kts @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/withJava/build.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/withJava/build.gradle new file mode 100644 index 000000000..d88de024c --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/withJava/build.gradle @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +plugins { + id 'java-library' + id 'org.apache.rat' +} diff --git a/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/withJava/settings.gradle b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/withJava/settings.gradle new file mode 100644 index 000000000..bd244d07a --- /dev/null +++ b/apache-rat-gradle-plugin/src/test/resources/TestGradleIntegration/withJava/settings.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + diff --git a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/GradleOption.java b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/GradleOption.java new file mode 100644 index 000000000..932370e56 --- /dev/null +++ b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/GradleOption.java @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.rat.documentation.options; + +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.apache.commons.cli.Option; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.text.WordUtils; +import org.apache.rat.OptionCollection; +import org.apache.rat.commandline.Arg; +import org.apache.rat.commandline.Converters; +import org.apache.rat.report.claim.ClaimStatistic; +import org.apache.rat.utils.CasedString; + +import static java.lang.String.format; + +/** + * A representation of a CLI option for a Gradle plugin extension and task property. + */ +public class GradleOption extends AbstractOption { + /** + * Default values for CLI options + */ + private static final Map DEFAULT_VALUES = new HashMap<>(); + /** + * List of CLI options that are not supported by Gradle. + */ + private static final Set

    + *
  1. the package name for the class
  2. + *
  3. the simple class name
  4. + *
  5. the directory in which to write the class file.
  6. + *
+ * + * @param args the arguments + * @throws IOException on error + */ + public static void main(final String[] args) throws IOException { + if (args == null || args.length < 3) { + System.err.println("At least three arguments are required: package, simple class name, target directory."); + return; + } + + String packageName = args[0]; + String className = args[1]; + String destDir = args[2]; + List options = GradleOption.getGradleOptions(); + String pkgName = String.join(File.separator, new CasedString(StringCase.DOT, packageName).getSegments()); + File packageDir = new File(new File(destDir), pkgName); + generateFile("/GradleOptions.tpl", packageDir, className, className, options, packageName, false); + generateFile("/GradleTaskBase.tpl", packageDir, className + "TaskBase", className, options, packageName, false); + generateFile("/GradleConfiguration.tpl", packageDir, className + "ToConfiguration", className, options, packageName, true); + } + + private static void generateFile(final String templateResource, final File packageDir, final String className, final String baseName, + final List options, final String packageName, final boolean isInternal) throws IOException { + File dir = isInternal ? new File(packageDir, "internal") : packageDir; + File file = new File(dir, className + ".java"); + System.out.println("Creating " + file); + file.getParentFile().mkdirs(); + try (InputStream template = GradleGenerator.class.getResourceAsStream(templateResource); + FileWriter writer = new FileWriter(file)) { + if (template == null) { + throw new RuntimeException("Template /Gradle.tpl not found"); + } + LineIterator iter = IOUtils.lineIterator(new InputStreamReader(template, StandardCharsets.UTF_8)); + while (iter.hasNext()) { + String line = iter.next(); + switch (line.trim()) { + case "${static}": + for (Map.Entry entry : GradleOption.getRenameMap().entrySet()) { + writer.append(format(" xlateName.put(\"%s\", \"%s\");%n", entry.getKey(), entry.getValue())); + } + for (Option option : GradleOption.getFilteredOptions()) { + writer.append(format(" unsupportedArgs.add(\"%s\");%n", argsKey(option))); + } + for (GradleOption option : options) { + if (option.isDeprecated()) { + writer.append(format(" deprecatedArgs.put(\"%s\", \"%s\");%n", argsKey(option.getOption()), + format("Use of deprecated option '%s'. %s", option.getName(), option.getDeprecated()))); + } + } + break; + case "${properties}": + writeProperties(writer, options); + break; + case "${applyConventions}": + writeApplyConventions(writer, options, baseName); + break; + case "${converterBody}": + writeConverterMethodBody(writer, options); + break; + case "${taskBaseBody}": + writeTaskBaseBody(writer, options); + break; + case "${package}": + writer.append(format("package %s%s;%n", packageName, isInternal ? ".internal" : "")); + break; + case "${baseImport}": + writer.append(format("import %s.*;%n", packageName)); + break; + case "${interface}": + writer.append(format("public interface %s {%n", className)); + break; + case "${taskBaseInterface}": + writer.append(format("public interface %1$s extends %2$s {%n", className, baseName)); + break; + case "${class}": + writer.append(format("public class %s {%n", className)); + break; + case "${constructor}": + writer.append(format(" public %1$s(%2$s options) {%n", className, baseName)); + break; + case "${constructorBody}": + writer.append(" setDeprecationReporter();").append(System.lineSeparator()); + break; + case "${commonArgs}": + try (InputStream argsTpl = GradleGenerator.class.getResourceAsStream("/Args.tpl")) { + if (argsTpl == null) { + throw new RuntimeException("Args.tpl not found"); + } + IOUtils.copy(argsTpl, writer, StandardCharsets.UTF_8); + } + break; + default: + writer.append(line).append(System.lineSeparator()); + break; + } + } + } + } + + private static String getComment(final GradleOption option) { + String desc = option.getDescription(); + if (desc == null) { + throw new IllegalStateException(format("Description for %s may not be null", option.getName())); + } + if (!desc.contains(".")) { + throw new IllegalStateException(format("First sentence of description for %s must end with a '.'", option.getName())); + } + if (option.hasArg() && option.getArgName() != null) { + Supplier sup = OptionCollection.getArgumentTypes().get(option.getArgName()); + if (sup == null) { + throw new IllegalStateException(format("Argument type %s must be in OptionCollection.ARGUMENT_TYPES", option.getArgName())); + } + desc = format("%s Argument%s should be %s%s. (See Argument Types for clarification)", desc, option.hasArgs() ? "s" : "", + option.hasArgs() ? "" : "a ", option.getArgName()); + } + StringBuilder sb = new StringBuilder() + .append(format(" /**%n * %s%n", StringEscapeUtils.escapeHtml4(desc))); + if (option.isDeprecated()) { + sb.append(format(" * @deprecated %s%n", StringEscapeUtils.escapeHtml4(option.getDeprecated()))); + } + return sb.append(format(" */%n")).toString(); + } + + private static void writeApplyConventions(final FileWriter writer, final List options, final String baseName) throws IOException { + writer.append(format(" default void applyConventions(%1$s from) {", baseName)) + .append(System.lineSeparator()); + for (GradleOption option : options) { + writer.append(format(" %1$s().convention(from.%1$s());", option.getPropertyFunctionName())) + .append(System.lineSeparator()); + } + writer.append(" }") + .append(System.lineSeparator()); + } + + private static void writeConverterMethodBody(final FileWriter writer, final List options) throws IOException { + for (GradleOption option : options) { + writer.append(option.getConverterBody(" ")) + .append(System.lineSeparator()); + } + } + + private static void writeProperties(final FileWriter writer, final List options) throws IOException { + for (GradleOption option : options) { + writer.append(getComment(option)) + .append(option.getMethodSignature(" ")).append(";").append(System.lineSeparator()); + } + } + + private static void writeTaskBaseBody(final FileWriter writer, final List options) throws IOException { + for (GradleOption option : options) { + writer.append(option.getTaskBaseOverride(" ")) + .append(System.lineSeparator()); + } + } +} diff --git a/apache-rat-tools/src/main/resources/GradleConfiguration.tpl b/apache-rat-tools/src/main/resources/GradleConfiguration.tpl new file mode 100644 index 000000000..f097ca3f1 --- /dev/null +++ b/apache-rat-tools/src/main/resources/GradleConfiguration.tpl @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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} + +import org.apache.commons.cli.Option; +import org.apache.commons.lang3.StringUtils; +import org.apache.rat.commandline.Arg; +import org.apache.rat.DeprecationReporter; +import org.apache.rat.utils.DefaultLog; +import org.apache.rat.utils.Log; +${baseImport} + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/* DO NOT EDIT - GENERATED FILE */ + +/** + * Build RAT tool command line arguments from Gradle properties. + * + *

This interface is generated by the RAT code generator for Gradle. Do not edit. + */ +@SuppressWarnings({"StringOperationCanBeSimplified", "SimplifyStreamApiCallChains", "DataFlowIssue", "ArraysAsListWithZeroOrOneArgument", "deprecation"}) +${class} + + private static final Map xlateName = new HashMap<>(); + private static final List unsupportedArgs = new ArrayList<>(); + private static final Map deprecatedArgs = new HashMap<>(); + + static { +${static} + } + + /** + * Returns the list of unsupported args. + * @return the list of kebab style names that are unsupported by the Gradle plugin. + */ + public static List unsupportedArgs() { + return Collections.unmodifiableList(unsupportedArgs); + } + +${constructor} +${constructorBody} + +${converterBody} + } + + public Map> getArgsMap() { + Map> map = new HashMap<>(); + args.forEach((k, l) -> map.put(k, new ArrayList<>(l))); + return map; + } + + /** + * Reads values for the Arg. + * + * @param arg The Arg to get the values for. + * @return The list of values or an empty list. + */ + public List getValues(final Arg arg) { + List result = new ArrayList<>(); + for (Option option : arg.group().getOptions()) { + if (option.getLongOpt() != null) { + List args = getArg(option.getLongOpt()); + if (args != null) { + result.addAll(args); + } + } + } + return result; + } + + /** + * Removes all values for an Arg. + * @param arg The arg to remove values for. + */ + public void removeKey(final Arg arg) { + for (Option option : arg.group().getOptions()) { + if (option.getLongOpt() != null) { + removeArg(option.getLongOpt()); + } + } + } + +${commonArgs} +} diff --git a/apache-rat-tools/src/main/resources/GradleOptions.tpl b/apache-rat-tools/src/main/resources/GradleOptions.tpl new file mode 100644 index 000000000..c0e39fcc6 --- /dev/null +++ b/apache-rat-tools/src/main/resources/GradleOptions.tpl @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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} + +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; + +/* DO NOT EDIT - GENERATED FILE */ + +/** + * Generated class providing all RAT options in the "Gradle way". + * + *

This interface is generated by the RAT code generator for Gradle. Do not edit. + */ +@SuppressWarnings({"NullableProblems", "DeprecatedIsStillUsed", "UnstableApiUsage"}) +${interface} + +${properties} + /** + * Apply the configuration options of the given instance as conventions to this instance. + * @param from The instance to apply the conventions from. + */ +${applyConventions} +} diff --git a/apache-rat-tools/src/main/resources/GradleTaskBase.tpl b/apache-rat-tools/src/main/resources/GradleTaskBase.tpl new file mode 100644 index 000000000..59b257970 --- /dev/null +++ b/apache-rat-tools/src/main/resources/GradleTaskBase.tpl @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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} + +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; + +/* DO NOT EDIT - GENERATED FILE */ + +/** + * Generated interface extending the base RAT configuration properties enriched with Gradle task input related + * annotations, which are necessary for "proper" task-input determination and in turn caching. + * + *

This interface is generated by the RAT code generator for Gradle. Do not edit. + */ +@SuppressWarnings({"NullableProblems"}) +${taskBaseInterface} + +${taskBaseBody} +} diff --git a/pom.xml b/pom.xml index 8ebb1a5f0..6cbc9ac1b 100644 --- a/pom.xml +++ b/pom.xml @@ -938,6 +938,7 @@ agnostic home for software distribution comprehension and audit tools. apache-rat apache-rat-tools apache-rat-testdata + apache-rat-gradle-plugin diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b4ffc7900..9139e7a11 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -63,6 +63,9 @@ The type attribute can be one of: TODO: collect all dependabot updates for release 1.0.0. + + Add Gradle plugin for 0.17 generation methods. + --> + +