How to disable Android NDK build for some build variant - c++

I am using Android Studio 2.2 and have setup Gradle to build c/c++ sources with NDK via CMake.
Now I would like to disable NDK build for buildType "debug". For buildType "release" I would like to keep it.
The goal is to make NDK sources compile on the build server (using "release") but disable it for developers (using "debug").
This is the build.gradle file currently in use:
android {
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
defaultConfig {
externalNativeBuild {
cmake {
arguments "-DANDROID_TOOLCHAIN=clang"
cppFlags "-std=c++14"
}
}
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
}
buildTypes {
release {
externalNativeBuild {
cmake {
arguments "-DANDROID_TOOLCHAIN=clang"
cppFlags "-std=c++14"
}
}
ndk {
abiFilters 'armeabi-v7a'
}
}
}
}
How can I disable NDK build (externalNativeBuild) for defaultConfig or buildType "debug"?
Other developers won't have NDK installed (local.properties without ndk.dir=PATH_TO_NDK). Is this possible to configure?
Thanks in advance
Edit:
This externalNativeBuild must be configured with a 'com.android.library'-module, not a 'com.android.application'-module.

Here is how I solved it.
This way Gradle build works for developers with and without NDK installed (and on the build server), which was the goal.
/*
* As soon as Gradle is linked to the externalNativeBuild (cmake / ndkBuild) with a path to
* CMakeLists.txt / Android.mk, the ndk.dir from local.properties file or the ANDROID_NDK_HOME
* environment variable needs to be set, otherwise gradle fails.
* E.g.:
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
*/
// Only enable externalNativeBuild on machines with NDK installed -> valid ndkDir
def ndkDir = project.android.ndkDirectory;
if (ndkDir != null && !ndkDir.toString().isEmpty()) {
externalNativeBuild.cmake.path = "CMakeLists.txt"
}

Regarding #1, if I understand correctly you want to do (or skip) certain things according to the buildType. If so, you can look at one of the many discussions on this subject, such as this one: get current buildType.
Regarding #2, it is not very clear what you want. If you are seeking an alternative to setting the NDK path in local.properties, you can set it via ANDROID_NDK_HOME environment variable. If you want to prevent gradle from failing, check if the ndk.dir exists in the relevant places of your build.gradle script (using properties.getProperty('ndk.dir')).

Related

Configure C++ standard in gradle c++ project?

I have a gradle project defined by the following build script (build.gradle):
plugins {
id 'cpp-application'
}
application {
targetMachines.add(machines.linux.x86_64)
targetMachines.add(machines.windows.x86_64)
}
This is my directory structure:
./src
./src/main
./src/main/cpp
./src/main/cpp/descriptor.cpp
./src/main/cpp/reader_writer.cpp
./src/main/cpp/constant_pool.cpp
./src/main/cpp/unjar.cpp
./src/main/cpp/attribute.cpp
./src/main/cpp/main.cpp
./src/main/cpp/java_serde.cpp
./src/main/headers
./src/main/headers/unjar.h
./src/main/headers/attribute.h
./src/main/headers/constant_pool.h
./src/main/headers/descriptor.h
./src/main/headers/java_serde.h
./src/main/headers/reader_writer.h
./build.gradle
Now I want to compile my project, where I am using C++17 features in some source files. I am oblivious as to how I can set the C++ standard version for my gradle build.
How do I tell gradle to compile my code with C++17?
Set the following inside your application configuration clause:
compilerArgs.add '-std=c++17'
If you are targeting multiple platforms, you can set compilerArgs conditionally:
compilerArgs.addAll toolChain.map { toolChain ->
if (toolChain in [ Gcc, Clang ]) {
return ['-std=++17']
} else if (toolChain in VisualCpp) {
return '/std=c++17'
}
return []
}
In your gradle script where you kick off cmake, add cppFlags:
cmake {
cppFlags = "std=c++17"
}
I hope it helps you:
cmake {
cppFlags = "std=c++17"}
} <- at 3rd line

How to change CMake path in Android Studio

I copied an existing Android Studio project from another user and I can't compile C++ files using CMake because it uses the other user's CMake path
I already tried to delete and re-create the CMakeLists.txt file and re-installed CMake using SDK Manager
Here's my gradle:
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
defaultConfig {
. . .
externalNativeBuild {
cmake {
arguments '-DANDROID_TOOLCHAIN=clang'
}
}
}
buildTypes {
release {
. . .
}
}
lintOptions {
abortOnError false
}
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
}
}
}
About 2 months ago, the project worked perfectly. Then I made some minor modifications and now I have this error.
It's just like there's a way to indicate which CMake use, but I don't know where to find it.
Here's a resume of what the console show me when I want to clean the project:
Caused by: net.rubygrapefruit.platform.NativeException: Could not start '/Users/Old_User/Library/Android/sdk/cmake/3.6.4111459/bin/cmake'
at net.rubygrapefruit.platform.internal.DefaultProcessLauncher.start(DefaultProcessLauncher.java:27)
at net.rubygrapefruit.platform.internal.WrapperProcessLauncher.start(WrapperProcessLauncher.java:36)
The rest of the error log is about the same message saying that it can find the cmake file
It appears that android build system is not able to find cmake utility. Please check if it is installed.

