Build a Make based C++ project using Android NDK - makefile

I need to build csparse library for an Android project. The project supports Make build system.
I am not familiar with use of NDK for Make build. I have used Androids CMake toolchain for building CMake based projects.
I am looking for pointers to usage of NDK for building C++ projects based on Make. I do not want to manually write a CMakeLists.txt for building the project with CMake toolchain because there are too many source files in the csparse project!
Any help is appreciated!

https://developer.android.com/ndk/guides/other_build_systems covers using the NDK with build systems other than ndk-build and CMake. The last section explicitly covers traditional make projects.

OK, It wasn't so difficult. It seems CSparse is a self-contained and does not depend on other linear-algebra libraries such as CHOLMOD, BLAS, LAPACK, Metis... etc
Here is the CMakeLists.txt file from my Android project :
cmake_minimum_required(VERSION 3.4.1)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include/CSparse/Source
)
add_library( # Sets the name of the library.
csparse-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
include/CSparse/Source/cs_add.c
include/CSparse/Source/cs_amd.c
include/CSparse/Source/cs_chol.c
include/CSparse/Source/cs_cholsol.c
include/CSparse/Source/cs_compress.c
include/CSparse/Source/cs_counts.c
include/CSparse/Source/cs_cumsum.c
include/CSparse/Source/cs_dfs.c
include/CSparse/Source/cs_dmperm.c
include/CSparse/Source/cs_droptol.c
include/CSparse/Source/cs_dropzeros.c
include/CSparse/Source/cs_dupl.c
include/CSparse/Source/cs_entry.c
include/CSparse/Source/cs_ereach.c
include/CSparse/Source/cs_etree.c
include/CSparse/Source/cs_fkeep.c
include/CSparse/Source/cs_gaxpy.c
include/CSparse/Source/cs_happly.c
include/CSparse/Source/cs_house.c
include/CSparse/Source/cs_ipvec.c
include/CSparse/Source/cs_leaf.c
include/CSparse/Source/cs_load.c
include/CSparse/Source/cs_lsolve.c
include/CSparse/Source/cs_ltsolve.c
include/CSparse/Source/cs_lu.c
include/CSparse/Source/cs_lusol.c
include/CSparse/Source/cs_malloc.c
include/CSparse/Source/cs_maxtrans.c
include/CSparse/Source/cs_multiply.c
include/CSparse/Source/cs_norm.c
include/CSparse/Source/cs_permute.c
include/CSparse/Source/cs_pinv.c
include/CSparse/Source/cs_post.c
include/CSparse/Source/cs_print.c
include/CSparse/Source/cs_pvec.c
include/CSparse/Source/cs_qr.c
include/CSparse/Source/cs_qrsol.c
include/CSparse/Source/cs_randperm.c
include/CSparse/Source/cs_reach.c
include/CSparse/Source/cs_scatter.c
include/CSparse/Source/cs_scc.c
include/CSparse/Source/cs_schol.c
include/CSparse/Source/cs_spsolve.c
include/CSparse/Source/cs_sqr.c
include/CSparse/Source/cs_symperm.c
include/CSparse/Source/cs_tdfs.c
include/CSparse/Source/cs_transpose.c
include/CSparse/Source/cs_updown.c
include/CSparse/Source/cs_usolve.c
include/CSparse/Source/cs_util.c
include/CSparse/Source/cs_utsolve.c
)
add_library(native-lib SHARED native-lib.cpp)
find_library( # Sets the name of the path variable.
log-lib
log)
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
csparse-lib
)
I hope this answer helps others.

Related

CMake: Use variables from existing Makefile of 3rdparty library

I'm facing the following scenario:
Existing project which uses cmake
External 3rdparty library which only comes with Makefiles
The difference of my situation compared to existing questions is that I don't need to have cmake to build the 3rdparty library via the Makefile. Instead, the 3rdparty library provides a library.mk Makefile which has variables like LIB_SRCS and LIB_INCS containing all source and header files required to compile the library.
My idea is to include the library.mk into the project's CMakeLists.txt and then adding those $(LIB_SRCS) and $(LIB_INCS) to target_sources().
My question: How can I include library.mk into the existing CMakeLists.txt to get access to the $(LIB_SRCS) and $(LIB_INCS) for adding them to target_sources()? I'm looking for something like this:
include("/path/to/library.mk") # Somehow include the library's `library.mk` to expose variables to cmake.
add_executable(my_app)
target_sources(
my_app
PRIVATE
main.c
$(LIB_SRCS) # Add 3rd-party library source files
$(LIB_INCS) # Add 3rd-party library header files
)
Using include() does not work as the library.mk is not a CMake list/file.
Since you can't be sure that your target system will even have Make on it, the only option is to parse the strings out of the .mk file, which might be easy if the variables are set directly as a list of filenames, or really hard if they are set with expansions of other variables, conditionals, etc. Do this with FILE(STRINGS) cmake doc.
Your plan will only work if the Makefiles are trivial, and do not set important compiler flags, define preprocessor variables, modify the include directory, etc. And if they really are trivial, skip the parsing, and just do something like aux_source_directory(<dir> <variable>) to collect all the sources from the library directory.
You might also consider building and maintaining a CMakeLists.txt for this third-party library. Do the conversion once, and store it as a branch off of the "vendor" main branch in your version control system. Whenever you update, update the vendor branch from upstream, and merge or rebase your modifications. Or just store it in your existing project, referring to the source directory of the 3rd-party stuff.

NDK Error "Unexpected native build target xyz. Valid targets are:"

I have an Android Studio project which depends on a native shared library. I have created a cmake file to compile the library and I have added a soft link to the shared library inside the android project (in src/main/jniLibs/armeabi). That way when the android project is built, the shared library is included in the package.
Here is the relevant part of build.gradle:
android {
...
externalNativeBuild {
cmake {
path "../cpp/CMakeLists.txt"
}
}
}
The problem is that gradle tries to open the shared library before invoking the instructions to build it.
Information:Gradle tasks [:app:assembleDebug]
Error:Could not list contents of 'app/src/main/jniLibs/armeabi/libfoo.so'. Couldn't follow symbolic link.
How can I invoke the cmake from inside the project and include the library in the project at the same time?
--
EDIT
In the cmake the shared library is built with ExternalProject_Add. Unfortunately gradle doesn't see that target, nor does it see imported shared libraries as targets. So this does not work:
add_library(libfoo SHARED IMPORTED GLOBAL)
add_dependencies(libfoo libactual)
I tried to invoke building the particular target with a gradle config:
defaultConfig {
...
externalNativeBuild {
cmake {
targets "libfoo"
}
}
}
But gradle still doesn't see it and fails with:
Unexpected native build target libfoo. Valid values are:
The valid values are basically an empty list.
Currently I work around this by creating a fictional executable depending on the library.
add_executable(libfoo a.c)
add_dependencies(libfoo libactual)
In my case, I added a new CMake target, but having none was cached somehow (by CMake or Gradle).
Simply close Android Studio, remove the entire build or .build directory, then open Android Studio and build again.
Note that sub-projects have their own separate build directory.
So, you may need to search for the word build, and after ensuring found result is not required, remove them too.
If still not fixed, remember that CMake has it's own separate cache files, which normallay are inside said directories unless you run CMake directly (outside of Android Studio).

CMake Best Practice for using Targets

I compiled all the information and now iam trying to implement CMake build system for a super project and i really need a guidance here,it would be great to transform all data i acquired by reading about CMake across different sites to practical knowledge
i have a fundamental questions regarding understating CMake Targets
In case of multi directories structure,i want to achieve most portability thus having target for each directory and then use these different targets as link for others in another directories, my goal is to be agile as possible and not be coupled with a certain directory structure, make use of CMake to figure the dependency, where i would be only concerned about targets
my final target is a library of libraries
USE CASE :
First level directory {PROJECT} ( 2 folders ) :
CMakeLists.txt common_env source
​
​First level CMakeLists.txt :
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(dassys)
# iam using this because target scope change with sub-directory
include(common/CMakeLists.txt)
include(src/CMakeLists.txt)
#Add Library
add_library(dassys INTERFACE)
#Creating library
target_link_libraries(dassys INTERFACE
common
src
)
Second Level directory {{PROJECT} /common}(2 folders 1 file ):
cfgd_*** CMakeLists.txt dstdh header.h
Second Level CMakeLists.txt
#Adding Subdirectory
include(${CMAKE_CURRENT_LIST_DIR}/dstdh/CMakeLists.txt)
include(${CMAKE_CURRENT_LIST_DIR}/cfgd_***/CMakeLists.txt)
#add_subdirectory(dstdh)
#add_subdirectory(cfgd_***)
#Add Library
add_library(common INTERFACE)
#Creating library
target_link_libraries(common INTERFACE
dstdh
cfgd_***
)
----------------------------------------------------------------------------------------------------------------------------------
​Thrid Level directory { {PROJECT} /common/dstdh }(6 files ):
CMakeLists.txt dassert.h dfloat.h dstdbit.h dstdbool.h dstdint.h dstdio.h dstring.h
Thrid Level CMakeLists.txt
#Adding Library
add_library(dstdh INTERFACE )
target_sources(dstd INTERFACE
${CMAKE_CURRENT_LIST_DIR}/dassert.h
${CMAKE_CURRENT_LIST_DIR}/dfloat.h
${CMAKE_CURRENT_LIST_DIR}/dstdbit.h
${CMAKE_CURRENT_LIST_DIR}/dstdbool.h
${CMAKE_CURRENT_LIST_DIR}/dstdint.h
${CMAKE_CURRENT_LIST_DIR}/dstdio.h
${CMAKE_CURRENT_LIST_DIR}/dstring.h
)
#Add include for Header only library
target_include_directories(dstdh INTERFACE
"${CMAKE_CURRENT_LIST_DIR}/dstdh/"
)
and here is my question, i need to link against dstd library in another directory
how can this be done because as far as i understand
target_link_libraries ( mylib dstd ) should work in different directory because i have target which is dstd INTERFACE library, and i need Cmake to resolve this dependency by finding this target and link against it
i get compilation error as dstdint.h is not found when mylib is being compiled

