Dependencies between Android native modules (prefab) fail to build - android-gradle-plugin

Our Android application consists of 40-some Android Library Modules (ALMs), each of which also builds a C++ shared library with externalNativeBuild and CMake. So far we had the dependencies between these libs set up like this:
The dependent ALM references the dependency ALM with api project(':lib')
The dependent CMake script references the dependency .so with add_library(SHARED IMPORTED lib) and set_target_properties(lib PROPERTIES IMPORTED_LOCATION ...) and a relative path.
Recently we had to upgrade to the latest Android API version. This started off a cascade because now we were getting deprecated warnings in code generated by the navigation-ktx library, but upgrading that requires upgrading Gradle and the Android Gradle plugin. After that I started getting errors like liblib.so, needed by 'project', missing and no known rule to make it.
It looks like the latest Gradle parallelizes build tasks more heavily, and this means the dependent CMake/Ninja builds are being started concurrently with their dependencies, resulting in this error because the dependency is not yet built. I figured out that what we were doing was not entirely supported, but there is a "supported" way to do that now, so I refactored our entire build to use Prefab.
Now I started getting other errors, alternating between:
1.
C++ build system [prefab] failed while executing ...
Usage: prefab [OPTIONS] PACKAGE_PATH...
Error: Invalid value for "PACKAGE_PATH": Directory ... is not readable.
ld: error: undefined symbol ...
I looked into build/intermediates and found that in the 2nd case, the cmake config script was generated incorrectly: instead of add_library(lib::lib SHARED IMPORTED ) it had add_library(lib::lib INTERFACE IMPORTED) like it it was a header only library, and there was no IMPORTED_LOCATION set in the file.
What am I doing wrong and what should I do to unbreak our build?

It looks like the toolchain support for prefab interdependencies within a project is not quite finished. Others are reporting the same kind of errors at https://issuetracker.google.com/issues/265544858:
This appears to be a race condition with generating prefab cmake files.
It says in https://issuetracker.google.com/issues/221231432 that the header-only cmake config is generated to satisfy Android Studio's IDE features (completion, etc.) before the library is actually built.
Treat as-yet-unconfigured modules as if they are Header-only libraries for Android Studio purposes. This works because Android Studio doesn't care about linker flags for the purposes of providing language services.

Related

Compiling project that depend on LLVM using CMake on Windows

I'm a *nix user, installing LLVM is easy for me, just download the precompiled file, set LLVM_DIR, and you're done. But I'm having a lot of problems with Windows ...
I downloaded LLVM-<version>-win64.exe from the GitHub release, but I can't find LLVMConfig.cmake file. Then I tried to compile LLVM from the source following this documentation.
When I started compiling my own project, I got this error:
'C:/<...>/Debug/libLLVMSupport.lib', needed by '<...>.exe', missing and no known rule to make it
I guess maybe I'm missing some compile options. but I can't find the documentation for LLVM_ENABLE_PROJECTS or BUILD_SHARED_LIBS, not even a list of component names.
I tried to add -DBUILD_SHARED_LIBS=ON but CMake told me BUILD_SHARED_LIBS option is not supported on Windows.

librsvg - cmake module debug mode appends a suffix

I'm trying to get a native Windows build working in which we depend on librsvg-2. It's correctly installed through vcpkg. We usually build with cmake and have made a custom module to find this library. This works great in the unix world, but not within Windows and targeting either a Debug build or a Release build (from visual studio). This is due to the fact the actual lib file gets suffixed with a d on Windows.
This is our FindLibRSVG.cmake
include(LibFindMacros)
libfind_package(LibRSVG Cairo)
libfind_package(LibRSVG GDK-PixBuf)
libfind_pkg_detect(LibRSVG librsvg-2.0
FIND_PATH librsvg/rsvg.h PATH_SUFFIXES librsvg-2 librsvg-2.0
FIND_LIBRARY rsvg-2
)
libfind_process(LibRSVG)
To get the build working on Windows i have to specify a flag to our cmake command like this:
cmake .. -DLibRSVG_LIBRARY=./vcpkg_installed/x64-windows/debug/lib/rsvg-2.40d.lib
Do note the d at the end of the library.
I know there is a cmake module SelectLibraryConfigurations available but i'm not quite sure how to use this properly.
What i'd like to achieve is to be able to build in debug mode without having to provide this flag.
Note: The LibFindMacros implementation can be found here

How to only build libraries that are actual dependencies in CMake

I have a library that is brought in as an external to several applications and included using add_subdirectory. It has four build flavours due to the flexibility of its use: GUI/CLI and shared/static. GUI apps tend to build the GUI flavour as then the user warnings it produces appear as pop-up dialogs. However it is also used by some command line apps which use the CLI version that produces user warnings on the terminal. Some applications use the shared version because they use other libraries that use this same library, and need shared resources such as output logfile handles. Some applications use the static version because they are small utilities that we want to distribute as standalone binaries without dependencies.
If I am building a CMake project that only uses one of these four flavours, I really only want to build that one single flavour of the library. Yet despite this CMake insists on building all four, which is a total waste of time.
How can I flag to CMake that the library should only be built if it is explicitly listed as a dependency of something else that is being built?
For each target, set the EXCLUDE_FROM_ALL property to TRUE in the library's CMakeLists.txt. It will then only be built if actually required.
set_target_properties(MyLib PROPERTIES
EXCLUDE_FROM_ALL TRUE
)

CMake linking error at runtime from other lib in subdirectory

I am trying to get a project running that is using dynamic openCASCADE libraries.
So first i set up a minimal test project using the following CMake file:
...
include_directories(/home/user/occt/build_r/include/opencascade/)
add_executable(testOCCT
main.cpp
step2stl.cpp
)
find_package(OpenCASCADE REQUIRED NO_DEFAULT_PATH)
set(OCCT_LIBS TKMesh; TKSTEP; TKSTL; TKXSBase; TKernel)
target_link_libraries(testOCCT ${OCCT_LIBS})
This is working as it should.
Now i want to link the same libraries to another library that is used in a larger project.
For the project there are 3 CMake files, one for the project and two subdirectory, one for the executable one for the myLib, which are added by add_subdirectory().
In the CMake file for myLib which is inside one of the subdirectories I've added:
...
include_directories(/home/user/occt/build_r/include/opencascade/)
...
add_library(${MY_LIB_NAME} SHARED ${my_sources})
find_package(OpenCASCADE REQUIRED NO_DEFAULT_PATH)
set(OCCT_LIBS TKMesh; TKSTEP; TKSTL; TKXSBase; TKernel)
target_link_libraries(${MY_LIB_NAME} ${OCCT_LIBS})
so basically the same as in the test project.
However now I get an error (at runtime):
symbol lookup error: myLib.so: undefined symbol: _ZN24BRepMesh_IncrementalMeshC1ERK12TopoDS_Shapedbdb
Ok, so i found the problem myself. The reason it did not work was not because of CMake, but that there was some other version of the libraries installed over the packet manager, which then got loaded instead of the right one.
purging those libs solved the problem.
However what I still don't get is why it loaded the correct library version on the test project and the wrong on the other (both tested on the same machine)

CMake package configuration files for upstream projects using Qt5 problems

I am working on a larger C++ library that is using CMake and depends on Qt.
We moved from Qt4 to Qt5 and now I encounter a problem when using our lib
in an upstream project. As a minimal working example demonstrating the problem please have a look at this repo:
https://github.com/philthiel/cmake_qt5_upstream
It contains two separate CMake projects:
MyLIB: a tiny library that uses QString from Qt5::Core.
It generates and installs package configuration files
MyLIBConfig.cmake, MyLIBConfigVersion.cmake, and MyLIBTargets.cmake
in order to be searchable by CMake find_package()
MyAPP: a tiny executable depending on MyLIB
The project uses find_package(MyLIB) and creates an executable that uses MyLIB
The problem is that CMake gives me the following error message when configuring the MyAPP project:
CMake Error at CMakeLists.txt:11 (add_executable):
Target "MyAPP" links to target "Qt5::Core" but the target was not found.
Perhaps a find_package() call is missing for an IMPORTED target, or an
ALIAS target is missing?
The reason for this behaviour is that in the automatically generated MyLIBTargets.cmake file the INTERFACE_LINK_LIBRARIES entry for Qt5 Core is the Qt5::Core symbol. Using Qt4, the absolute path to the Qt core lib was specified here.
Now, I simply can resolve this by using
find_package(Qt5Core 5.X REQUIRED)
in the MyAPP project.
However, I would like to know if this is the intended/generic way to go, i.e. requesting upstream projects of our lib to search for the required transitive Qt5 dependencies themselves, or if I probably misuse CMake here and need to change my configuration procedure?
The CMake docu on package file generation
https://cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html
mentions that macros can be provided by the package configuration files to upstream. Maybe this would be the correct place to search for imported targets like Qt5 and break upstream configuration runs when these dependencies are not found?
Best,
Philipp
[edit of the edit] Full Source Example
You need to deliver a CMake config file for your project, and probably the ConfigFile should be generated via CMake itself (because you cannot know for shure where the user will install your software).
Tip, use the ECM cmake modules to ease the creation of that:
find_package(ECM REQUIRED NO_MODULE)
include(CMakePackageConfigHelpers)
ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX ATCORE
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/atcore_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5AtCoreConfigVersion.cmake"
SOVERSION 1
)
configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5AtCoreConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/KF5AtCoreConfig.cmake"
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
)
and the KF5AtCoreConfig.cmake.in:
#PACKAGE_INIT#
find_dependency(Qt5Widgets "#REQUIRED_QT_VERSION#")
find_dependency(Qt5SerialPort "#REQUIRED_QT_VERSION#")
find_dependency(KF5Solid "#KF5_DEP_VERSION#")
include("${CMAKE_CURRENT_LIST_DIR}/KF5AtCoreTargets.cmake")
This will generate the correct FindYourSortware.cmake with all your dependencies.
[edit] Better explanation on what's going on.
If you are providing a library that will use Qt, and that would also need to find the Qt5 library before compilling the user's source, you need to provide yourself a FindYourLibrary.cmake code, that would call
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Whatever)
Now, if it's your executable that needs to be linked, use the Components instead of the way you are doing it now.
find_package(Qt5 REQUIRED COMPONENTS Core)
then you link your library with
target_link_libraries(YourTarget Qt5::Core)