On 20/03/2013, at 3:40 PM, Luke Daley <[email protected]> wrote:

> 
> 
> On 18/03/2013, at 18:39, Adam Murdoch <[email protected]> wrote:
> 
>> 
>> On 12/03/2013, at 3:50 AM, Peter Niederwieser <[email protected]> wrote:
>> 
>>> Hi all,
>>> 
>>> it recently came up in one of my code reviews that plugin classes should
>>> live under `org.gradle.api.$function.plugins`, task classes under
>>> `org.gradle.api.$function.tasks`, and all other classes under
>>> `org.gradle.api.$function`. I'd like to question the use of the `plugins`
>>> and `tasks` subpackages. In my mind, the function should be dominant, and
>>> the `plugins` and `tasks` subpackages are often just unnecessary baggage
>>> (may contain just a single class, require extra imports, etc.). They also
>>> encourage to group only/primarily by plugins and tasks, rather than by
>>> function, which I think is the wrong axis of separation. (For an example,
>>> see the `plugins` subproject.) Hence, I propose that only
>>> `org.gradle.api.$function` should be mandatory, with the further package
>>> structure (if any) left to the plugin author. (We'd probably still have to
>>> use `internal` for non-public packages.)
>>> 
>>> We already have a couple of plugin subprojects that don't have `plugins` and
>>> `tasks` subpackages, but I was asked to bring this up here for
>>> clarification.
>> 
>> In general, our plugins are made up of the following parts:
>> 
>> 1. The plugin implementation class.
>> 2. An extension and related classes.
>> 3. A bunch of model classes.
>> 4. A bunch of task implementation and related classes.
>> 5. Internal classes.
>> 
>> Except for #1, some or all of these may be empty or may contain a single 
>> class.
>> 
>> I've made a distinction between extension and model here. The model, if 
>> present, describes things (publications, native libraries, source sets, 
>> etc). The extension is just a bunch of settings (report directory, etc). 
>> Either may be attached to the project via the extensions container.
>> 
>> We need to come up with some mechanism to enforce the following constraints:
>> 
>> 1. The public API of model classes may refer only to other model classes. 
>> That is, the model must be independent of the how.
>> 2. The public API of task classes may refer only to model classes. That is, 
>> the tasks must be independent of wiring and must act only on the model.
>> 3. The public API of extension classes may refer only to model classes. That 
>> is, the plugin settings must be independent of the how.
>> 4. The public API of plugin classes may refer only to model, task or 
>> extension classes. That is, the plugin does the wiring.
>> 
>> The current plan to implement this is:
>> 
>> - model classes in org.gradle.${function}
>> - task classes in org.gradle.${function}.tasks
>> - plugin and extension classes in org.gradle.${function}.plugins
>> - internal stuff in org.gradle.${function}.internal
>> 
>> This is combined with the classycle package cycle check to do the 
>> enforcement. Note that ${function} may be a hierarchy, not just a single 
>> element.
>> 
>> There are other ways to implement this, of course. I'm open to suggestions 
>> (somewhat). I'm very keen that at least the following groups are separated:
>> 
>> - model classes
>> - plugin, extension, tasks classes
>> - internal classes
>> 
>> I'm not interested in leaving this unspecified and up to the plugin author.
> 
> This logical separation makes sense, no contest there. 
> 
> What are you hoping to achieve by forcing this separation? That is, what 
> features and automation capabilities?

Good question.

For the internal classes:
- I want to verify that no internal classes are statically reachable from the 
public classes.
- I want to exclude the internal classes from the documentation and some static 
analysis (as we do now).
- I want to improve our DSL layer so that it can report problems using public 
type names and methods/properties from the public types, rather than the 
internal types, and to link to the documentation for the type. We need to be 
able to inspect a type and determine which bits are public and which bits are 
internal.
- I want a dependency on the Gradle API to expose only the public classes at 
compilation time.

For the model classes:
- I want to verify that the model classes are independent of any plugin or 
task, so that they can be defined by different or multiple plugins, so that 
different task implementations can be used to do the work, and to decouple the 
plugins that consume the model from the plugins that define the model.

For the task classes:
- I want to verify that the task classes are independent of any plugin, so that 
they can be used without the (opinionated) plugins or by other plugins.

For the model/task/plugin/extension classes:
- I want to inspect these classes to generate the DSL reference. Different 
types have different things we'd need to inspect for.
- I want to inspect these classes to generate the IDE content assistance 
meta-data.
- I want it to be easy for a human to look at a type and be able to group it.
- I think we'll want to decorate these things differently and inject different 
stuff into them.

Some of this stuff we'll do at runtime, but some we'll need to do at build time.

We generally have 2 options for implementing the above stuff: we use a naming 
scheme to separate types into their roles, or we inspect the type's 
source/bytecode and determine its role. At the moment, we don't have any marker 
that allows us to distinguish between classes used in the model, classes used 
in the tasks, and classes used in the plugins. Obviously, we can tell that a 
given type is-a Plugin or is-a Task, but we can't group the associated classes, 
e.g. there's nothing on JavaCompileOptions that statically allows us to tell 
that it is a task class.

We could possibly implement this by traversing and inspecting the static 
dependency graph between types, so that, for example, a type that is reachable 
from a Task type cannot then depend on any Task or Plugin types.

I think, however, naming scheme is the simplest option - this might mean 
prefixing or suffixing types with their role, or separating roles into 
packages, or some combination.


> 
> If being able to physically separate these groups is a requirement, then 
> packages would be the cheapest way to do it.
> 
> 
>> 
>> For reference, here's what we currently use. It's all over the place:
>> 
>> - Announce:
>>      - org.gradle.api.announce (model, extension, plugin)
>> - Build announcements:
>>      - org.gradle.api.announce (plugin)
>> - Antlr:
>>      - org.gradle.api.plugins.antlr (model, plugin, task)
>> - Checkstyle/CodeNarc/FindBugs/Pmd/JDepend:
>>      - org.gradle.api.plugins.quality (extension, plugin, tasks)
>> - core:
>>      - org.gradle.api.tasks (tasks)
>> - Native binaries:
>>      - org.gradle.plugins.binaries (plugin)
>>      - org.gradle.plugins.binaries.model (model)
>> - C++:
>>      - org.gradle.plugins.cpp (plugins, extension, model)
>> - GCC:
>>      - org.gradle.plugins.cpp.gcc (plugin, model)
>> - Visual studio:
>>      - org.gradle.plugins.cpp.msvcpp (plugin)
>> - Eclipse CDT (C++)
>>      - org.gradle.plugins.cpp.cdt (plugin)
>>      - org.gradle.plugins.cpp.cdt.tasks (tasks)
>>      - org.gradle.plugins.cpp.cdt.model (model)
>> - Help:
>>      - org.gradle.api.plugins (plugin)
>>      - org.gradle.configuration (task)
>> - Project reports:
>>      - org.gradle.api.plugins (plugin, extension)
>>      - org.gradle.api.tasks.diagnostics (tasks)
>> - EAR:
>>      - org.gradle.plugins.ear (plugin, extension, task)
>>      - org.gradle.plugins.ear.descriptor (model)
>> - Eclipse:
>>      - org.gradle.plugins.ide.eclipse (plugin, tasks)
>>      - org.gradle.plugins.ide.eclipse.model (model) 
>> - IDEA:
>>      - org.gradle.plugins.ide.idea (plugin, tasks)
>>      - org.gradle.plugins.ide.model (model)
>> - Ivy-publish:
>>      - org.gradle.api.publish.ivy (model)
>>      - org.gradle.api.publish.ivy.plugins (plugin)
>>      - org.gradle.api.publish.ivy.tasks (tasks)
>> - Jetty:
>>      - org.gradle.api.plugins.jetty (plugin, extension, tasks)
>> - Maven (old):
>>      - org.gradle.api.plugins (plugins, extension)
>>      - org.gradle.api.artifacts.maven (model)
>> - Maven-publish:
>>      - org.gradle.api.publish.maven (model)
>>      - org.gradle.api.publish.maven.plugins (plugin)
>>      - org.gradle.api.publish.maven.tasks (tasks)
>> - Publish:
>>      - org.gradle.api.publish (model, extension)
>>      - org.gradle.api.publish.plugins (plugin)
>> - Pom conversion:
>>      - org.gradle.api.plugins.maven (plugin)
>> - OSGi:
>>      - org.gradle.api.plugins.osgi (plugin, extension, model)
>> - Distribution:
>>      - org.gradle.api.distribution (model)
>>      - org.gradle.api.distribution.plugins (plugin)
>> - Java library distribution:
>>      - org.gradle.api.plugins (plugin)
>> - Java:
>>      - org.gradle.api.java.archives (model)
>>      - org.gradle.api.tasks (model)
>>      - org.gradle.api.plugins (plugin, extension)
>>      - org.gradle.api.tasks.bundling (tasks)
>>      - org.gradle.api.tasks.compile (tasks)
>>      - org.gradle.api.tasks.javadoc (tasks)
>>      - org.gradle.external.javadoc (tasks)
>>      - org.gradle.api.tasks.testing (tasks)
>>      - org.gradle.api.tasks.testing.junit (tasks)
>>      - org.gradle.api.tasks.testing.testng (tasks)
>>      - org.gradle.api.tasks.testing.logging (tasks)
>> - Groovy:
>>      - org.gradle.api.plugins (plugin)
>>      - org.gradle.api.tasks.compile (tasks)
>>      - org.gradle.api.tasks.javadoc (tasks)
>> - Scala:
>>      - org.gradle.api.plugins.scala (plugin)
>>      - org.gradle.api.tasks (model)
>>      - org.gradle.api.tasks.scala (tasks)
>> - WAR:
>>      - org.gradle.api.plugins (plugin, extension)
>>      - org.gradle.api.tasks.bundling (tasks)
>> - Application:
>>      - org.gradle.api.plugins (plugin)
>>      - org.gradle.api.tasks.application (tasks)
>> - Reporting base:
>>      - org.gradle.api.plugins (plugin)
>>      - org.gradle.api.reporting (extension)
>> - Build dashboard:
>>      - org.gradle.api.reporting (model, task)
>>      - org.gradle.api.reporting.plugins (plugin)
>> - Signing:
>>      - org.gradle.plugins.signing (plugin, extension, model, tasks)
>>      - org.gradle.plugins.signing.signatory (model)
>>      - org.gradle.plugins.signing.type (model)
>>      - org.gradle.plugins.signing.type.pgp (model)
>> - Sonar (old):
>>      - org.gradle.api.plugins.sonar (plugin, tasks)
>>      - org.gradle.api.plugins.sonar.model (model)
>> - Sonar-runner:
>>      - org.gradle.api.sonar.runner (plugin, extension, tasks, model)
>> - Wrapper:
>>      - org.gradle.api.tasks.wrapper (tasks)
>> 
>> 
>> --
>> Adam Murdoch
>> Gradle Co-founder
>> http://www.gradle.org
>> VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
>> http://www.gradleware.com
>> 
>> Join us at the Gradle Summit 2013, June 13th and 14th in Santa Clara, CA: 
>> http://www.gradlesummit.com
>> 


--
Adam Murdoch
Gradle Co-founder
http://www.gradle.org
VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
http://www.gradleware.com

Join us at the Gradle Summit 2013, June 13th and 14th in Santa Clara, CA: 
http://www.gradlesummit.com

Reply via email to