-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce API distinction in plugin development
Signed-off-by: Daniel Lacasse <daniel@lacasse.io>
- Loading branch information
Showing
13 changed files
with
1,217 additions
and
19 deletions.
There are no files selected for viewing
458 changes: 458 additions & 0 deletions
458
...ctionalTest/groovy/dev/gradleplugins/GradlePluginDevelopmentPluginApiFunctionalTests.java
Large diffs are not rendered by default.
Oops, something went wrong.
51 changes: 51 additions & 0 deletions
51
...velopment/src/functionalTest/groovy/dev/gradleplugins/testers/DependencyWiringTester.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package dev.gradleplugins.testers; | ||
|
||
import dev.gradleplugins.buildscript.GradleDsl; | ||
import dev.gradleplugins.buildscript.ast.ExpressionBuilder; | ||
import dev.gradleplugins.buildscript.ast.expressions.Expression; | ||
import dev.gradleplugins.buildscript.io.GradleBuildFile; | ||
import dev.gradleplugins.runnerkit.GradleRunner; | ||
|
||
import static dev.gradleplugins.buildscript.blocks.GradleBuildScriptBlocks.afterEvaluate; | ||
import static dev.gradleplugins.buildscript.syntax.Syntax.groovyDsl; | ||
import static dev.gradleplugins.buildscript.syntax.Syntax.string; | ||
|
||
public interface DependencyWiringTester { | ||
ExpressionBuilder<?> bucketDsl(); | ||
|
||
GradleBuildFile buildFile(); | ||
|
||
GradleRunner runner(); | ||
|
||
default void assertBucketDependencyIs(Expression expression) { | ||
buildFile().append(afterEvaluate(it -> it.add(bucketDsl().call(string("com.example:foo:1.0"))))); | ||
buildFile().append(groovyDsl( | ||
"Set<String> allDependencies(Configuration configuration) {", | ||
" return configuration.allDependencies.collect {", | ||
" if (it instanceof ExternalModuleDependency) {", | ||
" return \"${it.group}:${it.name}:${it.version}\".toString()", | ||
" } else if (it instanceof ProjectDependency) {", | ||
" return it.projectPath", | ||
" } else if (it instanceof SelfResolvingDependency) {", | ||
" return it.targetComponentId?.displayName ?: '<no-display-name>'", | ||
" } else {", | ||
" throw new RuntimeException()", | ||
" }", | ||
" }", | ||
"}" | ||
)); | ||
buildFile().append(groovyDsl( | ||
"tasks.register('verify') {", | ||
" doLast {", | ||
" assert " + expression.toString(GradleDsl.GROOVY), | ||
" }", | ||
"}" | ||
)); | ||
|
||
runner().withTasks("verify").build(); | ||
} | ||
|
||
default Expression containedIn(Expression configurationName) { | ||
return groovyDsl("allDependencies(configurations[" + configurationName.toString(GradleDsl.GROOVY) + "]).contains('com.example:foo:1.0')"); | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
...evelopment/src/functionalTest/groovy/dev/gradleplugins/testers/GradlePluginApiTester.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package dev.gradleplugins.testers; | ||
|
||
import dev.gradleplugins.buildscript.io.GradleBuildFile; | ||
import dev.gradleplugins.buildscript.io.GradleSettingsFile; | ||
import dev.gradleplugins.fixtures.sample.GradlePluginApi; | ||
import dev.gradleplugins.runnerkit.GradleRunner; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.nio.file.Path; | ||
|
||
import static dev.gradleplugins.buildscript.syntax.Syntax.groovyDsl; | ||
|
||
public abstract class GradlePluginApiTester { | ||
GradleBuildFile pluginBuildFile; | ||
|
||
public abstract GradleRunner runner(); | ||
|
||
public abstract Path testDirectory(); | ||
|
||
public final GradleBuildFile pluginBuildFile() { | ||
return pluginBuildFile; | ||
} | ||
|
||
public abstract GradlePluginApi fixture(); | ||
|
||
@BeforeEach | ||
final void givenProject() { | ||
pluginBuildFile = GradleBuildFile.inDirectory(testDirectory()); | ||
|
||
fixture().writeToProject(testDirectory().toFile()); | ||
|
||
pluginBuildFile.append(groovyDsl( | ||
"group = 'com.example'", | ||
"version = '1.0'", | ||
"", | ||
"repositories {", | ||
" gradlePluginDevelopment()", | ||
"}", | ||
"", | ||
"gradlePlugin {", | ||
" plugins {", | ||
" basic {", | ||
" id = '" + fixture().getPluginId() + "'", | ||
" implementationClass = 'com.example.BasicPlugin'", | ||
" }", | ||
" }", | ||
"}", | ||
"", | ||
"Set<File> artifactFiles(def publications) {", | ||
" return publications.artifacts.files.files", | ||
"}" | ||
)); | ||
GradleSettingsFile.inDirectory(testDirectory()); | ||
} | ||
|
||
@Test | ||
void rewiresApiElementsToIncludeOnly__ApiJar__ApiClasses() { | ||
pluginBuildFile.append(groovyDsl( | ||
"tasks.register('verify') {", | ||
" doLast {", | ||
" assert artifactFiles(configurations.apiElements.outgoing) == [file('build/libs/my-plugin-1.0-api.jar')] as Set", | ||
" assert artifactFiles(configurations.apiElements.outgoing.variants.classes) == [file('build/tmp/syncApiClasses')] as Set", | ||
" }", | ||
"}" | ||
)); | ||
|
||
runner().withTasks("verify").build(); | ||
} | ||
|
||
// TODO: If we use a custom pluginSourceSet -> the runtimeElements and apiElements won't contain any outgoing information... | ||
|
||
@Test | ||
void rewiresRuntimeElementsToInclude__ApiAndMainJar__ApiAndMainClasses__ApiAndMainResources() { | ||
pluginBuildFile.append(groovyDsl( | ||
"tasks.register('verify') {", | ||
" doLast {", | ||
" assert artifactFiles(configurations.runtimeElements.outgoing) == [file('build/libs/my-plugin-1.0.jar'), file('build/libs/my-plugin-1.0-api.jar')] as Set", | ||
" assert artifactFiles(configurations.runtimeElements.outgoing.variants.classes) == [file('build/classes/java/main'), file('build/tmp/syncApiClasses')] as Set", | ||
" assert artifactFiles(configurations.runtimeElements.outgoing.variants.resources) == [file('build/resources/main'), file('build/tmp/syncApiResources')] as Set", | ||
" }", | ||
"}" | ||
)); | ||
|
||
runner().withTasks("verify").build(); | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
...ugin-development/src/main/java/dev/gradleplugins/GradlePluginDevelopmentApiExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package dev.gradleplugins; | ||
|
||
import org.gradle.api.plugins.ExtensionAware; | ||
import org.gradle.api.provider.Property; | ||
import org.gradle.api.tasks.SourceSet; | ||
import org.gradle.api.tasks.TaskProvider; | ||
import org.gradle.api.tasks.bundling.Jar; | ||
import org.gradle.plugin.devel.GradlePluginDevelopmentExtension; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* An extension for controlling the Gradle plugin development API. | ||
* The extension is registered on the {@literal gradlePlugin} extension. | ||
* The API refers to what a user can build against, e.g. some classes are for internal use only. | ||
* This extension offers some hook points to accomplish the separation at the source set or Jar task level. | ||
*/ | ||
public interface GradlePluginDevelopmentApiExtension { | ||
/** | ||
* Configures the API source set for the plugin. | ||
* | ||
* <p> | ||
* <code> | ||
* // Configure Gradle plugin API as a custom source set | ||
* gradlePlugin { | ||
* api { | ||
* sourceSet = sourceSets.register('api') | ||
* } | ||
* } | ||
* </code> | ||
* </p> | ||
* | ||
* @return a property to configure the API source set, never null. | ||
*/ | ||
Property<SourceSet> getSourceSet(); | ||
|
||
/** | ||
* Returns the task provider for the exported API JAR. | ||
* | ||
* Note: Once this property is queried, it's not possible to change the Jar task anymore. | ||
* | ||
* @return a task provider to the exported API JAR, never null | ||
*/ | ||
TaskProvider<Jar> getJarTask(); | ||
|
||
/** | ||
* Configures the API Jar for the plugin. | ||
* | ||
* <p> | ||
* <code> | ||
* // Remove `api` packages from implementation JAR | ||
* tasks.named('jar', Jar) { | ||
* exclude('**\/api\/**') | ||
* } | ||
* | ||
* // Configure Gradle plugin API as a custom JAR that only include `api` packages | ||
* gradlePlugin { | ||
* api { | ||
* jarTask = tasks.register('apiJar', Jar) { | ||
* from(sourceSet.flatMap { it.output.elements }) | ||
* include('**\/api\/**') | ||
* archiveClassifier = 'api' | ||
* } | ||
* } | ||
* } | ||
* </code> | ||
* </p> | ||
* | ||
* @param jarTaskProvider a JAR task to use as the exported API JAR, never null. | ||
*/ | ||
void setJarTask(TaskProvider<Jar> jarTaskProvider); | ||
|
||
/** | ||
* Returns {@literal api} extension from Gradle plugin development extension. | ||
* The plugin {@literal dev.gradleplugins.gradle-plugin-base} registers this extension. | ||
* | ||
* @param extension the {@literal gradlePlugin} extension, must not be null | ||
* @return the compatibility extension, never null | ||
*/ | ||
static GradlePluginDevelopmentApiExtension api(GradlePluginDevelopmentExtension extension) { | ||
Objects.requireNonNull(extension); | ||
return (GradlePluginDevelopmentApiExtension) ((ExtensionAware) extension).getExtensions().getByName("api"); | ||
} | ||
} |
Oops, something went wrong.