Skip to content

[BUG] -DCMAKE_CXX_FLAGS overrides default Android compiler flags with new toolchain file #1693

Closed
@rprichard

Description

@rprichard

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 from ANDROID_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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions