diff --git a/.travis.yml b/.travis.yml
index 1a788c90..b430d45e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,17 +1,38 @@
-language: java
+language: android
android:
+ components:
+ # Update tools and then platform-tools explicitly so lint gets an updated database. Can be removed once 3.0 is out.
+ - tools
+ - platform-tools
+
jdk:
- oraclejdk8
-after_success:
- - .buildscript/deploy_snapshot.sh
-
env:
global:
- secure: "S2ANsF5glMuPrjilyE6+JqVp/t9bEQgkLXNHN1quLebTaJcveNFgyCPCvcevKJLiHCb4USY0cteZYj1U0QOvVR3zi/chr6Pxuo1kwgrfKpdBCWa1oxzLt0ipkVgH60VwxQNWjoUQCleOhu3F16X7IvCx32Wu7OqpmenJ6FJZYUw="
- secure: "ZetM/udS7DJMVdsFlSTCf88D6J4zQPS6hY3qc/f0BfYJP6qpOkHMBuxfIJMxkyQ9Rwi45RAX5kFFHJg/4wYiCQhlCSKy1gZaf4PORbhxTQR+rJCRtUi5dfHz2S2og/qWZm8KgaCalKW1s+f6/shFIBLcHvw8gnQmKvcIv7BHNaQ="
+before_install:
+ # Install SDK license so Android Gradle plugin can install deps.
+ - mkdir "$ANDROID_HOME/licenses" || true
+ - echo "d56f5187479451eabf01fb78af6dfcb131a6481e" > "$ANDROID_HOME/licenses/android-sdk-license"
+ # Install the rest of tools (e.g., avdmanager)
+ #- sdkmanager tools
+ # Install the system image
+ #- sdkmanager "android-26"
+
+script:
+ - ./gradlew check -s
+
+
+after_success:
+ - .buildscript/deploy_snapshot.sh
+
+after_failure:
+ - cat /home/travis/build/f2prateek/dart/henson-plugin/build/reports/tests/functionalTest/classes/dart.henson.plugin.HensonPluginFunctionalTest.html
+
branches:
except:
- gh-pages
@@ -23,4 +44,6 @@ sudo: false
cache:
directories:
- - $HOME/.m2
+ - $HOME/.gradle/caches/
+ - $HOME/.gradle/wrapper/
+ - $HOME/.android/build-cache
diff --git a/henson-plugin/build.gradle b/henson-plugin/build.gradle
new file mode 100644
index 00000000..8612eb7b
--- /dev/null
+++ b/henson-plugin/build.gradle
@@ -0,0 +1,65 @@
+apply plugin: 'groovy'
+apply plugin: 'java-gradle-plugin'
+
+
+sourceSets {
+ functionalTest {
+ groovy.srcDir file('src/functTest/groovy')
+ resources.srcDir file('src/functTest/resources')
+ compileClasspath += sourceSets.main.output + configurations.testRuntime
+ runtimeClasspath += output + compileClasspath
+ }
+}
+
+gradlePlugin {
+ plugins {
+ hensonPlugin {
+ id = 'dart.henson-plugin'
+ implementationClass = 'dart.henson.plugin.HensonPlugin'
+ }
+ }
+ testSourceSets sourceSets.functionalTest
+}
+
+task functionalTest(type: Test) {
+ description = 'Runs the functional tests.'
+ group = 'verification'
+ testClassesDirs = sourceSets.functionalTest.output.classesDirs
+ classpath = sourceSets.functionalTest.runtimeClasspath
+ mustRunAfter test
+}
+
+check.dependsOn functionalTest
+
+project.afterEvaluate {
+ tasks.getByName('compileGroovy').doFirst {
+ //we create a file in the plugin project containing the current project version
+ //it will be used later by the plugin to add the proper version of
+ //dependencies to the project that uses the plugin
+ def prop = new Properties()
+ def propFile = new File("${project.rootDir}/henson-plugin/src/main/resources/build.properties")
+ propFile.parentFile.mkdirs()
+ prop.setProperty("dart.version", "$version")
+ propFile.createNewFile()
+ prop.store(propFile.newWriter(), null)
+ }
+}
+
+repositories {
+ jcenter()
+ google()
+ mavenLocal()
+}
+
+dependencies {
+ compile localGroovy()
+ implementation 'com.android.tools.build:gradle:3.0.0'
+
+ functionalTestCompile('org.spockframework:spock-core:1.1-groovy-2.4') {
+ exclude group: 'org.codehaus.groovy'
+ }
+ functionalTestCompile gradleTestKit()
+}
+
+//TODO : Does the release work ?
+apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
\ No newline at end of file
diff --git a/henson-plugin/gradle.properties b/henson-plugin/gradle.properties
new file mode 100644
index 00000000..f9011324
--- /dev/null
+++ b/henson-plugin/gradle.properties
@@ -0,0 +1,3 @@
+POM_ARTIFACT_ID=henson-plugin
+POM_NAME=Henson Gradle Plugin
+POM_PACKAGING=jar
\ No newline at end of file
diff --git a/henson-plugin/src/functTest/groovy/dart/henson/plugin/HensonPluginFunctionalTest.groovy b/henson-plugin/src/functTest/groovy/dart/henson/plugin/HensonPluginFunctionalTest.groovy
new file mode 100644
index 00000000..3793d6f0
--- /dev/null
+++ b/henson-plugin/src/functTest/groovy/dart/henson/plugin/HensonPluginFunctionalTest.groovy
@@ -0,0 +1,239 @@
+package dart.henson.plugin
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+
+import spock.lang.Specification
+
+import java.util.zip.ZipFile
+
+import static org.gradle.testkit.runner.TaskOutcome.FAILED
+import static groovy.io.FileType.FILES
+
+class HensonPluginFunctionalTest extends Specification {
+ @Rule TemporaryFolder testProjectDir = new TemporaryFolder()
+ File settingsFile
+ File buildFile
+ File manifestFile
+ File srcMain
+ File srcNavigationMain
+
+ def setup() {
+ settingsFile = testProjectDir.newFile('settings.gradle')
+ buildFile = testProjectDir.newFile('build.gradle')
+ testProjectDir.newFolder('src','main')
+ manifestFile = testProjectDir.newFile('src/main/AndroidManifest.xml')
+ testProjectDir.newFolder('src','main', 'java', 'test')
+ srcMain = testProjectDir.newFile('src/main/java/test/FooActivity.java')
+ testProjectDir.newFolder('src','navigation', 'main', 'java', 'test')
+ srcNavigationMain = testProjectDir.newFile('src/navigation/main/java/test/Foo.java')
+ }
+
+ def "fails on non android projects"() {
+ buildFile << """
+ plugins {
+ id 'java-library'
+ id 'dart.henson-plugin'
+ }
+ """
+
+ when:
+ def result = GradleRunner.create()
+ .withProjectDir(testProjectDir.root)
+ .withArguments('build')
+ .withPluginClasspath()
+ .build()
+
+ then:
+ org.gradle.testkit.runner.UnexpectedBuildFailure ex = thrown()
+ ex.message.contains("'android' or 'android-library' plugin required.")
+ }
+
+ def "applies to android projects"() {
+ manifestFile << """
+
+
+
+
+ """
+ srcMain << """
+ package test;
+
+ import android.app.Activity;
+ import android.os.Bundle;
+ import test.HensonNavigator;
+ import android.content.Intent;
+
+ class FooActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ Foo foo = new Foo();
+ Intent intent = HensonNavigator.gotoTestActivity(this)
+ .s("s")
+ .build();
+ }
+ }
+ """
+ srcNavigationMain << """
+ package test;
+
+ import dart.BindExtra;
+ import dart.DartModel;
+
+ @DartModel("test.TestActivity")
+ class Foo {
+ @BindExtra String s;
+ }
+ """
+
+ settingsFile << """
+ rootProject.name = "test-project"
+ """
+
+ buildFile << """
+ buildscript {
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ maven {
+ url 'https://oss.sonatype.org/content/repositories/snapshots'
+ }
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.0.1'
+ }
+ }
+
+ plugins {
+ //the order matters here
+ id 'com.android.application'
+ id 'dart.henson-plugin'
+ }
+
+ henson {
+ navigatorPackageName = "test"
+ }
+
+ android {
+ compileSdkVersion 26
+ defaultConfig {
+ applicationId 'test'
+ minSdkVersion 26
+ targetSdkVersion 26
+ versionCode 1
+ versionName '1.0.0'
+ }
+ flavorDimensions "color"
+
+ productFlavors {
+ red {
+ applicationId "com.blue"
+ dimension "color"
+ }
+ blue {
+ applicationId "com.red"
+ dimension "color"
+ }
+ }
+ }
+
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ maven {
+ url 'https://oss.sonatype.org/content/repositories/snapshots'
+ }
+ }
+
+ """
+
+ when:
+ def result = GradleRunner.create()
+ .withProjectDir(testProjectDir.root)
+ .withArguments('--no-build-cache', 'tasks', '--all', '-d', '-s')
+ .withPluginClasspath()
+ .build()
+
+ then:
+ result.output.contains("navigationApiCompileJava")
+ result.output.contains("navigationApiCompileJavaRed")
+ result.output.contains("navigationApiCompileJavaBlue")
+ result.output.contains("navigationApiCompileJavaRelease")
+ result.output.contains("navigationApiCompileJavaDebug")
+ result.output.contains("navigationApiCompileJavaBlueRelease")
+ result.output.contains("navigationApiCompileJavaBlueDebug")
+ result.output.contains("navigationApiCompileJavaRedRelease")
+ result.output.contains("navigationApiCompileJavaRedDebug")
+
+ result.output.contains("navigationApiJar")
+ result.output.contains("navigationApiJarRed")
+ result.output.contains("navigationApiJarBlue")
+ result.output.contains("navigationApiJarRelease")
+ result.output.contains("navigationApiJarDebug")
+ result.output.contains("navigationApiJarBlueRelease")
+ result.output.contains("navigationApiJarBlueDebug")
+ result.output.contains("navigationApiJarRedRelease")
+ result.output.contains("navigationApiJarRedDebug")
+
+ when:
+ def runner = GradleRunner.create()
+ .withProjectDir(testProjectDir.root)
+ .withArguments('--no-build-cache', 'clean', 'assemble', 'navigationApiJar', 'navigationApiJarRed', 'navigationApiJarRelease', 'navigationApiJarBlueDebug', '-d', '-s')
+ .withPluginClasspath()
+
+ def projectDir = runner.projectDir
+ result = runner.build()
+
+ then:
+ println result.output
+ result.task(":assemble").outcome != FAILED
+ //result.task(":tasks").outcome == SUCCESS
+ result.task(":navigationApiJar").outcome != FAILED
+ result.task(":navigationApiJarRed").outcome != FAILED
+ result.task(":navigationApiJarRelease").outcome != FAILED
+ result.task(":navigationApiJarBlueDebug").outcome != FAILED
+
+ testJarsContent(projectDir)
+ }
+
+ boolean testJarsContent(projectDir) {
+ new File(projectDir, "/build/libs").eachFileRecurse(FILES) { file ->
+ if (file.name.endsWith('.jar')) {
+ println "Testing jar: ${file.name}"
+ def content = getJarContent(file)
+ assert content.contains("META-INF/")
+ assert content.contains("META-INF/MANIFEST.MF")
+ assert content.contains("test/")
+ assert content.contains("test/Foo.class")
+ assert content.contains("test/Foo__ExtraBinder.class")
+ assert content.contains("test/Henson\$1.class")
+ assert content.contains("test/Henson\$WithContextSetState.class")
+ assert content.contains("test/Henson.class")
+ assert content.contains("test/TestActivity__IntentBuilder\$AllSet.class")
+ assert content.contains("test/TestActivity__IntentBuilder.class")
+ }
+ }
+ true
+ }
+
+ List getJarContent(file) {
+ def List result
+ if(file.name.endsWith('.jar')) {
+ result = new ArrayList<>()
+ def zip = new ZipFile(file)
+ zip.entries().each { entry ->
+ result.add(entry.name)
+ }
+ }
+ result
+ }
+}
diff --git a/henson-plugin/src/main/groovy/dart/henson/plugin/HensonPlugin.groovy b/henson-plugin/src/main/groovy/dart/henson/plugin/HensonPlugin.groovy
new file mode 100644
index 00000000..6502cd59
--- /dev/null
+++ b/henson-plugin/src/main/groovy/dart/henson/plugin/HensonPlugin.groovy
@@ -0,0 +1,375 @@
+package dart.henson.plugin
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import com.android.build.gradle.AppPlugin
+import com.android.build.gradle.LibraryPlugin
+import org.gradle.api.Task
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.file.UnionFileCollection
+import org.gradle.api.plugins.PluginCollection
+import org.gradle.api.tasks.bundling.Jar
+import org.gradle.api.tasks.compile.JavaCompile
+
+import java.security.InvalidParameterException
+import java.util.zip.ZipFile
+
+class HensonPlugin implements Plugin {
+
+ public static final String NAVIGATION_API_COMPILE_TASK_PREFIX = 'navigationApiCompileJava'
+ public static final String NAVIGATION_API_JAR_TASK_PREFIX = 'navigationApiJar'
+
+ void apply(Project project) {
+ final def log = project.logger
+ final String LOG_TAG = "HENSON"
+
+ //check project
+ def hasAppPlugin = project.plugins.withType(AppPlugin)
+ def hasLibPlugin = project.plugins.withType(LibraryPlugin)
+ checkProject(hasAppPlugin, hasLibPlugin)
+
+ //we use the file build.properties that contains the version of
+ //the dart & henson version to use. This avoids all problems related to using version x.y.+
+ def dartVersionName = getVersionName()
+
+ //the extension is created but will be read only during execution time
+ //(it's not available before)
+ project.extensions.create('henson', HensonPluginExtension)
+
+ //we do the following for all sourcesets, of all build types, of all flavors, and all variants
+ // create source sets
+ // create configurations
+ // create dependencies
+ // create tasks: compile and jar
+ // create artifacts
+
+ processMain(project, dartVersionName)
+ processBuildTypes(project, dartVersionName)
+ processProductFlavors(project, dartVersionName)
+ processVariants(project, dartVersionName)
+
+ //add the artifact of navigation for the variant to the variant configuration
+ addNavigationArtifactsToVariantConfigurations(project)
+
+ //create the task for generating the henson navigator
+ detectNavigationApiDependenciesAndGenerateHensonNavigator(project)
+ }
+
+ private void detectNavigationApiDependenciesAndGenerateHensonNavigator(Project project) {
+ project.android.applicationVariants.all { variant ->
+ def taskDetectModules = project.tasks.create("detectModule${variant.name.capitalize()}") {
+ doFirst {
+ //create hensonExtension
+ def hensonExtension = project.extensions.getByName('henson')
+ if(hensonExtension==null || hensonExtension.navigatorPackageName == null) {
+ throw new InvalidParameterException("The property 'henson.navigatorPackageName' must be defined in your build.gradle")
+ }
+ def hensonNavigatorPackageName = hensonExtension.navigatorPackageName
+
+ variant.compileConfiguration.resolve()
+ variant.compileConfiguration.each { dependency ->
+ println "Detected dependency: ${dependency.properties}"
+ List targetActivities = new ArrayList()
+ if (dependency.name.matches(".*-navigationApi.*.jar")) {
+ println "Detected navigation API dependency: ${dependency.name}"
+ println "Detected navigation API dependency: ${dependency.name}"
+ def file = dependency.absoluteFile
+ def entries = getJarContent(file)
+ entries.each { entry ->
+ if(entry.matches(".*__IntentBuilder.class")) {
+ println "Detected intent builder: ${entry}"
+ def targetActivityFQN = entry.substring(0, entry.length() - "__IntentBuilder.class".length()).replace('/', '.')
+ targetActivities.add(targetActivityFQN)
+ }
+ }
+ }
+ def variantSrcFolderName = new File(project.projectDir, "src/${variant.name}/java/")
+ String hensonNavigator = generateHensonNavigatorClass(targetActivities, hensonNavigatorPackageName)
+ variantSrcFolderName.mkdirs()
+ File generatedFolder = new File(variantSrcFolderName, hensonNavigatorPackageName.replace('.', '/').concat('/'))
+ generatedFolder.mkdirs()
+ def generatedFile = new File(generatedFolder, "HensonNavigator.java")
+ generatedFile.withPrintWriter { out ->
+ out.println(hensonNavigator)
+ }
+ }
+ }
+ }
+ //we put the task right before compilation so that all dependencies are resolved
+ // when the task is executed
+ taskDetectModules.dependsOn = variant.javaCompiler.dependsOn
+ variant.javaCompiler.dependsOn(taskDetectModules)
+ }
+ }
+
+ private String generateHensonNavigatorClass(List targetActivities, packageName) {
+ String packageStatement = "package ${packageName};\n"
+ String importStatement = "import android.content.Context;\n"
+ targetActivities.each { targetActivity ->
+ importStatement += "import ${targetActivity}__IntentBuilder;\n"
+ }
+ String classStartStatement = "public class HensonNavigator {\n"
+ String methodStatement = ""
+ targetActivities.each { targetActivity ->
+ String targetActivitySimpleName = targetActivity.substring(1+targetActivity.lastIndexOf('.'), targetActivity.length())
+ methodStatement += "public static ${targetActivitySimpleName.capitalize()}__IntentBuilder goto${targetActivitySimpleName.capitalize()}(Context context) {\n"
+ methodStatement += " return new ${targetActivitySimpleName}__IntentBuilder(context);\n"
+ methodStatement += "}"
+ methodStatement += "\n"
+ }
+ String classEndStatement = "}"
+ new StringBuilder()
+ .append(packageStatement)
+ .append(importStatement)
+ .append(classStartStatement)
+ .append(methodStatement)
+ .append(classEndStatement)
+ .toString()
+ }
+
+ private void processMain(Project project, dartVersionName) {
+ def suffix = ""
+ def pathSuffix = "main/"
+ def sourceSetName = "main"
+
+ createSourceSetAndConfiguration(project, sourceSetName, suffix, pathSuffix, dartVersionName)
+ createEmptyNavigationApiCompileTask(project, suffix)
+ createEmptyNavigationApiJarTask(project, suffix)
+ }
+
+ private Object processVariants(Project project, dartVersionName) {
+ project.android.with {
+ buildTypes.all { buildType ->
+ productFlavors.all { productFlavor ->
+ println "Processing variant: ${productFlavor.name}${buildType.name.capitalize()}"
+ processVariant(project, productFlavor, buildType, dartVersionName)
+ }
+ }
+ }
+ }
+
+ private void processProductFlavors(Project project, dartVersionName) {
+ project.android.productFlavors.all { productFlavor ->
+ println "Processing flavor: ${productFlavor.name}"
+ processFlavorOrBuildType(project, productFlavor, dartVersionName)
+ }
+ }
+
+ private void processBuildTypes(Project project, dartVersionName) {
+ project.android.buildTypes.all { buildType ->
+ println "Processing buildType: ${buildType.name}"
+ processFlavorOrBuildType(project, buildType, dartVersionName)
+ }
+ }
+
+ private void processVariant(Project project, productFlavor, buildType, dartVersionName) {
+ def variantName = "${productFlavor.name}${buildType.name.capitalize()}"
+ def suffix = "${productFlavor.name.capitalize()}${buildType.name.capitalize()}"
+ def pathSuffix = "${productFlavor.name}${buildType.name.capitalize()}/"
+ createSourceSetAndConfiguration(project, variantName, suffix, pathSuffix, dartVersionName)
+
+ def navigationVariant = createNavigationVariant(project, productFlavor, buildType)
+ def navigationApiCompiler = createNavigationApiCompileTask(project, suffix, pathSuffix, navigationVariant)
+ def mainCompiler = project.tasks.getByName(NAVIGATION_API_COMPILE_TASK_PREFIX)
+ def productFlavorCompiler = project.tasks.getByName(NAVIGATION_API_COMPILE_TASK_PREFIX + String.valueOf(productFlavor.name.capitalize()))
+ def buildTypeCompiler = project.tasks.getByName(NAVIGATION_API_COMPILE_TASK_PREFIX + String.valueOf(buildType.name.capitalize()))
+ navigationApiCompiler.dependsOn(mainCompiler, productFlavorCompiler, buildTypeCompiler)
+
+ def navigationApiJarTask = createNavigationApiJarTask(project, navigationApiCompiler, suffix)
+ def mainNavigationApiJarTask = project.tasks.getByName(NAVIGATION_API_JAR_TASK_PREFIX)
+ def productFlavorNavigationApiJarTask = project.tasks.getByName(NAVIGATION_API_JAR_TASK_PREFIX + String.valueOf(productFlavor.name.capitalize()))
+ def buildTypeNavigationApiJarTask = project.tasks.getByName(NAVIGATION_API_JAR_TASK_PREFIX + String.valueOf(buildType.name.capitalize()))
+ navigationApiJarTask.dependsOn(mainNavigationApiJarTask, productFlavorNavigationApiJarTask, buildTypeNavigationApiJarTask)
+
+ addArtifact(project, suffix, suffix)
+ }
+
+ private void processFlavorOrBuildType(Project project, productFlavorOrBuildType, dartVersionName) {
+ def sourceSetName = "${productFlavorOrBuildType.name}"
+ def suffix = "${productFlavorOrBuildType.name.capitalize()}"
+ def pathSuffix = "${productFlavorOrBuildType.name}/"
+
+ createSourceSetAndConfiguration(project, sourceSetName, suffix, pathSuffix, dartVersionName)
+
+ createEmptyNavigationApiCompileTask(project, suffix)
+ createEmptyNavigationApiJarTask(project, suffix)
+ }
+
+ private void createSourceSetAndConfiguration(Project project, String sourceSetName, String suffix, String pathSuffix, dartVersionName) {
+ def newSourceSetName = "navigation" + suffix
+ def newSourceSetPath = "src/navigation/" + pathSuffix
+ createNavigationSourceSet(project, sourceSetName, newSourceSetName, newSourceSetPath)
+
+ def newArtifactName = "navigationApi" + suffix
+ createNavigationConfiguration(project, newArtifactName, suffix, dartVersionName)
+ }
+
+ private Object getVersionName() {
+ Properties properties = new Properties()
+ properties.load(getClass().getClassLoader().getResourceAsStream("build.properties"))
+ properties.get("dart.version")
+ }
+
+ private NavigationVariant createNavigationVariant(Project project, productFlavor, buildType) {
+ def navigationVariant = new NavigationVariant()
+ navigationVariant.flavorName = productFlavor.name
+ navigationVariant.buildTypeName = buildType.name
+ navigationVariant.sourceSets = [project.android.sourceSets["navigation"] \
+ , project.android.sourceSets["navigation${navigationVariant.buildTypeName.capitalize()}"] \
+ , project.android.sourceSets["navigation${navigationVariant.flavorName.capitalize()}"] \
+ , project.android.sourceSets["navigation${navigationVariant.flavorName.capitalize()}${navigationVariant.buildTypeName.capitalize()}"]
+ ]
+ navigationVariant.apiConfigurations = [project.configurations["navigationApi"] \
+ , project.configurations["navigation${navigationVariant.buildTypeName.capitalize()}Api"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}Api"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}${navigationVariant.buildTypeName.capitalize()}Api"]
+ ]
+ navigationVariant.implementationConfigurations = [project.configurations["navigationImplementation"] \
+ , project.configurations["navigation${navigationVariant.buildTypeName.capitalize()}Implementation"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}Implementation"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}${navigationVariant.buildTypeName.capitalize()}Implementation"]
+ ]
+ navigationVariant.compileOnlyConfigurations = [project.configurations["navigationCompileOnly"] \
+ , project.configurations["navigation${navigationVariant.buildTypeName.capitalize()}CompileOnly"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}CompileOnly"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}${navigationVariant.buildTypeName.capitalize()}CompileOnly"]
+ ]
+ navigationVariant.annotationProcessorConfigurations = [project.configurations["navigationAnnotationProcessor"] \
+ , project.configurations["navigation${navigationVariant.buildTypeName.capitalize()}AnnotationProcessor"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}AnnotationProcessor"] \
+ , project.configurations["navigation${navigationVariant.flavorName.capitalize()}${navigationVariant.buildTypeName.capitalize()}AnnotationProcessor"]
+ ]
+ navigationVariant
+ }
+
+ private Task createEmptyNavigationApiCompileTask(Project project, taskSuffix) {
+ project.tasks.create(NAVIGATION_API_COMPILE_TASK_PREFIX + String.valueOf(taskSuffix))
+ }
+
+ private Task createEmptyNavigationApiJarTask(Project project, taskSuffix) {
+ project.tasks.create(NAVIGATION_API_JAR_TASK_PREFIX + String.valueOf(taskSuffix))
+ }
+
+ private Task createNavigationApiCompileTask(Project project, taskSuffix, destinationPath, navigationVariant) {
+ def newDestinationDirName = "${project.buildDir}/navigation/classes/java/${destinationPath}"
+ def newGeneratedDirName = "${project.buildDir}/generated/source/apt/navigation/${destinationPath}"
+
+ FileCollection effectiveClasspath = new UnionFileCollection()
+ navigationVariant.apiConfigurations.each { effectiveClasspath.add(it) }
+ navigationVariant.implementationConfigurations.each { effectiveClasspath.add(it) }
+ navigationVariant.compileOnlyConfigurations.each { effectiveClasspath.add(it) }
+
+ FileCollection effectiveAnnotationProcessorPath = new UnionFileCollection()
+ navigationVariant.annotationProcessorConfigurations.each { effectiveAnnotationProcessorPath.add(it) }
+
+ project.tasks.create("${NAVIGATION_API_COMPILE_TASK_PREFIX}${taskSuffix}", JavaCompile) {
+ setSource(navigationVariant.sourceSets.collect { sourceSet -> sourceSet.java.sourceFiles })
+ setDestinationDir(project.file("${newDestinationDirName}"))
+ classpath = effectiveClasspath
+ options.compilerArgs = ["-s", "${newGeneratedDirName}"]
+ options.annotationProcessorPath = effectiveAnnotationProcessorPath
+ targetCompatibility = JavaVersion.VERSION_1_7
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ doFirst {
+ project.file("${newGeneratedDirName}").mkdirs()
+ project.file("${newDestinationDirName}").mkdirs()
+ }
+ }
+ }
+
+ private Task createNavigationApiJarTask(Project project, navigationApiCompileTask, taskSuffix) {
+ def task = project.tasks.create(NAVIGATION_API_JAR_TASK_PREFIX + String.valueOf(taskSuffix), Jar) {
+ baseName = "${project.name}-navigationApi${taskSuffix}"
+ from navigationApiCompileTask.destinationDir
+ }
+ task.dependsOn(navigationApiCompileTask)
+ task
+ }
+
+ private void addArtifact(Project project, artifactSuffix, taskSuffix) {
+ println project.configurations.getByName("navigationApi")
+ project.artifacts.add("navigationApi${artifactSuffix}", project.tasks[NAVIGATION_API_JAR_TASK_PREFIX + String.valueOf(taskSuffix)])
+ }
+
+ private void addNavigationArtifactsToVariantConfigurations(Project project) {
+ //the project main source itself will depend on the navigation
+ //we must wait until the variant created the proper configurations to add the dependency.
+ project.android.applicationVariants.all { variant ->
+ //we use the api configuration to make sure the resulting apk will contain the classes of the navigation jar.
+ def configurationPrefix = variant.name
+ def artifactSuffix = variant.name.capitalize()
+ project.dependencies.add("${configurationPrefix}Api", project.dependencies.project(path: "${project.path}", configuration: "navigationApi${artifactSuffix}"))
+ }
+ }
+
+ private void addDependenciesToConfiguration(Project project, configurationSuffix, dartVersionName) {
+ project.dependencies {
+ "navigation${configurationSuffix}CompileOnly" "com.google.android:android:4.1.1.4"
+ "navigation${configurationSuffix}CompileOnly" "com.f2prateek.dart:dart:${dartVersionName}"
+ "navigation${configurationSuffix}CompileOnly" "com.f2prateek.dart:henson:${dartVersionName}"
+ "navigation${configurationSuffix}Api" "com.f2prateek.dart:dart-annotations:${dartVersionName}"
+ "navigation${configurationSuffix}AnnotationProcessor" "com.f2prateek.dart:henson-processor:${dartVersionName}"
+ "navigation${configurationSuffix}AnnotationProcessor" "com.f2prateek.dart:dart-processor:${dartVersionName}"
+ }
+ }
+
+ private void createNavigationConfiguration(Project project, newArtifactName, newConfigurationSuffix, dartVersionName) {
+ println "Creating configuration: ${newArtifactName}"
+ project.configurations {
+ //the name of the artifact
+ "${newArtifactName}" {
+ canBeResolved true
+ }
+
+ //the api scope: is there any convention ? apiElements?
+ "navigation${newConfigurationSuffix}Api" {
+ canBeResolved true
+ }
+
+ "navigation${newConfigurationSuffix}Implementation" {
+ canBeResolved true
+ }
+
+ "navigation${newConfigurationSuffix}CompileOnly" {
+ canBeResolved true
+ }
+
+ //the ap scope
+ "navigation${newConfigurationSuffix}AnnotationProcessor" {
+ canBeResolved true
+ }
+ }
+ addDependenciesToConfiguration(project, newConfigurationSuffix, dartVersionName)
+ }
+
+ private void createNavigationSourceSet(Project project, sourceSetName, newSourceSetName, newSourceSetPath) {
+ println "Creating sourceSet: ${sourceSetName}->${newSourceSetName} with root in '${newSourceSetPath}'"
+ project.android.sourceSets {
+ "${newSourceSetName}" {
+ setRoot "${newSourceSetPath}"
+ }
+ }
+ }
+
+ private boolean checkProject(PluginCollection hasApp,
+ PluginCollection hasLib) {
+ if (!hasApp && !hasLib) {
+ throw new IllegalStateException("'android' or 'android-library' plugin required.")
+ }
+ return !hasApp
+ }
+
+
+ private List getJarContent(file) {
+ def List result
+ if(file.name.endsWith('.jar')) {
+ result = new ArrayList<>()
+ def zip = new ZipFile(file)
+ zip.entries().each { entry ->
+ result.add(entry.name)
+ }
+ }
+ result
+ }
+}
diff --git a/henson-plugin/src/main/groovy/dart/henson/plugin/HensonPluginExtension.groovy b/henson-plugin/src/main/groovy/dart/henson/plugin/HensonPluginExtension.groovy
new file mode 100644
index 00000000..5cca0c69
--- /dev/null
+++ b/henson-plugin/src/main/groovy/dart/henson/plugin/HensonPluginExtension.groovy
@@ -0,0 +1,5 @@
+package dart.henson.plugin
+
+class HensonPluginExtension {
+ String navigatorPackageName
+}
diff --git a/henson-plugin/src/main/groovy/dart/henson/plugin/NavigationVariant.groovy b/henson-plugin/src/main/groovy/dart/henson/plugin/NavigationVariant.groovy
new file mode 100644
index 00000000..c3c735d5
--- /dev/null
+++ b/henson-plugin/src/main/groovy/dart/henson/plugin/NavigationVariant.groovy
@@ -0,0 +1,14 @@
+package dart.henson.plugin
+
+import com.android.build.gradle.api.AndroidSourceSet
+
+class NavigationVariant {
+
+ def buildTypeName
+ def flavorName
+ List sourceSets
+ def apiConfigurations
+ def implementationConfigurations
+ def compileOnlyConfigurations
+ def annotationProcessorConfigurations
+}
diff --git a/henson-plugin/src/main/resources/build.properties b/henson-plugin/src/main/resources/build.properties
new file mode 100644
index 00000000..b4624f18
--- /dev/null
+++ b/henson-plugin/src/main/resources/build.properties
@@ -0,0 +1,2 @@
+#Fri Dec 01 09:28:50 PST 2017
+dart.version=3.0.0-RC1-SNAPSHOT
diff --git a/settings.gradle b/settings.gradle
index 2c09b94f..eb5099aa 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -4,6 +4,7 @@ include 'dart'
include 'dart-processor'
include 'henson'
include 'henson-processor'
+include 'henson-plugin'
//TODO make it self contained, not a child
//android apps break with a build task