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