You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by pa...@apache.org on 2016/12/24 10:29:26 UTC

[14/19] zest-java git commit: build: detangle buildSrc

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseApprovedProjectsTask.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseApprovedProjectsTask.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseApprovedProjectsTask.groovy
new file mode 100644
index 0000000..e95e405
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseApprovedProjectsTask.groovy
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.release
+
+import groovy.json.JsonBuilder
+import groovy.transform.CompileStatic
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+
+/**
+ * Write paths of release approved projects to a JSON file.
+ *
+ * This task sole purpose is proper up-do-date behaviour when changing {@literal dev-status.xml} files.
+ */
+@CompileStatic
+class ReleaseApprovedProjectsTask extends DefaultTask
+{
+  ReleaseApprovedProjectsTask()
+  {
+    description = 'Apply release specification to projects in the build'
+  }
+
+  @InputFiles
+  FileCollection getDevStatusFiles()
+  {
+    return project.files( project.allprojects
+                                 .collect( { project -> project.file( 'dev-status.xml' ) } )
+                                 .findAll( { it.exists() } ) )
+  }
+
+  @OutputFile
+  File getJsonApprovedProjects()
+  {
+    return new File( new File( project.buildDir, 'release' ), 'approved-projects.json' )
+  }
+
+  @TaskAction
+  void approveProjects()
+  {
+    def releaseSpec = project.extensions.getByType( ReleaseSpecExtension )
+    jsonApprovedProjects.parentFile.mkdirs()
+    jsonApprovedProjects.text = new JsonBuilder( releaseSpec.approvedProjects.collect( { it.path } ) ).toPrettyString()
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleasePlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleasePlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleasePlugin.groovy
new file mode 100644
index 0000000..b36a967
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleasePlugin.groovy
@@ -0,0 +1,277 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.release
+
+import groovy.transform.CompileStatic
+import org.apache.http.client.methods.CloseableHttpResponse
+import org.apache.http.client.methods.HttpPost
+import org.apache.http.entity.ContentType
+import org.apache.http.entity.mime.MultipartEntityBuilder
+import org.apache.http.impl.client.CloseableHttpClient
+import org.apache.http.impl.client.HttpClients
+import org.apache.polygene.gradle.BasePlugin
+import org.apache.polygene.gradle.TaskGroups
+import org.apache.polygene.gradle.structure.distributions.DistributionsPlugin
+import org.apache.polygene.gradle.structure.manual.ManualPlugin
+import org.apache.polygene.gradle.structure.reports.ReportsPlugin
+import org.gradle.api.GradleException
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.execution.TaskExecutionGraph
+import org.gradle.api.file.CopySpec
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.bundling.Zip
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+
+@CompileStatic
+class ReleasePlugin implements Plugin<Project>
+{
+  static class TaskNames
+  {
+    static final String RELEASE_ASF = 'releaseAsf'
+    static final String PUBLISH_ASF_MAVEN = 'publishAsfMavenArtifacts'
+    private static final String PREPARE_ASF_MAVEN = 'prepareAsfMavenBundle'
+    private static final String UPLOAD_ASF_MAVEN = 'uploadAsfMavenBundle'
+    private static final String CLOSE_ASF_MAVEN = 'closeAsfMavenRepository'
+    private static final String CHECK_ASF_MAVEN = 'checkAsfMavenArtifacts'
+    private static final String PROMOTE_ASF_MAVEN = 'promoteAsfMavenRepository'
+    static final String PUBLISH_ASF_DIST = 'publishAsfDistributions'
+    private static final String CHECKOUT_ASF_DIST = 'checkoutAsfDistributions'
+    private static final String COPY_ASF_DIST = 'copyAsfDistributions'
+    private static final String COMMIT_ASF_DIST = 'commitAsfDistributions'
+    static final String PUBLISH_ASF_DOC = 'publishAsfDocumentation'
+    private static final String CHECKOUT_ASF_DOC = 'checkoutAsfDocumentation'
+    private static final String COPY_ASF_DOC = 'copyAsfDocumentation'
+    private static final String COPY_ASF_DOC_LATEST = 'copyAsfDocumentationAsLatest'
+    private static final String COMMIT_ASF_DOC = 'commitAsfDocumentation'
+  }
+
+  @Override
+  void apply( final Project project )
+  {
+    project.plugins.apply BasePlugin
+    project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
+      def check = taskGraph.allTasks.any { task -> task.name.contains( 'Asf' ) }
+      if( check )
+      {
+        checkAsfPreconditions( project )
+      }
+    }
+    applyAsfRelease project
+  }
+
+  static void checkAsfPreconditions( Project project )
+  {
+    def releaseSpec = project.extensions.getByType ReleaseSpecExtension
+    if( releaseSpec.developmentVersion )
+    {
+      throw new InvalidUserDataException(
+        'Development version is unreleasable, please clean and retry with a -Dversion=' )
+    }
+    def polygeneWeb = new File( project.rootProject.projectDir.parentFile, 'polygene-web' )
+    if( !polygeneWeb.exists() )
+    {
+      throw new InvalidUserDataException(
+        'To perform ASF releases you need to checkout the SVN web site directory under ../polygene-web' )
+    }
+    def polygeneDist = new File( project.rootProject.projectDir.parentFile, 'polygene-dist' )
+    if( !polygeneDist.exists() )
+    {
+      throw new InvalidUserDataException(
+        'To perform ASF releases you need to checkout the SVN dist directory under ../polygene-dist' )
+    }
+    // TODO Check Nexus credentials availability
+    // TODO Check svn command line availability
+  }
+
+  static void applyAsfRelease( Project project )
+  {
+    Task releaseTask = project.tasks.create( TaskNames.RELEASE_ASF ) { Task task ->
+      task.group = TaskGroups.RELEASE
+      task.description = 'Rolls out an Apache Software Foundation release.'
+    }
+    def subTasks = [
+      applyPublishAsfMavenArtifacts( project ),
+      applyPublishAsfDistributions( project ),
+      applyPublishAsfDocumentation( project )
+    ]
+    // Two upload strategies for now
+    if( project.findProperty( 'useMavenBundle' ) )
+    {
+      // Use maven artifact bundle
+      releaseTask.dependsOn subTasks
+    }
+    else
+    {
+      // Use :**:uploadArchives for now
+      // TODO Remove this once the bundle strategy is done
+      def releaseSpec = project.extensions.getByType ReleaseSpecExtension
+      releaseSpec.publishedProjects.each { p ->
+        releaseTask.dependsOn "${ p.path }:uploadArchives"
+      }
+      releaseTask.dependsOn subTasks.drop( 1 )
+    }
+  }
+
+  static Task applyPublishAsfMavenArtifacts( Project project )
+  {
+    def releaseSpec = project.extensions.getByType ReleaseSpecExtension
+    def distributions = project.rootProject.project ':distributions'
+    def prepare = project.tasks.create( TaskNames.PREPARE_ASF_MAVEN, Zip ) { Zip task ->
+      // TODO Consume distributions through configurations
+      task.dependsOn "${ distributions.path }:${ DistributionsPlugin.TaskNames.STAGE_MAVEN_BINARIES }"
+      task.from "${ distributions.buildDir }/stage/maven-binaries"
+      task.into '.'
+      task.destinationDir = project.file( "$project.buildDir/asf/maven" )
+      task.baseName = "apache-polygene-${ project.version }-maven-artifacts"
+      task.exclude '**/maven-metadata*.*'
+    }
+    def upload = project.tasks.create( TaskNames.UPLOAD_ASF_MAVEN ) { Task task ->
+      task.dependsOn prepare
+      // ASF Nexus instance is a Nexus Pro so has the Unpack Plugin that allow uploading
+      // artifacts "en masse" as a ZIP file
+      // TODO Ensure that we have the 'Unpack' privilege in ASF Nexus
+      // Looks like we can upload 'Artifact Bundles' using Nexus UI
+      task.doLast {
+        def uploadUrl = releaseSpec.releaseVersion ?
+                        'https://repository.apache.org/service/local/staging/deploy/maven2' :
+                        'https://repository.apache.org/content/repositories/snapshots'
+        CloseableHttpClient httpClient = HttpClients.createDefault()
+        try
+        {
+          // TODO Add Nexus Authentication
+          HttpPost post = new HttpPost( "$uploadUrl/content-compressed" )
+          MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+          builder.addBinaryBody( 'fieldname',
+                                 prepare.archivePath,
+                                 ContentType.APPLICATION_OCTET_STREAM,
+                                 prepare.archivePath.getName() )
+          post.setEntity( builder.build() )
+          CloseableHttpResponse response = httpClient.execute( post )
+          if( response.statusLine.statusCode != 200 )
+          {
+            throw new GradleException( "Unable to upload maven artifacts to ASF Nexus, got ${ response.statusLine }" )
+          }
+        }
+        finally
+        {
+          httpClient.close()
+        }
+      }
+    }
+    def close = project.tasks.create( TaskNames.CLOSE_ASF_MAVEN ) { Task task ->
+      task.mustRunAfter upload
+      // TODO Close Nexus repository
+      task.enabled = false
+    }
+    def check = project.tasks.create( TaskNames.CHECK_ASF_MAVEN ) { Task task ->
+      task.mustRunAfter close
+      // TODO Run tests against binaries from Nexus staged repository
+      task.enabled = false
+    }
+    def promote = project.tasks.create( TaskNames.PROMOTE_ASF_MAVEN ) { Task task ->
+      task.mustRunAfter check
+      // TODO Promote Nexus repository
+      task.enabled = false
+    }
+    def publish = project.tasks.create( TaskNames.PUBLISH_ASF_MAVEN ) { Task task ->
+      task.group = TaskGroups.RELEASE
+      task.description = 'Publishes maven artifacts to ASF Nexus.'
+      task.dependsOn upload, close, check, promote
+    }
+    return publish
+  }
+
+  static Task applyPublishAsfDistributions( Project project )
+  {
+    def distributions = project.rootProject.project ':distributions'
+    def checkout = project.tasks.create( TaskNames.CHECKOUT_ASF_DIST ) { Task task ->
+      // TODO SVN checkout ASF distribution directory
+      task.enabled = false
+    }
+    def copy = project.tasks.create( TaskNames.COPY_ASF_DIST, Copy ) { Copy task ->
+      task.mustRunAfter checkout
+      // TODO Consume distributions through configurations
+      task.dependsOn "${ distributions.path }:${ LifecycleBasePlugin.ASSEMBLE_TASK_NAME }"
+      task.from new File( distributions.buildDir, 'distributions' )
+      task.into new File( project.rootProject.projectDir.parentFile, 'polygene-dist' )
+    }
+    def commit = project.tasks.create( TaskNames.COMMIT_ASF_DIST ) { Task task ->
+      task.mustRunAfter copy
+      // TODO SVN commit ASF distribution directory
+      task.enabled = false
+    }
+    def publish = project.tasks.create( TaskNames.PUBLISH_ASF_DIST ) { Task task ->
+      task.group = TaskGroups.RELEASE
+      task.description = 'Publishes distributions to ASF SVN.'
+      task.dependsOn checkout, copy, commit
+    }
+    // TODO SVN Upload DISTRIBUTIONS using svn command line so credentials are handled outside of the build
+    return publish
+  }
+
+  static Task applyPublishAsfDocumentation( Project project )
+  {
+    def releaseSpec = project.extensions.getByType ReleaseSpecExtension
+    def manual = project.rootProject.project ':manual'
+    def reports = project.rootProject.project ':reports'
+    def checkout = project.tasks.create( TaskNames.CHECKOUT_ASF_DOC ) { Task task ->
+      // TODO SVN checkout ASF distribution directory
+      task.enabled = false
+    }
+    def copy = project.tasks.create( TaskNames.COPY_ASF_DOC, Copy ) { Copy task ->
+      task.mustRunAfter checkout
+      // TODO Consume documentation and reports through configurations
+      task.dependsOn "${ manual.path }:${ ManualPlugin.TaskNames.WEBSITE }"
+      task.dependsOn "${ reports.path }:${ ReportsPlugin.TaskNames.JAVADOCS }"
+      def webRoot = new File( project.rootProject.projectDir.parentFile, 'polygene-web' )
+      def dirName = releaseSpec.releaseVersion ? project.version : 'develop'
+      task.destinationDir = webRoot
+      task.from( new File( manual.buildDir, 'docs/website' ) ) { CopySpec spec ->
+        spec.into "site/content/java/$dirName"
+      }
+      task.from( new File( reports.buildDir, 'docs/javadocs' ) ) { CopySpec spec ->
+        spec.into "site/content/java/$dirName/javadocs"
+      }
+    }
+    project.tasks.create( TaskNames.COPY_ASF_DOC_LATEST, Copy ) { Copy task ->
+      def webRoot = new File( project.rootProject.projectDir.parentFile, 'polygene-web' )
+      task.from new File( webRoot, "site/content/java/$project.version" )
+      task.into new File( webRoot, "site/content/java/latest" )
+      task.doFirst {
+        if( !releaseSpec.releaseVersion )
+        {
+          throw new InvalidUserDataException( 'Development version cannot be `latest`.' )
+        }
+      }
+    }
+    def commit = project.tasks.create( TaskNames.COMMIT_ASF_DOC ) { Task task ->
+      task.mustRunAfter copy
+      // TODO SVN commit ASF documentation directory
+      task.enabled = false
+    }
+    def publish = project.tasks.create( TaskNames.PUBLISH_ASF_DOC ) { Task task ->
+      task.group = TaskGroups.RELEASE
+      task.description = 'Publishes documentation to ASF HTTP.'
+      task.dependsOn checkout, copy, commit
+    }
+    return publish
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecExtension.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecExtension.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecExtension.groovy
new file mode 100644
index 0000000..5051400
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecExtension.groovy
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.release
+
+import groovy.transform.CompileStatic
+import org.gradle.api.Project
+
+/**
+ * Provide release approved projects.
+ *
+ * There's no up-to-date checking on Gradle extensions.
+ * Depend on {@link ReleaseApprovedProjectsTask} to get a good up-to-date behavior.
+ */
+@CompileStatic
+class ReleaseSpecExtension
+{
+  static final String NAME = 'releaseSpec'
+
+  private final Project project
+
+  final Set<Project> approvedProjects
+  final Set<Project> unapprovedProjects
+  final Set<Project> publishedProjects
+
+  ReleaseSpecExtension( Project project )
+  {
+    this.project = project
+    def spec = new ModuleReleaseSpec()
+    def candidateProjects = project.allprojects.findAll { p -> p.file( 'dev-status.xml' ).exists() }
+    approvedProjects = candidateProjects.findAll { p -> spec.satisfiedBy p }
+    unapprovedProjects = candidateProjects.minus approvedProjects
+    publishedProjects = releaseVersion ? approvedProjects : candidateProjects
+  }
+
+  boolean isDevelopmentVersion()
+  {
+    return project.version == '0'
+  }
+
+  boolean isSnapshotVersion()
+  {
+    return project.version.toString().contains( 'SNAPSHOT' )
+  }
+
+  boolean isReleaseVersion()
+  {
+    return !snapshotVersion && !developmentVersion
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecPlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecPlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecPlugin.groovy
new file mode 100644
index 0000000..fd2d35a
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecPlugin.groovy
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.release
+
+import groovy.transform.CompileStatic
+import org.apache.polygene.gradle.TaskGroups
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+
+@CompileStatic
+class ReleaseSpecPlugin implements Plugin<Project>
+{
+  static class TaskNames
+  {
+    static final String RELEASE_APPROVED_PROJECTS = 'releaseSpecApprovedProjects'
+    static final String REPORT_RELEASE_SPEC = 'reportReleaseSpec'
+    static final String CHECK_RELEASE_SPEC = 'checkReleaseSpec'
+  }
+
+  @Override
+  void apply( final Project project )
+  {
+    applyReleaseSpecExtension project
+    if( project == project.rootProject )
+    {
+      configureReleaseSpecTasks project
+    }
+  }
+
+  private static void applyReleaseSpecExtension( Project project )
+  {
+    project.extensions.create( ReleaseSpecExtension.NAME, ReleaseSpecExtension, project.rootProject )
+  }
+
+  private static void configureReleaseSpecTasks( Project project )
+  {
+    project.tasks.create( TaskNames.RELEASE_APPROVED_PROJECTS, ReleaseApprovedProjectsTask ) { Task task ->
+      task.group = TaskGroups.RELEASE
+    }
+    project.tasks.create( TaskNames.REPORT_RELEASE_SPEC, ReleaseSpecReportTask ) { Task task ->
+      task.group = TaskGroups.RELEASE
+    }
+    project.tasks.create( TaskNames.CHECK_RELEASE_SPEC, CheckReleaseSpecTask ) { Task task ->
+      task.group = TaskGroups.RELEASE_VERIFICATION
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecReportTask.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecReportTask.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecReportTask.groovy
new file mode 100644
index 0000000..3ecd8f5
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/release/ReleaseSpecReportTask.groovy
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.release
+
+import groovy.transform.CompileStatic
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.TaskAction
+
+@CompileStatic
+class ReleaseSpecReportTask extends DefaultTask
+{
+  ReleaseSpecReportTask()
+  {
+    description = 'Report module(s) that do or don\'t fit the release criteria.'
+    dependsOn {
+      project.extensions.getByType( ReleaseSpecExtension ).approvedProjects
+             .collect { each -> each.configurations.getByName( 'runtime' ) }
+    }
+  }
+
+  @TaskAction
+  void report()
+  {
+    def releaseSpec = project.extensions.getByType( ReleaseSpecExtension )
+    println 'Approved Projects'
+    releaseSpec.approvedProjects.each { println "\t${ it.path }" }
+    println 'NOT Approved Projects'
+    releaseSpec.unapprovedProjects.each { println "\t${ it.path }" }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/AggregatedJacocoReportTask.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/AggregatedJacocoReportTask.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/AggregatedJacocoReportTask.groovy
new file mode 100644
index 0000000..155853d
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/AggregatedJacocoReportTask.groovy
@@ -0,0 +1,99 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.reports
+
+import groovy.transform.CompileStatic
+import groovy.transform.TypeCheckingMode
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+
+@CompileStatic
+class AggregatedJacocoReportTask extends DefaultTask
+{
+  public static final String JACOCO_CONFIGURATION = 'jacoco'
+
+  @InputFiles
+  FileCollection getJacocoExecDataDirectories()
+  {
+    return project.files( project.subprojects.collect( { Project p -> "${ p.buildDir.absolutePath }/jacoco" } ) )
+  }
+
+  @OutputDirectory
+  File getOutputDirectory()
+  {
+    return project.file( "$project.buildDir/reports/coverage" )
+  }
+
+  @CompileStatic( TypeCheckingMode.SKIP )
+  @TaskAction
+  void report()
+  {
+    def coveredProjects = project.rootProject.subprojects.findAll { p -> new File( p.buildDir, 'jacoco' ).exists() }
+    def coreProjects = coveredProjects.findAll { p -> p.path.startsWith ':core' }
+    def libProjects = coveredProjects.findAll { p -> p.path.startsWith ':libraries' }
+    def extProjects = coveredProjects.findAll { p -> p.path.startsWith ':extensions' }
+    def toolsProjects = coveredProjects.findAll { p -> p.path.startsWith ':tools' }
+    def tutoProjects = coveredProjects.findAll { p -> p.path.startsWith ':tutorials' }
+    def samplesProjects = coveredProjects.findAll { p -> p.path.startsWith ':samples' }
+    def classpath = project.configurations.getByName( JACOCO_CONFIGURATION ).asPath
+    project.ant {
+      taskdef name: 'jacocoreport', classname: 'org.jacoco.ant.ReportTask', classpath: classpath
+      mkdir dir: outputDirectory
+      jacocoreport {
+        executiondata {
+          coveredProjects.collect { p ->
+            fileset( dir: "${ p.buildDir.path }/jacoco" ) { include name: '*.exec' }
+          }
+        }
+        structure( name: 'Apache Polygene\u2122 (Java Edition) SDK' ) {
+          group( name: 'Core' ) {
+            classfiles { coreProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
+            sourcefiles { coreProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
+          }
+          group( name: 'Libraries' ) {
+            classfiles { libProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
+            sourcefiles { libProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
+          }
+          group( name: 'Extensions' ) {
+            classfiles { extProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
+            sourcefiles { extProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
+          }
+          group( name: 'Tools' ) {
+            classfiles { toolsProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
+            sourcefiles { toolsProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
+          }
+          group( name: 'Tutorials' ) {
+            classfiles { tutoProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
+            sourcefiles { tutoProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
+          }
+          group( name: 'Samples' ) {
+            classfiles { samplesProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
+            sourcefiles { samplesProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
+          }
+        }
+        csv destfile: "${ outputDirectory }/jacoco.csv", encoding: 'UTF-8'
+        xml destfile: "${ outputDirectory }/jacoco.xml", encoding: 'UTF-8'
+        html destdir: outputDirectory, encoding: 'UTF-8', locale: 'en', footer: 'Apache Polygene\u2122 (Java Edition) SDK'
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/ReportsPlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/ReportsPlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/ReportsPlugin.groovy
new file mode 100644
index 0000000..0452d04
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/reports/ReportsPlugin.groovy
@@ -0,0 +1,152 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.reports
+
+import groovy.transform.CompileStatic
+import org.apache.polygene.gradle.BasePlugin
+import org.apache.polygene.gradle.TaskGroups
+import org.apache.polygene.gradle.code.CodePlugin
+import org.apache.polygene.gradle.code.PublishedCodePlugin
+import org.apache.polygene.gradle.dependencies.DependenciesDeclarationExtension
+import org.apache.polygene.gradle.dependencies.DependenciesPlugin
+import org.apache.polygene.gradle.structure.RootPlugin
+import org.apache.polygene.gradle.structure.release.ReleaseSpecExtension
+import org.apache.polygene.gradle.structure.release.ReleaseSpecPlugin
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.plugins.JavaPluginConvention
+import org.gradle.api.plugins.ReportingBasePlugin
+import org.gradle.api.tasks.javadoc.Javadoc
+import org.gradle.api.tasks.testing.TestReport
+import org.gradle.external.javadoc.StandardJavadocDocletOptions
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+
+// TODO Expose project output into configurations
+@CompileStatic
+class ReportsPlugin implements Plugin<Project>
+{
+  static class TaskNames
+  {
+    static final String COVERAGE = 'coverage'
+    static final String TEST = 'test'
+    static final String JAVADOCS = 'javadocs'
+  }
+
+  @Override
+  void apply( Project project )
+  {
+    project.plugins.apply BasePlugin
+    project.plugins.apply org.gradle.api.plugins.BasePlugin
+    project.plugins.apply ReportingBasePlugin
+    project.plugins.apply DependenciesPlugin
+    applyTest project
+    applyCoverage project
+    applyJavadocs project
+    PublishedCodePlugin.configureJavadoc project
+  }
+
+  private static void applyTest( Project project )
+  {
+    def task = project.tasks.create( TaskNames.TEST, TestReport ) { TestReport task ->
+      task.group = TaskGroups.VERIFICATION
+      task.description = 'Generates global test report'
+      task.destinationDir = project.file( "$project.buildDir/reports/tests" )
+      task.reportOn {
+        project.rootProject.subprojects
+               .findAll { p -> p.plugins.hasPlugin CodePlugin }
+               .collect { it.tasks.getByName( 'test' ) }
+      }
+    }
+    project.tasks.getByName( LifecycleBasePlugin.CHECK_TASK_NAME ).dependsOn task
+  }
+
+  private static void applyCoverage( Project project )
+  {
+    def dependencies = project.rootProject.extensions.getByType DependenciesDeclarationExtension
+    project.configurations.create AggregatedJacocoReportTask.JACOCO_CONFIGURATION
+    project.dependencies.add AggregatedJacocoReportTask.JACOCO_CONFIGURATION,
+                             "org.jacoco:org.jacoco.ant:${ dependencies.buildToolsVersions.jacoco }"
+    def task = project.tasks.create( TaskNames.COVERAGE, AggregatedJacocoReportTask )
+      { AggregatedJacocoReportTask task ->
+        task.group = TaskGroups.VERIFICATION
+        task.description = 'Generates global coverage report'
+        task.dependsOn {
+          project.rootProject.subprojects
+                 .findAll { p -> p.plugins.hasPlugin CodePlugin }
+                 .collect( { p -> p.tasks.getByName JavaPlugin.TEST_TASK_NAME } )
+        }
+      }
+    project.tasks.getByName( LifecycleBasePlugin.CHECK_TASK_NAME ).dependsOn task
+  }
+
+  private static void applyJavadocs( Project project )
+  {
+    def releaseSpec = project.extensions.getByType ReleaseSpecExtension
+    def javadocsTask = project.tasks.create( TaskNames.JAVADOCS, Javadoc ) { Javadoc task ->
+      task.onlyIf { !releaseSpec.developmentVersion }
+      task.group = TaskGroups.DOCUMENTATION
+      task.description = 'Builds the whole SDK public Javadoc'
+      task.dependsOn { project.rootProject.tasks.getByName ReleaseSpecPlugin.TaskNames.RELEASE_APPROVED_PROJECTS }
+      task.destinationDir = project.file "$project.buildDir/docs/javadocs"
+      def options = task.options as StandardJavadocDocletOptions
+      options.docFilesSubDirs = true
+      options.encoding = 'UTF-8'
+      options.overview = "${ project.projectDir }/src/javadoc/overview.html"
+      task.title = "${ RootPlugin.PROJECT_TITLE } ${ project.version }"
+      options.group( [
+        "Core API"      : [ "org.apache.polygene.api",
+                            "org.apache.polygene.api.*" ],
+        "Core Bootstrap": [ "org.apache.polygene.bootstrap",
+                            "org.apache.polygene.bootstrap.*" ],
+        "Core SPI"      : [ "org.apache.polygene.spi",
+                            "org.apache.polygene.spi.*" ],
+        "Libraries"     : [ "org.apache.polygene.library.*" ],
+        "Extensions"    : [ "org.apache.polygene.valueserialization.*",
+                            "org.apache.polygene.entitystore.*",
+                            "org.apache.polygene.index.*",
+                            "org.apache.polygene.metrics.*",
+                            "org.apache.polygene.cache.*",
+                            "org.apache.polygene.migration",
+                            "org.apache.polygene.migration.*" ],
+        "Tools"         : [ "org.apache.polygene.tools.*",
+                            "org.apache.polygene.envisage",
+                            "org.apache.polygene.envisage.*" ],
+        "Test Support"  : [ "org.apache.polygene.test",
+                            "org.apache.polygene.test.*" ]
+      ] )
+    }
+    project.tasks.getByName( LifecycleBasePlugin.CHECK_TASK_NAME ).dependsOn javadocsTask
+    project.tasks.withType( Javadoc ) { Javadoc task ->
+      def apiSources = releaseSpec.publishedProjects.findAll { approved ->
+        ( approved.path.startsWith( ':core' ) && !approved.path.startsWith( ':core:runtime' ) ) ||
+        approved.path.startsWith( ':libraries' ) ||
+        approved.path.startsWith( ':extensions' ) ||
+        approved.path.startsWith( ':tools' )
+      }
+      apiSources.each { Project apiProject ->
+        apiProject.afterEvaluate { Project evaluatedApiProject ->
+          def mainSourceSet = evaluatedApiProject.convention.getPlugin( JavaPluginConvention )
+                                                 .sourceSets.getByName( 'main' )
+          task.source mainSourceSet.allJava
+          task.classpath += mainSourceSet.compileClasspath
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/samples/SamplePlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/samples/SamplePlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/samples/SamplePlugin.groovy
new file mode 100644
index 0000000..6970463
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/samples/SamplePlugin.groovy
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.samples
+
+import groovy.transform.CompileStatic
+import org.apache.polygene.gradle.code.CodePlugin
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+@CompileStatic
+class SamplePlugin implements Plugin<Project>
+{
+  @Override
+  void apply( Project project )
+  {
+    project.plugins.apply CodePlugin
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/PerformanceTestsPlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/PerformanceTestsPlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/PerformanceTestsPlugin.groovy
new file mode 100644
index 0000000..a1890d8
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/PerformanceTestsPlugin.groovy
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.tests
+
+import groovy.transform.CompileStatic
+import org.apache.polygene.gradle.TaskGroups
+import org.gradle.api.Action
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPluginConvention
+import org.gradle.api.tasks.bundling.Jar
+import org.gradle.api.tasks.testing.Test
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+
+// TODO Add profiling tasks (jfr or honest? flamegraphs?)
+// TODO Add simple regression assertions, how? testing against a previous version?
+@CompileStatic
+class PerformanceTestsPlugin implements Plugin<Project>
+{
+  static class TaskNames
+  {
+    static final String PERFORMANCE_TEST = 'performanceTest'
+    static final String PERFORMANCE_PROFILE = 'performanceProfile'
+    static final String PERFORMANCE_CHECK = 'performanceCheck'
+  }
+
+  @Override
+  void apply( final Project project )
+  {
+    def sourceSets = project.convention.getPlugin( JavaPluginConvention ).sourceSets
+    sourceSets.create 'perf'
+    project.dependencies.add 'perfCompile', sourceSets.getByName( 'main' ).output
+    project.dependencies.add 'perfCompile', sourceSets.getByName( 'test' ).output
+    project.dependencies.add 'perfCompile', project.configurations.getByName( 'testCompile' )
+    project.dependencies.add 'perfRuntime', project.configurations.getByName( 'testRuntime' )
+    project.tasks.getByName( LifecycleBasePlugin.CHECK_TASK_NAME ).dependsOn 'compilePerfJava'
+    project.tasks.create( TaskNames.PERFORMANCE_TEST, Test, { Test task ->
+      task.group = TaskGroups.PERFORMANCE
+      task.description = 'Runs performance tests.'
+      task.maxParallelForks = 1
+      task.forkEvery = 1L
+      task.testClassesDir = sourceSets.getByName( 'perf' ).output.classesDir
+      task.classpath = sourceSets.getByName( 'perf' ).runtimeClasspath
+      task.systemProperty 'jar.path', ( project.tasks.getByName( 'jar' ) as Jar ).archivePath
+    } as Action<Test> )
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/TestPlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/TestPlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/TestPlugin.groovy
new file mode 100644
index 0000000..5f91dfe
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tests/TestPlugin.groovy
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.tests
+
+import groovy.transform.CompileStatic
+import org.apache.polygene.gradle.code.CodePlugin
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+@CompileStatic
+class TestPlugin implements Plugin<Project>
+{
+  @Override
+  void apply( Project project )
+  {
+    project.plugins.apply CodePlugin
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tools/ToolPlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tools/ToolPlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tools/ToolPlugin.groovy
new file mode 100644
index 0000000..a57a129
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tools/ToolPlugin.groovy
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.tools
+
+import org.apache.polygene.gradle.code.PublishedCodePlugin
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+class ToolPlugin implements Plugin<Project>
+{
+  @Override
+  void apply( Project project )
+  {
+    project.plugins.apply PublishedCodePlugin
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tutorials/TutorialPlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tutorials/TutorialPlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tutorials/TutorialPlugin.groovy
new file mode 100644
index 0000000..a831fd8
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/structure/tutorials/TutorialPlugin.groovy
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.structure.tutorials
+
+import groovy.transform.CompileStatic
+import org.apache.polygene.gradle.code.CodePlugin
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+@CompileStatic
+class TutorialPlugin implements Plugin<Project>
+{
+  @Override
+  void apply( Project project )
+  {
+    project.plugins.apply CodePlugin
+  }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/tasks/ExecLogged.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/tasks/ExecLogged.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/tasks/ExecLogged.groovy
index 01b4e71..b3ba57d 100644
--- a/buildSrc/src/main/groovy/org/apache/polygene/gradle/tasks/ExecLogged.groovy
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/tasks/ExecLogged.groovy
@@ -18,6 +18,8 @@
 package org.apache.polygene.gradle.tasks
 
 import groovy.transform.CompileStatic
+import java.nio.file.Files
+import java.util.function.BiConsumer
 import org.gradle.api.Action
 import org.gradle.api.GradleException
 import org.gradle.api.Project
@@ -27,14 +29,19 @@ import org.gradle.api.tasks.TaskAction
 import org.gradle.internal.logging.ConsoleRenderer
 import org.gradle.process.ExecSpec
 
+/**
+ * Execute command and log standard output and error to files.
+ *
+ * See the {@literal stdoutFile} and {@literal stderrFile} properties.
+ */
 @CompileStatic
 class ExecLogged extends AbstractExecTask<ExecLogged>
 {
   @OutputFile
-  File stdoutFile = project.file( "$project.buildDir/tmp/${ getName() }/stdout.log" )
+  File stdoutFile = project.file "$project.buildDir/log/${ getName() }/${ getName() }-stdout.log"
 
   @OutputFile
-  File stderrFile = project.file( "$project.buildDir/tmp/${ getName() }/stderr.log" )
+  File stderrFile = project.file "$project.buildDir/log/${ getName() }/${ getName() }-stderr.log"
 
   ExecLogged()
   {
@@ -44,36 +51,51 @@ class ExecLogged extends AbstractExecTask<ExecLogged>
   @TaskAction
   protected void exec()
   {
-    [ stdoutFile, stderrFile ].each { it.parentFile.mkdirs() }
-    def outStream = stdoutFile.newOutputStream()
-    def errStream = stderrFile.newOutputStream()
-    try
-    {
+    doExecLogged stdoutFile, stderrFile, { OutputStream outStream, OutputStream errStream ->
+      standardOutput = outStream
+      errorOutput = errStream
       super.exec()
     }
-    catch( Exception ex )
-    {
-      throw new GradleException( errorMessage( ex, stdoutFile, stderrFile ), ex )
-    }
-    finally
-    {
-      close outStream, errStream
-    }
   }
 
+  /**
+   * Execute command and log standard output and error to files.
+   *
+   * @param project Project reference
+   * @param stdoutFile Standard output log file
+   * @param stderrFile Standard error log file
+   * @param specAction Execution specification
+   */
   static void execLogged( Project project, File stdoutFile, File stderrFile, Action<? super ExecSpec> specAction )
   {
-    [ stdoutFile, stderrFile ].each { it.parentFile.mkdirs() }
-    def outStream = stdoutFile.newOutputStream()
-    def errStream = stderrFile.newOutputStream()
-    try
-    {
+    doExecLogged stdoutFile, stderrFile, { OutputStream outStream, OutputStream errStream ->
       project.exec { ExecSpec spec ->
-        specAction.execute( spec )
         spec.standardOutput = outStream
         spec.errorOutput = errStream
+        specAction.execute spec
       }
     }
+  }
+
+  private static void doExecLogged( File stdoutFile, File stderrFile,
+                                    BiConsumer<OutputStream, OutputStream> exec )
+  {
+    [ stdoutFile, stderrFile ].each { Files.createDirectories( it.parentFile.toPath() ) }
+    def outStream, errStream
+    if( stdoutFile.absolutePath == stderrFile.absolutePath )
+    {
+      outStream = stdoutFile.newOutputStream()
+      errStream = outStream
+    }
+    else
+    {
+      outStream = stdoutFile.newOutputStream()
+      errStream = stderrFile.newOutputStream()
+    }
+    try
+    {
+      exec.accept( outStream, errStream )
+    }
     catch( Exception ex )
     {
       throw new GradleException( errorMessage( ex, stdoutFile, stderrFile ), ex )
@@ -88,8 +110,7 @@ class ExecLogged extends AbstractExecTask<ExecLogged>
     throws IOException
   {
     def errors = [ ] as List<IOException>
-    for( Closeable closeable : closeables )
-    {
+    closeables.each { Closeable closeable ->
       try
       {
         closeable.close()
@@ -101,7 +122,7 @@ class ExecLogged extends AbstractExecTask<ExecLogged>
     }
     if( !errors.empty )
     {
-      def ex = new IOException( 'Failed to close some' )
+      def ex = new IOException( 'Failed to close some stream(s)' )
       errors.each { ex.addSuppressed it }
       throw ex
     }
@@ -109,6 +130,7 @@ class ExecLogged extends AbstractExecTask<ExecLogged>
 
   private static String errorMessage( Exception ex, File stdoutFile, File stderrFile )
   {
+    // TODO Remove use of Gradle internals
     def consoleRenderer = new ConsoleRenderer()
     return "${ ex.message }\n" +
            "\tSTDOUT ${ consoleRenderer.asClickableFileUrl( stdoutFile ) }\n" +

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/test/AggregatedJacocoReportTask.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/test/AggregatedJacocoReportTask.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/test/AggregatedJacocoReportTask.groovy
deleted file mode 100644
index ca10b80..0000000
--- a/buildSrc/src/main/groovy/org/apache/polygene/gradle/test/AggregatedJacocoReportTask.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.polygene.gradle.test
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.Project
-import org.gradle.api.file.FileCollection
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-
-class AggregatedJacocoReportTask extends DefaultTask
-{
-  @InputFiles
-  FileCollection getJacocoExecDataDirectories()
-  {
-    return project.files( project.subprojects.collect( { Project p -> "${ p.buildDir.path }/jacoco" } ) )
-  }
-
-  @OutputDirectory
-  File getOutputDirectory()
-  {
-    return project.file( "$project.buildDir/reports/coverage" )
-  }
-
-  @TaskAction
-  void report()
-  {
-    def coveredProjects = project.subprojects.findAll { p -> new File( "${ p.buildDir.path }/jacoco" ).exists() }
-    def coreProjects = coveredProjects.findAll { p -> p.name.startsWith( 'org.apache.polygene.core' ) }
-    def libProjects = coveredProjects.findAll { p -> p.name.startsWith( 'org.apache.polygene.lib' ) }
-    def extProjects = coveredProjects.findAll { p -> p.name.startsWith( 'org.apache.polygene.ext' ) }
-    def toolsProjects = coveredProjects.findAll { p -> p.name.startsWith( 'org.apache.polygene.tool' ) }
-    def tutoProjects = coveredProjects.findAll { p -> p.name.startsWith( 'org.apache.polygene.tuto' ) }
-    def samplesProjects = coveredProjects.findAll { p -> p.name.startsWith( 'org.apache.polygene.sample' ) }
-    def classpath = project.configurations.getByName( 'jacoco' ).asPath
-    project.ant {
-      taskdef name: 'jacocoreport', classname: 'org.jacoco.ant.ReportTask', classpath: classpath
-      mkdir dir: outputDirectory
-      jacocoreport {
-        executiondata {
-          coveredProjects.collect { p -> fileset( dir: "${ p.buildDir.path }/jacoco" ) { include( name: '*.exec' ) } }
-        }
-        structure( name: "Apache Polygene\u2122 (Java Edition) SDK" ) {
-          group( name: "Core" ) {
-            classfiles { coreProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
-            sourcefiles { coreProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
-          }
-          group( name: "Libraries" ) {
-            classfiles { libProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
-            sourcefiles { libProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
-          }
-          group( name: "Extensions" ) {
-            classfiles { extProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
-            sourcefiles { extProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
-          }
-          group( name: "Tools" ) {
-            classfiles { toolsProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
-            sourcefiles { toolsProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
-          }
-          group( name: "Tutorials" ) {
-            classfiles { tutoProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
-            sourcefiles { tutoProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
-          }
-          group( name: "Samples" ) {
-            classfiles { samplesProjects.collect { p -> fileset dir: "${ p.buildDir.path }/classes/main" } }
-            sourcefiles { samplesProjects.collect { p -> fileset dir: "${ p.projectDir.path }/src/main/java" } }
-          }
-        }
-        csv destfile: "${ outputDirectory }/jacoco.csv", encoding: "UTF-8"
-        xml destfile: "${ outputDirectory }/jacoco.xml", encoding: "UTF-8"
-        html destdir: outputDirectory, encoding: "UTF-8", locale: "en", footer: "Apache Polygene\u2122 (Java Edition) SDK"
-      }
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Environment.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Environment.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Environment.groovy
new file mode 100644
index 0000000..1362821
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Environment.groovy
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.util
+
+class Environment
+{
+  static boolean isExecutableInPath( String executable )
+  {
+    def pathDirs = System.getenv( 'PATH' ).split( File.pathSeparator )
+    def flattened = pathDirs.collect( { String pathDir -> new File( pathDir, 'executable' ) } ).flatten() as List<File>
+    return flattened.find( { File pathDir -> pathDir.isFile() } ) != null
+  }
+
+  private Environment() {}
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Licensing.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Licensing.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Licensing.groovy
new file mode 100644
index 0000000..319ea20
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/polygene/gradle/util/Licensing.groovy
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.polygene.gradle.util
+
+class Licensing
+{
+  static String withLicenseHeader( String base, String flavour )
+  {
+    def header
+    switch( flavour )
+    {
+      case 'java': case 'groovy': case 'js':
+        header = licenseHeader_wrap( base, '/*', ' * ', ' */' ); break
+      case 'xml': case 'html':
+        header = licenseHeader_wrap( base, '<!--', '  ', '-->' ); break
+      case 'txt': case 'shell': case 'python': case 'ruby':
+        header = licenseHeader_wrap( base, null, '# ', null ); break
+      case 'adoc': case 'asciidoc':
+        header = licenseHeader_wrap( base, null, '// ', null ); break
+      default:
+        header = base
+    }
+    header
+  }
+
+  private static String licenseHeader_wrap( String base, String top, String left, String bottom )
+  {
+    ( top ? "$top\n" : '' ) +
+    base.readLines().collect { "${ left }${ it }" }.join( '\n' ) + '\n' +
+    ( bottom ? "$bottom\n" : '' )
+  }
+
+  private Licensing() {}
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/groovy/org/apache/polygene/gradle/version/VersionClassPlugin.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/polygene/gradle/version/VersionClassPlugin.groovy b/buildSrc/src/main/groovy/org/apache/polygene/gradle/version/VersionClassPlugin.groovy
deleted file mode 100644
index 2a99206..0000000
--- a/buildSrc/src/main/groovy/org/apache/polygene/gradle/version/VersionClassPlugin.groovy
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.polygene.gradle.version
-
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.api.Plugin
-import org.gradle.api.Task
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.api.plugins.JavaPluginConvention
-import org.gradle.api.tasks.SourceSet
-import org.gradle.api.tasks.bundling.Jar
-
-// TODO:release:perf Placeholder for date for dev versions
-// TODO:release:perf Add git data, placeholders for dev versions
-@CompileStatic
-class VersionClassPlugin implements Plugin<Project>
-{
-  def void apply( Project project )
-  {
-    project.getPlugins().apply( JavaPlugin.class )
-    def genSrc = 'generated-src/version'
-    def generatedSrcDir = new File( project.buildDir, genSrc )
-
-    Task makeVersionClassTask = project.task( 'makeVersionClass' )
-    makeVersionClassTask.doLast {
-      def now = new Date()
-      def tmpGroup = project.name
-      if( tmpGroup.startsWith( "org.apache.polygene.core" ) )
-      {
-        tmpGroup = tmpGroup - ".core"
-      }
-      tmpGroup = tmpGroup.replace( '-', '_' )
-      def outFilename = "java/" + tmpGroup.replace( '.', '/' ) + "/BuildVersion.java"
-      def outFile = new File( generatedSrcDir, outFilename )
-      outFile.getParentFile().mkdirs()
-      def f = new FileWriter( outFile )
-      f.write( 'package ' + tmpGroup + ';\n' )
-      f.write( """
-/**
- * Simple class for storing the version derived from the build system.
- *
- */
-public interface BuildVersion
-{
-    /** The version of the project from the gradle build.gradle file. */
-    String VERSION = \"""" + project.version + """\";
-
-    /** The name of the project from the gradle build.gradle file. */
-    String NAME = \"""" + project.name + """\";
-
-    /** The group of the project from the gradle build.gradle file. */
-    String GROUP = \"""" + project.group + """\";
-
-    /** The date this file was generated, usually the last date that the project was modified. */
-    String DATE = \"""" + now + """\";
-
-    /** The full details of the version, including the build date. */
-    String DETAILED_VERSION = GROUP + ":" + NAME + ":" + VERSION + " " + DATE;
-}\n
-""" )
-      f.close()
-    }
-    def sourceSets = project.convention.getPlugin( JavaPluginConvention ).sourceSets
-    sourceSets.create( "version" ) { SourceSet sourceSet ->
-      sourceSet.java { SourceDirectorySet dirSet ->
-        dirSet.srcDir project.buildDir.name + '/' + genSrc + '/java'
-      }
-    }
-    makeVersionClassTask.getInputs().files( sourceSets.getByName( 'main' ).allSource )
-    makeVersionClassTask.getOutputs().file( generatedSrcDir )
-    if( project.getBuildFile() != null && project.getBuildFile().exists() )
-    {
-      makeVersionClassTask.getInputs().files( project.getBuildFile() )
-    }
-    project.getTasks().getByName( 'compileJava' ).dependsOn( 'compileVersionJava' )
-    project.getTasks().getByName( 'compileVersionJava' ).dependsOn( 'makeVersionClass' )
-    project.getTasks().getByName( 'jar' ) { Jar task ->
-      task.from sourceSets.getByName( 'version' ).output
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-core.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-core.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-core.properties
new file mode 100644
index 0000000..d6a8da2
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-core.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.core.CorePlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-dependencies-declaration.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-dependencies-declaration.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-dependencies-declaration.properties
new file mode 100644
index 0000000..eb710e6
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-dependencies-declaration.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.dependencies.DependenciesDeclarationPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-distributions.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-distributions.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-distributions.properties
new file mode 100644
index 0000000..143746f
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-distributions.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.distributions.DistributionsPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-extension.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-extension.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-extension.properties
new file mode 100644
index 0000000..449ac1a
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-extension.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.extensions.ExtensionPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-internal.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-internal.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-internal.properties
new file mode 100644
index 0000000..2749ce9
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-internal.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.internals.InternalPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-library.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-library.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-library.properties
new file mode 100644
index 0000000..da861fc
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-library.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.libraries.LibraryPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-manual.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-manual.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-manual.properties
new file mode 100644
index 0000000..7e53bce
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-manual.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.manual.ManualPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-release.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-release.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-release.properties
new file mode 100644
index 0000000..a3db27b
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-release.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.release.ReleasePlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-reports.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-reports.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-reports.properties
new file mode 100644
index 0000000..a6371c9
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-reports.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.reports.ReportsPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-root.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-root.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-root.properties
new file mode 100644
index 0000000..a4fd101
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-root.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.RootPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-sample.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-sample.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-sample.properties
new file mode 100644
index 0000000..ab50e44
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-sample.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.samples.SamplePlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-settings.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-settings.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-settings.properties
new file mode 100644
index 0000000..d1714ba
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-settings.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.SettingsPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-test.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-test.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-test.properties
new file mode 100644
index 0000000..eb5bada
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-test.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.tests.TestPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tool.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tool.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tool.properties
new file mode 100644
index 0000000..1457955
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tool.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.tools.ToolPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tutorial.properties
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tutorial.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tutorial.properties
new file mode 100644
index 0000000..00780ce
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/polygene-tutorial.properties
@@ -0,0 +1,18 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+implementation-class=org.apache.polygene.gradle.structure.tutorials.TutorialPlugin

http://git-wip-us.apache.org/repos/asf/zest-java/blob/de73010f/buildSrc/src/test/groovy/org/apache/polygene/gradle/release/ModuleReleaseSpecTest.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/test/groovy/org/apache/polygene/gradle/release/ModuleReleaseSpecTest.groovy b/buildSrc/src/test/groovy/org/apache/polygene/gradle/release/ModuleReleaseSpecTest.groovy
index 6ca353d..203d79b 100644
--- a/buildSrc/src/test/groovy/org/apache/polygene/gradle/release/ModuleReleaseSpecTest.groovy
+++ b/buildSrc/src/test/groovy/org/apache/polygene/gradle/release/ModuleReleaseSpecTest.groovy
@@ -17,6 +17,7 @@
  */
 package org.apache.polygene.gradle.release
 
+import org.apache.polygene.gradle.structure.release.ModuleReleaseSpec
 import spock.lang.Specification
 import spock.lang.Unroll