diff options
author | Yigit Boyar <yboyar@google.com> | 2015-06-12 15:42:03 -0700 |
---|---|---|
committer | Yigit Boyar <yboyar@google.com> | 2015-06-12 23:11:46 +0000 |
commit | 40d0967b36f0bd77393719f0442626c838697290 (patch) | |
tree | c1ebd7fcb8434a43b4de4c9be21fcab87a1218e9 /gradlePlugin | |
parent | b6887f1479c3ecec38a7989748ef33de1fbcd973 (diff) | |
download | data-binding-40d0967b36f0bd77393719f0442626c838697290.tar.gz |
Remove kotlin from gradle plugin
This CL removes dependency on kotlin the gradle plugin.
One step closer to integrating w/ Android plugin
Bug: 21815393
Change-Id: Ida966e4bb8d92da1b8734b6b40da230fbac5c8d2
Diffstat (limited to 'gradlePlugin')
7 files changed, 493 insertions, 337 deletions
diff --git a/gradlePlugin/build.gradle b/gradlePlugin/build.gradle index 83aa27cc..cd1099e1 100644 --- a/gradlePlugin/build.gradle +++ b/gradlePlugin/build.gradle @@ -15,8 +15,6 @@ */ apply plugin: 'java' -apply plugin: "kotlin" - sourceCompatibility = config.javaTargetCompatibility targetCompatibility = config.javaSourceCompatibility @@ -24,9 +22,6 @@ buildscript { repositories { mavenCentral() } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}" - } } tasks.create(name : 'copyBuildVersion') << { @@ -41,7 +36,6 @@ tasks['copyBuildVersion'].dependsOn('processResources') dependencies { compile "com.android.tools.build:gradle:${config.androidPluginVersion}" - compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}" compile gradleApi() compile project(":compiler") } diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java new file mode 100644 index 00000000..ad64057e --- /dev/null +++ b/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 android.databinding.tool; + +import com.google.common.base.Preconditions; + +import com.android.build.gradle.AppExtension; +import com.android.build.gradle.BaseExtension; +import com.android.build.gradle.LibraryExtension; +import com.android.build.gradle.api.ApplicationVariant; +import com.android.build.gradle.api.LibraryVariant; +import com.android.build.gradle.api.TestVariant; +import com.android.build.gradle.internal.api.ApplicationVariantImpl; +import com.android.build.gradle.internal.api.LibraryVariantImpl; +import com.android.build.gradle.internal.api.TestVariantImpl; +import com.android.build.gradle.internal.core.GradleVariantConfiguration; +import com.android.build.gradle.internal.variant.ApplicationVariantData; +import com.android.build.gradle.internal.variant.BaseVariantData; +import com.android.build.gradle.internal.variant.LibraryVariantData; +import com.android.build.gradle.internal.variant.TestVariantData; +import com.android.build.gradle.tasks.ProcessAndroidResources; +import com.android.builder.model.ApiVersion; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.logging.Logger; +import org.gradle.api.plugins.ExtraPropertiesExtension; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.compile.AbstractCompile; + +import android.databinding.tool.writer.JavaFileWriter; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; + +import javax.xml.bind.JAXBException; + +public class DataBinderPlugin implements Plugin<Project> { + + private Logger logger; + + class GradleFileWriter extends JavaFileWriter { + + private final String outputBase; + + public GradleFileWriter(String outputBase) { + this.outputBase = outputBase; + } + + @Override + public void writeToFile(String canonicalName, String contents) { + String asPath = canonicalName.replace('.', '/'); + File f = new File(outputBase + "/" + asPath + ".java"); + logD("Asked to write to " + canonicalName + ". outputting to:" + + f.getAbsolutePath()); + //noinspection ResultOfMethodCallIgnored + f.getParentFile().mkdirs(); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(f); + IOUtils.write(contents, fos); + } catch (IOException e) { + logE(e, "cannot write file " + f.getAbsolutePath()); + } finally { + IOUtils.closeQuietly(fos); + } + } + } + + @Override + public void apply(Project project) { + if (project == null) { + return; + } + logger = project.getLogger(); + + String myVersion = readMyVersion(); + logD("data binding plugin version is %s", myVersion); + if (StringUtils.isEmpty(myVersion)) { + throw new IllegalStateException("cannot read version of the plugin :/"); + } + + project.getDependencies().add("compile", "com.android.databinding:library:" + myVersion); + boolean addAdapters = true; + if (project.hasProperty("ext")) { + Object ext = project.getProperties().get("ext"); + if (ext instanceof ExtraPropertiesExtension) { + ExtraPropertiesExtension propExt = (ExtraPropertiesExtension) ext; + if (propExt.has("addDataBindingAdapters")) { + addAdapters = Boolean.valueOf( + String.valueOf(propExt.get("addDataBindingAdapters"))); + } + } + } + if (addAdapters) { + project.getDependencies() + .add("compile", "com.android.databinding:adapters:" + myVersion); + } + project.getDependencies().add("provided", "com.android.databinding:compiler:" + myVersion); + project.afterEvaluate(new Action<Project>() { + @Override + public void execute(Project project) { + try { + createXmlProcessor(project); + } catch (Throwable t) { + logE(t, "failed to setup data binding"); + } + } + }); + } + + String readMyVersion() { + try { + InputStream stream = getClass().getResourceAsStream("/data_binding_build_info"); + try { + return IOUtils.toString(stream, "utf-8").trim(); + } finally { + IOUtils.closeQuietly(stream); + } + } catch (IOException exception) { + logE(exception, "Cannot read data binding version"); + } + return null; + } + + private void createXmlProcessor(Project project) + throws NoSuchFieldException, IllegalAccessException { + Object androidExt = project.getExtensions().getByName("android"); + if (!(androidExt instanceof BaseExtension)) { + return; + } + if (androidExt instanceof AppExtension) { + createXmlProcessorForApp(project, (AppExtension) androidExt); + } else if (androidExt instanceof LibraryExtension) { + createXmlProcessorForLibrary(project, (LibraryExtension) androidExt); + } else { + logE(new UnsupportedOperationException("cannot understand android ext"), + "unsupported android extension. What is it? %s", androidExt); + } + } + + private void createXmlProcessorForLibrary(Project project, LibraryExtension lib) + throws NoSuchFieldException, IllegalAccessException { + File sdkDir = lib.getSdkDirectory(); + for (TestVariant variant : lib.getTestVariants()) { + logD("test variant %s. dir name %s", variant, variant.getDirName()); + BaseVariantData variantData = getVariantData(variant); + attachXmlProcessor(project, variantData, sdkDir, false);//tests extend apk variant + } + for (LibraryVariant variant : lib.getLibraryVariants()) { + logD("library variant %s. dir name %s", variant, variant.getDirName()); + BaseVariantData variantData = getVariantData(variant); + attachXmlProcessor(project, variantData, sdkDir, true); + } + } + + private void createXmlProcessorForApp(Project project, AppExtension appExt) + throws NoSuchFieldException, IllegalAccessException { + File sdkDir = appExt.getSdkDirectory(); + for (TestVariant testVariant : appExt.getTestVariants()) { + TestVariantData variantData = getVariantData(testVariant); + attachXmlProcessor(project, variantData, sdkDir, false); + } + for (ApplicationVariant appVariant : appExt.getApplicationVariants()) { + ApplicationVariantData variantData = getVariantData(appVariant); + attachXmlProcessor(project, variantData, sdkDir, false); + } + } + + private LibraryVariantData getVariantData(LibraryVariant variant) + throws NoSuchFieldException, IllegalAccessException { + Field field = LibraryVariantImpl.class.getDeclaredField("variantData"); + field.setAccessible(true); + return (LibraryVariantData) field.get(variant); + } + + private TestVariantData getVariantData(TestVariant variant) + throws IllegalAccessException, NoSuchFieldException { + Field field = TestVariantImpl.class.getDeclaredField("variantData"); + field.setAccessible(true); + return (TestVariantData) field.get(variant); + } + + private ApplicationVariantData getVariantData(ApplicationVariant variant) + throws IllegalAccessException, NoSuchFieldException { + Field field = ApplicationVariantImpl.class.getDeclaredField("variantData"); + field.setAccessible(true); + return (ApplicationVariantData) field.get(variant); + } + + private void attachXmlProcessor(Project project, final BaseVariantData variantData, + final File sdkDir, + final Boolean isLibrary) { + final GradleVariantConfiguration configuration = variantData.getVariantConfiguration(); + final ApiVersion minSdkVersion = configuration.getMinSdkVersion(); + ProcessAndroidResources generateRTask = variantData.generateRClassTask; + final String packageName = generateRTask.getPackageForR(); + String fullName = configuration.getFullName(); + List<File> resourceFolders = Arrays.asList(variantData.mergeResourcesTask.getOutputDir()); + + final File codeGenTargetFolder = new File(project.getBuildDir() + "/data-binding-info/" + + configuration.getDirName()); + String writerOutBase = codeGenTargetFolder.getAbsolutePath(); + JavaFileWriter fileWriter = new GradleFileWriter(writerOutBase); + final LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(packageName, resourceFolders, + fileWriter, minSdkVersion.getApiLevel(), isLibrary); + final ProcessAndroidResources processResTask = generateRTask; + final File xmlOutDir = new File(project.getBuildDir() + "/layout-info/" + + configuration.getDirName()); + final File generatedClassListOut = isLibrary ? new File(xmlOutDir, "_generated.txt") : null; + logD("xml output for %s is %s", variantData, xmlOutDir); + String layoutTaskName = "dataBindingLayouts" + StringUtils + .capitalize(processResTask.getName()); + String infoClassTaskName = "dataBindingInfoClass" + StringUtils + .capitalize(processResTask.getName()); + + final DataBindingProcessLayoutsTask[] processLayoutsTasks + = new DataBindingProcessLayoutsTask[1]; + project.getTasks().create(layoutTaskName, + DataBindingProcessLayoutsTask.class, + new Action<DataBindingProcessLayoutsTask>() { + @Override + public void execute(final DataBindingProcessLayoutsTask task) { + processLayoutsTasks[0] = task; + task.setXmlProcessor(xmlProcessor); + task.setSdkDir(sdkDir); + task.setXmlOutFolder(xmlOutDir); + task.setMinSdk(minSdkVersion.getApiLevel()); + + logD("TASK adding dependency on %s for %s", task, processResTask); + processResTask.dependsOn(task); + for (Object dep : processResTask.getDependsOn()) { + if (dep == task) { + continue; + } + logD("adding dependency on %s for %s", dep, task); + task.dependsOn(dep); + } + processResTask.doLast(new Action<Task>() { + @Override + public void execute(Task unused) { + try { + task.writeLayoutXmls(); + } catch (JAXBException e) { + logE(e, "cannot write layout xmls"); + } + } + }); + } + }); + final DataBindingProcessLayoutsTask processLayoutsTask = processLayoutsTasks[0]; + project.getTasks().create(infoClassTaskName, + DataBindingExportInfoTask.class, + new Action<DataBindingExportInfoTask>() { + + @Override + public void execute(DataBindingExportInfoTask task) { + task.dependsOn(processLayoutsTask); + task.dependsOn(processResTask); + task.setXmlProcessor(xmlProcessor); + task.setSdkDir(sdkDir); + task.setXmlOutFolder(xmlOutDir); + task.setExportClassListTo(generatedClassListOut); + task.setEnableDebugLogs(logger.isEnabled(LogLevel.DEBUG)); + variantData.registerJavaGeneratingTask(task, codeGenTargetFolder); + } + }); + String packageJarTaskName = "package" + StringUtils.capitalize(fullName) + "Jar"; + final Task packageTask = project.getTasks().findByName(packageJarTaskName); + if (packageTask instanceof Jar) { + String removeGeneratedTaskName = "dataBindingExcludeGeneratedFrom" + + StringUtils.capitalize(packageTask.getName()); + if (project.getTasks().findByName(removeGeneratedTaskName) == null) { + final AbstractCompile javaCompileTask = variantData.javaCompileTask; + Preconditions.checkNotNull(javaCompileTask); + + project.getTasks().create(removeGeneratedTaskName, + DataBindingExcludeGeneratedTask.class, + new Action<DataBindingExcludeGeneratedTask>() { + @Override + public void execute(DataBindingExcludeGeneratedTask task) { + packageTask.dependsOn(task); + task.dependsOn(javaCompileTask); + task.setAppPackage(packageName); + task.setInfoClassQualifiedName(xmlProcessor.getInfoClassFullName()); + task.setPackageTask((Jar) packageTask); + task.setLibrary(isLibrary); + task.setGeneratedClassListFile(generatedClassListOut); + } + }); + } + } + } + + private void logD(String s, Object... args) { + logger.info(formatLog(s, args)); + } + + private void logE(Throwable t, String s, Object... args) { + logger.error(formatLog(s, args), t); + } + + private String formatLog(String s, Object... args) { + return "[data binding plugin]: " + String.format(s, args); + } +} diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java new file mode 100644 index 00000000..54774674 --- /dev/null +++ b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 android.databinding.tool; + +import android.databinding.tool.util.L; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; +import kotlin.properties.Delegates; +import java.io.File; + +/** + * Task to pass environment info to javac + */ +public class DataBindingExportInfoTask extends DefaultTask { + private LayoutXmlProcessor xmlProcessor; + private File sdkDir; + private File xmlOutFolder; + private File exportClassListTo; + private boolean enableDebugLogs = false; + @TaskAction + public void exportInfo() { + xmlProcessor.writeInfoClass(sdkDir, xmlOutFolder, exportClassListTo, enableDebugLogs); + } + + public LayoutXmlProcessor getXmlProcessor() { + return xmlProcessor; + } + + public void setXmlProcessor(LayoutXmlProcessor xmlProcessor) { + this.xmlProcessor = xmlProcessor; + } + + public File getSdkDir() { + return sdkDir; + } + + public void setSdkDir(File sdkDir) { + this.sdkDir = sdkDir; + } + + public File getXmlOutFolder() { + return xmlOutFolder; + } + + public void setXmlOutFolder(File xmlOutFolder) { + this.xmlOutFolder = xmlOutFolder; + } + + public File getExportClassListTo() { + return exportClassListTo; + } + + public void setExportClassListTo(File exportClassListTo) { + this.exportClassListTo = exportClassListTo; + } + + public boolean isEnableDebugLogs() { + return enableDebugLogs; + } + + public void setEnableDebugLogs(boolean enableDebugLogs) { + this.enableDebugLogs = enableDebugLogs; + } +}
\ No newline at end of file diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingProcessLayoutsTask.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingProcessLayoutsTask.java new file mode 100644 index 00000000..7a6100d2 --- /dev/null +++ b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingProcessLayoutsTask.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 android.databinding.tool; + +import android.databinding.tool.util.L; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; +import org.xml.sax.SAXException; +import java.io.File; +import java.io.IOException; + +import javax.xml.bind.JAXBException; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; + +/** + * Task that parses xml files and generated metadata. + * Will be removed when aapt supports binding tags. + */ +public class DataBindingProcessLayoutsTask extends DefaultTask { + + private LayoutXmlProcessor xmlProcessor; + + private File sdkDir; + + private File xmlOutFolder; + + private int minSdk; + + @TaskAction + public void processResources() + throws ParserConfigurationException, SAXException, XPathExpressionException, + IOException { + L.d("running process layouts task %s", getName()); + xmlProcessor.processResources(minSdk); + } + + public void writeLayoutXmls() throws JAXBException { + xmlProcessor.writeLayoutInfoFiles(xmlOutFolder); + } + + public LayoutXmlProcessor getXmlProcessor() { + return xmlProcessor; + } + + public void setXmlProcessor(LayoutXmlProcessor xmlProcessor) { + this.xmlProcessor = xmlProcessor; + } + + public File getSdkDir() { + return sdkDir; + } + + public void setSdkDir(File sdkDir) { + this.sdkDir = sdkDir; + } + + public File getXmlOutFolder() { + return xmlOutFolder; + } + + public void setXmlOutFolder(File xmlOutFolder) { + this.xmlOutFolder = xmlOutFolder; + } + + public int getMinSdk() { + return minSdk; + } + + public void setMinSdk(int minSdk) { + this.minSdk = minSdk; + } +} diff --git a/gradlePlugin/src/main/kotlin/DataBindingExportInfoTask.kt b/gradlePlugin/src/main/kotlin/DataBindingExportInfoTask.kt deleted file mode 100644 index ee99c6b4..00000000 --- a/gradlePlugin/src/main/kotlin/DataBindingExportInfoTask.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed 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 android.databinding.tool - -import android.databinding.tool.util.L -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.TaskAction -import kotlin.properties.Delegates -import java.io.File - -open class DataBindingExportInfoTask : DefaultTask() { - var xmlProcessor: LayoutXmlProcessor by Delegates.notNull() - var sdkDir : File by Delegates.notNull() - var xmlOutFolder : File by Delegates.notNull() - var exportClassListTo : File? = null - var enableDebugLogs = false - @TaskAction - public fun doIt() { - L.d("running process layouts task %s", getName()) - xmlProcessor.writeInfoClass(sdkDir, xmlOutFolder, exportClassListTo, enableDebugLogs) - } -}
\ No newline at end of file diff --git a/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt b/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt deleted file mode 100644 index 3bc0e0fb..00000000 --- a/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed 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 android.databinding.tool - -import android.databinding.tool.util.L -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.TaskAction -import kotlin.properties.Delegates -import java.io.File - -open class DataBindingProcessLayoutsTask : DefaultTask() { - var xmlProcessor: LayoutXmlProcessor by Delegates.notNull() - var sdkDir : File by Delegates.notNull() - var xmlOutFolder : File by Delegates.notNull() - var minSdk: kotlin.Int by Delegates.notNull() - - @TaskAction - public fun doIt() { - L.d("running process layouts task %s", getName()) - xmlProcessor.processResources(minSdk) - } - - public fun writeLayoutXmls() { - xmlProcessor.writeLayoutInfoFiles(xmlOutFolder) - } -} diff --git a/gradlePlugin/src/main/kotlin/plugin.kt b/gradlePlugin/src/main/kotlin/plugin.kt deleted file mode 100644 index e63e32df..00000000 --- a/gradlePlugin/src/main/kotlin/plugin.kt +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed 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 android.databinding.tool - -import android.databinding.tool.expr.Dependency -import org.gradle.api.Plugin -import org.gradle.api.Project -import com.android.build.gradle.AppExtension -import com.android.build.gradle.internal.api.ApplicationVariantImpl -import com.android.build.gradle.internal.variant.ApplicationVariantData -import java.io.File -import org.gradle.api.file.FileCollection -import android.databinding.tool.writer.JavaFileWriter -import org.gradle.api.Action -import com.android.build.gradle.BaseExtension -import com.android.build.gradle.LibraryExtension -import com.android.build.gradle.api.LibraryVariant -import com.android.build.gradle.api.ApplicationVariant -import com.android.build.gradle.internal.variant.BaseVariantData -import com.android.build.gradle.internal.variant.LibraryVariantData -import com.android.build.gradle.internal.api.LibraryVariantImpl -import com.android.build.gradle.api.TestVariant -import com.android.build.gradle.internal.variant.TestVariantData -import com.android.build.gradle.internal.api.TestVariantImpl -import com.android.ide.common.res2.ResourceSet -import com.google.common.base.Preconditions -import org.apache.commons.io.IOUtils -import org.gradle.api.artifacts -import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency -import org.gradle.api.logging.LogLevel -import java.io.FileFilter -import java.io.FilenameFilter -import java.util.ArrayList -import javax.inject.Inject -import javax.xml.xpath.XPathFactory -import kotlin.dom.elements -import kotlin.dom.parseXml -import kotlin.properties.Delegates -import org.gradle.api.logging.Logger -import org.gradle.api.plugins.ExtraPropertiesExtension - - -open class DataBinderPlugin : Plugin<Project> { - var logger : Logger by Delegates.notNull() - - inner class GradleFileWriter(var outputBase: String) : JavaFileWriter() { - override fun writeToFile(canonicalName: String, contents: String) { - val f = File("$outputBase/${canonicalName.replace("\\.".toRegex(), "/")}.java") - logD("Asked to write to ${canonicalName}. outputting to:${f.getAbsolutePath()}") - f.getParentFile().mkdirs() - f.writeText(contents, "utf-8") - } - } - - fun readMyVersion() : String { - val stream = javaClass.getResourceAsStream("/data_binding_build_info") - return IOUtils.toString(stream, "utf-8").trim() - } - - override fun apply(project: Project?) { - if (project == null) return - - logger = project.getLogger() - - var myVersion = readMyVersion() - logD("data binding plugin version is ${myVersion}") - if (myVersion == "") { - throw IllegalStateException("cannot read version of the plugin :/") - } - - project.getDependencies().add("compile", "com.android.databinding:library:$myVersion") - var addAdapters = true - if (project.hasProperty("ext")) { - val ext = project.getProperties().get("ext") - if (ext is ExtraPropertiesExtension) { - if (ext.has("addDataBindingAdapters")) { - addAdapters = ext.get("addDataBindingAdapters") as Boolean - } - } - } - if (addAdapters) { - project.getDependencies().add("compile", "com.android.databinding:adapters:$myVersion") - } - project.getDependencies().add("provided", "com.android.databinding:compiler:$myVersion") - project.afterEvaluate { - createXmlProcessor(project) - } - } - - fun logD(s: String) { - log(LogLevel.INFO, s) - } - fun logE(s: String) { - log(LogLevel.ERROR, s) - } - fun log(level : LogLevel, s: String) { - logger.log(level, "[data binding plugin]: $s") - } - - fun createXmlProcessor(p: Project) { - val androidExt = p.getExtensions().getByName("android") - if (androidExt !is BaseExtension) { - return - } - if (androidExt is AppExtension) { - createXmlProcessorForApp(p, androidExt) - } else if (androidExt is LibraryExtension) { - createXmlProcessorForLibrary(p, androidExt) - } else { - logE("unsupported android extension. What is it? ${androidExt}") - throw RuntimeException("unsupported android extension. What is it? ${androidExt}") - } - } - - fun createXmlProcessorForLibrary(project : Project, lib : LibraryExtension) { - val sdkDir = lib.getSdkDirectory() - lib.getTestVariants().forEach { variant -> - logD("test variant $variant. dir name ${variant.getDirName()}") - val variantData = getVariantData(variant) - attachXmlProcessor(project, variantData, sdkDir, false)//tests extend apk variant - } - lib.getLibraryVariants().forEach { variant -> - logD("lib variant $variant . dir name ${variant.getDirName()}") - val variantData = getVariantData(variant) - attachXmlProcessor(project, variantData, sdkDir, true) - } - } - - fun getVariantData(appVariant : LibraryVariant) : LibraryVariantData { - val clazz = javaClass<LibraryVariantImpl>() - val field = clazz.getDeclaredField("variantData") - field.setAccessible(true) - return field.get(appVariant) as LibraryVariantData - } - - fun getVariantData(testVariant : TestVariant) : TestVariantData { - val clazz = javaClass<TestVariantImpl>() - val field = clazz.getDeclaredField("variantData") - field.setAccessible(true) - return field.get(testVariant) as TestVariantData - } - - fun getVariantData(appVariant : ApplicationVariant) : ApplicationVariantData { - val clazz = javaClass<ApplicationVariantImpl>() - val field = clazz.getDeclaredField("variantData") - field.setAccessible(true) - return field.get(appVariant) as ApplicationVariantData - } - - fun createXmlProcessorForApp(project : Project, appExt: AppExtension) { - val sdkDir = appExt.getSdkDirectory() - appExt.getTestVariants().forEach { testVariant -> - val variantData = getVariantData(testVariant) - attachXmlProcessor(project, variantData, sdkDir, false) - } - appExt.getApplicationVariants().forEach { appVariant -> - val variantData = getVariantData(appVariant) - attachXmlProcessor(project, variantData, sdkDir, false) - } - } - - fun attachXmlProcessor(project : Project, variantData : BaseVariantData<*>, sdkDir : File, - isLibrary : Boolean) { - val configuration = variantData.getVariantConfiguration() - val minSdkVersion = configuration.getMinSdkVersion() - val generateRTask = variantData.generateRClassTask - val packageName = generateRTask.getPackageForR() - val fullName = configuration.getFullName() - val resourceFolders = arrayListOf(variantData.mergeResourcesTask.getOutputDir()) - - val codeGenTargetFolder = File("${project.getBuildDir()}/data-binding-info/${configuration.getDirName()}") - val writerOutBase = codeGenTargetFolder.getAbsolutePath(); - val fileWriter = GradleFileWriter(writerOutBase) - val xmlProcessor = LayoutXmlProcessor(packageName, resourceFolders, fileWriter, - minSdkVersion.getApiLevel(), isLibrary) - val processResTask = generateRTask - val xmlOutDir = File("${project.getBuildDir()}/layout-info/${configuration.getDirName()}") - val generatedClassListOut = if (isLibrary) File(xmlOutDir, "_generated.txt") else null - logD("xml output for ${variantData} is ${xmlOutDir}") - val layoutTaskName = "dataBindingLayouts${processResTask.getName().capitalize()}" - val infoClassTaskName = "dataBindingInfoClass${processResTask.getName().capitalize()}" - - var processLayoutsTask: DataBindingProcessLayoutsTask? = null - project.getTasks().create(layoutTaskName, - javaClass<DataBindingProcessLayoutsTask>(), - object : Action<DataBindingProcessLayoutsTask> { - override fun execute(task: DataBindingProcessLayoutsTask) { - processLayoutsTask = task - task.xmlProcessor = xmlProcessor - task.sdkDir = sdkDir - task.xmlOutFolder = xmlOutDir - task.minSdk = minSdkVersion.getApiLevel() - - logD("TASK adding dependency on ${task} for ${processResTask}") - processResTask.dependsOn(task) - processResTask.getDependsOn().filterNot { it == task }.forEach { - logD("adding dependency on ${it} for ${task}") - task.dependsOn(it) - } - processResTask.doLast { - task.writeLayoutXmls() - } - } - }) - project.getTasks().create(infoClassTaskName, - javaClass<DataBindingExportInfoTask>(), - object : Action<DataBindingExportInfoTask> { - override fun execute(task: DataBindingExportInfoTask) { - task.dependsOn(processLayoutsTask!!) - task.dependsOn(processResTask) - task.xmlProcessor = xmlProcessor - task.sdkDir = sdkDir - task.xmlOutFolder = xmlOutDir - task.exportClassListTo = generatedClassListOut - task.enableDebugLogs = logger.isEnabled(LogLevel.DEBUG) - variantData.registerJavaGeneratingTask(task, codeGenTargetFolder) - } - }) - val packageJarTaskName = "package${fullName.capitalize()}Jar" - val packageTask = project.getTasks().findByName(packageJarTaskName) - if (packageTask is org.gradle.api.tasks.bundling.Jar) { - val removeGeneratedTaskName = "dataBindingExcludeGeneratedFrom${packageTask.getName().capitalize()}" - if (project.getTasks().findByName(removeGeneratedTaskName) == null) { - val javaCompileTask = variantData.javaCompileTask - Preconditions.checkNotNull(javaCompileTask) - - project.getTasks().create(removeGeneratedTaskName, - javaClass<DataBindingExcludeGeneratedTask>(), - object : Action<DataBindingExcludeGeneratedTask> { - override fun execute(task: DataBindingExcludeGeneratedTask) { - packageTask.dependsOn(task) - task.dependsOn(javaCompileTask) - task.setAppPackage(packageName) - task.setInfoClassQualifiedName(xmlProcessor.getInfoClassFullName()) - task.setPackageTask(packageTask) - task.setLibrary(isLibrary) - task.setGeneratedClassListFile(generatedClassListOut) - } - }) - } - } - } -} |