1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-27 08:38:13 -07:00

New android build work.

This commit is contained in:
Cedric Beust 2015-12-02 02:34:12 -08:00
parent c10acfbaf7
commit ca1eff62ce
6 changed files with 213 additions and 55 deletions

View file

@ -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',

View file

@ -128,6 +128,8 @@ private class Main @Inject constructor(
// }
private fun runWithArgs(jc: JCommander, args: Args, argv: Array<String>): 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()

View file

@ -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("")
}
}

View file

@ -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<File> {
val result = arrayListOf<File>()
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<URI> {
override fun reposFor(project: Project?): List<HostInfo> {
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()
}

View file

@ -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>) {
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<File>) {
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<String>()
private val lineSet = hashSetOf<String>()
init {
file.forEachLine { line ->
if (line.contains("<resources>") && header.isNullOrBlank()) {
lineSet.add(line)
header = line
} else if (line.contains("</resources>") && 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<String>()
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<String>()
// 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<File>) {
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")
}
}

View file

@ -0,0 +1,3 @@
package com.beust.kobalt.plugin.android
//import com.android.builder.dependency.LibraryDependency