-
-
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 |
It doesn't appear to search for my local conan profile, $HOME/.conan/profiles/android-21 and instead generates the following error.
Yep, there is no heuristic in the plugin to search/decide which profile to use.
Plus, it was developed with respect to the fact that Android support different ABIs: armv7, arm64, x86, x86_64 (and mips in the past)
So it doesn't appear that I can use this until I have the templated conan profiles.
If by 'templated' you mean 'default file naming convention', you are right.
But this can be changed by passing another profile
value in https://gist.github.com/CAMOBAP795/ed9aa6a7549787b5eea4b2048b896747#file-build-gradle-L6 this line
Could you please share the profiles and the project hierarchy you used to run this? Thanks.
Added profiles to the gist, according to project hierarchy, I have a single module project, the sample of build.gradle
you can see in this gist
P.S. According to your description, a single thing which you need to change to make this plugin works for you is:
conan {
...
profile = 'android-21'
}
P.P.S. Let me know if you still have any troubles, I will be glad to help you!
@CAMOBAP795
Thanks for getting back to me so quickly.
OK, moving forward here. I can get it all to chug forward but getting it to jive with cmake is a sticking point. How did you resolve the conanbuildinfo.cmake path relative to the host arch and build type? Previous we just used a single conan_build/ dir , but now we have a hierarchy like so.
ppetraki@vanguard:~/Sandbox/Games/conansdl2$ tree app/.externalNativeBuild/
app/.externalNativeBuild/
└── conan
├── regularDebug
│ ├── armeabi-v7a
│ │ ├── conanbuildinfo.cmake
│ │ ├── conanbuildinfo.txt
│ │ ├── conaninfo.txt
│ │ ├── conan.lock
│ │ └── graph_info.json
│ └── x86_64
│ ├── conanbuildinfo.cmake
│ ├── conanbuildinfo.txt
│ ├── conaninfo.txt
│ ├── conan.lock
│ └── graph_info.json
└── regularRelease
├── armeabi-v7a
│ ├── conanbuildinfo.cmake
│ ├── conanbuildinfo.txt
│ ├── conaninfo.txt
│ ├── conan.lock
│ └── graph_info.json
└── x86_64
├── conanbuildinfo.cmake
├── conanbuildinfo.txt
├── conaninfo.txt
├── conan.lock
└── graph_info.json
7 directories, 20 files
[CMakeLists.txt]
1 project(SDL2CYCLEBLOCKS)
2
3 # For more information about using CMake with Android Studio, read the
4 # documentation: https://d.android.com/studio/projects/add-native-code.html
5
6 # Sets the minimum version of CMake required to build the native library.
7
8 cmake_minimum_required(VERSION 3.4.1)
9
10 include(${CMAKE_CURRENT_SOURCE_DIR}/../../../conan_build/conanbuildinfo.cmake)
11
12
How did you manage that? Thanks.
Good point!
Here is my project hierarchy
├── app
│ ├── build.gradle
│ └── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ │ └── ...
│ │ ├── cpp
│ │ │ ├── CMakeLists.txt
│ │ │ ├── conanfile.txt
│ │ │ ├── jni
│ │ │ │ └── ...
│ └── test
│ └── ...
├── build.gradle
├── gradle
│ └── ...
├── gradle.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
Here is my generators from conanofile.txt
...
[generators]
cmake
cmake_paths
cmake_find_package
And in top-level CMakeLists.txt
I have:
# Conan setup
string(REPLACE /cmake/ /conan/ CONAN_BINARY_DIR ${CMAKE_BINARY_DIR})
list(APPEND CMAKE_MODULE_PATH ${CONAN_BINARY_DIR})
...
I know, It more looks like a 'hack' and depends on undocumented FS structure (which generated by android gradle plugin)
But I didn't find a better solution than this, open on any proposals on this
OK we already have the same hierarchy. The cmake fix you supplied appears to be incomplete in some way. It can't call conan_basic_setup.
Here's my full cmake:
ppetraki@vanguard:~/Sandbox/Games/conansdl2$ cat app/src/main/cpp/CMakeLists.txt
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)
string(REPLACE /cmake/ /conan/ CONAN_BINARY_DIR ${CMAKE_BINARY_DIR})
list(APPEND CMAKE_MODULE_PATH ${CONAN_BINARY_DIR})
#include(${CMAKE_CURRENT_SOURCE_DIR}/../../../conan_build/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
)
@ppetraki if you rely on conan_basic_setup
probably you should replace
#include(${CMAKE_CURRENT_SOURCE_DIR}/../../../conan_build/conanbuildinfo.cmake)
with
include(${CONAN_BINARY_DIR}/conanbuildinfo.cmake)
This should help
That's a chicken egg issue, I don't have CONAN_BINARY_DIR yet. I've managed to get this to work with some changes.
- Use conan_build as the top level dir
- Conditionally include the conanbuildinfo.cmake based on build_type
This plugin is a great start. However if you really want to get it into conan's mainline, it must support conanbuildinfo.cmake.
This gives me the following conan config tree:
ppetraki@vanguard:~/Sandbox/Games/conansdl2$ tree app/conan_build/
app/conan_build/
├── regularDebug
│ ├── armeabi-v7a
│ │ ├── conanbuildinfo.cmake
│ │ ├── conanbuildinfo.txt
│ │ ├── conaninfo.txt
│ │ ├── conan.lock
│ │ └── graph_info.json
│ └── x86_64
│ ├── conanbuildinfo.cmake
│ ├── conanbuildinfo.txt
│ ├── conaninfo.txt
│ ├── conan.lock
│ └── graph_info.json
└── regularRelease
├── armeabi-v7a
│ ├── conanbuildinfo.cmake
│ ├── conanbuildinfo.txt
│ ├── conaninfo.txt
│ ├── conan.lock
│ └── graph_info.json
└── x86_64
├── conanbuildinfo.cmake
├── conanbuildinfo.txt
├── conaninfo.txt
├── conan.lock
└── graph_info.json
and is driven by only these changes in my cmake:
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)
if( ${CMAKE_BUILD_TYPE} STREQUAL "Debug" )
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../conan_build/regularDebug/${ANDROID_ABI}/conanbuildinfo.cmake OPTIONAL)
elseif( ${CMAKE_BUILD_TYPE} STREQUAL "Release" )
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../conan_build/regularRelease/${ANDROID_ABI}/conanbuildinfo.cmake OPTIONAL)
else()
message( FATAL_ERROR "conan build type mapping, ${CMAKE_BUILD_TYPE}, is unaccounted for" )
endif()
conan_basic_setup(TARGETS)
Sure enough everything builds now.
ppetraki@vanguard:~/Sandbox/Games/conansdl2$ find . -name libmain.so
./app/.cxx/cmake/regularRelease/x86_64/lib/libmain.so
./app/.cxx/cmake/regularRelease/armeabi-v7a/lib/libmain.so
./app/.cxx/cmake/debug/armeabi-v7a/lib/libmain.so
./app/.cxx/cmake/regularDebug/x86_64/lib/libmain.so
./app/.cxx/cmake/regularDebug/armeabi-v7a/lib/libmain.so
./app/.cxx/cmake/release/armeabi-v7a/lib/libmain.so
Though I'm getting an SDL error now when I try to run the emulator (x86_64) saying that it can't find libmain.so. Well, I definitely built it :). The arm target works fine (HW).
The clean target is being a problem for me. Does that work for you?
@CAMOBAP795
Turns out I really needed to target x86 instead of x86_64 and configure gradle to build a universal apk. It's working now. I've fixed some of the clean target issues by added a dependency on "externalNativeBuildCleanRegularDebug". The other weird thing I'm seeing is it doesn't always regenerate the conan install targets. Sometimes it will drop release, sometimes it adds sastRelease and sastDebug. It's odd.
While my if else hack works. What would be ideal is if the plugin generated a top level buildinfo,cmake file in conan_build/ (or whatever) that accounted for all the build types generated by gradle and how they map to cmake build types. Then we can just include a single path and it will "just work".
That would be sick (⌐■_■)
@ppetraki, my bad there were flavors in build.gradle
, please remove them (I have updated build.gradle
in this gist)
please run ./gradlew clean build
and show the error (you should not rely on sast
or regular
anymore in paths)
To support x86 please list it in https://gist.github.com/CAMOBAP795/ed9aa6a7549787b5eea4b2048b896747#file-build-gradle-L22
@CAMOBAP795
Thanks.
To build the fat APK you need to add this:
// 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
}
}
I tried to reuse the abiFilters array (list) but there's a type error. I just don't know enough Java/Groovy yet to write anything more than shell like code atm :)
If flavors don't matter for the NDK build. Then lets dump it's metadata from the conan install path and use the actual build type in the format Cmake expects. Then we can do a direct include like this:.
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../conan_build/${CMAKE_BUILD_TYPE}/${ANDROID_ABI}/conanbuildinfo.cmake)
All that has to change in the plugin is:
diff --git a/conan.gradle b/conan.gradle
index e5a2c88..0857349 100644
--- a/conan.gradle
+++ b/conan.gradle
@@ -9,7 +9,7 @@ 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/${flavor}/${abi}'
+ String outputDirPath = '${projectDir}/conan_build/${buildType}/${abi}'
}
Where buildType is defined in the params map as follows
def params = ['abi': abi, 'flavor': flavor, 'projectDir': project.projectDir, 'buildType': buildType.capitalize()]
Now the conan_build tree looks like this:
ppetraki@vanguard:~/Sandbox/Games/conansdl2$ !tree
tree app/conan_build/
app/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
8 directories, 30 files
@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.
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
Would it be possible to release this as proper Gradle Plugin?
@janseeger probably not (at least in near future)
Hi, I'm trying to get your plugin working that I found out about here. I don't know much about gradle so please bear with me. It doesn't appear to search for my local conan profile, $HOME/.conan/profiles/android-21 and instead generates the following error.
It also looks like gradle intends to template whatever that profile is.
So it doesn't appear that I can use this until I have the templated conan profiles.
Other observations.
These variables appear to be defined twice between the build and plugin. Only the plugin's data members appear to be used.
These should probably only appear in the plugin.
Could you please share the profiles and the project hierarchy you used to run this? Thanks.