1
0
Fork 0
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:
Cedric Beust 2015-11-21 02:49:59 -08:00
parent 354668b6a3
commit e16892dbdb
10 changed files with 194 additions and 60 deletions

View file

@ -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
}
}
}

View file

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

View file

@ -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> {

View file

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

View file

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

View file

@ -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;"

View file

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

View file

@ -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"

View file

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

View 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)
}
}