mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-26 00:17:11 -07:00
Better variant support.
This commit is contained in:
parent
354668b6a3
commit
e16892dbdb
10 changed files with 194 additions and 60 deletions
|
@ -12,7 +12,16 @@ class Variant(val productFlavor: ProductFlavorConfig? = null, val buildType: Bui
|
|||
val isDefault : Boolean
|
||||
get() = productFlavor == null && buildType == null
|
||||
|
||||
fun toTask(taskName: String) = taskName + productFlavor?.name?.capitalize() + buildType?.name?.capitalize()
|
||||
fun toTask(taskName: String) = taskName +
|
||||
(productFlavor?.name?.capitalize() ?: "") +
|
||||
(buildType?.name?.capitalize() ?: "")
|
||||
|
||||
fun variantSourceDirectories(context: KobaltContext) : List<File> {
|
||||
val result =
|
||||
if (isDefault) listOf("src/main")
|
||||
else (listOf(buildType?.name) + listOf(productFlavor?.name)).filterNotNull()
|
||||
return result.map { File(it) }
|
||||
}
|
||||
|
||||
fun sourceDirectories(project: Project) : List<File> {
|
||||
val sourceDirectories = project.sourceDirectories.map { File(it) }
|
||||
|
@ -88,4 +97,36 @@ class Variant(val productFlavor: ProductFlavorConfig? = null, val buildType: Bui
|
|||
}
|
||||
}
|
||||
|
||||
override fun toString() = toTask("")
|
||||
|
||||
companion object {
|
||||
fun allVariants(project: Project): List<Variant> {
|
||||
val result = arrayListOf<Variant>()
|
||||
|
||||
if (project.buildTypes.size > 0) {
|
||||
project.buildTypes.keys.forEach {
|
||||
val bt = project.buildTypes[it]
|
||||
if (project.productFlavors.size > 0) {
|
||||
project.productFlavors.keys.forEach {
|
||||
result.add(Variant(project.productFlavors[it], bt))
|
||||
}
|
||||
} else {
|
||||
result.add(Variant(null, bt))
|
||||
}
|
||||
}
|
||||
} else if (project.productFlavors.size > 0) {
|
||||
project.productFlavors.keys.forEach {
|
||||
val pf = project.productFlavors[it]
|
||||
if (project.buildTypes.size > 0) {
|
||||
project.buildTypes.keys.forEach {
|
||||
result.add(Variant(pf, project.buildTypes[it]))
|
||||
}
|
||||
} else {
|
||||
result.add(Variant(pf, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,21 +32,16 @@ abstract public class BasePlugin : IPlugin {
|
|||
*/
|
||||
protected fun addVariantTasks(project: Project, taskName: String, runAfter : List<String>,
|
||||
runTask: (Project) -> TaskResult) {
|
||||
project.productFlavors.keys.forEach {
|
||||
val pf = project.productFlavors.get(it)
|
||||
project.buildTypes.keys.forEach { btName ->
|
||||
val bt = project.buildTypes[btName]
|
||||
val variant = Variant(pf, bt)
|
||||
Variant.allVariants(project).forEach { variant ->
|
||||
val taskName = variant.toTask(taskName)
|
||||
addTask(project, taskName, taskName,
|
||||
runAfter = runAfter.map { variant.toTask(it) },
|
||||
task = { p: Project ->
|
||||
context.variant = Variant(pf, bt)
|
||||
context.variant = variant
|
||||
runTask(project)
|
||||
TaskResult()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,9 @@ abstract class JvmCompilerPlugin @Inject constructor(
|
|||
@ExportedProjectProperty(doc = "Projects this project depends on", type = "List<ProjectDescription>")
|
||||
const val DEPENDENT_PROJECTS = "dependentProjects"
|
||||
|
||||
@ExportedProjectProperty(doc = "Compiler args", type = "List<String>")
|
||||
const val COMPILER_ARGS = "compilerArgs"
|
||||
|
||||
const val TASK_CLEAN = "clean"
|
||||
const val TASK_TEST = "test"
|
||||
|
||||
|
@ -133,10 +136,19 @@ abstract class JvmCompilerPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
protected val compilerArgs = arrayListOf<String>()
|
||||
private val compilerArgs = hashMapOf<String, List<String>>()
|
||||
|
||||
fun addCompilerArgs(vararg args: String) {
|
||||
compilerArgs.addAll(args)
|
||||
protected fun compilerArgsFor(project: Project) : List<String> {
|
||||
val result = project.projectProperties.get(COMPILER_ARGS)
|
||||
if (result != null) {
|
||||
return result as List<String>
|
||||
} else {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun addCompilerArgs(project: Project, vararg args: String) {
|
||||
project.projectProperties.put(COMPILER_ARGS, arrayListOf(*args))
|
||||
}
|
||||
|
||||
fun findSourceFiles(dir: String, sourceDirectories: Collection<String>): List<String> {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.beust.kobalt.plugin.android
|
|||
|
||||
import com.beust.kobalt.OperatingSystem
|
||||
import com.beust.kobalt.TaskResult
|
||||
import com.beust.kobalt.Variant
|
||||
import com.beust.kobalt.api.*
|
||||
import com.beust.kobalt.api.annotation.Directive
|
||||
import com.beust.kobalt.api.annotation.Task
|
||||
|
@ -23,6 +24,7 @@ import java.io.File
|
|||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
class AndroidConfig(var compileSdkVersion : String = "23",
|
||||
var buildToolsVersion : String = "23.0.1",
|
||||
|
@ -42,21 +44,20 @@ val Project.isAndroid : Boolean
|
|||
|
||||
@Singleton
|
||||
public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler)
|
||||
: ConfigPlugin<AndroidConfig>(), IClasspathContributor, IRepoContributor {
|
||||
: ConfigPlugin<AndroidConfig>(), IClasspathContributor, IRepoContributor, ICompilerFlagContributor {
|
||||
override val name = "android"
|
||||
|
||||
fun isAndroid(project: Project) = configurationFor(project) != null
|
||||
|
||||
override fun apply(project: Project, context: KobaltContext) {
|
||||
super.apply(project, context)
|
||||
log(1, "Applying plug-in Android on project $project")
|
||||
if (accept(project)) {
|
||||
project.compileDependencies.add(FileDependency(androidJar(project).toString()))
|
||||
}
|
||||
context.pluginInfo.classpathContributors.add(this)
|
||||
|
||||
// TODO: Find a more flexible way of enabling this, e.g. creating a contributor for it
|
||||
(Kobalt.findPlugin("java") as JvmCompilerPlugin).addCompilerArgs("-target", "1.6", "-source", "1.6")
|
||||
// (Kobalt.findPlugin("java") as JvmCompilerPlugin).addCompilerArgs("-target", "1.6", "-source", "1.6")
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,50 +115,68 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler)
|
|||
return TaskResult()
|
||||
}
|
||||
|
||||
open class AndroidCommand(androidHome: String, command: String) : RunCommand(command) {
|
||||
init {
|
||||
env.put("ANDROID_HOME", androidHome)
|
||||
}
|
||||
}
|
||||
|
||||
inner class AaptCommand(project: Project, aapt: String, val aaptCommand: String,
|
||||
cwd: File = File(project.directory)) : AndroidCommand(androidHome(project), aapt) {
|
||||
inner open class AndroidCommand(project: Project, command: String, cwd: File = File(project.directory))
|
||||
: RunCommand(command) {
|
||||
init {
|
||||
env.put("ANDROID_HOME", androidHome(project))
|
||||
directory = cwd
|
||||
}
|
||||
|
||||
fun call(args: List<String>) = run(arrayListOf(aaptCommand) + args,
|
||||
fun call(args: List<String>) = run(args,
|
||||
successCallback = { output ->
|
||||
log(1, "$command succeeded:")
|
||||
output.forEach {
|
||||
log(1, " $it")
|
||||
}
|
||||
},
|
||||
errorCallback = { output ->
|
||||
error("Error running $aaptCommand:")
|
||||
error("Error running $command:")
|
||||
output.forEach {
|
||||
error(" $it")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// inner class AaptCommand(project: Project, aapt: String, val aaptCommand: String,
|
||||
// cwd: File = File(project.directory)) : AndroidCommand(project, aapt) {
|
||||
// init {
|
||||
// directory = cwd
|
||||
// }
|
||||
//
|
||||
// override val commandName = "$aapt $aaptCommand"
|
||||
// }
|
||||
|
||||
private fun findResDirs(project: Project) = project.sourceDirectories.filter { it.contains("res") }
|
||||
|
||||
private fun findManifests(variant: Variant) = variant.variantSourceDirectories(context).map {
|
||||
File(it, "AndroidManifest.xml")
|
||||
}.filter {
|
||||
it.exists()
|
||||
}
|
||||
|
||||
private fun generateR(project: Project, generated: Path, aapt: String, resDir: String) {
|
||||
val compileSdkVersion = compileSdkVersion(project)
|
||||
val androidJar = Paths.get(androidHome(project), "platforms", "android-$compileSdkVersion", "android.jar")
|
||||
val applicationId = configurationFor(project)?.applicationId!!
|
||||
val manifestDir = Paths.get(project.directory, "app", "src", "main").toString()
|
||||
val manifest = Paths.get(manifestDir, "AndroidManifest.xml")
|
||||
val manifests = findManifests(context.variant)
|
||||
|
||||
val crunchedPngDir = KFiles.joinAndMakeDir(intermediates(project).toString(), "res", flavor)
|
||||
|
||||
AaptCommand(project, aapt, "crunch").call(listOf(
|
||||
val resDirArgs = arrayListOf("-S", resDir) + findResDirs(project).filter {
|
||||
File(it).exists()
|
||||
}.map {
|
||||
"-S $it"
|
||||
}.joinToString(" ").split(" ")
|
||||
AndroidCommand(project, aapt).call(listOf("crunch") + resDirArgs + listOf(
|
||||
"-v",
|
||||
"-S", "app/src/main/res",//resourceDir,
|
||||
"-S", resDir,
|
||||
"-C", crunchedPngDir
|
||||
))
|
||||
|
||||
AaptCommand(project, aapt, "package").call(listOf(
|
||||
val manifestArgs = manifests.map { "-M ${it.path}" }.joinToString(" ").split(" ")
|
||||
AndroidCommand(project, aapt).call(listOf("package") + resDirArgs + manifestArgs + listOf(
|
||||
"-f",
|
||||
"--no-crunch",
|
||||
"-I", androidJar.toString(),
|
||||
"-M", manifest.toString(),
|
||||
"-S", "app/src/main/res",
|
||||
"-S", resDir,
|
||||
// where to find more assets
|
||||
"-A", KFiles.joinAndMakeDir(intermediates(project).toString(), "assets", flavor),
|
||||
"-m", // create directory
|
||||
|
@ -206,19 +225,37 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler)
|
|||
|
||||
// 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)
|
||||
KFiles.copyRecursively(destDir.resolve("res"), resDir, deleteFirst = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun compile(project: Project, rDirectory: String): File {
|
||||
val sourceFiles = arrayListOf(Paths.get(rDirectory, "R.java").toFile().path)
|
||||
val c = sourceFiles.javaClass
|
||||
val buildDir = Paths.get(project.buildDirectory, "generated", "classes").toFile()
|
||||
val cai = CompilerActionInfo(project.directory, listOf(), sourceFiles, buildDir, listOf(
|
||||
"-source", "1.6", "-target", "1.6"))
|
||||
val cai = CompilerActionInfo(project.directory, listOf(), sourceFiles, buildDir, emptyList())
|
||||
javaCompiler.compile(project, context, cai)
|
||||
return buildDir
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements ICompilerFlagContributor
|
||||
* Make sure we compile and generate 1.6 sources unless the build file defined those (which can
|
||||
* happen if the developer is using RetroLambda for example).
|
||||
*/
|
||||
override fun flagsFor(project: Project) : List<String> {
|
||||
val result : ArrayList<String> = project.projectProperties.get(JvmCompilerPlugin.COMPILER_ARGS)?.let {
|
||||
arrayListOf<String>().apply { addAll(it as List<String>) }
|
||||
} ?: arrayListOf<String>()
|
||||
if (! result.contains("-source")) with(result) {
|
||||
addAll(listOf("-source", "1.6"))
|
||||
}
|
||||
if (! result.contains("-target")) with(result) {
|
||||
addAll(listOf("-target", "1.6"))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TASK_GENERATE_DEX = "generateDex"
|
||||
}
|
||||
|
@ -244,16 +281,17 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler)
|
|||
it.jarFile.get().path
|
||||
}.filter { ! it.endsWith(".aar") && ! it.endsWith("android.jar") }
|
||||
} ?: emptyList()
|
||||
RunCommand(dx).run(args + otherArgs)
|
||||
AndroidCommand(project, dx).run(args + otherArgs)
|
||||
|
||||
//
|
||||
// Add classes.dex to existing .ap_
|
||||
// Because aapt doesn't handle directory moving, we need to cd to classes.dex's directory so
|
||||
// that classes.dex ends up in the root directory of the .ap_.
|
||||
//
|
||||
AaptCommand(project, aapt(project), "add").apply {
|
||||
AndroidCommand(project, aapt(project)).apply {
|
||||
directory = File(outClassesDex).parentFile
|
||||
}.call(listOf("-v", KFiles.joinDir(File(temporaryApk(project, flavor)).absolutePath), classesDex))
|
||||
}.call(listOf("add") + listOf("-v", KFiles.joinDir(File(temporaryApk(project, flavor)).absolutePath),
|
||||
classesDex))
|
||||
|
||||
return TaskResult()
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public class JavaPlugin @Inject constructor(
|
|||
override fun doJavadoc(project: Project, cai: CompilerActionInfo) : TaskResult {
|
||||
val result =
|
||||
if (cai.sourceFiles.size > 0) {
|
||||
javaCompiler.javadoc(project, context, cai.copy(compilerArgs = compilerArgs))
|
||||
javaCompiler.javadoc(project, context, cai.copy(compilerArgs = compilerArgsFor(project)))
|
||||
} else {
|
||||
warn("Couldn't find any source files to run Javadoc on")
|
||||
TaskResult()
|
||||
|
@ -66,7 +66,7 @@ public class JavaPlugin @Inject constructor(
|
|||
override fun doCompile(project: Project, cai: CompilerActionInfo) : TaskResult {
|
||||
val result =
|
||||
if (cai.sourceFiles.size > 0) {
|
||||
javaCompiler.compile(project, context, cai.copy(compilerArgs = compilerArgs))
|
||||
javaCompiler.compile(project, context, cai.copy(compilerArgs = compilerArgsFor(project)))
|
||||
} else {
|
||||
warn("Couldn't find any source files to compile")
|
||||
TaskResult()
|
||||
|
@ -82,7 +82,7 @@ public class JavaPlugin @Inject constructor(
|
|||
copyResources(project, JvmCompilerPlugin.SOURCE_SET_TEST)
|
||||
val buildDir = makeOutputTestDir(project)
|
||||
javaCompiler.compile(project, context, CompilerActionInfo(project.directory, testDependencies(project),
|
||||
sourceFiles, buildDir, compilerArgs))
|
||||
sourceFiles, buildDir, compilerArgsFor(project)))
|
||||
} else {
|
||||
warn("Couldn't find any tests to compile")
|
||||
TaskResult()
|
||||
|
@ -99,12 +99,14 @@ public fun javaProject(vararg project: Project, init: JavaProject.() -> Unit): J
|
|||
}
|
||||
}
|
||||
|
||||
class JavaCompilerConfig {
|
||||
class JavaCompilerConfig(val project: Project) {
|
||||
fun args(vararg options: String) {
|
||||
(Kobalt.findPlugin("java") as JvmCompilerPlugin).addCompilerArgs(*options)
|
||||
(Kobalt.findPlugin("java") as JvmCompilerPlugin).addCompilerArgs(project, *options)
|
||||
}
|
||||
}
|
||||
|
||||
@Directive
|
||||
fun Project.javaCompiler(init: JavaCompilerConfig.() -> Unit) = JavaCompilerConfig().init()
|
||||
fun Project.javaCompiler(init: JavaCompilerConfig.() -> Unit) = let {
|
||||
JavaCompilerConfig(it).init()
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import com.google.inject.Singleton
|
|||
@Singleton
|
||||
class JavaProjectInfo : IProjectInfo {
|
||||
override val sourceDirectory = "java"
|
||||
override val defaultSourceDirectories = hashSetOf("src/main/java", "src/main/resources")
|
||||
override val defaultTestDirectories = hashSetOf("src/test/java", "src/test/resources")
|
||||
override val defaultSourceDirectories = hashSetOf("src/main/java", "src/main/resources", "src/main/res")
|
||||
override val defaultTestDirectories = hashSetOf("src/test/java", "src/test/resources", "src/test/res")
|
||||
|
||||
private fun generate(type: String, name: String, value: Any) =
|
||||
" public static $type $name = $value;"
|
||||
|
|
|
@ -85,7 +85,7 @@ class KotlinPlugin @Inject constructor(
|
|||
return kotlinCompilePrivate {
|
||||
classpath(cpList.map { it.jarFile.get().absolutePath })
|
||||
sourceFiles(sources)
|
||||
compilerArgs(compilerArgs)
|
||||
compilerArgs(compilerArgsFor(project))
|
||||
output = outputDirectory
|
||||
}.compile(project, context)
|
||||
}
|
||||
|
@ -120,11 +120,13 @@ fun kotlinProject(vararg project: Project, init: KotlinProject.() -> Unit): Kotl
|
|||
}
|
||||
}
|
||||
|
||||
class KotlinCompilerConfig {
|
||||
class KotlinCompilerConfig(val project: Project) {
|
||||
fun args(vararg options: String) {
|
||||
(Kobalt.findPlugin("kotlin") as JvmCompilerPlugin).addCompilerArgs(*options)
|
||||
(Kobalt.findPlugin("kotlin") as JvmCompilerPlugin).addCompilerArgs(project, *options)
|
||||
}
|
||||
}
|
||||
|
||||
@Directive
|
||||
fun Project.kotlinCompiler(init: KotlinCompilerConfig.() -> Unit) = KotlinCompilerConfig().init()
|
||||
fun Project.kotlinCompiler(init: KotlinCompilerConfig.() -> Unit) = let {
|
||||
KotlinCompilerConfig(it).init()
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import com.google.inject.Singleton
|
|||
@Singleton
|
||||
class KotlinProjectInfo : IProjectInfo {
|
||||
override val sourceDirectory = "kotlin"
|
||||
override val defaultSourceDirectories = hashSetOf("src/main/kotlin", "src/main/resources")
|
||||
override val defaultTestDirectories = hashSetOf("src/test/kotlin", "src/test/resources")
|
||||
override val defaultSourceDirectories = hashSetOf("src/main/kotlin", "src/main/resources", "src/main/res")
|
||||
override val defaultTestDirectories = hashSetOf("src/test/kotlin", "src/test/resources", "src/test/res")
|
||||
|
||||
private fun generate(type: String, name: String, value: Any) =
|
||||
" val $name : $type = $value"
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<class-name>com.beust.kobalt.plugin.android.AndroidPlugin</class-name>
|
||||
</repo-contributors>
|
||||
<compiler-flag-contributors>
|
||||
<class-name>com.beust.kobalt.plugin.android.AndroidPlugin</class-name>
|
||||
<class-name>com.beust.kobalt.plugin.apt.AptPlugin</class-name>
|
||||
</compiler-flag-contributors>
|
||||
</kobalt-plugin>
|
43
src/test/kotlin/com/beust/kobalt/VariantTest.kt
Normal file
43
src/test/kotlin/com/beust/kobalt/VariantTest.kt
Normal file
|
@ -0,0 +1,43 @@
|
|||
package com.beust.kobalt
|
||||
|
||||
import com.beust.kobalt.api.buildType
|
||||
import com.beust.kobalt.api.productFlavor
|
||||
import com.beust.kobalt.plugin.java.JavaProject
|
||||
import org.testng.Assert
|
||||
import org.testng.annotations.DataProvider
|
||||
import org.testng.annotations.Test
|
||||
import java.util.*
|
||||
|
||||
class VariantTest : KobaltTest() {
|
||||
|
||||
@DataProvider(name = "projectVariants")
|
||||
fun projectVariants() = arrayOf(
|
||||
arrayOf(emptySet<String>(), JavaProject().apply {
|
||||
}),
|
||||
arrayOf(hashSetOf("compileDev"), JavaProject().apply {
|
||||
productFlavor("dev") {}
|
||||
}),
|
||||
arrayOf(hashSetOf("compileDev", "compileProd"), JavaProject().apply {
|
||||
productFlavor("dev") {}
|
||||
productFlavor("prod") {}
|
||||
}),
|
||||
arrayOf(hashSetOf("compileDevDebug"), JavaProject().apply {
|
||||
productFlavor("dev") {}
|
||||
buildType("debug") {}
|
||||
}),
|
||||
arrayOf(hashSetOf("compileDevRelease", "compileDevDebug", "compileProdDebug", "compileProdRelease"),
|
||||
JavaProject().apply {
|
||||
productFlavor("dev") {}
|
||||
productFlavor("prod") {}
|
||||
buildType("debug") {}
|
||||
buildType("release") {}
|
||||
})
|
||||
)
|
||||
|
||||
@Test(dataProvider = "projectVariants", description =
|
||||
"Make sure we generate the correct dynamic tasks based on the product flavor and build types.")
|
||||
fun taskNamesShouldWork(expected: Set<String>, project: JavaProject) {
|
||||
val variantNames = HashSet(Variant.allVariants(project).map { it.toTask("compile") })
|
||||
Assert.assertEquals(variantNames, expected)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue