mirror of https://github.com/auygun/kaliber.git
381 lines
12 KiB
Groovy
381 lines
12 KiB
Groovy
apply plugin: 'com.android.application'
|
|
apply plugin: Utils
|
|
|
|
android {
|
|
compileSdk rootProject.ext.compileSdk
|
|
ndkVersion rootProject.ext.ndkVersion
|
|
|
|
defaultConfig {
|
|
minSdk rootProject.ext.minSdk
|
|
targetSdk rootProject.ext.targetSdk
|
|
ndk {
|
|
abiFilters = []
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
minifyEnabled false
|
|
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
|
'proguard-rules.pro'
|
|
}
|
|
}
|
|
|
|
flavorDimensions 'game', 'arch'
|
|
|
|
productFlavors {
|
|
helloWorld {
|
|
dimension 'game'
|
|
applicationId 'com.kaliber.helloworld'
|
|
resValue "string", "app_name", "Kaliber Hello World"
|
|
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
|
|
ext {
|
|
gnTarget = "hello_world"
|
|
}
|
|
}
|
|
demo {
|
|
dimension 'game'
|
|
applicationId 'com.kaliber.woom'
|
|
resValue "string", "app_name", "Kaliber Demo"
|
|
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
|
|
ext {
|
|
gnTarget = "demo"
|
|
}
|
|
}
|
|
woom {
|
|
dimension 'game'
|
|
applicationId 'com.woom.game'
|
|
resValue "string", "app_name", "woom"
|
|
resValue "string", "interstitial_ad_unit_id", "ca-app-pub-1321063817979967/8373182022"
|
|
resValue "string", "admob_application_id", "ca-app-pub-1321063817979967~1100949243"
|
|
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
|
|
ext {
|
|
gnTarget = "demo"
|
|
}
|
|
}
|
|
|
|
arm7 {
|
|
dimension 'arch'
|
|
ndk {
|
|
abiFilters = ["armeabi-v7a"]
|
|
}
|
|
}
|
|
arm8 {
|
|
dimension 'arch'
|
|
ndk {
|
|
abiFilters = ["arm64-v8a"]
|
|
}
|
|
}
|
|
x86 {
|
|
dimension 'arch'
|
|
ndk {
|
|
abiFilters = ["x86"]
|
|
}
|
|
}
|
|
x86_64 {
|
|
dimension 'arch'
|
|
ndk {
|
|
abiFilters = ["x86_64"]
|
|
}
|
|
}
|
|
|
|
allArchs {
|
|
dimension 'arch'
|
|
ndk {
|
|
abiFilters = ["armeabi-v7a", "arm64-v8a", "x86_64", "x86"]
|
|
}
|
|
}
|
|
armOnly {
|
|
dimension 'arch'
|
|
ndk {
|
|
abiFilters = ["armeabi-v7a", "arm64-v8a"]
|
|
}
|
|
}
|
|
x86Only {
|
|
dimension 'arch'
|
|
ndk {
|
|
abiFilters = ["x86_64", "x86"]
|
|
}
|
|
}
|
|
|
|
// Native library name is same as GN target name.
|
|
android.productFlavors.each { flavor ->
|
|
if (flavor.dimension == 'game') {
|
|
"${flavor.name}" {
|
|
resValue "string", "lib_name", flavor.ext.gnTarget
|
|
buildConfigField 'String', 'NATIVE_LIBRARY', "\"${flavor.ext.gnTarget}\""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sourceSets {
|
|
main {
|
|
java.srcDirs += ['../../../src/engine/platform/java/com/kaliber/base']
|
|
android.buildTypes.each { buildType ->
|
|
"${buildType.name}" {
|
|
assets.srcDirs = ["${utils.getGnOutDir(buildType.name)}/assets"]
|
|
}
|
|
}
|
|
}
|
|
android.buildTypes.each { buildType ->
|
|
"${buildType.name}" {
|
|
jniLibs.srcDirs = ["${utils.getGnOutDir(buildType.name)}/jniLibs"]
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace "com.kaliber.base"
|
|
}
|
|
|
|
dependencies {
|
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
|
implementation 'com.google.android.gms:play-services-ads:22.0.0'
|
|
}
|
|
|
|
//
|
|
// Tasks for GN build
|
|
//
|
|
|
|
// Generate `args.gn` which is used by GN to take in build arguments.
|
|
utils.addTask('generateGnArgsFor') { String taskName, String buildType, String arch ->
|
|
task(taskName, type: WriteFileTask) {
|
|
content = utils.generateGnArgsContent(buildType, arch)
|
|
target = project.layout.file(provider { new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn') })
|
|
}
|
|
}
|
|
|
|
// Run `gn gen` to generate ninja files.
|
|
utils.addTask('runGnFor') { String taskName, String buildType, String arch ->
|
|
task(taskName, type: Exec) {
|
|
dependsOn "generateGnArgsFor${arch}${buildType}"
|
|
|
|
executable rootProject.ext.gn
|
|
args '--fail-on-unused-args', 'gen', "${utils.getGnOutDir(buildType)}/${arch}"
|
|
|
|
// Need to run gn only once unless the configuration in `args.gn` changes.
|
|
inputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn'))
|
|
outputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'build.ninja'))
|
|
}
|
|
}
|
|
|
|
// Build the native library for the target game using ninja.
|
|
utils.addGameTask('runNinjaFor') { String taskName, String buildType, String arch, String game ->
|
|
task(taskName, type: Exec) {
|
|
dependsOn "runGnFor${arch}${buildType}"
|
|
|
|
executable rootProject.ext.ninja
|
|
args '-C', "${utils.getGnOutDir(buildType)}/${arch}", "src/${utils.getGnTargetFor(game)}"
|
|
|
|
// Always run ninja and let it figure out what needs to be compiled.
|
|
outputs.upToDateWhen { false }
|
|
}
|
|
}
|
|
|
|
// Assets can be obtained from the output directory of any arch but it would be good to combine them
|
|
// in a single directory in case we are buildig multi-arch and some build config have different assets.
|
|
utils.addGameTask('copyAssetsFor') { String taskName, String buildType, String arch, String game ->
|
|
task(taskName, type: Copy) {
|
|
dependsOn "runNinjaFor${game}${arch}${buildType}"
|
|
|
|
from "${utils.getGnOutDir(buildType)}/${arch}/assets"
|
|
into "${utils.getGnOutDir(buildType)}/assets"
|
|
}
|
|
}
|
|
|
|
// Copy the native library to a directory denoting its arch code as the Android Gradle plugin expects.
|
|
utils.addGameTask('copyJniLibsFor') { String taskName, String buildType, String arch, String game ->
|
|
task(taskName, type: Copy) {
|
|
dependsOn "runNinjaFor${game}${arch}${buildType}"
|
|
|
|
from("${utils.getGnOutDir(buildType)}/${arch}") {
|
|
include "lib${utils.getGnTargetFor(game)}.so"
|
|
}
|
|
into "${utils.getGnOutDir(buildType)}/jniLibs/${utils.getAbiCodeFor(arch)}"
|
|
}
|
|
}
|
|
|
|
tasks.configureEach { task ->
|
|
def variantPattern = /(\w+)(${utils.getArchTypesRegExp()})(${utils.getBuildTypesRegExp()})/
|
|
|
|
def match = task.name =~ /^merge/ + variantPattern + /JniLibFolders$/
|
|
if (match) {
|
|
utils.project.android.productFlavors.find { arch ->
|
|
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
|
|
// Depends on each arch type for multi-arch build flavors.
|
|
arch.ndk.abiFilters.each { abi ->
|
|
task.dependsOn "copyJniLibsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return
|
|
}
|
|
match = task.name =~ /^generate/ + variantPattern + /Assets$/
|
|
if (match) {
|
|
utils.project.android.productFlavors.find { arch ->
|
|
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
|
|
// Depends on each arch type for multi-arch build flavors.
|
|
arch.ndk.abiFilters.each { abi ->
|
|
task.dependsOn "copyAssetsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return
|
|
}
|
|
match = task.name =~ /^lintVitalAnalyze/ + variantPattern + /$/
|
|
if (match) {
|
|
utils.project.android.productFlavors.find { arch ->
|
|
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
|
|
// Depends on each arch type for multi-arch build flavors.
|
|
arch.ndk.abiFilters.each { abi ->
|
|
task.dependsOn "copyAssetsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
//
|
|
// Utils plugin
|
|
//
|
|
|
|
class Utils implements Plugin<Project> {
|
|
final def ARCH_CODES = ["armeabi-v7a": "Arm7",
|
|
"arm64-v8a": "Arm8",
|
|
"x86_64": "X86_64",
|
|
"x86": "X86"].asImmutable()
|
|
final def GN_CPU_CODES = ["Arm7": "arm",
|
|
"Arm8": "arm64",
|
|
"X86_64": "x64",
|
|
"X86": "x86"].asImmutable()
|
|
|
|
def project
|
|
|
|
@Inject
|
|
Utils(Project project) {
|
|
this.project = project
|
|
}
|
|
|
|
void apply(Project project) {
|
|
project.extensions.create('utils', Utils)
|
|
}
|
|
|
|
// Add a task for archs and buildTypes variants.
|
|
void addTask(String prefix, Closure taskClosure) {
|
|
forEachBuildVariant { String arch, String buildType ->
|
|
def taskName = "${prefix}${arch}${buildType}"
|
|
taskClosure(taskName, buildType, arch)
|
|
}
|
|
}
|
|
|
|
// Add a task for games, archs and buildTypes variants.
|
|
void addGameTask(String prefix, Closure taskClosure) {
|
|
forEachGameBuildVariant { String game, String arch, String buildType ->
|
|
def taskName = "${prefix}${game}${arch}${buildType}"
|
|
taskClosure(taskName, buildType, arch, game)
|
|
}
|
|
}
|
|
|
|
void forEachBuildVariant(Closure callback) {
|
|
project.android.productFlavors.each { arch ->
|
|
// Only need to add tasks for arch types which maps to a single ABI
|
|
if (arch.dimension == 'arch' && arch.ndk.abiFilters.size() == 1) {
|
|
project.android.buildTypes.each { buildType ->
|
|
callback(arch.name.capitalize(), buildType.name.capitalize())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void forEachGameBuildVariant(Closure callback) {
|
|
project.android.productFlavors.each { game ->
|
|
if (game.dimension == 'game') {
|
|
project.android.productFlavors.each { arch ->
|
|
// Only need to add tasks for arch types which maps to a single ABI
|
|
if (arch.dimension == 'arch' && arch.ndk.abiFilters.size() == 1) {
|
|
project.android.buildTypes.each { buildType ->
|
|
callback(game.name.capitalize(), arch.name.capitalize(), buildType.name.capitalize())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def getBuildTypesRegExp() {
|
|
def outList = []
|
|
project.android.buildTypes.each { buildType ->
|
|
outList += buildType.name.capitalize()
|
|
}
|
|
return outList.join('|')
|
|
}
|
|
|
|
def getArchTypesRegExp() {
|
|
def outList = []
|
|
project.android.productFlavors.each { flavor ->
|
|
if (flavor.dimension == 'arch') {
|
|
outList += flavor.name.capitalize()
|
|
}
|
|
}
|
|
return outList.join('|')
|
|
}
|
|
|
|
def getAbiCodeFor(String arch) {
|
|
def outStr = ''
|
|
project.android.productFlavors.find { flavor ->
|
|
if (flavor.name.capitalize() == arch) {
|
|
outStr = flavor.ndk.abiFilters.first()
|
|
return true
|
|
}
|
|
}
|
|
return outStr
|
|
}
|
|
|
|
def getGnTargetFor(String game) {
|
|
def outStr = ''
|
|
project.android.productFlavors.find { flavor ->
|
|
if (flavor.dimension == 'game' && flavor.name.capitalize() == game) {
|
|
outStr = flavor.ext.gnTarget
|
|
return true
|
|
}
|
|
}
|
|
return outStr
|
|
}
|
|
|
|
def generateGnArgsContent(String buildType, String arch) {
|
|
def content = 'target_os="android"\n'
|
|
content += 'target_cpu="' + GN_CPU_CODES[arch] + '"\n'
|
|
content += "is_debug=${buildType != 'Release'}\n"
|
|
content += 'ndk="' + project.android.ndkDirectory + '"\n'
|
|
content += "ndk_api=${project.rootProject.ext.minSdk}\n"
|
|
return content
|
|
}
|
|
|
|
def getGnOutDir(String buildType) {
|
|
return "${project.buildDir}/gn_out/${buildType.toLowerCase()}"
|
|
}
|
|
}
|
|
|
|
abstract class WriteFileTask extends DefaultTask {
|
|
@Input
|
|
abstract Property<String> getContent()
|
|
|
|
@OutputFile
|
|
abstract RegularFileProperty getTarget()
|
|
|
|
@TaskAction
|
|
void run() {
|
|
def file = target.get().asFile
|
|
file.parentFile.mkdirs()
|
|
def text = content.get()
|
|
if (!file.exists() || text != file.text)
|
|
file.text = text
|
|
}
|
|
}
|