How to fix Android NDK app crash with UnsatisfiedLinkError when loading protobuf-lite.so on Android 6.0?

I have created an Android application with a custom C++ library that depends on Google's protobuf-lite library. It works fine on all the recent devices I have tried to run it on (under Android 7, 8 and 8.1). However, I found that on older devices running Android 6.0.1 or 6.0 (Asus Nexus 7 and some old Motorola phone), the application crashes on loading the libprotobuf-lite.so dependency.
Here is the stacktrace I get:
E/AndroidRuntime: FATAL EXCEPTION:
main Process: com.mycompany.core, PID: 11582 java.lang.UnsatisfiedLinkError:
dlopen failed: cannot locate symbol "__aeabi_memmove8" referenced by "/data/app/com.mycompany.core-2/lib/arm/libprotobuf-lite.so"...
at java.lang.Runtime.loadLibrary(Runtime.java:372)
at java.lang.System.loadLibrary(System.java:1076)
at com.mycompany.core.CameraTestActivity.<clinit>(CameraTestActivity.java:46)
at java.lang.Class.newInstance(Native Method)
at android.app.Instrumentation.newActivity(Instrumentation.java:1067)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2317)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)$
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
This is really weird because the missing symbol "__aeabi_memmove8" appears to be a low-level standard library feature and I do not really grasp why it would not be found on Android 6.
In addition, I am quite confident that the issue comes from protobuf-lite not linking correctly because previous versions of my app, which were not using protobuf, ran fine on these Android 6 devices.
Below are some details on my config.
Protobuf version: 3.6.1
Lib package cross-compiled from source using Cmake GUI, NDK r18 toolchain and MinGW
Devices' ABI: armeabi-v7a
Android SDK compile version: API 28
Application build toolchain : Gradle + CMake.
Android Studio 3.1.3
Here is my build.gradle file:
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.mycompany.core"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
ndk{
abiFilters "arm64-v8a", "armeabi-v7a"
}
}
debug {
ndk{
abiFilters "arm64-v8a", "armeabi-v7a"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.0"
implementation "android.arch.lifecycle:viewmodel:1.1.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
And here is my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.4.1)
include_directories(src/main/cpp/protobuf/include)
file(GLOB SRCS
"src/main/cpp/core/*.cpp"
)
file(GLOB JNI_SRCS
"src/main/cpp/jni/*.cpp"
)
add_library(mycorelib SHARED ${SRCS} ${JNI_SRCS})
find_library(log-lib log)
add_library(libprotobuf-lite SHARED IMPORTED)
set_target_properties(libprotobuf-lite
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libprotobuf-lite.so)
target_link_libraries(mycorelib
android
jnigraphics
${log-lib}
libprotobuf-lite)
Has anyone run into this problem before? Any hint on how to fix this would be greatly appreciated.
This is https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md#cannot-locate-symbols.
Your protobuf library was built for a higher minSdkVersion than the rest of your app, and cannot run on the device you're using.
You need to change the version of the NDK that you are using. The article below suggests that moving to NDK 22 or newer will fix this.
unable to run on Android 6.0 after 7b77c0acedf708749b68304cc5f0ac469c9d7136

How to compile a Linux LTO-enabled library with Premake 5?

SO wisdom, I'm turning to you. I'm trying to build a 64-bit static lib using LTO with Makefiles and Premake 5 on Ubuntu 16.04 LTS.
Here's the premake script i'm using:
-- premake5.lua
workspace "TestApp"
location "TestApp" -- The directory of generated files - .sln, etc.
configurations { "Debug", "Shipping" }
platforms { "Linux_Static", "Linux_DLL" }
targetdir "TestApp/Build/%{cfg.platform}/%{cfg.buildcfg}"
objdir "TestApp/Build/"
language "C++"
architecture "x86_64"
system "linux"
filter "platforms:*Static"
kind "StaticLib"
filter "platforms:*DLL"
kind "SharedLib"
filter "kind:SharedLib"
defines { "TEST_USE_DLL", "TEST_DLL_EXPORT" }
-- Configuration filters
configuration "*"
flags { "ExtraWarnings", "C++14", "MultiProcessorCompile", "ShadowedVariables", "UndefinedIdentifiers" }
configuration { "Debug" }
symbols "On"
defines { "TEST_DEBUG" }
optimize "Debug"
configuration "Shipping"
defines { "TEST_SHIPPING" }
optimize "Full"
flags { "LinkTimeOptimization" }
-- step 1
--buildoptions "--plugin=$$(gcc --print-file-name=liblto_plugin.so)"
-- step 2
--toolset "clang"
-- step 3
--premake.tools.gcc.ar = "gcc-ar"
-- Projects
project "TestCore"
location "TestApp/Core"
files { "TestApp/Core/*.h", "TestApp/Core/*.cpp" }
includedirs { "TestApp/" }
project "UnitTests"
location "TestApp/Tests"
kind "ConsoleApp"
links { "TestCore" }
objdir "TestApp/Tests/Build/"
files { "TestApp/Tests/UnitTests/*.cpp", "TestApp/ThirdParty/Catch/*" }
includedirs { "TestApp/ThirdParty/Catch", "TestApp/" }
removedefines { "TEST_DLL_EXPORT" }
filter { "platforms:*DLL", "system:linux" }
runpathdirs { "Build/%{cfg.platform}/%{cfg.buildcfg}" }
"Shipping" is the faulty configuration. I also bundled the whole test project in a zip for you to try to reproduce the issue.
The errors I have when compiling the TestCore library are first plugin needed to handle lto object, then plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so is not licensed under a GPL-compatible license.
What can we do about it ? If you have any knowledge to make it work with GCC, please help.
what you would do to reproduce the GCC errors after extracting the zip:
cd testBreaking
premake5 gmake
cd TestApp
make config=shipping_linux_static TestCore (get the "plugin needed to handle lto object" error)
Uncomment line 37 of premake5.lua to get the "not licensed under a GPL-compatible license" error
Uncomment line 43 to use gcc-ar instead of ar, notice it doesn't work either
Using gcc option -fuse-linker-plugin doesn't help
Some more system info:
ubuntu 16.04 LTS
gcc 5.4, make 4.1, ar 2.26.1
premake 5.0.0-alpha11
I got it working with Clang. Using toolset clang for Shipping configuration (using LLVM 3.9), the library seems to compile fine. But I got another error:
error adding symbols: Archive has no index; run ranlib to add one
I managed to work around this issue by calling ranlib Build/Linux_Static/Shipping/libTestCore.a --plugin /usr/lib/llvm-3.9/lib/LLVMgold.so, then make again.
So it painfully works using Clang.
I read that I could create a specific premake toolset for this kind of thing, because it's recommended replacing all gnu utils with their gcc- counterparts (e.g. gcc-ar instead of ar), but having rapidly tinkered with premake.tools.gcc.ar = "gcc-ar" with no result, I'm not so sure it would help.

Choosing compiler options based on the operating system in boost-build

Currently I can build my program using boost build in different platforms by setting the toolset and parameters in the command line. For example :
Linux
b2
MacOS
b2 toolset=clang cxxflags="-stdlib=libc++" linkflags="-stdlib=libc++"
Is there a way to create a rule in the Jamroot file to decide which compiler to use based on the operating system? I am looking for something along these lines:
import os ;
if [ os.on-macos ] {
using clang : <cxxflags>"-stdlib=libc++" <linkflags>"-stdlib=libc++c ;"
}
in linux it automatically decides to use gcc but in the mac if I don't specify the clang toolset it will try (without success) to compile it with gcc.
Just for reference, here is my current jamroot (any suggestions also appreciated):
# Project requirements (note, if running on a Mac you have to build foghorn with clang with libc++)
project myproject
: requirements <cxxflags>-std=c++11 <linkflags>-std=c++11 ;
# Build binaries in src
lib boost_program_options ;
exe app
: src/main.cpp src/utils src/tools boost_program_options
;
How abou using a Jamroot? I have the following in mine. It selects between two GCC versions on Linux, depending on what's in an environmen variable, and chooses vacpp on AIX.
if [ os.name ] = LINUX
{
switch [ modules.peek : ODSHOME ]
{
case *gcc-4* : using gcc : 4.4 : g++-4.4 ;
case *gcc-3.3* : using gcc : 3.3 : g++-3.3 ;
case * : error Only gcc v4 and gcc v3.3 supported. ;
}
}
else if [ os.name ] = AIX
{
using vacpp ;
}
else
{
error Only Linux and AIX supported at present. ;
}
After a long time I have found out that there is really no way (apart from very hacky) to do this. The goal of Boost.Build is to let the toolset option for the user to define.
The user has several ways to specify the toolset:
in the command line with --toolset=gcc for example
in the user configuration by setting it in the user-config.jam for all projects compiled by the user
in the site configuration by setting it in the site-config.jam for all users
the user-config.jam can be in the user's $HOME or in the boost build path.
the site-config.jam should be in the /etc directory, but could also be in the two locations above.
In summary, setup your site-config or user-config for a pleasant experience, and write a nice README file for users trying to compile your program.
Hope this helps someone else.