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:
parent
c10acfbaf7
commit
ca1eff62ce
6 changed files with 213 additions and 55 deletions
|
@ -32,6 +32,8 @@ dependencies {
|
||||||
'org.jetbrains.dokka:dokka-fatjar:0.9.2',
|
'org.jetbrains.dokka:dokka-fatjar:0.9.2',
|
||||||
// "org.jetbrains.kotlin:kotlin-compiler:${kotlin_version}",
|
// "org.jetbrains.kotlin:kotlin-compiler:${kotlin_version}",
|
||||||
|
|
||||||
|
"com.android.tools.build:builder:1.5.0",
|
||||||
|
|
||||||
'com.beust:jcommander:1.48',
|
'com.beust:jcommander:1.48',
|
||||||
'com.squareup.okhttp:okhttp:2.5.0',
|
'com.squareup.okhttp:okhttp:2.5.0',
|
||||||
'org.jsoup:jsoup:1.8.3',
|
'org.jsoup:jsoup:1.8.3',
|
||||||
|
|
|
@ -128,6 +128,8 @@ private class Main @Inject constructor(
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private fun runWithArgs(jc: JCommander, args: Args, argv: Array<String>): Int {
|
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 file = File("/Users/beust/.kobalt/repository/com/google/guava/guava/19.0-rc2/guava-19.0-rc2.pom")
|
||||||
// val md5 = Md5.toMd5(file)
|
// val md5 = Md5.toMd5(file)
|
||||||
// val md52 = MessageDigest.getInstance("MD5").digest(file.readBytes()).toHexString()
|
// val md52 = MessageDigest.getInstance("MD5").digest(file.readBytes()).toHexString()
|
||||||
|
|
|
@ -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("")
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ import com.google.inject.Inject
|
||||||
import com.google.inject.Singleton
|
import com.google.inject.Singleton
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.net.URI
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
@ -98,11 +97,11 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v
|
||||||
runBefore = arrayOf("compile"), runAfter = arrayOf("clean"))
|
runBefore = arrayOf("compile"), runAfter = arrayOf("clean"))
|
||||||
fun taskGenerateRFile(project: Project): TaskResult {
|
fun taskGenerateRFile(project: Project): TaskResult {
|
||||||
|
|
||||||
merger.merge(project, context)
|
|
||||||
|
|
||||||
val intermediates = AndroidFiles.intermediates(project)
|
val intermediates = AndroidFiles.intermediates(project)
|
||||||
val resDir = "temporaryBogusResDir"
|
val resDir = AndroidFiles.mergedResources(project, context.variant)
|
||||||
explodeAarFiles(project, intermediates, File(resDir))
|
val explodedLocations = explodeAarFiles(project, intermediates)
|
||||||
|
merger.merge(project, context, explodedLocations)
|
||||||
val generated = AndroidFiles.generated(project)
|
val generated = AndroidFiles.generated(project)
|
||||||
val success = generateR(project, generated, aapt(project))
|
val success = generateR(project, generated, aapt(project))
|
||||||
return TaskResult(success)
|
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,
|
* 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
|
* 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 {
|
project.compileDependencies.filter {
|
||||||
it.jarFile.get().name.endsWith(".aar")
|
it.jarFile.get().name.endsWith(".aar")
|
||||||
}.forEach {
|
}.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
|
result.add(destDir)
|
||||||
log(2, "Copying the resources to $resDir")
|
|
||||||
KFiles.copyRecursively(destDir.resolve("res"), resDir, deleteFirst = false)
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compile(project: Project, rDirectory: String): File {
|
private fun compile(project: Project, rDirectory: String): File {
|
||||||
|
@ -350,10 +349,11 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v
|
||||||
}
|
}
|
||||||
|
|
||||||
// IRepoContributor
|
// IRepoContributor
|
||||||
override fun reposFor(project: Project?): List<URI> {
|
override fun reposFor(project: Project?): List<HostInfo> {
|
||||||
val home = androidHomeNoThrows(project)
|
val home = androidHomeNoThrows(project)
|
||||||
return if (home != null) {
|
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 {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
package com.beust.kobalt.plugin.android
|
package com.beust.kobalt.plugin.android
|
||||||
|
|
||||||
|
import com.beust.kobalt.KobaltException
|
||||||
import com.beust.kobalt.Variant
|
import com.beust.kobalt.Variant
|
||||||
import com.beust.kobalt.api.KobaltContext
|
import com.beust.kobalt.api.KobaltContext
|
||||||
import com.beust.kobalt.api.Project
|
import com.beust.kobalt.api.Project
|
||||||
import com.beust.kobalt.misc.KFiles
|
import com.beust.kobalt.misc.KFiles
|
||||||
import com.beust.kobalt.misc.log
|
import com.beust.kobalt.misc.log
|
||||||
import com.google.inject.Inject
|
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.Files
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.xml.bind.JAXBContext
|
import javax.xml.bind.JAXBContext
|
||||||
import javax.xml.bind.Marshaller
|
|
||||||
import javax.xml.bind.annotation.XmlAttribute
|
import javax.xml.bind.annotation.XmlAttribute
|
||||||
import javax.xml.bind.annotation.XmlElement
|
import javax.xml.bind.annotation.XmlElement
|
||||||
import javax.xml.bind.annotation.XmlRootElement
|
import javax.xml.bind.annotation.XmlRootElement
|
||||||
|
@ -21,17 +22,17 @@ import javax.xml.bind.annotation.XmlValue
|
||||||
* Merges manifests and resources.
|
* Merges manifests and resources.
|
||||||
*/
|
*/
|
||||||
class Merger @Inject constructor() {
|
class Merger @Inject constructor() {
|
||||||
fun merge(project: Project, context: KobaltContext) {
|
fun merge(project: Project, context: KobaltContext, explodedLocations: List<File>) {
|
||||||
File(AndroidFiles.mergedResourcesNoVariant(project)).deleteRecursively()
|
File(AndroidFiles.mergedResourcesNoVariant(project)).deleteRecursively()
|
||||||
mergeResources(project, context.variant)
|
mergeResources(project, context.variant, explodedLocations)
|
||||||
mergeAndroidManifest(project, context.variant)
|
mergeAndroidManifest(project, context.variant, explodedLocations)
|
||||||
log(2, "All done merging")
|
log(2, "All done merging")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: not implemented yet, just copying the manifest to where the merged manifest should be.
|
* 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)
|
val dest = AndroidFiles.mergedManifest(project, variant)
|
||||||
log(2, "----- Merging manifest (not implemented, copying it to $dest)")
|
log(2, "----- Merging manifest (not implemented, copying it to $dest)")
|
||||||
KFiles.copy(Paths.get(project.directory, "src/main/AndroidManifest.xml"), Paths.get(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)
|
fun doMerge(fromFile: File, toFile: File)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
class ValueFile(file: File) {
|
||||||
* Merge files found in values/, e.g. values/strings.xml.
|
private var header = ""
|
||||||
* All the files are enumerated for each one, look if a file by the same name is present in the merged
|
private var footer = ""
|
||||||
* directory. If not, copy it. If there is, look for a merger for this file and run it.
|
private val lines = arrayListOf<String>()
|
||||||
*/
|
private val lineSet = hashSetOf<String>()
|
||||||
class ValuesFileMerger : IFileMerger {
|
|
||||||
override fun canMerge(fromFile: File, toFile: File) : Boolean {
|
init {
|
||||||
return fromFile.parentFile.name == "values"
|
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) {
|
fun merge(file: File) : ValueFile {
|
||||||
FileInputStream(toFile).use { toInputStream ->
|
fun log(s: String) {
|
||||||
val toXml = readValuesXml(toInputStream)
|
log(3, " Merge(${file.name}): $s")
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
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
|
* 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)
|
val dest = AndroidFiles.Companion.mergedResources(project, variant)
|
||||||
log(2, "----- Merging res/ directory to $dest")
|
log(2, "----- Merging res/ directory to $dest")
|
||||||
listOf(variant.buildType.name, variant.productFlavor.name, "main").forEach {
|
val srcFromDirs = listOf(variant.buildType.name, variant.productFlavor.name, "main").map {
|
||||||
log(3, " Current variant: $it")
|
File(project.directory, "src/$it/res")
|
||||||
|
}
|
||||||
val fromDir = 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 {
|
KFiles.findRecursively(fromDir).forEach {
|
||||||
val fromFile = File(fromDir, it)
|
val fromFile = File(fromDir, it)
|
||||||
val toFile = File(dest, it)
|
val toFile = File(dest, it)
|
||||||
|
@ -131,6 +185,7 @@ class Merger @Inject constructor() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println("DONE MERGING")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package com.beust.kobalt.plugin.android
|
||||||
|
|
||||||
|
//import com.android.builder.dependency.LibraryDependency
|
Loading…
Add table
Add a link
Reference in a new issue