Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Designing and writing Gradle plugins

Designing and writing Gradle plugins

Implementing plugins can be a daunting task for Gradle beginners and advanced users alike. The task requires many intricate considerations, decisions and deep knowledge about (sometimes undocumented) features. Have you ever asked yourself one of the following questions, then this session is for you!

- What's the best structure for my plugin code?
- How do I make my plugin as performant, flexible and user-friendly as possible?
- How do I ensure the best forward and backward compatibility for builds consuming my plugin?
- How do I expose a declarative DSL to configure my plugin's runtime behavior?

The main focus of this talk lies on the aspect of designing and writing plugins by applying established, best practices. We'll look at architectural considerations, technologies and tooling involved in building plugins as well as concrete recipes for implementing typical use cases faced during plugin development.

Benjamin Muschko

June 23, 2017
Tweet

More Decks by Benjamin Muschko

Other Decks in Programming

Transcript

  1. Designing and Wri,ng Gradle Plugins
    Benjamin Muschko

    View Slide

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

    View Slide

  3. Designing…
    1

    View Slide

  4. Binary plugins FTW!

    View Slide

  5. Prefer statically-typed language
    + @CompileStatic

    View Slide

  6. Only use public API if possible
    build.gradle
    dependencies {

    compile gradleApi()

    }

    Be cau'ous - dependency pulls in
    full Gradle run'me API

    View Slide

  7. Minimize external
    dependencies

    View Slide

  8. 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

    View Slide

  9. impact on
    performance

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. Convention
    over configuration

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. CAPABIlities
    vs. conventions

    View Slide

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

    View Slide

  18. 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

    View Slide

  19. implementing…
    2

    View Slide

  20. Use the Plugin Development plugin
    build.gradle
    dependencies {

    compile 'java-gradle-plugin'

    }

    Reduces boilerplate code significantly

    View Slide

  21. Prefer writing
    custom tasks types

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. 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

    View Slide

  25. 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 super CustomData> action) {

    action.execute(customData);

    }

    }
    build.gradle
    do this

    View Slide

  26. Enable tasks to be
    incremental

    View Slide

  27. 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

    View Slide

  28. Lazy evaluation of
    properties

    View Slide

  29. 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

    View Slide

  30. 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

    View Slide

  31. 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

    View Slide

  32. 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

    View Slide

  33. 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

    View Slide

  34. What could be next?

    View Slide

  35. 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

    View Slide

  36. 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

    View Slide

  37. 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

    View Slide

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

    View Slide

  39. We want your
    feedback!

    View Slide

  40. 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/

    View Slide

  41. w
    Thank you
    Gradle Summit 2017

    View Slide