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

Get Gradle to do more of the work in our Gradle plugin #2838

Merged
merged 7 commits into from
Feb 28, 2024

Conversation

swankjesse
Copy link
Collaborator

In particular leave more of the dependency management up to Gradle.

In particular leave more of the dependency management up to
Gradle.
@swankjesse swankjesse marked this pull request as ready for review February 27, 2024 03:51
Copy link
Member

@oldergod oldergod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very exciting.

@@ -218,7 +197,16 @@ class WirePlugin : Plugin<Project> {
val task = project.tasks.register(taskName, WireTask::class.java) { task: WireTask ->
task.group = GROUP
task.description = "Generate protobuf implementation for ${source.name}"
task.source(protoSourceInput.configuration)

// Flatten all the input files here. Changes to any of them will cause the task to re-run.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

Copy link
Collaborator

@martinbonnin martinbonnin Feb 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it even required? Given there is sourceInput and protoInput below, might be possible for WireTask to extend DefaultTask instead of DefaultTask?

Edit I read below we want NO-SOURCE if no source is found. In which case, I think protoSource should be in source and protoPath passed out of the band? in a regular task property?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you’re strictly correct, and also I don’t think it’ll matter much in practice?
Both skipping the task and running it when there’s no sources will both be super fast and rare.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed 👍

Comment on lines 86 to +92
fun sourcePathDirDoesNotExist() {
val fixtureRoot = File("src/test/projects/sourcepath-nonexistent-dir")

val result = gradleRunner.runFixture(fixtureRoot) { buildAndFail() }
val result = gradleRunner.runFixture(fixtureRoot) { build() }

assertThat(result.task(":generateProtos")).isNull()
assertThat(result.output).contains(
"""
|Invalid path string: "src/main/proto".
|For individual files, use the following syntax:
|wire {
| sourcePath {
| srcDir 'dirPath'
| include 'relativePath'
| }
|}
""".trimMargin(),
)
assertThat(result.task(":generateMainProtos")?.getOutcome())
.isEqualTo(TaskOutcome.NO_SOURCE)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm cool with it

@oldergod
Copy link
Member

oldergod commented Feb 27, 2024

I pushed a commit with spotless and apiDump

@oldergod oldergod force-pushed the jwilson.0226.gradle_more_work branch from dbfed0e to 3151263 Compare February 27, 2024 10:38
Copy link
Collaborator

@martinbonnin martinbonnin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few non-blocking comments, mainly for possible future improvements.

This change is a nice simplification 👍

Comment on lines -123 to -125
public final fun getSourceJars ()Ljava/util/Set;
public final fun getSourcePaths ()Ljava/util/Set;
public final fun getSourceTrees ()Ljava/util/Set;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically breaking but I'm assuming these APIs that are really supposed to be called from user land?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, exactly. These should have always been implementation details.

(protoSourceInput.dependencies + protoPathInput.dependencies).filterIsInstance<ProjectDependency>()
for (projectDependency in projectDependencies) {
projectDependenciesJvmConfiguration.dependencies.add(projectDependency)
if (extension.protoSourceProtoRootSets.all { it.roots.isEmpty() }) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this probably resolves configurations eagerly. I think this is the case already so not a huge deal in the grand scheme of things but would be a good candidate for future improvements.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. We can move this behaviour to execution time.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In follow-up!

Comment on lines +188 to +189
| srcDir 'dirPath'
| include 'relativePath'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For those cases, I like using a syntax that works in both Groovy and Kotlin:

Suggested change
| srcDir 'dirPath'
| include 'relativePath'
| srcDir("dirPath")
| include("relativePath")

It's less "idiomatic" in Groovy though so either work with me

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -218,7 +197,16 @@ class WirePlugin : Plugin<Project> {
val task = project.tasks.register(taskName, WireTask::class.java) { task: WireTask ->
task.group = GROUP
task.description = "Generate protobuf implementation for ${source.name}"
task.source(protoSourceInput.configuration)

// Flatten all the input files here. Changes to any of them will cause the task to re-run.
Copy link
Collaborator

@martinbonnin martinbonnin Feb 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it even required? Given there is sourceInput and protoInput below, might be possible for WireTask to extend DefaultTask instead of DefaultTask?

Edit I read below we want NO-SOURCE if no source is found. In which case, I think protoSource should be in source and protoPath passed out of the band? in a regular task property?

Comment on lines 37 to 39
// We store [file] relative to the [project] in order to not invalidate the cache when we don't
// have to.
return InputLocation(project.relativePath(file.path), includes, excludes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: It's technically ok to move the directory around as long as its contents stay the same so there might still be spurious cache invalidations here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remote caching is the big thing I wanna preserve. I think we get that as long as the paths are relative to the project.

@swankjesse
Copy link
Collaborator Author

Build failure in samples means I’ve made an unintentional behaviour change and our tests didn’t catch it! Yikes!

> Task :samples:android-lib-java-sample:javaPreCompileDebug
e: file:///home/runner/work/wire/wire/samples/android-app-variants-sample/src/testRelease/java/com/squareup/wire/android/app/variants/ReleaseUnitTest.kt:50:23 Unresolved reference: ReleaseType

@oldergod
Copy link
Member

oldergod commented Feb 27, 2024

Samples are also used as tests. Way simpler to work with than testing against the plugin directly.

@swankjesse
Copy link
Collaborator Author

Ugh, the failing Windows build shows me I’ve made a design error in my implementation.

The string I’m passing to InputLocation() is the relative path on the file system from my projects’ directory to the .jar or directory. For example, given these inputs:
projectDirectory=/Users/jwilson/Development/greeter/hello
path=/Users/jwilson/Development/greeter/hello/src/main/proto
It correctly returns a relative path output:
result=src/main/proto

Unfortunately if my path isn’t in the project directory, the result I’m producing is bananas and won’t work with Gradle’s cache:
projectDirectory=/Users/jwilson/Development/greeter/hello
path=/Users/jwilson/.gradle/caches/com/example/library/protos.jar
This produces a nonsense relative path:
result=../../../.gradle/caches/com/example/library/1.2.3/protos-1.2.3.jar
In particular, the relative path is now conditional on where my project and library directories are on the file system, and I don’t want that.

I think instead I need to change InputLocation to be either a File relative to the project, or a symbolic identifier for whatever library it’s pulling in, like say
library=com.example.library:protos:1.2.3

Ugh.

@swankjesse
Copy link
Collaborator Author

Here’s a tracking issue to fix that path problem: #2842

@swankjesse swankjesse merged commit df90610 into master Feb 28, 2024
7 checks passed
@swankjesse swankjesse deleted the jwilson.0226.gradle_more_work branch February 28, 2024 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants