Provide type-safe accessors for contributed extensions #159

Open
bamboo opened this Issue Oct 18, 2016 · 3 comments

Projects

None yet

3 participants

@bamboo
Member
bamboo commented Oct 18, 2016 edited
plugins {
    id("application")
}

distributions { // type-safe extension member automatically generated by Gradle Script Kotlin
   main {
      contents {
         from(createDocs) {
            into("docs")
         }
      }
   }
}
@bamboo bamboo added this to the 0.5.0 milestone Oct 18, 2016
@bamboo bamboo changed the title from Provide type-safe accessors to contributed extensions and conventions to Provide type-safe accessors for contributed extensions and conventions Oct 18, 2016
@oehme
Member
oehme commented Oct 18, 2016

I'm curious, how would we know when to create these extensions? The plugins add them at runtime, so does that mean we execute them in order to give auto completion?

@bamboo bamboo modified the milestone: 0.6.0, 0.5.0 Oct 21, 2016
@bamboo bamboo modified the milestone: 0.6.0, 1.0.0 Nov 24, 2016
@bamboo bamboo modified the milestone: 0.7.0, 1.0.0 Dec 15, 2016
@bamboo bamboo modified the milestone: 0.8.0, 0.7.0 Jan 6, 2017
@bamboo bamboo added the epic label Jan 18, 2017
@eskatos eskatos changed the title from Provide type-safe accessors for contributed extensions and conventions to Provide type-safe accessors for contributed extensions Jan 20, 2017
@eskatos
Member
eskatos commented Jan 20, 2017 edited

Gradle provide two distinct extension mechanisms: Extensions and Conventions.

Extensions allows to extend a type, e.g. Project, with "namespaced" extensions.
For example, the application plugin adds a DistributionContainer as a Project extension, named distributions that you can use as follows:

distributions {
    main {
       contents {
          from(someTask) { into("some/dir") }
       }
    }
}

Conventions allows to extend a type, e.g. Project, with "mixed in" extensions.
For example, the application plugin adds a ApplicationPluginConvention to Project that you ca use as follows:

mainClassName = "com.acme.Main"

At first we considered both but in the light of our findings, see below, this issue only considers Extensions.

https://discuss.gradle.org/t/whats-the-difference-between-plugin-conventions-vs-plugin-extensions/5523

Extensions and conventions are similar (but not identical) ways to dynamically extend the build model. Extensions are the newer concept and have largely replaced conventions. In short, only use extensions, don't use conventions.

An extension is an instance of an arbitrary (typically user-defined) class that's attached to the build model under a user-defined name. The extension class can define arbitrary methods. Assuming it is attached to a 'Project' object, an extension allows you to add 'project.foo.someMethod', but not 'project.someMethod'. Since each extension has its own namespace ('foo' in this case), the chance of name collisions is greatly reduced (compared to conventions).

https://discuss.gradle.org/t/is-the-new-plugin-extension-approach-the-way-to-go/6739

You should prefer the extension mechanism over the convention object mechanism. We will migrate the built-in plugins over time and new plugins will use the extension mechanism. The announce, ide, and c++ plugins all use extensions rather than convention objects.

However, the convention object mechanism is not going away any time soon. We will probably offer some migration mechanism, so that you can use both, and Gradle will warn the plugin users that they should use the new syntax for accessing the properties, rather than the new syntax.

We'll add some way to migrate from convention objects to extensions, so that a plugin can change to using an extension, but users don't have to change their build scripts right away.

This will probably be some method on ExtensionContainer to register the extension as both a convention object and an extension. Gradle will then write a deprecation warning whenever the build script (or some other plugin) attempts to use the object via the convention mechanism.

So, if I have:

apply plugin: 'my-plugin'

myPluginProperty = 'some-value'

and I change 'my-plugin', the above will continue to work, but you get a warning reminding you to change your build script to:

apply plugin: 'my-plugin'

myPlugin { myPluginProperty = 'some-value' }

In the meantime, if you have plugins which use the convention mechanism, it's quite fine to keep using it. The convention mechanism will be supported for a long time. The plan is something like this: 1. Both mechanism supported. Recommend people use extensions. 2. Provide a migration mechanism so that existing plugins can start migration 3. Migrate the built-in plugins 4. After a reasonable migration period, deprecate convention objects 5. After another reasonable migration period, remove convention objects.

We're still at step 1. You don't have to do anything until we finish step 2.

Supporting conventions has been extracted as #250.

@eskatos
Member
eskatos commented Jan 20, 2017

Back to extensions.

ExtensionContainer does not allow to list available types keyed by extension name.
ExtensionContainerInternal allows to get all extensions keyed by name, but from that we can't get the actual extension types as eachExtension.getClass() returns their "decorated" type.

ExtensionContainer needs to keep track of each extension registration type and expose that.

Moreover, extensions might have a public type and a different implementation type, possibly also an internal type. ExtensionContainer should allow its clients to register a "public type" for extensions.

See gradle/gradle#1220

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment