Configure CMake to add_library base on current Android build type

Viewed 388

I store my credential keys inside a cpp native file and use CMake to build then link it to my app:

Current code:

My src/main/cpp/credentials-provider-dev.cpp file:

JNIEXPORT jobject JNICALL
Java_com_{package}_CredentialsProvider_extractApiCredentials(JNIEnv *env, jobject instance) {
    jclass cls = env -> FindClass("com/{path}/models/ApiCredentials");
    jmethodID methodId = env -> GetMethodID(cls, "<init>",
    "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");

    return env -> NewObject(cls, methodId,
                            env->NewStringUTF("key"),
                            env->NewStringUTF("other key"),
                            env->NewStringUTF("another key"),
                            env->NewStringUTF("key again"),
                            NULL, NULL
    );
}

My build.gradle defines the link to CMakeLists.txt file:

externalNativeBuild {
    cmake {
        path 'src/main/cpp/CMakeLists.txt'
    }
}

My src/main/cpp/CMakeLists.txt file:

cmake_minimum_required(VERSION 3.4.1)

add_library(
    credentials-provider-dev
    SHARED
    credentials-provider-dev.cpp)

The credentials-provider-dev file only define my dev environment credentials and this code works fine when I build in Debug Type.

Problem:

I also have staging and release build and I want to use different credentials-provider-{dev/staging/production}.cpp file for each build type:

debug {
    ext.alwaysUpdateBuildId = false
    applicationIdSuffix ".debug"
}

staging {
    initWith debug
    debuggable true
}

release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-app.pro'
    signingConfig signingConfigs.release
}

Attempts:

I use native code to store these keys because of better key protection. And I don't want to merge all the build keys into 1 file because of repo security. Only the person has the responsibility to access production build have the credentials-provider-production.cpp file and be able to build release. So my teammates can only have the dev file and build debug mode only.

I tried with find_library to check for staging cpp file existence before calling add_library like this but it didn't work, the lib is still not added:

find_file(
    STAGING_KEY_LIB
    PATHS main/cpp/credentials-provider-staging.cpp)
if (STAGING_KEY_LIB)
    add_library(
        credentials-provider-staging
        SHARED
        main/cpp/credentials-provider-staging.cpp)
endif()

I also tried the CMAKE_BUILD_TYPE parameter sent to the CMakeLists.txt like below. But base on the document: The valid values are Release and Debug. I want to have Staging build too so this method didn't work

add_library(
    credentials-provider-${CMAKE_BUILD_TYPE}
    SHARED
    credentials-provider-${CMAKE_BUILD_TYPE}.cpp)

In summary:

Using native code: How can I separate my secret-key files into different build type-based files? Anyone has experience with this please help. Thanks

2 Answers

This is my current solution:

Project structure:

src
| - CMakeLists.txt
|
| - debug/cpp
  | - CMakeLists.txt
  | - credentials-debug.cpp
|
| - main/cpp
  | - CMakeLists.txt
  | - credentials-release.cpp
|
| - staging/cpp
  | - CMakeLists.txt
  | - credentials-staging.cpp

src/CMakeLists.txt:

cmake_minimum_required(VERSION 3.4.1)

add_subdirectory(debug/cpp)

add_subdirectory(staging/cpp)

add_subdirectory(main/cpp)

src/debug/cpp/CMakeLists.txt:

add_library(
    credentials-debug
    SHARED
    credentials-debug.cpp)

src/main/cpp/CMakeLists.txt: (ignored in .gitignore & will be empty on other machine)

add_library(
    credentials-release
    SHARED
    credentials-release.cpp)

src/staging/cpp/CMakeLists.txt: (ignored in .gitignore & will be empty on other machine)

add_library(
    credentials-staging
    SHARED
    credentials-staging.cpp)

So each child CMakeLists.txt file loads a different cpp file.

  • On my local machine, I'll have all 3 (CMakeLists + cpp) files.

  • On other teammates that don't have permission to build staging or release. They don't have the staging + release cpp files and just a placeholder empty CMakeList files

  • In the Kotlin code: I can extract the debug/staging/release build type to decide to load the right library

Pros:

  • Change the build type is a 1 step process, just change the build variant from Android Studio and the code automatically pick the right native lib to import
  • Important credentials files are ignored from repo

Cons:

  • On my machine, all available cpp files for 3 build types are built from the beginning but only 1 of them is used
  • When someone pulls the project, they have to manually create empty CMakeLists in main/cpp and staging/cpp folder so the add_subdirectory won't throw an error

I'm using the CMAKE_BUILD_TYPE which is passed by Android to CMake. In my case I wanted to use a specific library version, but this approach can also be used with an CMake IF to define a different flow.

string(TOLOWER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
target_link_libraries(Bar  ${PROJECT_SOURCE_DIR}/../../Foo/lib/build/intermediates/library_and_local_jars_jni/${BUILD_TYPE}/jni/arm64-v8a/libFoo.so)
Related