diff --git a/build.gradle b/build.gradle index f1f94066..8167f7b8 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,8 @@ dependencies { 'org.jetbrains.dokka:dokka-fatjar:0.9.2', // "org.jetbrains.kotlin:kotlin-compiler:${kotlin_version}", + "com.android.tools.build:builder:1.5.0", + 'com.beust:jcommander:1.48', 'com.squareup.okhttp:okhttp:2.5.0', 'org.jsoup:jsoup:1.8.3', diff --git a/src/main/kotlin/com/beust/kobalt/Main.kt b/src/main/kotlin/com/beust/kobalt/Main.kt index a446cc99..f0c0d398 100644 --- a/src/main/kotlin/com/beust/kobalt/Main.kt +++ b/src/main/kotlin/com/beust/kobalt/Main.kt @@ -128,6 +128,8 @@ private class Main @Inject constructor( // } private fun runWithArgs(jc: JCommander, args: Args, argv: Array): Int { + //@@ +// AndroidBuild().run() // val file = File("/Users/beust/.kobalt/repository/com/google/guava/guava/19.0-rc2/guava-19.0-rc2.pom") // val md5 = Md5.toMd5(file) // val md52 = MessageDigest.getInstance("MD5").digest(file.readBytes()).toHexString() diff --git a/src/main/kotlin/com/beust/kobalt/plugin/android/AndroidBuild.kt b/src/main/kotlin/com/beust/kobalt/plugin/android/AndroidBuild.kt new file mode 100644 index 00000000..06faa139 --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/plugin/android/AndroidBuild.kt @@ -0,0 +1,96 @@ +package com.beust.kobalt.plugin.android + +import com.android.builder.core.AndroidBuilder +import com.android.builder.core.ErrorReporter +import com.android.builder.model.SyncIssue +import com.android.builder.sdk.DefaultSdkLoader +import com.android.builder.sdk.SdkLoader +import com.android.ide.common.blame.Message +import com.android.ide.common.process.* +import com.android.ide.common.res2.MergedResourceWriter +import com.android.ide.common.res2.NoOpResourcePreprocessor +import com.android.utils.StdLogger +import com.beust.kobalt.homeDir +import com.beust.kobalt.misc.KFiles +import com.beust.kobalt.misc.log +import java.io.File + +class KobaltProcessResult : ProcessResult { + override fun getExitValue(): Int { + return 0 + } + + override fun assertNormalExitValue(): ProcessResult? { + throw UnsupportedOperationException() + } + + override fun rethrowFailure(): ProcessResult? { + throw UnsupportedOperationException() + } +} + +class KobaltJavaProcessExecutor : JavaProcessExecutor { + override fun execute(javaProcessInfo: JavaProcessInfo?, processOutputHandler: ProcessOutputHandler?) + : ProcessResult? { + log(1, "Executing " + javaProcessInfo!!) + return KobaltProcessResult() + } +} + +class KobaltProcessOutputHandler : BaseProcessOutputHandler() { + override fun handleOutput(processOutput: ProcessOutput) = + log(1, "AndroidBuild output" + processOutput.standardOutput) +} + +class KobaltErrorReporter : ErrorReporter(ErrorReporter.EvaluationMode.STANDARD) { + override fun handleSyncError(data: String?, type: Int, msg: String?): SyncIssue? { + throw UnsupportedOperationException() + } + + override fun receiveMessage(message: Message?) { + throw UnsupportedOperationException() + } +} + +class ProjectLayout { + val mergeBlame: File? = null + val publicText: File? = null + +} + +class AndroidBuild { + val annotationsJar = File("/Users/beust/adt-bundle-mac-x86_64-20140702/sdk/tools/lib/annotations.jar") + val adb = File("/Users/beust/adt-bundle-mac-x86_64-20140702/sdk/platform-tools/adb") + + fun run() { + val logger = StdLogger(StdLogger.Level.VERBOSE) + val processExecutor = DefaultProcessExecutor(logger) + val javaProcessExecutor = KobaltJavaProcessExecutor() + val sdkLoader : SdkLoader = DefaultSdkLoader.getLoader(File("/Users/beust/adt-bundle-mac-x86_64-20140702/sdk")) + val repositories = sdkLoader.repositories + val androidBuilder = AndroidBuilder("com.beust.kobalt", "Cedric Beust", + processExecutor, + javaProcessExecutor, + KobaltErrorReporter(), + StdLogger(StdLogger.Level.VERBOSE), + true /* verbose */) + val sdkInfo = androidBuilder.sdkInfo + val target = androidBuilder.target + val dxJar = androidBuilder.dxJar + + val processOutputHandler = KobaltProcessOutputHandler() + val dir : String = KFiles.joinDir(homeDir("kotlin/kobalt-examples/android-flavors")) + val layout = ProjectLayout() + val preprocessor = NoOpResourcePreprocessor() + + val writer = MergedResourceWriter(File(dir), + androidBuilder.getAaptCruncher(processOutputHandler), + false /* don't crunch */, + false /* don't process 9patch */, + layout.publicText, + layout.mergeBlame, + preprocessor) + println("Repositories: $repositories") + println("") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/beust/kobalt/plugin/android/AndroidPlugin.kt b/src/main/kotlin/com/beust/kobalt/plugin/android/AndroidPlugin.kt index 0f720403..a5d7341d 100644 --- a/src/main/kotlin/com/beust/kobalt/plugin/android/AndroidPlugin.kt +++ b/src/main/kotlin/com/beust/kobalt/plugin/android/AndroidPlugin.kt @@ -15,7 +15,6 @@ import com.google.inject.Inject import com.google.inject.Singleton import java.io.File import java.io.FileInputStream -import java.net.URI import java.nio.file.Path import java.nio.file.Paths @@ -98,11 +97,11 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v runBefore = arrayOf("compile"), runAfter = arrayOf("clean")) fun taskGenerateRFile(project: Project): TaskResult { - merger.merge(project, context) val intermediates = AndroidFiles.intermediates(project) - val resDir = "temporaryBogusResDir" - explodeAarFiles(project, intermediates, File(resDir)) + val resDir = AndroidFiles.mergedResources(project, context.variant) + val explodedLocations = explodeAarFiles(project, intermediates) + merger.merge(project, context, explodedLocations) val generated = AndroidFiles.generated(project) val success = generateR(project, generated, aapt(project)) return TaskResult(success) @@ -166,7 +165,8 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v * Extract all the .aar files found in the dependencies and add the android.jar to classpathEntries, * which will be added to the classpath at compile time */ - private fun explodeAarFiles(project: Project, outputDir: String, resDir: File) { + private fun explodeAarFiles(project: Project, outputDir: String) : List { + val result = arrayListOf() project.compileDependencies.filter { it.jarFile.get().name.endsWith(".aar") }.forEach { @@ -189,10 +189,9 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v } } - // Copy all the resources from this aar into the same intermediate directory - log(2, "Copying the resources to $resDir") - KFiles.copyRecursively(destDir.resolve("res"), resDir, deleteFirst = false) + result.add(destDir) } + return result } private fun compile(project: Project, rDirectory: String): File { @@ -350,10 +349,11 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v } // IRepoContributor - override fun reposFor(project: Project?): List { + override fun reposFor(project: Project?): List { val home = androidHomeNoThrows(project) return if (home != null) { - listOf(Paths.get(KFiles.joinDir(home, "extras", "android", "m2repository")).toUri()) + val uri = Paths.get(KFiles.joinDir(home, "extras", "android", "m2repository")).toUri() + listOf(HostInfo(uri.toString())) } else { emptyList() } diff --git a/src/main/kotlin/com/beust/kobalt/plugin/android/Merger.kt b/src/main/kotlin/com/beust/kobalt/plugin/android/Merger.kt index 839c4a66..b43d686d 100644 --- a/src/main/kotlin/com/beust/kobalt/plugin/android/Merger.kt +++ b/src/main/kotlin/com/beust/kobalt/plugin/android/Merger.kt @@ -1,17 +1,18 @@ package com.beust.kobalt.plugin.android +import com.beust.kobalt.KobaltException import com.beust.kobalt.Variant import com.beust.kobalt.api.KobaltContext import com.beust.kobalt.api.Project import com.beust.kobalt.misc.KFiles import com.beust.kobalt.misc.log import com.google.inject.Inject -import java.io.* +import java.io.File +import java.io.InputStream import java.nio.file.Files import java.nio.file.Paths import java.util.* import javax.xml.bind.JAXBContext -import javax.xml.bind.Marshaller import javax.xml.bind.annotation.XmlAttribute import javax.xml.bind.annotation.XmlElement import javax.xml.bind.annotation.XmlRootElement @@ -21,17 +22,17 @@ import javax.xml.bind.annotation.XmlValue * Merges manifests and resources. */ class Merger @Inject constructor() { - fun merge(project: Project, context: KobaltContext) { + fun merge(project: Project, context: KobaltContext, explodedLocations: List) { File(AndroidFiles.mergedResourcesNoVariant(project)).deleteRecursively() - mergeResources(project, context.variant) - mergeAndroidManifest(project, context.variant) + mergeResources(project, context.variant, explodedLocations) + mergeAndroidManifest(project, context.variant, explodedLocations) log(2, "All done merging") } /** * TODO: not implemented yet, just copying the manifest to where the merged manifest should be. */ - private fun mergeAndroidManifest(project: Project, variant: Variant) { + private fun mergeAndroidManifest(project: Project, variant: Variant, explodedLocations: List) { val dest = AndroidFiles.mergedManifest(project, variant) log(2, "----- Merging manifest (not implemented, copying it to $dest)") KFiles.copy(Paths.get(project.directory, "src/main/AndroidManifest.xml"), Paths.get(dest)) @@ -42,43 +43,91 @@ class Merger @Inject constructor() { fun doMerge(fromFile: File, toFile: File) } - /** - * Merge files found in values/, e.g. values/strings.xml. - * All the files are enumerated for each one, look if a file by the same name is present in the merged - * directory. If not, copy it. If there is, look for a merger for this file and run it. - */ - class ValuesFileMerger : IFileMerger { - override fun canMerge(fromFile: File, toFile: File) : Boolean { - return fromFile.parentFile.name == "values" + class ValueFile(file: File) { + private var header = "" + private var footer = "" + private val lines = arrayListOf() + private val lineSet = hashSetOf() + + init { + file.forEachLine { line -> + if (line.contains("") && header.isNullOrBlank()) { + lineSet.add(line) + header = line + } else if (line.contains("") && footer.isNullOrBlank()) { + lineSet.add(line) + footer = line + } else { + if (footer.isNullOrBlank()) { + lines.add(line) + lineSet.add(line) + } else { + throw KobaltException("Extra text after the footer") + } + } + } } - override fun doMerge(fromFile: File, toFile: File) { - FileInputStream(toFile).use { toInputStream -> - val toXml = readValuesXml(toInputStream) - FileInputStream(fromFile).use { fromInputStream -> - val fromXml = readValuesXml(fromInputStream) - val seen = toXml.strings.map { it.name!! }.toHashSet() - fromXml.strings.forEach { - if (!seen.contains(it.name!!)) { - log(3, " Unconflicted string: ${it.name}") - toXml.strings.add(it) - } else { - log(3, " String ${it.name} already present, ignoring") - } - } - } - val mergedText = StringWriter() - val pw = PrintWriter(mergedText) - - JAXBContext.newInstance(ValuesXml::class.java).createMarshaller().let { marshaller -> - with(marshaller) { - setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true) - setProperty(Marshaller.JAXB_ENCODING, "UTF-8") - marshal(toXml, pw) - } - } - KFiles.saveFile(toFile, mergedText.toString()) + fun merge(file: File) : ValueFile { + fun log(s: String) { + log(3, " Merge(${file.name}): $s") } + file.forEachLine { line -> + if (! lineSet.contains(line)) { + lines.add(line) + lineSet.add(line) + log("Unconflicted: $line") + } else { + log("Already present, ignoring: $line") + } + } + return this + } + + fun toXml() = (listOf(header) + lines + listOf(footer)).joinToString("\n") + } + + /** + * Merge files found in values/, e.g. values/strings.xml. + * All the files are enumerated. If a file by the same name is present in the merged + * directory, merge it. Otherwise, copy it. + */ + class ValuesFileMerger : IFileMerger { + override fun canMerge(fromFile: File, toFile: File) = fromFile.parentFile.name == "values" + + override fun doMerge(fromFile: File, toFile: File) { + if (toFile.path.endsWith("values.xml")) { + println("DONOTCOMMIT VALUES.XML") + } + + val mergedXml = ValueFile(fromFile).merge(toFile).toXml() + KFiles.saveFile(toFile, mergedXml.toString()) +// FileInputStream(toFile).use { toInputStream -> +// val toXml = readValuesXml(toInputStream) +// FileInputStream(fromFile).use { fromInputStream -> +// val fromXml = readValuesXml(fromInputStream) +// val seen = toXml.strings.map { it.name!! }.toHashSet() +// fromXml.strings.forEach { +// if (!seen.contains(it.name!!)) { +// log(3, " Unconflicted string: ${it.name}") +// toXml.strings.add(it) +// } else { +// log(3, " String ${it.name} already present, ignoring") +// } +// } +// } +// val mergedText = StringWriter() +// val pw = PrintWriter(mergedText) +// +// JAXBContext.newInstance(ValuesXml::class.java).createMarshaller().let { marshaller -> +// with(marshaller) { +// setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true) +// setProperty(Marshaller.JAXB_ENCODING, "UTF-8") +// marshal(toXml, pw) +// } +// } +// KFiles.saveFile(toFile, mergedText.toString()) +// } } @@ -109,13 +158,18 @@ class Merger @Inject constructor() { /** * Spec: http://developer.android.com/sdk/installing/studio-build.html */ - private fun mergeResources(project: Project, variant: Variant) { + private fun mergeResources(project: Project, variant: Variant, explodedLocations: List) { val dest = AndroidFiles.Companion.mergedResources(project, variant) log(2, "----- Merging res/ directory to $dest") - listOf(variant.buildType.name, variant.productFlavor.name, "main").forEach { - log(3, " Current variant: $it") - - val fromDir = File(project.directory, "src/$it/res") + val srcFromDirs = listOf(variant.buildType.name, variant.productFlavor.name, "main").map { + File(project.directory, "src/$it/res") + } + val fromDirs = explodedLocations.map { File(it, "res") } + srcFromDirs +// listOf(variant.buildType.name, variant.productFlavor.name, "main").forEach { +// log(3, " Current variant: $it") +// +// val fromDir = File(project.directory, "src/$it/res") + fromDirs.filter { it.exists() }.forEach { fromDir -> KFiles.findRecursively(fromDir).forEach { val fromFile = File(fromDir, it) val toFile = File(dest, it) @@ -131,6 +185,7 @@ class Merger @Inject constructor() { } } + println("DONE MERGING") } } diff --git a/src/main/kotlin/com/beust/kobalt/plugin/android/Merger2.kt b/src/main/kotlin/com/beust/kobalt/plugin/android/Merger2.kt new file mode 100644 index 00000000..63e9320d --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/plugin/android/Merger2.kt @@ -0,0 +1,3 @@ +package com.beust.kobalt.plugin.android + +//import com.android.builder.dependency.LibraryDependency \ No newline at end of file