Native C++ Code in Android Studio with a Static Library - c++

I am trying to make a native C++ code in Android Studio and with CMake. My C++ code uses precompiled static library (.a file).
I included its header .h in my C++ code. I also linked the location of the .h and the .a files in my CMakeList.txt as below:
include_directories(".h file location")
Then:
add_library(lib_fastcv STATIC IMPORTED)
set_target_properties(lib_fastcv PROPERTIES IMPORTED_LOCATION
".a file location")
And at the end:
target_link_libraries (...lib_fastcv....)
However, as soon as I use any function from the .a static library it complains that it cannot recognize the function which means the static library is not linked correctly to my C++ code.
Does anyone know what else I need to do?
Should I also edit my build.gradle to include information about the library file?

this is my solution:
So, first of all, CMake can be weird to use at the beginning.
1- native-lib1 is the output product of CMake. Later java will only see .so of this
add_library( # This is out .so product (libnative-lib1.so)
native-lib1
# Sets the library as a shared library.
SHARED
# These are the input .cpp source files
native-lib.cpp
SRC2.cpp
Any other cpp/c source file you want to compile
)
2- Any library that you included in your souse file, its .h needs to be addressed here:
target_include_directories(# This is out .so product (libnative-lib1.so)
native-lib1 PRIVATE
${CMAKE_SOURCE_DIR}/include)
3- Any actual library you used, its atual .a or .cpp should be addrressed to CMake this way:
target_link_libraries(
# This is out .so product (libnative-lib1.so)
native-lib1
#These are any extra library files that I need for building my source .cpp files to final .so product
${CMAKE_SOURCE_DIR}/lib/libfastcv.a
# Links the target library to the log library
# included in the NDK.
${log-lib})
Then build.gradle it should be mentioned what abi formats we want it to make to make sure you pre built .a files are compatible.
flavorDimensions "version"
productFlavors {
create("arm7") {
ndk.abiFilters("armeabi-v7a")
}
Finally, as a note, the below command did not work to filter abis and above command work, even though they look similar in logic and form to me:
cmake {
cppFlags "-std=c++11"
abiFilters 'armeabi-v7a'
}
}
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
// This is not working to eliminate
abiFilters 'armeabi-v7a'
}
}

Related

C++ - Why is a Static Library unusable without source files?

So from what I understand, a static compiled mylib.a file should just be usable as plug-and-play. But I can't use it without the source code, I'm trying to avoid that.
I adapted this tutorial to compile using CMake with the following directory structure:
/
lib
libmy_math.a
main.cpp
my_math.h
my_math.cpp
CMakeLists.txt
The contents of the files are exactly as in the tutorial. The only added file is CMakeLists.txt which basically just runs the library compiling part before building:
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(Test)
### Uncomment this ONCE to compile a libmy_math.a file
# add_library(my_math STATIC my_math.cpp)
add_executable(Test main.cpp)
find_library(MY_MATH_LIB my_math HINTS ./lib)
target_link_libraries(Test PUBLIC ${MY_MATH_LIB})
As the CMake file says, uncommenting Line 5 compiles the library, which is then linked to my code when compiling.
I'm trying to package my code so that I don't show my client the source code for the compiled library (my_math), but if I delete the my_math.h and my_math.cpp files, I get a "file not found" error on import:
/Users/Joe/Desktop/Test/main.cpp:1:10: fatal error: 'my_math.h' file not found
#include "my_math.h"
^~~~~~~~~~~
I thought you could compile libraries without needing the source code. What am I missing here?
A static library does not contain each and every definition a header can contain - think of macros, etc. Thus you still need the header. However, you don't need the .cpp anymore to link the library.
Besides the problem stated in Iorro's answer the find_library call here will not work as expected.
find_library(MY_MATH_LIB my_math HINTS ./lib)
target_link_libraries(Test PUBLIC ${MY_MATH_LIB})
What this does is, while running CMake, look for the my_math.a file. However, at that point of the build process, my_math.a has not been built yet. You're just running CMake for the first time, so all you have is source files. Nothing has been compiled yet.
What you want to do instead is link to the library target directly, as CMake already knows about that and can resolve the dependency on its own. So you need to replace the two lines above with:
target_link_libraries(Test PUBLIC my_math)
This now introduces a curious asymmetry in how your test consumes the library (as it is part of the same build) vs. how your clients consume the same library (which are not part of the same build and thus need to do a find_package call before they can use it). Look at CMake's config-file packages for a mechanism that allows you to restore symmetry here, at the cost of a significant amount of boilerplate required in your CMake script.

