Skip to content

Instantly share code, notes, and snippets.

@ppetraki
Last active February 9, 2023 01:24
Show Gist options
  • Save ppetraki/ca8630cc272edcd6a881ed82aa30b559 to your computer and use it in GitHub Desktop.
Save ppetraki/ca8630cc272edcd6a881ed82aa30b559 to your computer and use it in GitHub Desktop.
gradle plugin to drive conan multi arch android build
apply plugin: 'com.android.application'
apply from: "${rootDir}/conan.gradle"
android {
compileSdkVersion 29
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "org.libsdl.app"
minSdkVersion 26
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++17"
abiFilters 'x86', 'armeabi-v7a', 'x86_64'
}
}
} //defaultConfig
// https://stackoverflow.com/questions/36414219/install-failed-no-matching-abis-failed-to-extract-native-libraries-res-113
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a', 'x86_64'
// include defaultConfig.externalNativeBuild.getCmake().getAbiFilters().
universalApk true
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
} //android
// XXX the plugin should handle this
clean.doLast {
delete file('conan_build')
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
project(SDL2CYCLEBLOCKS)
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../conan_build/${CMAKE_BUILD_TYPE}/${ANDROID_ABI}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)
# MainActivity will dynamically load this lib
add_library(main SHARED main.cpp)
include_directories(
CONAN_PKG::sdl2
CONAN_PKG::sdl2pp
)
# get android env
add_library(native_app_glue STATIC
${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
target_include_directories(native_app_glue
PUBLIC
${ANDROID_NDK}/sources/android/native_app_glue
)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
target_link_libraries(main
android
native_app_glue
${log-lib}
EGL
GLESv2
CONAN_PKG::sdl2
CONAN_PKG::sdl2pp
)
ls ~/.conan/profiles/
android-armeabi-v7a android-x86 android-x86_64
The plugin is looking for profiles names in the format "android-${abi}"
The contents of these files vary little. They use the cmake_wrapper package so all that's changing
is the host. This could be optimized to driven by the conan CLI and managed by the plugin. Ideally, the
plugin should provide it's own default profile for the NDK and decorate it from the data provided by the
build.
#[file: android-armeabi-v7a]
[build_requires]
android_ndk_installer/r20@bincrafters/stable
[settings]
os=Android
os.api_level=21
os_build=Linux
# https://docs.conan.io/en/latest/systems_cross_building/cross_building.html#cross-building-android
# Android armbeabi-v7a armv7
# Android armv64-v8a armv8
# Android armeabi armv6 (as a minimal compatible, will be compatible with v7 too)
arch=armv7
arch_build=x86_64
compiler=clang
compiler.version=8
compiler.libcxx=libc++
[options]
*:pic = True
#[file: android-x86]
[build_requires]
android_ndk_installer/r20@bincrafters/stable
[settings]
os=Android
os.api_level=21
os_build=Linux
# https://docs.conan.io/en/latest/systems_cross_building/cross_building.html#cross-building-android
# target device
arch=x86
# builder
arch_build=x86_64
compiler=clang
compiler.version=8
compiler.libcxx=libc++
[options]
*:pic = True
#[file: android-x86_64]
[build_requires]
android_ndk_installer/r20@bincrafters/stable
[settings]
os=Android
os.api_level=21
os_build=Linux
# https://docs.conan.io/en/latest/systems_cross_building/cross_building.html#cross-building-android
# Android armbeabi-v7a armv7
# Android armv64-v8a armv8
# Android armeabi armv6 (as a minimal compatible, will be compatible with v7 too)
arch=x86_64
arch_build=x86_64
compiler=clang
compiler.version=8
compiler.libcxx=libc++
[options]
*:pic = True
// From https://gist.github.com/CAMOBAP795/ed9aa6a7549787b5eea4b2048b896747
// with some changes
//
// Originally discovered here: https://github.com/conan-io/docs/issues/1259
import java.nio.file.Paths
import groovy.text.SimpleTemplateEngine
class ConanPluginExtension {
String conanfile = "src/main/cpp/conanfile.txt"
String profile = 'android-${abi}' // TODO maybe support map abi->filename
String outputDirPath = '${projectDir}/conan_build/${buildType}/${abi}'
}
class ConanPlugin implements Plugin<Project> {
def android
def extension
def conanfilePath
def conanProfileFileNameTemplate
def conanOutputDirPathTemplate
void validate(Project project) {
android = project.extensions.android
assert android: "Cannot be applied for non android projects"
conanfilePath = Paths.get(project.projectDir.absolutePath, extension.conanfile).toString()
assert project.file(conanfilePath).exists(): "conan file ${conanfilePath} doesn't exists"
println "ConanPlugin: conan profile template: ${extension.profile}"
println "ConanPlugin: conan dir path template: ${extension.outputDirPath}"
conanProfileFileNameTemplate = extension.profile
conanOutputDirPathTemplate = extension.outputDirPath
}
void createTasksForAndroidExternalNativeBuild(Project project) {
android.applicationVariants.all { variant ->
def engine = new SimpleTemplateEngine()
for (def abi in android.defaultConfig.externalNativeBuild.cmake.abiFilters) {
def flavor = variant.name
def taskSuffix = "${abi.capitalize()}${flavor.capitalize()}"
def buildType = flavor.toLowerCase().contains('release') ? 'release' : 'debug'
def params = ['abi': abi, 'flavor': flavor, 'projectDir': project.projectDir, 'buildType': buildType.capitalize()]
println """
----------------------------
ConanPlugin(
abi: ${params['abi']}
flavor: ${params['flavor']}
projectDir: ${params['projectDir']}
buildType: ${params['buildType']}
)
----------------------------
""".stripMargin()
def conanProfileFileName = engine.createTemplate(conanProfileFileNameTemplate).make(params).toString()
def conanOutputDirPath = engine.createTemplate(conanOutputDirPathTemplate).make(params).toString()
println """
----------------------------
ConanPlugin(
conanProfileFileName: ${conanProfileFileName}
conanOutputDirPath: ${conanOutputDirPath}
)
----------------------------
""".stripMargin()
def conanInstallTaskName = "conanInstall${taskSuffix}"
println "ConanPlugin: Executing conan install task: ${conanInstallTaskName}"
def conanInstallTask = project.task(conanInstallTaskName, type: Exec) {
group 'Conan tasks'
description 'Run conan to get and build missing dependencies'
workingDir conanOutputDirPath
commandLine 'conan', 'install', conanfilePath,
'--profile', conanProfileFileName,
'--settings', "build_type=${params['buildType']}",
'--install-folder', workingDir,
'--build', 'missing'
inputs.files conanfilePath
outputs.dir workingDir
doFirst {
workingDir.mkdirs()
}
}
def conanCheckProfileTaskName = "conanCheckProfileFor${taskSuffix}"
project.task(conanCheckProfileTaskName) {
group 'Conan tasks'
description 'Check that conan profile file exists'
doFirst {
if (!project.file(conanProfileFileName).exists()) {
def conanProfilePath = "${System.properties['user.home']}/.conan/profiles/${conanProfileFileName}"
assert project.file(conanProfilePath).exists() \
: "Conan profile file \"${conanProfilePath}\" missing please check README.md"
}
}
}
def conanCleanTaskName = "conanClean${taskSuffix}"
project.task(conanCleanTaskName, type: Delete) {
group 'Conan tasks'
description 'Delete conan generated files'
delete conanOutputDirPath
}
conanInstallTask.dependsOn(conanCheckProfileTaskName)
['externalNativeBuildCleanRegularDebug',
'externalNativeBuildCleanRegularRelease',
'generateRegularDebugSources',
'generateRegularReleaseSources',
"externalNativeBuild${flavor.capitalize()}",
"externalNativeBuildClean${flavor.capitalize()}" ].each {
if (project.tasks.findByName("${it}")) {
println "ConanPlugin: Adding dependency ${it}->conanInstallTaskName"
project.tasks.findByName("${it}").dependsOn(conanInstallTaskName)
}
}
} //void createTasksForAndroidExternalNativeBuild
} //class ConanPlugin
void apply(Project project) {
extension = project.extensions.create('conan', ConanPluginExtension)
project.afterEvaluate {
validate(project)
createTasksForAndroidExternalNativeBuild(project)
}
}
}
apply plugin: ConanPlugin
ppetraki@vanguard:~/Sandbox/Games/conansdl2$ tree app/
app/
├── app.iml
├── build
│   ├── generated
│   │   └── source
│   │   └── buildConfig
│   │   └── debug
│   │   └── org
│   │   └── libsdl
│   │   └── app
│   │   └── BuildConfig.java
│   └── intermediates
│   └── check_manifest_result
│   └── debug
│   └── checkDebugManifest
│   └── out
├── build.gradle
├── conan_build
│   ├── Debug
│   │   ├── armeabi-v7a
│   │   │   ├── conanbuildinfo.cmake
│   │   │   ├── conanbuildinfo.txt
│   │   │   ├── conaninfo.txt
│   │   │   ├── conan.lock
│   │   │   └── graph_info.json
│   │   ├── x86
│   │   │   ├── conanbuildinfo.cmake
│   │   │   ├── conanbuildinfo.txt
│   │   │   ├── conaninfo.txt
│   │   │   ├── conan.lock
│   │   │   └── graph_info.json
│   │   └── x86_64
│   │   ├── conanbuildinfo.cmake
│   │   ├── conanbuildinfo.txt
│   │   ├── conaninfo.txt
│   │   ├── conan.lock
│   │   └── graph_info.json
│   └── Release
│   ├── armeabi-v7a
│   │   ├── conanbuildinfo.cmake
│   │   ├── conanbuildinfo.txt
│   │   ├── conaninfo.txt
│   │   ├── conan.lock
│   │   └── graph_info.json
│   ├── x86
│   │   ├── conanbuildinfo.cmake
│   │   ├── conanbuildinfo.txt
│   │   ├── conaninfo.txt
│   │   ├── conan.lock
│   │   └── graph_info.json
│   └── x86_64
│   ├── conanbuildinfo.cmake
│   ├── conanbuildinfo.txt
│   ├── conaninfo.txt
│   ├── conan.lock
│   └── graph_info.json
├── libs
├── proguard-rules.pro
└── src
├── androidTest
│   └── java
│   └── com
│   └── example
│   └── conansdl2
│   └── ExampleInstrumentedTest.java
├── main
│   ├── AndroidManifest.xml
│   ├── cpp
│   │   ├── CMakeLists.txt
│   │   ├── conanfile.txt
│   │   └── main.cpp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment