Skip to content

Instantly share code, notes, and snippets.

@CAMOBAP
Last active March 25, 2024 14:57
Show Gist options
  • Save CAMOBAP/ed9aa6a7549787b5eea4b2048b896747 to your computer and use it in GitHub Desktop.
Save CAMOBAP/ed9aa6a7549787b5eea4b2048b896747 to your computer and use it in GitHub Desktop.
include(default)
standalone_toolchain=/opt/android-ndk-toolchains/arm-27
ndk_home=/opt/android-ndk
api_level=27
target_host=arm-linux-androideabi
cc_compiler=clang
cxx_compiler=clang++
target_specific_flags=-mfpu=neon
[settings]
os=Android
os.api_level=$api_level
arch=armv7hf
compiler=clang
compiler.version=7.0
compiler.libcxx=libstdc++
[env]
ANDROID_NDK_HOME=$ndk_home
CONAN_CMAKE_FIND_ROOT_PATH=$standalone_toolchain/sysroot
PATH=[$standalone_toolchain/bin]
CHOST=$target_host
AR=$target_host-ar
AS=$target_host-as
RANLIB=$target_host-ranlib
CC=$target_host-$cc_compiler
CXX=$target_host-$cxx_compiler
LD=$target_host-ld
STRIP=$target_host-strip
CFLAGS= -fPIE -fPIC -I$standalone_toolchain/include/c++/4.9.x -D__ANDROID__ -D__ANDROID_API__=$api_level $target_specific_flags
CXXFLAGS= -fPIE -fPIC -I$standalone_toolchain/include/c++/4.9.x -D__ANDROID__ -D__ANDROID_API__=$api_level $target_specific_flags
LDFLAGS= -pie -march=armv7-a -Wl,--fix-cortex-a8
standalone_toolchain=/opt/android-ndk-toolchains/x86_64-27
ndk_home=/opt/android-ndk
api_level=27
target_host=x86_64-linux-android
cc_compiler=clang
cxx_compiler=clang++
target_specific_flags=-mfpu=neon
[settings]
os=Android
os.api_level=$api_level
arch=x86_64
compiler=clang
compiler.version=7.0
compiler.libcxx=libstdc++
[env]
ANDROID_NDK_HOME=$ndk_home
CONAN_CMAKE_FIND_ROOT_PATH=$standalone_toolchain/sysroot
PATH=[$standalone_toolchain/bin]
CHOST=$target_host
AR=$target_host-ar
AS=$target_host-as
RANLIB=$target_host-ranlib
CC=$target_host-$cc_compiler
CXX=$target_host-$cxx_compiler
LD=$target_host-ld
STRIP=$target_host-strip
CFLAGS= -fPIE -fPIC -I$standalone_toolchain/include/c++/4.9.x -D__ANDROID__ -D__ANDROID_API__=$api_level $target_specific_flags
CXXFLAGS= -fPIE -fPIC -I$standalone_toolchain/include/c++/4.9.x -D__ANDROID__ -D__ANDROID_API__=$api_level $target_specific_flags
LDFLAGS= -pie
apply plugin: 'com.android.application'
apply from: "${rootDir}/conan.gradle"
conan {
conanfile = 'src/main/cpp/conanfile.txt'
profile = 'android-${abi}'
}
android {
compileSdkVersion 28
defaultConfig {
applicationId "org.camobap.conan.gradle.example"
minSdkVersion 27
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
abiFilters "armeabi-v7a", "x86_64"
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_STL=c++_shared",
}
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
clean.doLast {
delete file('.externalNativeBuild')
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-annotations:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test:rules:1.1.1'
}
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}/.externalNativeBuild/conan/${flavor}/${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"
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]
def conanProfileFileName = engine.createTemplate(conanProfileFileNameTemplate).make(params).toString()
def conanOutputDirPath = engine.createTemplate(conanOutputDirPathTemplate).make(params).toString()
def conanInstallTaskName = "conanInstall${taskSuffix}"
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=${buildType.capitalize()}",
'--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)
project.tasks.findByName("externalNativeBuild${flavor.capitalize()}").dependsOn(conanInstallTaskName)
project.tasks.findByName("externalNativeBuildClean${flavor.capitalize()}").dependsOn(conanCleanTaskName)
}
}
}
void apply(Project project) {
extension = project.extensions.create('conan', ConanPluginExtension)
project.afterEvaluate {
validate(project)
createTasksForAndroidExternalNativeBuild(project)
}
}
}
apply plugin: ConanPlugin
@ppetraki
Copy link

@CAMOBAP795

I solved the "distclean" problem. It was a matter of different tasks being invoked by the IDE vs the CLI. I've created my own gist now as the plugin has diverged quite a bit from your original version. Feel free to merge the changes.

https://gist.github.com/ppetraki/ca8630cc272edcd6a881ed82aa30b559

I think this can be done with even less external config. Especially if you use the cmake_wrapper. As all of my profiles vary by just a single config string that could easily be driven to the conan CLI. Then the plugin could provide it's own default NDK profile and decorate it. Making it completely self contained.

@CAMOBAP
Copy link
Author

CAMOBAP commented Nov 30, 2019

Hi @ppetraki

Sorry for log silence

To build the fat APK you need to add this: ...

This not true, android building FAT APK out of the box, moreover split section used to build separate APK for each ABI, according to official doe https://developer.android.com/studio/build/configure-apk-splits:

universalApk
If true, Gradle generates a universal APK in addition to per-ABI APKs. A universal APK contains code and resources for all ABIs in a single APK. The default value is false. Note that this option is only available in the splits.abi block. When building multiple APKs based on screen density, Gradle always generates a universal APK that contains code and resources for all screen densities.

If flavors don't matter for the NDK build....

Flavors have no relationships with NDK, most close equivalent in CMake-word is a target

All that has to change in the plugin is ...

You can achieve the same by modifying the 'user' side only, no need to touch plugin for this

conan {
    conanfile = 'src/main/cpp/conanfile.txt'
    profile = 'android-${abi}'
    outputDirPath = '${projectDir}/conan_build/${buildType}/${abi}'
}

So according what I found from your comment there is no reason to modify the plugin by itself,

@ppetraki correct me if I'm wrong

@janseeger
Copy link

Would it be possible to release this as proper Gradle Plugin?

@CAMOBAP
Copy link
Author

CAMOBAP commented Mar 25, 2024

@janseeger probably not (at least in near future)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment