How to create a static library (.a file) in Android Studio 3.2 with CMake
Asked Answered
W

5

4

Now i create a new project include c++ support in Android Studio 3.2, it has native-lib.cpp naturally, the CMakeLists.txt looks like this:

add_library( # Sets the name of the library.
    native-lib

    # Sets the library as a shared library.
    SHARED

    # Provides a relative path to your source file(s).
    src/main/cpp/native-lib.cpp)

now if i build this project, i can find libnative-lib.so in some "debug" directoies, that's OK, but i want static library ie .a files.

Change SHARED to STATIC won't generate those files, what else should i do?

The CMake docs does not mention other way than add_library().

Every docs i searched only talk about SHARED/STATIC.

What did i miss?

Wanhsien answered 23/10, 2018 at 15:1 Comment(2)
"Change SHARED to STATIC won't generate those files" Where did you look for it? I would expect it to exist under app/.externalNativeBuild/cmake/buildVariant/abi/src/main/cpp/ (where buildVariant is one of your build variants, and abi is one of the ABIs you're building for).Duteous
You will need to use the static library in a shared library for it to be useful for an app. Unless you're distributing the library, a static library alone is useless.Cavein
W
4

First, i want to thank all guys above, without your inspiration, i cannot get this answer.

I write this answer down NOT becase those answers are wrong, i just want to help newbies like me, setp by step, with all details should know.

This answer was proved in Android Studio 3.2.1 with CMake 3.4.1, i triple checked.

Now if you create a new project with C++ support, click "next" all the way, you will get a CMakeLists.txt, whick should looks like :

cmake_minimum_required(VERSION 3.4.1)

add_library( 
        native-lib
        SHARED
        src/main/cpp/native-lib.cpp)

find_library( 
        log-lib
        log)

target_link_libraries( 
        native-lib
        ${log-lib})

Change SHARED to STATIC won't output any .a file, you will get nothing, even no .so file( of course, there is no SHARED library be added in this file).

This is Gradle/Android Studio 's "fault", the answers above already mentioned it, if you use CMake alone, you'll definetely get a .a file.

OK, now is my TRICK:

We already have a add SHARED library, then we add a STATIC library, which uses the same source file. Add below in your CMakeLists.txt:

add_library(
        native-lib-static
        STATIC
        src/main/cpp/native-lib.cpp
)

"native-lib-static" can be replaced with any name but "native-lib" since it is used for the SHARED version.

Change your target_link_libraries like this:

target_link_libraries(
        native-lib
        native-lib-static
        ${log-lib})

enter image description here

Gradle->app->build->assembleDebug/assembleRelease

Then you'll get libnative-lib-static.a, in

app\.externalNativeBuild\cmake\debug(release)\<abi>\libnative-lib-static.a

This path is set in app\build.gradle:

android{
    defaultConfig{
         externalNativeBuild{
             CMake{

ATM i'm not sure whether Google will change it in the future, but you can always search the project folder for *.a files.

I don't think i miss anything this time.

All credit to @Michael @Dan Albert @Alex Cohn @shizhen

Wanhsien answered 1/11, 2018 at 2:25 Comment(2)
This doesn't work now for me, Android studio Version is 2020.3.1, on mac, M1 chip. The answer from Suen works for me.Hippogriff
this did work for me, however I found the static libraries at app/.cxx/<8char_generated_string>/<arch> (Android Studio 2020.3.1)Duley
B
5

Adding to @DanAlbert 's comment, the question is not how useful a static library alone can be. The issue is that Android Studio via its Gradle plugin, by default, will not build a CMake target that is a STATIC library.

Luckily, you can explicitly specify the targets you want to produce, e.g.

android { defaultConfig { externalNativeBuild { cmake {
    targets "native_staticlib"
} } } }

Actually, there is not much added value building the static library via integrated externalNativeBuild. You can achieve same if you run cmake --build from command line.

Bradbury answered 24/10, 2018 at 10:36 Comment(0)
W
4

First, i want to thank all guys above, without your inspiration, i cannot get this answer.

I write this answer down NOT becase those answers are wrong, i just want to help newbies like me, setp by step, with all details should know.

This answer was proved in Android Studio 3.2.1 with CMake 3.4.1, i triple checked.

Now if you create a new project with C++ support, click "next" all the way, you will get a CMakeLists.txt, whick should looks like :

cmake_minimum_required(VERSION 3.4.1)

add_library( 
        native-lib
        SHARED
        src/main/cpp/native-lib.cpp)

find_library( 
        log-lib
        log)

target_link_libraries( 
        native-lib
        ${log-lib})

Change SHARED to STATIC won't output any .a file, you will get nothing, even no .so file( of course, there is no SHARED library be added in this file).

This is Gradle/Android Studio 's "fault", the answers above already mentioned it, if you use CMake alone, you'll definetely get a .a file.

OK, now is my TRICK:

We already have a add SHARED library, then we add a STATIC library, which uses the same source file. Add below in your CMakeLists.txt:

add_library(
        native-lib-static
        STATIC
        src/main/cpp/native-lib.cpp
)

"native-lib-static" can be replaced with any name but "native-lib" since it is used for the SHARED version.

Change your target_link_libraries like this:

target_link_libraries(
        native-lib
        native-lib-static
        ${log-lib})

enter image description here

Gradle->app->build->assembleDebug/assembleRelease

Then you'll get libnative-lib-static.a, in

app\.externalNativeBuild\cmake\debug(release)\<abi>\libnative-lib-static.a

This path is set in app\build.gradle:

android{
    defaultConfig{
         externalNativeBuild{
             CMake{

ATM i'm not sure whether Google will change it in the future, but you can always search the project folder for *.a files.

I don't think i miss anything this time.

All credit to @Michael @Dan Albert @Alex Cohn @shizhen

Wanhsien answered 1/11, 2018 at 2:25 Comment(2)
This doesn't work now for me, Android studio Version is 2020.3.1, on mac, M1 chip. The answer from Suen works for me.Hippogriff
this did work for me, however I found the static libraries at app/.cxx/<8char_generated_string>/<arch> (Android Studio 2020.3.1)Duley
A
4

explicitly state build targets in your app level build.gradle

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                # state here
                targets "native-lib" 
            }
        }
    }
}

the output native-lib.a is in both build/intermediates/cmake/debug/obj/<abi> and .externalNativeBuild/cmake/debug/<abi> directory

see google ndk-samples

Abduction answered 12/6, 2020 at 8:37 Comment(1)
This should be answer @2020Omega
M
1

Change SHARED to STATIC won't generate those files, what else should i do?

You can have the static library by setting your lib as STATIC and the generated .a is located at <project-dir>/build/intermediates/cmake/debug/obj/<abi>/libnative-lib.a

add_library( native-lib
         STATIC
         src/main/cpp/native-lib.cpp)

But, like @Dan Albert said, this static lib itself is not much useful. You still need to link it with your other parts of code/libs to come out a final shared library, so that your Android app can use.

For example,

add_library(my-shared-native SHARED src/main/cpp/other-source.cpp)
target_link_libraries(my-shared-native -Wl,--whole-archive native-lib -Wl,--no-whole-archive)

Then on your Java side, you can use this shared library, i.e. libmy-shared-native.so.

static {
    System.loadLibrary("my-shared-native");
}
Milone answered 24/10, 2018 at 1:22 Comment(1)
Thanks. What does '-Wl,--whole-archive native-lib -Wl,--no-whole-archive' mean ?Myrtamyrtaceous
G
0

My Android Studio Version: Android Studio Dolphin | 2021.3.1 Patch 1

First, change SHARED to STATIC in CMakeList:

cmake_minimum_required(VERSION 3.18.1)

project("mystaticapplication")

add_library( # Sets the name of the library.
        mystaticapplication

        # Sets the library as a shared library.
        STATIC

        # Provides a relative path to your source file(s).
        native-lib.cpp)

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( # Specifies the target library.
        mystaticapplication

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

And then, add the externalNativeBuild block to your module-level build.gradle file:

android {
    ...

    defaultConfig {
        ...

        externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=latest', '-DANDROID_TOOLCHAIN=clang'
                targets 'mystaticapplication'
                cppFlags ''
            }
        }
    }

    ...

    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.18.1'
        }
    }

    ...
}

Finally, Rebuild Project, It will generate the .a file you want:

...\app\build\intermediates\cxx\Debug\...\obj\arm64-v8a\libmystaticapplication.a
Germ answered 18/10, 2022 at 5:27 Comment(1)
This works but the header files don't get generated. I have to run ninja install manually for header files to be visible. Any idea what could be wrong?Bindery

© 2022 - 2024 — McMap. All rights reserved.