cmake import exist SimpleAmqp library

Now my CMakeList.txt looking like this.
cmake_minimum_required(VERSION 3.6)
project(RabbitMQClient)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES main.cpp)
include_directories(src)
include_directories(dependentFile)
add_executable(RabbitMQClient ${SOURCE_FILES})
find_library(SimpleAmqpClient SimpleAmqpClient.2.lib PATHS ./SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
set(IMPORTED_IMPLIB ./SimpleAmqpClientLib)
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
when Linking the RabbitMQClient it complains.
cannot find -lSimpleAmqpClient
I want to use the SimpleAmqpClient library in the project,however not quite familiar with cmake not sure the find_library,IMPORTED_IMPLIB,PUBLIC in target_link_libraries was correctly used.Any help would be appreciate.
You have messed up with variables, targets and properties.
Proper usage of IMPORTED libraries for linking would be:
# This command sets *variable* SimpleAmqpClient_LIBRARY
find_library(SimpleAmqpClient_LIBRARY SimpleAmqpClient.2.lib
PATHS ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib # Specify a *directory*, not a library *file*
)
# Next, create an IMPORTED *target*
add_library(SimpleAmqpClient SHARED IMPORTED)
# And set IMPORTED_LIB *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES IMPORTED_LIB ${SimpleAmqpClient_LIBRARY})
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
However, there some simplifications which could be done:
Normally, find_library is used when you don't know complete path to the library file. E.g., its directory may be different on different machines, or its prefix/extension may be different on different plaforms.
If you know complete path to the library, just use this path directly (e.g., assign it to the variable).
Normally, property IMPORTED_LOCATION is used for specify library to link with. Property IMPORTED_LIB is specific for Windows .dlls, when linking requires not a library file (.dll), but some other one (.lib).
However, CMake perfectly understands .lib file in IMPORTED_LOCATION property even for Windows .dlls, so your code need not distinguish SHARED Windows libraries from others: just use IMPORTED_LOCATION property in all cases.
Simplified version of the code:
# Create an IMPORTED library *target*
add_library(SimpleAmqpClient IMPORTED)
# Set IMPORTED_LOCATION *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)

Can CMake build subprojects (with benefits)?

I have some huge project that is being compiled in CMake.
It is developed for quite a long time, have 8000+ source/header files (over 500Mbytes, over 500 CMakefile.txt files).
They use directory structure like this
PROJECT_NAME
src
/ subdir_name
/ other_dir_name
/ some_different_dir
/ MY_SPECIFIC_DIR <---
/ yet_another_dir
build
and build it out-source, like this:
name#host:~/PROJECT_NAME/build> cmake ../src
name#host:~/PROJECT_NAME/build> make all
then it's build as one BIG binary (details are not important).
I cannot touch anything else, just content of MY_SPECIFIC_DIR - it's source and CMake files.
So, I have source code in MY_SPECIFIC_DIR tweak CMakefile.txt files somehow and would like to build it like this:
name#host:~/PROJECT_NAME/build_specific> cmake ../src/MY_SPECIFIC_DIR
name#host:~/PROJECT_NAME/build_specific> make all
This should build things in MY_SPECIFIC_DIR into single binary with some few links to other subprojects. But also (obviously) don't change anything about how whole project is compiled.
My question is:
Is my desired setup
posible
using CMake?
Can I somehow test in CMakeFile.txt that it is root project and build it in different way then when it is builded as a whole?
Unless, I have to resort to different means and use standard make for this.
I don't know CMake so I'm hoping for YES/NO anwer, preferable even for technique how to achieve this. And then learn the CMake and do it.
Also, I must use CMake version 2.6.
Thanks
Basic concept is to use
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
... code for stand-alone app
else()
... what was in this file before
endif()

Resources