Description
Description
The NDK automatically switches from the legacy CMake toolchain file to the new toolchain file if the CMake version is new enough. There are some differences in the way the two modes operate regarding the -DCMAKE_${LANG}_FLAGS
variable that may have been unintentional.
With the legacy toolchain file, the CMAKE_${LANG}_FLAGS
were appended to a set of default flags. With the new toolchain file, setting CMAKE_${LANG}_FLAGS
instead overrides the default flags.
The result is that projects that upgrade to a newer CMake (or which start passing -DCMAKE_${LANG}_FLAGS
) lose the default compiler flags.
This situation is made worse with Gradle. The Android Studio template for a new NDK project adds these build.gradle lines:
externalNativeBuild {
cmake {
cppFlags ''
}
}
cppFlags is turned into a command-line argument setting CMAKE_CXX_FLAGS. However, when it's empty, the Android Gradle Plugin (AGP) instead omits the -DCMAKE_CXX_FLAGS=...
argument. That's fine for the legacy toolchain, but with the new toolchain file, it means that adding a new argument to the cppFlags ''
also suppresses many default flags. (With the new toolchain file, passing -DCMAKE_CXX_FLAGS=
also overrides the defaults, but AGP isn't passing that.)
This behavioral change broke a project that was passing -std=...
in cppFlags to set the C++ dialect. After upgrading CMake, the compiler stopped building with -ffunction-sections
and -fdata-sections
, which bloated the app's shared object. (For this particular project, -std=...
was the only thing in cppFlags, so I think it was fixed by switching to CXX_STANDARD.)
Example:
# Test project
cat >CMakeLists.txt <<EOF
project(hello)
cmake_minimum_required(VERSION 3.18.1)
add_executable(hello hello.cpp)
EOF
cat >hello.cpp <<EOF
int main() {}
EOF
Legacy toolchain file (without -DCMAKE_CXX_FLAGS):
$ rm -fr out && cmake -GNinja -Bout \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_TOOLCHAIN_FILE=/x/android-ndk-r24/build/cmake/android.toolchain.cmake \
-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON \
&& ninja -Cout -v
...
/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ \
--target=armv7-none-linux-androideabi19 \
--sysroot=/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
-g -DANDROID -fdata-sections -ffunction-sections -funwind-tables \
-fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
-march=armv7-a -mthumb -Wformat -Werror=format-security \
-Os -DNDEBUG -fPIE \
-MD -MT CMakeFiles/hello.dir/hello.cpp.o \
-MF CMakeFiles/hello.dir/hello.cpp.o.d \
-o CMakeFiles/hello.dir/hello.cpp.o \
-c /x/cmaketest/hello.cpp
Legacy toolchain file (with -DCMAKE_CXX_FLAGS):
$ rm -fr out && cmake -GNinja -Bout \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_TOOLCHAIN_FILE=/x/android-ndk-r24/build/cmake/android.toolchain.cmake \
-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON \
-DCMAKE_CXX_FLAGS=-std=c++17 \
&& ninja -Cout -v
...
/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ \
--target=armv7-none-linux-androideabi19 \
--sysroot=/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
-g -DANDROID -fdata-sections -ffunction-sections -funwind-tables \
-fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
-march=armv7-a -mthumb -Wformat -Werror=format-security \
-std=c++17 -Os -DNDEBUG -fPIE \
-MD -MT CMakeFiles/hello.dir/hello.cpp.o \
-MF CMakeFiles/hello.dir/hello.cpp.o.d \
-o CMakeFiles/hello.dir/hello.cpp.o \
-c /x/cmaketest/hello.cpp
These default flags come from ANDROID_COMPILER_FLAGS in $NDKr24/build/cmake/android-legacy.toolchain.cmake
:
-g -DANDROID -fdata-sections -ffunction-sections -funwind-tables \
-fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
-march=armv7-a -mthumb -Wformat -Werror=format-security`
New toolchain file (without -DCMAKE_CXX_FLAGS):
$ rm -fr out && cmake -GNinja -Bout \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_TOOLCHAIN_FILE=/x/android-ndk-r24/build/cmake/android.toolchain.cmake \
-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF \
&& ninja -Cout -v
...
/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ \
--target=armv7-none-linux-androideabi19 \
--sysroot=/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
-DANDROID -fdata-sections -ffunction-sections -funwind-tables \
-fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
-march=armv7-a -mthumb -Wformat -Werror=format-security \
-fexceptions -frtti -stdlib=libc++ -Os -DNDEBUG -fPIE \
-MD -MT CMakeFiles/hello.dir/hello.cpp.o \
-MF CMakeFiles/hello.dir/hello.cpp.o.d \
-o CMakeFiles/hello.dir/hello.cpp.o \
-c /x/cmaketest/hello.cpp
New toolchain file (with -DCMAKE_CXX_FLAGS):
$ rm -fr out && cmake -GNinja -Bout \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_TOOLCHAIN_FILE=/x/android-ndk-r24/build/cmake/android.toolchain.cmake \
-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF \
-DCMAKE_CXX_FLAGS=-std=c++17 \
&& ninja -Cout -v
...
/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ \
--target=armv7-none-linux-androideabi19 \
--sysroot=/x/android-ndk-r24/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
-std=c++17 -Os -DNDEBUG -fPIE \
-MD -MT CMakeFiles/hello.dir/hello.cpp.o \
-MF CMakeFiles/hello.dir/hello.cpp.o.d \
-o CMakeFiles/hello.dir/hello.cpp.o \
-c /x/cmaketest/hello.cpp
Adding -DCMAKE_CXX_FLAGS suppresses these flags:
-DANDROID -fdata-sections -ffunction-sections -funwind-tables \
-fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
-march=armv7-a -mthumb -Wformat -Werror=format-security \
-fexceptions -frtti -stdlib=libc++
Using the new toolchain file results in explicit -fexceptions and -frtti arguments, which were not passed with the legacy toolchain file. This also means that:
- With the new toolchain file, passing
-DCMAKE_CXX_FLAGS=...
suppresses the flags produced by-DANDROID_CPP_FEATURES="no-exceptions no-rtti"
- With the legacy toolchain file, the
-DCMAKE_CXX_FLAGS=...
flags are appended to those resulting fromANDROID_CPP_FEATURES
.
Affected versions
r23, r24, r25, Canary
Canary version
n/a
Host OS
Linux, Mac, Windows
Host OS version
any
Affected ABIs
armeabi-v7a, arm64-v8a, x86, x86_64
Build system
CMake
Other build system
No response
minSdkVersion
n/a
Device API level
n/a