Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter files with custom options being built #53

Merged
merged 11 commits into from
Jan 21, 2022
2 changes: 1 addition & 1 deletion .github/workflows/build-on-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ jobs:
- name: Upload code coverage report
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
fail_ci_if_error: false
verbose: true
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ package io.spine.internal.dependency
@Suppress("MemberVisibilityCanBePrivate") // used directly from outside
object Protobuf {
private const val group = "com.google.protobuf"
const val version = "3.19.2"
const val version = "3.19.3"
val libs = listOf(
"${group}:protobuf-java:${version}",
"${group}:protobuf-java-util:${version}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import org.gradle.process.CommandLineArgumentProvider
* }
*```
*/
@Suppress("unused")
fun JavaCompile.configureErrorProne() {
options.errorprone
.errorproneArgumentProviders
Expand All @@ -67,7 +68,8 @@ private object ErrorProneConfig {
listOf(

// Exclude generated sources from being analyzed by ErrorProne.
"-XepExcludedPaths:.*/generated/.*",
// Include all directories started from `generated`, such as `generated-proto`.
"-XepExcludedPaths:.*/generated.*/.*",

// Turn the check off until ErrorProne can handle `@Nested` JUnit classes.
// See issue: https://github.com/google/error-prone/issues/956
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.internal.gradle
package io.spine.internal.gradle.publish

import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import io.spine.internal.gradle.Repository
import java.io.FileNotFoundException
import java.net.URL
import org.gradle.api.DefaultTask
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@

@file:Suppress("unused")

package io.spine.internal.gradle
package io.spine.internal.gradle.publish

import io.spine.internal.gradle.publish.PublishingRepos
import org.gradle.api.Plugin
import org.gradle.api.Project

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ abstract class PublishExtension @Inject constructor() {
abstract val projectsToPublish: SetProperty<String>
abstract val targetRepositories: SetProperty<Repository>
abstract val spinePrefix: Property<Boolean>
abstract val customPrefix: Property<String>

/**
* The project to be published _instead_ of [projectsToPublish].
Expand All @@ -54,30 +55,40 @@ abstract class PublishExtension @Inject constructor() {
const val name = "spinePublishing"

/** The prefix to be used before the project name if [spinePrefix] is set to `true`. */
const val artifactPrefix = "spine-"
const val standardPrefix = "spine-"

/**
* Creates a new instance of the extension and adds it to the given project.
*/
fun createIn(project: Project): PublishExtension {
val extension = project.extensions.create(name, PublishExtension::class.java)
extension.spinePrefix.convention(true)
extension.customPrefix.convention("")
return extension
}
}

/**
* Obtains an artifact ID of the given project, taking into account the value of
* the [spinePrefix] property. If the property is set to `true`, [artifactPrefix] will
* the [spinePrefix] and [customPrefix] properties.
*
* If the `customPrefix` property is set to a non-empty string, it will be used before
* the published project name. Otherwise, the [spinePrefix] property is taken into account.
*
* If the `spinePrefix` property is set to `true`, [standardPrefix] will
* be used before the project name. Otherwise, just the name of the project will be
* used as the artifact ID.
*/
fun artifactId(project: Project): String =
if (spinePrefix.get()) {
"$artifactPrefix${project.name}"
fun artifactId(project: Project): String {
val customPrefix = customPrefix.get()
return if (customPrefix.isNotEmpty()) {
"$customPrefix${project.name}"
} else if (spinePrefix.get()) {
"$standardPrefix${project.name}"
} else {
project.name
}
}

/**
* Instructs to publish the passed project _instead_ of [projectsToPublish].
Expand Down
8 changes: 3 additions & 5 deletions buildSrc/src/main/kotlin/pmd-settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,9 @@ pmd {
// Disable the default rule set to use the custom rules (see below).
ruleSets = listOf()

// Load PMD settings from a file in `buildSrc/resources/`.
val classLoader = Pmd.javaClass.classLoader
val settingsResource = classLoader.getResource("pmd.xml")!!
val pmdSettings: String = settingsResource.readText()
val textResource: TextResource = resources.text.fromString(pmdSettings)
// Load PMD settings.
val pmdSettings = file("$rootDir/config/quality/pmd.xml")
val textResource: TextResource = resources.text.fromFile(pmdSettings)
ruleSetConfig = textResource

reportsDir = file("build/reports/pmd")
Expand Down
75 changes: 75 additions & 0 deletions cli/src/main/kotlin/io/spine/protodata/cli/DescriptorExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2022, TeamDev. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.protodata.cli

import com.google.protobuf.Descriptors.FileDescriptor
import com.google.protobuf.ExtensionRegistry
import io.spine.code.java.ClassName

/**
* Obtains a name of the outer Java class associated with this file.
*/
internal val FileDescriptor.outerClassName: String
get() {
val outerClass = ClassName.outerClass(this)
return outerClass.binaryName()
}

/**
* Obtains an outer Java class associated with this proto file, if such a class already exists.
* Otherwise, returns `null`.
*/
internal val FileDescriptor.outerClass: Class<*>?
get() {
val outerClass: Class<*>?
try {
val classLoader = javaClass.classLoader
outerClass = classLoader.loadClass(outerClassName)
} catch (e: ClassNotFoundException) {
return null
}
return outerClass
}

/**
* Reflectively calls the static `registerAllExtensions(..)` method, such as
* [io.spine.option.OptionsProto.registerAllExtensions], on the [outerClass] generated for
* this Protobuf file with the custom options declared.
*
* @throws IllegalStateException if the outer class for this proto file does not exist
*/
internal fun FileDescriptor.registerAllExtensions(registry: ExtensionRegistry) {
if (outerClass == null) {
throw IllegalStateException(
"The outer class $outerClassName for the file $name does not exist."
alexander-yevsyukov marked this conversation as resolved.
Show resolved Hide resolved
)
}
val method = outerClass!!.getDeclaredMethod(
"registerAllExtensions", ExtensionRegistry::class.java
)
method.invoke(null, registry)
}
25 changes: 5 additions & 20 deletions cli/src/main/kotlin/io/spine/protodata/cli/FileOptionsProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,20 @@ package io.spine.protodata.cli

import com.google.protobuf.Descriptors.FileDescriptor
import com.google.protobuf.ExtensionRegistry
import io.spine.code.java.ClassName
import io.spine.protodata.option.OptionsProvider

/**
* An [OptionsProvider] which provides all the options defined in a single Protobuf file.
*/
internal class FileOptionsProvider(
private val descriptor: FileDescriptor
) : OptionsProvider {
internal class FileOptionsProvider(private val descriptor: FileDescriptor) : OptionsProvider {

/**
* Supplies the given [registry] with the options from the associated file descriptor.
* Supplies the given [registry] with the options from the associated descriptor
* of the proto file. The outer class for the file must exist.
*
* Reflectively calls the static `registerAllExtensions(..)` method, such
* as [io.spine.option.OptionsProto.registerAllExtensions], on the outer class generated for
* the Protobuf file where the custom options are declared.
* @throws IllegalStateException if the outer class for the proto file does not exist.
*/
override fun registerIn(registry: ExtensionRegistry) {
val outerClassName = outerClassName()
val classLoader = this.javaClass.classLoader
val optionsClass = classLoader.loadClass(outerClassName)
val method = optionsClass.getDeclaredMethod(
"registerAllExtensions", ExtensionRegistry::class.java
)
method.invoke(null, registry)
}

private fun outerClassName(): String {
val outerClass = ClassName.outerClass(descriptor)
return outerClass.binaryName()
descriptor.registerAllExtensions(registry)
}
}
12 changes: 10 additions & 2 deletions cli/src/main/kotlin/io/spine/protodata/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ internal class Run(version: String) : CliktCommand(
private fun filterOptionFiles(files: FileSet): Sequence<FileOptionsProvider> {
val fileProviders = files.files()
.filter { it.extensions.isNotEmpty() }
// Filter out files that do not have outer classes yet.
// These are `.proto` files being processed by ProtoData that contain
// option definitions. We cannot use these files because there is no binary Java
// code generated for them at this stage. Because of this they cannot be added to
// an `ExtensionRegistry` later.
.filter { it.outerClass != null }
.map(::FileOptionsProvider)
.asSequence()
return fileProviders
Expand All @@ -315,8 +321,10 @@ internal class Run(version: String) : CliktCommand(
return classNames.map { builder.tryCreate(it, classLoader) }
}

private fun <T: Any> ReflectiveBuilder<T>.tryCreate(className: String,
classLoader: ClassLoader): T {
private fun <T : Any> ReflectiveBuilder<T>.tryCreate(
className: String,
classLoader: ClassLoader
): T {
try {
return createByName(className, classLoader)
} catch (e: ClassNotFoundException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ internal open class ReflectiveBuilder<T: Any> {
* It is necessary that the class defined by the [className] parameter is a subtype of `T`.
* Otherwise, a casting error occurs.
*
* @param className name of the concrete class to instantiate
* @param classLoader the [ClassLoader] to load the class by its name
* @param className
* name of the concrete class to instantiate
* @param classLoader
* the [ClassLoader] to load the class by its name
*/
fun createByName(className: String, classLoader: ClassLoader): T {
val cls = classLoader.loadClass(className).kotlin
Expand Down
4 changes: 2 additions & 2 deletions compiler/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@

import io.spine.internal.dependency.JUnit
import io.spine.internal.dependency.Jackson
import io.spine.internal.gradle.CheckVersionIncrement
import io.spine.internal.gradle.IncrementGuard
import io.spine.internal.gradle.publish.CheckVersionIncrement
import io.spine.internal.gradle.publish.IncrementGuard
import io.spine.internal.gradle.publish.PublishingRepos

plugins {
Expand Down
Loading