Slide 1

Slide 1 text

Designing and Wri,ng Gradle Plugins Benjamin Muschko

Slide 2

Slide 2 text

writing plugins is easy they said… me a couple of years ago

Slide 3

Slide 3 text

Designing… 1

Slide 4

Slide 4 text

Binary plugins FTW!

Slide 5

Slide 5 text

Prefer statically-typed language + @CompileStatic

Slide 6

Slide 6 text

Only use public API if possible build.gradle dependencies {
 compile gradleApi()
 }
 Be cau'ous - dependency pulls in full Gradle run'me API

Slide 7

Slide 7 text

Minimize external dependencies

Slide 8

Slide 8 text

identifying external plugin dependencies build.gradle plugins {
 id "com.github.gradle-guides.site" version "0.1"
 } ./gradlew buildEnvironment > Task :buildEnvironment ------------------------------------------------------------ Root project ------------------------------------------------------------ classpath \--- gradle.plugin.com.github.gradle-guides:gradle-site-plugin:0.1 \--- org.freemarker:freemarker:2.3.26-incubating

Slide 9

Slide 9 text

impact on performance

Slide 10

Slide 10 text

learn performance tips & tricks Op'mizing Gradle Build Performance h2ps:/ /guides.gradle.org/performance/

Slide 11

Slide 11 text

scan your build Iden'fy and share performance hotspots h2ps:/ /gradle.com/scans/get-started

Slide 12

Slide 12 text

dig deep with the Gradle profiler Profiling and benchmarking info h2ps:/ /github.com/gradle/gradle-profiler

Slide 13

Slide 13 text

Convention over configuration

Slide 14

Slide 14 text

Conventions Out-of-the-box SitePlugin.java public class SitePlugin implements Plugin {
 public void apply(Project project) {
 SitePluginExtension sitePluginExtension = ↵ project.getExtensions().create("site", ↵ SitePluginExtension.class, project); 
 sitePluginExtension.setOutputDir(new ↵ File(project.getBuildDir(), "docs/site"));
 
 ...
 }
 } sets default value

Slide 15

Slide 15 text

reconfiguring conventional values build.gradle apply plugin: 'com.github.gradle-guides.site'
 
 site {
 outputDir = file("$buildDir/site")
 websiteUrl = 'http://gradle.org'
 vcsUrl = 'https://github.com/gradle-guides/↵ gradle-site-plugin'
 } User declares the “what” not the “how” overrides defaults

Slide 16

Slide 16 text

CAPABIlities vs. conventions

Slide 17

Slide 17 text

Common pattern in plugin architecture Java Base Plugin Java Plugin Capabilities Conventions applies

Slide 18

Slide 18 text

CAPAbilities vs. conventions public class BaseSitePlugin extends Plugin {
 public void apply(Project project) {
 // define capabilities
 }
 } public class SitePlugin extends Plugin {
 public void apply(Project project) {
 project.getPlugins().apply(BaseSitePlugin.class);
 
 // define conventions
 }
 } SitePlugin.java BaseSitePlugin.java applies plugin

Slide 19

Slide 19 text

implementing… 2

Slide 20

Slide 20 text

Use the Plugin Development plugin build.gradle dependencies {
 compile 'java-gradle-plugin'
 }
 Reduces boilerplate code significantly

Slide 21

Slide 21 text

Prefer writing custom tasks types

Slide 22

Slide 22 text

reusing custom tasks import org.gradle.plugins.site.tasks.SiteGenerate task generateDefaultSite(type: SiteGenerate)
 
 task generateSiteWithCustomValues(type: SiteGenerate) {
 outputDir = file("$buildDir/myAwesomeSite")
 
 customData {
 websiteUrl = 'http://gradle.org'
 vcsUrl = 'https://github.com/gradle-guides/↵ gradle-site-plugin'
 }
 } build.gradle Impera've logic is hidden in implementa'on

Slide 23

Slide 23 text

modeling DSL-like APIs task generateSiteWithCustomValues(type: SiteGenerate) {
 customData {
 websiteUrl = 'http://gradle.org'
 vcsUrl = 'https://github.com/gradle-guides/↵ gradle-site-plugin'
 }
 } build.gradle site {
 customData {
 websiteUrl = 'http://gradle.org'
 vcsUrl = 'https://github.com/gradle-guides/↵ gradle-site-plugin'
 }
 } or

Slide 24

Slide 24 text

avoid closures in Apis SiteGenerate.gradle public class SiteGenerate extends DefaultTask {
 private final CustomData customData = new CustomData();
 
 public void customData(Closure closure) {
 closure.setResolveStrategy(Closure.DELEGATE_FIRST);
 closure.setDelegate(customData);
 closure.call();
 }
 } avoid Harder to use from Java and Kotlin

Slide 25

Slide 25 text