cmake/cpack distribute a library and its dependencies

Let's say I have to distribute a C++ library libA which depends on other libraries libB and libC.
The final user will have to include just the header files of libA but needs to link also the others two.
What's the preferable way to distribute all together the libraries?
Is it possible to use cpack to take care of it?
If you have to hide the source code, then you could precompile the libraries yourselves and just distribute the static .a files and the header files seperately for each distribution.
However, If you want the code to be transparent to the end users and let the users compile the libraries on their own. Then you could try cmake.
The common folder structure for a library will look like this
MainFolder
src ( To have all the required Src files )
include ( all the necessary files to be included )
test ( to run test cases upon installation to validate a successfull installation -- optional )
Then you could create a CMakeLists.txt file in the root folder which would look like this,
cmake_minimum_required(VERSION 3.2)
project(MyAwesomeLibrary)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")// Sets up the compiler and flags
include_directories("${CMAKE_SOURCE_DIR}/include") // This includes the .h files in include directory
// Set the Codes to be compiled for Different Libraries
set (LIB_A ${CMAKE_SOURCE_DIR}/lib_a.c);
set (LIB_B ${CMAKE_SOURCE_DIR}/lib_b.c);
set (LIB_C ${CMAKE_SOURCE_DIR}/lib_c.c);
add_library(MyAwesomeStaticLib STATIC ${LIB_A} ${LIB_B} ${LIB_C}) // Generates the static library
If you have multiple files to be compiled for each of the libraries then you can refer this link.
how would I compile multiple files in a folder?
Then the end user can use the library by linking the generated MyAwesomeStaticLib.a file and also use the -I flag to include the include directories.

Create shared library (.lib and .dll) using qmake and MSVC2015

I'm porting a shared library from CMake to QMake (for better integration with the rest of our codebase), and am having trouble in Windows with having qmake generate both the .lib and .dll files.
I have ported this over to Linux and MacOS (easier with Unix shared library formats), but am having trouble with Windows.
Please note that I am not using the Mingw compiler, but the MSVC 2015 compiler. Unfortunately, I can't use the Mingw compiler for various reasons.
Relevant code in my .pro file:
Qt -= core gui #not using the qt library
CONFIG += c++14 warn_on
TEMPLATE = lib
TARGET = MyLibrary
With CMake, I was able to set certain .h files as PUBLIC_HEADER files, and after running make install, it generated the proper .lib and .dll files.
But with qmake, it only generated a very tiny .dll file (<15kb). I tried adding CONFIG += static but that created a .lib file with all the symbols there, not the shared library files I am looking for.
Edit: After looking more into it, a better question could be:
Is there a qmake equivalent (or workaround) of CMake's PUBLIC_HEADER property?
In a nutshell: #Matt is right, you need to take active action in your source code to export classes, functions and other symbols. This is mandatory in Windows, but it is also a recommended practice in Unix. When the MSVC++ compiler sees exported symbols, it automatically creates both the .DLL and the .LIB import library.
To do this in a cross-platform way with qmake, a nice guide is following the pattern of the Qt Creator wizard: Welcome -> New Project -> Library -> C++ library. Project location..., name: 'testlib' (for instance). Next, next, choose your kit, next and finish. The files created by the wizard in the project directory are:
testlib.cpp
testlib_global.h
testlib.h
testlib.pro
testlib.pro.user
The interesting bits are in testlib.pro:
TEMPLATE = lib
DEFINES += TESTLIB_LIBRARY
And the header "testlib_global.h"
#ifndef TESTLIB_GLOBAL_H
#define TESTLIB_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(TESTLIB_LIBRARY)
# define TESTLIB_EXPORT Q_DECL_EXPORT
#else
# define TESTLIB_EXPORT Q_DECL_IMPORT
#endif
#endif // TESTLIB_GLOBAL_H
This header should be included in other headers, that would be using the TESTLIB_EXPORT macro like this (testlib.h):
#ifndef TESTLIB_H
#define TESTLIB_H
#include "testlib_global.h"
class TESTLIB_EXPORT Testlib
{
public:
Testlib();
// ...
};
The key is to define TESTLIB_LIBRARY in the project .pro that builds the library, so the TESTLIB_EXPORT macro is defined as Q_DECL_EXPORT, which in Windows is defined as __declspec(dllexport), and avoid defining TESTLIB_LIBRARY in the projects using the library. More about this here.
Now, the second part of your question. In CMake documentation, it says:
PUBLIC_HEADER
Specify public header files in a FRAMEWORK shared library target.
Shared library targets marked with the FRAMEWORK property generate
frameworks on macOS, iOS and normal shared libraries on other
platforms. This property may be set to a list of header files to be
placed in the Headers directory inside the framework folder. On
non-Apple platforms these headers may be installed using the
PUBLIC_HEADER option to the install(TARGETS) command.
So the PUBLIC_HEADER attribute is only about installing the public headers. To do so in qmake, you need to use INSTALLS in testlib.pro, for instance:
unix {
target.path = /usr/local/lib
headers.path = /usr/local/include
}
!isEmpty(target.path) {
INSTALLS += target
}
headers.files = $$HEADERS
!isEmpty(headers.path) {
INSTALLS += headers
}

