Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building+linking libpng in Android Studio with CMake on Windows

I'm trying to start a native app project with Android Studio and CMake on Windows 10, but I'm stuck at including libpng.

For starters it's the first time I've seen a CMakeLists.txt file. It took me a day to figure out target_link_libraries(native-activity ... png) could not be target_link_libraries(png native-activity ...) since all the error messages were about files not being created and commands failing due to missing requirements from the toolchain (why were the essential errors at the end of the list? not cool!).

After finally managing to include libpng in the project I now get a build error:

Error:Execution failed for task ':app:externalNativeBuildDebug'.
...
error: unknown target CPU 'armv5te'
CMake Error at scripts/genout.cmake:78 (message):
    Failed to generate
    C:/APP_PATH/app/libpng-1.6.28/build/scripts/symbols.out.tf1
ninja: build stopped: subcommand failed.

I've recursively grep'ed my project, .android, .AndroidStudio2.2 directories, as well as filenames, and found absolutely nothing with armv5te except for the genout.cmake. My abiFilters line is abiFilters 'x86'.

How do I build libpng to link to my native app? Also, in Android Studio it shows the project now includes the libpng source files (with no less than 9 projects dedicated to it!). Is there any way to remove it?

Here is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.4.1)

# build native_app_glue as a static lib
add_library(app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)

set(png_src_dir ../../../../libpng-1.6.28)
set(PNG_STATIC ON)
add_subdirectory(${png_src_dir} ${png_src_dir}/build)

# now build app's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14")

add_library(native-activity SHARED
            main.cpp logger.cpp logger.h game.cpp game.h
            shaders.cpp shaders.h assets.cpp assets.h)

target_include_directories(native-activity PRIVATE ${ANDROID_NDK}/sources/android/native_app_glue
                           C:/devlibs/include
                           ${png_src_dir})

# add lib dependencies
target_link_libraries(native-activity app-glue android log EGL GLESv2 png)
like image 407
Olivetree Avatar asked Dec 14 '22 00:12

Olivetree


2 Answers

I've managed libpng to work with android NDK app (CMake build system instead of Android.mk) as static library. I used libpng-android repackaging. Here are things I've done:

  1. git clone https://github.com/julienr/libpng-android.git into ${YOUR_LIBS_FOLDER} (I used ${ANDROID_NDK_ROOT_DIRECTORY}/sources/android).
  2. Add ${ANDROID_NDK_ROOT_DIRECTORY} (home/username/Android/sdk/ndk-bundle for me) to global $PATH variable which is needed for build script).
  3. Build lib with ndk-build (there is ./build.sh for that in directory with lib). Library will be built for different ABI targets ( arm64-v8a, armeabi, x86_64 etc).
  4. At this point you have library headers at ${YOUR_LIBS_FOLDER}/libpng-android/jni and libpng.a at ${YOUR_LIBS_FOLDER}/libpng-android/obj/local/${ANDROID_ABI}/, where ${ANDROID_ABI} is target platform.
  5. Finally you could include lib at CMakeLists.txt. libpng requires zlib compression library so you need to link against it aswell (zlib is provided by android studio so just add -lz flag).

Here is related piece from my CMakeLists.txt:

add_library(libpng STATIC IMPORTED)
set_target_properties(libpng PROPERTIES IMPORTED_LOCATION
    ${YOUR_LIBS_FOLDER}/libpng-android/obj/local/${ANDROID_ABI}/libpng.a)

add_library(appManager SHARED src/main/cpp/appManager.cpp)

target_include_directories(appManager PRIVATE ${YOUR_LIBS_FOLDER}/libpng-android/jni/)

target_link_libraries(appManager
                       android
                       libpng
                       z)

Few things to note there:

  • ${ANDROID_ABI} is a variable set by Android Studio build system.
  • Once again: you need to link against zlib, that is why we have libpng z instead of libpng in target_link_libraries.
like image 156
Ramil Kudashev Avatar answered Feb 09 '23 01:02

Ramil Kudashev


With Android Studio 3.6, including libpng into your project is rather easy.

  • Download the latest sources from sourceforge and unpack the zip file.
  • In Android Studio, make sure you have NDK and CMake installed (in SDK Manager)
  • Create a new Android Library module, call it pngAndroid (I would recommend to set the Package Name to org.libpng.libpng, same as MacOS). Keep it Java, and choose the min SDK that fits your app.
  • For this module, choose File/Link C++ Project with Gradle. Find CMakleLists.txt of libpng that you downloaded as step 1.
  • In the pngAndroid's build.gradle, add

    android.defaultConfig.externalNativeBuild.cmake.targets "png_static"
    
  • In Project Structure dialog, specify module dependency between your Module and pngAndroid.

When you build the pngAndroid Module, you will see files libpng16d.a in build/intermediates/cmake/debug/obj directory under pngAndroid.

In your Module, you also have CMakleLists.txt. In this script, you have a target, e.g.

add_library(native-library SHARED …

You should add libpng as dependency of native-library:

target_link_libraries(native-library libpng)

Elsewhere in this CMakleLists.txt you should define the imported libpng:

add_library(libpng STATIC IMPORTED)
set_target_properties(libpng PROPERTIES IMPORTED_LOCATION
    ../pngAndroid/build/intermediates/cmake/debug/obj/${ANDROID_ABI}/libpng16d.a)
set_target_properties(libpng PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
    ../../lpng1637)
set_target_properties(libpng PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES
    z)

Note that here I used relative paths a) to the pngAndroid Module that you added to your Android Studio project, and b) to the libpng sources that you downloaded. In your setup, the relative paths may be different.


For completeness, here is a cleaned-up build.gradle script for pngAndroid to build shared library libpng16d.so:

apply plugin: 'com.android.library'

android {
    compileSdkVersion 29
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        externalNativeBuild {
            cmake {
                arguments "-DSKIP_INSTALL_ALL=YES"
                targets "png"
            }
        }
    }

    externalNativeBuild {
        cmake {
            path file('../../lpng1637/CMakeLists.txt')
        }
    }
}

When you build the module, you get in build/outputs an aar file that contains the jni directory with armeabi-v7a/libpng16d.so and all other ABI variants.


Last, it's worth mentioning that Google is working on an easier native package manager. Unfortunately, it's still not released.

like image 38
Alex Cohn Avatar answered Feb 09 '23 00:02

Alex Cohn