generation of closure method SiteGenerate.gradle task generateSiteWithCustomValues(type: SiteGenerate) {
 customData {
 ... }
 } public class SiteGenerate extends DefaultTask {
 private final CustomData customData = new CustomData();
 
 public void customData(Action action) {
 action.execute(customData);
 }
 } build.gradle do this

Slide 26

Slide 26 text

Enable tasks to be incremental

Slide 27

Slide 27 text

Declare inputs & outputs public class SiteGenerate extends DefaultTask {
 private final PropertyState outputDir;
 private final CustomData customData;
 
 ...
 @Nested
 public CustomData getCustomData() {
 return customData;
 }
 
 @OutputDirectory
 public File getOutputDir() {
 return outputDir.get();
 }
 } SiteGenerate.java use annotations

Slide 28

Slide 28 text

Lazy evaluation of properties

Slide 29

Slide 29 text

typical use case SiteGenerate.java SitePluginExtension.java build.gradle Capture user-provided input Hold user-provided values Lazily evaluate and apply user-provided values Capture values during configura'on phase Evaluate and use value during execu'on 'me

Slide 30

Slide 30 text

Evaluate property at execution time public class SiteGenerate extends DefaultTask {
 private final PropertyState outputDir;
 
 public SiteGenerate() {
 this.outputDir = getProject().property(File.class);
 }
 
 @OutputDirectory
 public File getOutputDir() {
 return outputDir.get();
 }
 
 @TaskAction
 public void generate() {
 getOutputDir();
 }
 } SiteGenerate.java lazily evaluate value

Slide 31

Slide 31 text

avoid applying plugins if possible project.getPlugins().apply(JavaPlugin.class);
 JavaPluginConvention javaConvention = project.getConvention()↵ .getPlugin(JavaPluginConvention.class);
 projectDescriptor.setJavaProject(new JavaProjectDescriptor(↵ javaConvention.getSourceCompatibility().toString(),↵ javaConvention.getTargetCompatibility().toString()) SitePlugin.java Imposes poten'ally unnecessary conven'ons avoid

Slide 32

Slide 32 text

React to applied plugins project.getPlugins().withType(JavaPlugin.class, new Action() {
 @Override
 public void execute(JavaPlugin javaPlugin) {
 JavaPluginConvention javaConvention = project.getConvention()↵ .getPlugin(JavaPluginConvention.class);
 projectDescriptor.setJavaProject(↵ new JavaProjectDescriptor(↵ javaConvention.getSourceCompatibility().toString(),↵ javaConvention.getTargetCompatibility().toString()));
 }
 }); SitePlugin.java do this

Slide 33

Slide 33 text

Assign appropriate plugin identifiers . !"" src !"" main !"" resources !"" META-INF !"" gradle-plugins #"" com.github.gradle-guides.base-site.properties !"" com.github.gradle-guides.site.properties 
 Add domain to ensure uniqueness src/main/resources/META-INF/gradle-plugins

Slide 34

Slide 34 text

What could be next?

Slide 35

Slide 35 text

top 10 internal apis on github Class Name Count org.gradle.internal.reflect.Instantiator 1103 org.gradle.api.internal.file.FileResolver 868 org.gradle.util.GradleVersion 680 org.gradle.internal.os.OperatingSystem 680 org.gradle.util.ConfigureUtil 608 org.gradle.internal.service.ServiceRegistry 546 org.gradle.util.GFileUtils 329 org.gradle.util.TestUtil 258 org.gradle.api.internal.ConventionTask 195 org.gradle.api.internal.project.IsolatedAntBuilder 114 Poten'al candidates for public APIs

Slide 36

Slide 36 text

public apis with bigger impact ➞ Separa'on between internal & public API ➞ Declaring custom repository types ➞ BeQer support for wri'ng language plugins ➞ Improved TestKit fixtures ➞ Publishing custom ar'facts

Slide 37

Slide 37 text

better documentation, more guides ➞ Improved user guide documenta'on ➞ More sample projects ➞ Guides on tes'ng & documen'ng plugins ➞ Tutorial for wri'ng a plugin by example

Slide 38

Slide 38 text

improvements to Plugin Ecosystem ➞ Feature-rich plugin portal ➞ Cross-version compa'bility tes'ng ➞ Development support by plugin dev plugin ➞ BeQer IDE support

Slide 39

Slide 39 text

We want your feedback!

Slide 40

Slide 40 text

Resources Designing Gradle plugins h2ps:/ /guides.gradle.org/designing-gradle-plugins/ Implemen'ng Gradle plugins h2ps:/ /guides.gradle.org/implemen,ng-gradle-plugins/ Gradle Site Plugin h2ps:/ /github.com/gradle-guides/gradle-site-plugin/

Slide 41

Slide 41 text

w Thank you Gradle Summit 2017