In CLion, header only library: file "does not belong to any project target, code insight features might not work properly"

I have a header-only library project set up with the cmake command:
add_library(my_library INTERFACE)
and I also added
target_sources(my_library INTERFACE ${MY_LIRBARY_HEADER_FILES})
but when I open a source file, I get the warning:
This file does not belong to any project target, code insight features might not work properly
and I lose a lot of the functionality on things like code completion.
What is the proper way to set this up so CLion provides its usual functionality on a header-only library?
Little background
I was having the same problem, albeit the project was not header-only, nevertheless, the open files from inc folder were throwing the aforementioned warning, even though the CMake file clearly marked that folder to be include_directory.
*.hpp files do not belong to ${SOURCE}
include_directories("${PROJECT_SOURCE_DIR}/inc/")
add_subdirectory(src)
add_executable(${EXECUTABLE_NAME} main.cpp ${SOURCE})
Since this is a perfectly valid CMake file and adding the include files to source files is not idiomatic, I did not want to amend the CMake file.
The solution
As described on the official JetBrains Forum, the CMake file is indeed valid and the warning is shown because of the inability of CLion to properly index header files. The suggested workaround extracted from the link is to right-click the folder and Mark directory as | Library Files/Project Sources and Headers.
So, this header isn't includes in executables and CLion notifies you that some code insight features might not work properly. As workaround you can use "Mark directory as" Library Files/Project Source and Headers for folder.
Clion takes information about source files from CMake build system. When you add any cpp file to sources list CMake automatically tell about header with same name. So if cpp/h names differs (or you don't have cpp file at all) you should include header manually.
set(Sources my_lib.cpp)
set(Headers header_of_my_lib.h)
add_executable(superlib ${Sources} ${Headers})
If you don't have any executable you can omit last line, CLion will still know about files
This warning is an IDE issue that Android Studio cannot recognise the current directory if it does not include any source files.
Workaround
Adding am empty source file, e.g empty_xxx.c under the directory in question and adding below line in your corresponding CMakeList.txt
add_library(${TARGET_NAME_XXX} SHARED ${SOME_DIR_HAVING_THIS_WARNING}/empty_xxx.c)
will help get rid of this warning.
You can add the header files to your project like this:
set(SOURCE_FILES main.cpp MyClass1.cpp MyClass1.h MyClass2.cpp MyClass2.h)
You can also set it in multiple steps like so:
set(SOURCE_FILES main.cpp)
set(SOURCE_FILES ${SOURCE_FILES} MyClass1.cpp MyClass1.h)
set(SOURCE_FILES ${SOURCE_FILES} MyClass2.cpp MyClass2.h)
Though as mentioned in the comments, you probably shouldn't be adding the header files to your project at all.

Preventing CMake-generated makefile for optional-header-only library from compiling source files in header-only mode

I have a library that can both be used as an header-only library and as a traditional library. To enable this optional-header-only functionality, the library includes .cpp source files if compiled in header-only mode. Example:
// Vector.hpp
// (Module file), intended to be included manually by the user
#ifndef LIBRARY_MODULE_VECTOR
#define LIBRARY_MODULE_VECTOR
#include "Library/Vector/Inc/Vector2.hpp"
#include "Library/Vector/Inc/Vector3.hpp"
#include "Library/Vector/Inc/VectorUtils.hpp"
#if defined(LIBRARY_HEADERONLY)
#include "Library/Vector/Src/Vector2.cpp"
#include "Library/Vector/Src/Vector3.cpp"
#include "Library/Vector/Src/VectorUtils.cpp"
#endif
#endif
When the user includes Vector.hpp in one of his/her projects, if LIBRARY_HEADERONLY is defined, the implementation source files will be included right after the header files. Careful usage of the inline keyword is applied to avoid multiple definitions.
If LIBRARY_HEADERONLY is undefined, the .cpp files will be compiled and the library will have to be linked.
My build system of choice is CMake.
Using a CMake flag, the user can define or undefine LIBRARY_HEADERONLY.
The CMakeLists.txt file is similar to this:
# (not shown) set flag and cache variables...
# Include library directory
include_directories("./${INCLUDE_DIRECTORY}")
# Glob all library header/source files
file(GLOB_RECURSE SRC_LIST "${INC_DIR}/*" "${SRC_DIR}/*")
# (Not shown) Check if header-only mode is enabled
# (from now on we assume header-only mode is enabled and that
# LIBRARY_HEADERONLY is defined)
# Use all source/header files as a library target
add_library(HEADER_ONLY_TARGET STATIC ${SRC_LIST})
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
install(DIRECTORY ${INC_DIR} DESTINATION .)
Unfortunately, the CMake-generated makefile always compiles the .cpp files, even when header-only mode is enabled and the target is HEADER_ONLY_TARGET.
How can I prevent the CMake-generated makefile from compiling the source files in header-only mode?
Note that IDEs reliant on CMake-generated output, such as Qt Creator, should display both the header and source files as part of the project.
If I don't glob any source file, but only the .hpp header files, CMake will complain that no source files were selected for a library target, and IDEs that rely on CMake files will not display any item.
Try setting the source files' HEADER_FILE_ONLY property to prevent them from building, e.g.:
if (LIBRARY_HEADERONLY)
set_source_files_properties(${SRC_LIST} PROPERTIES HEADER_FILE_ONLY 1)
...
endif()
Also see the documentation.
if (Create_Header_Only)
add_library(TargetName INTERFACE)
# Clients will build with -DLIBRARY_HEADERONLY
target_compile_definitions(TargetName INTERFACE LIBRARY_HEADERONLY)
else()
add_library(TargetName STATIC thesource.cpp)
endif()
# Clients will build with -Iinclude
target_include_directories(TargetName INTERFACE include)
Client code just does:
add_executable(mine main.cpp)
target_link_libraries(mine TargetName)
See also Transitive Usage Requirements and all the rest of those CMake manuals regarding creating packages, etc.
An approach of defining all library types and letting the consumer choose between them is outlined in:
Opt-in header-only libraries with CMake
Something like:
add_library(lib_shared SHARED ...)
add_library(lib_shared STATIC ...)
add_library(lib_iface INTERFACE)
So that the consumer makes the choice of which to link to:
# target_link_libraries(consumer lib_static)
# target_link_libraries(consumer lib_shared)
target_link_libraries(consumer lib_iface)
Why do you not separate the GLOB for HEADERS and SRC files? You could add a dummy.cpp (a empty .cpp file) for creating header-only libraries. I mean:
# Glob all library header files
file(GLOB_RECURSE HEADER_ONLY_LIST "${INC_DIR}/*.hpp")
# Glob all library src files
file(GLOB_RECURSE SRC_ONLY_LIST "${SRC_DIR}/*.cpp")
# Check if LIBRARY_HEADERONLY is defined, so you can filter the target files
if(LIBRARY_HEADERONLY)
set(SRC_LIST ${HEADER_ONLY_LIST} dummy.cpp) # I don't know if it's needed to add a dummy.cpp file with your set_target_properties(HEADER_ONLY_TARGET ...)
else()
set(SRC_LIST ${HEADER_ONLY_LIST} ${SRC_ONLY_LIST})
endif(LIBRARY_HEADERONLY)
# Use all source/header files as a library target
add_library(HEADER_ONLY_TARGET STATIC ${SRC_LIST})
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
install(DIRECTORY ${INC_DIR} DESTINATION .)
Anyway, CMake 3.X.X gives you the chance for creating INTERFACE libraries (header-only ones).