How to deploy a Find*.cmake file for an Autotools library in the correct place for Yocto? - cmake

I created a new layer over an existing Yocto git for my company project.
In this layer I added a few external autotools based libraries.
A few applications need to link against this libraries and the application projects are all cmake based.
Taking one of this libraries (e.g. libcoap) I could easily find some FindCoAP.cmake to add to my library recipe.
Now if I was running on PC, it would simply be a matter of placing this FindCoAP.cmake file in cmake's ${CMAKE_ROOT}/Modules dir, but how should I, from inside a bitbake recipe (do_install hook), proceed to make my Find*.cmake modules available to anyone's dependent projects?
Should I try to get Yocto's cmake CMAKE_ROOT variable from system-information like this or is it a safer and more reliable way?
do_install_append() {
cmake --system-information | grep CMAKE_ROOT | cut -d \" -f2
install -d ${D}/$CMAKE_ROOT}/Modules
install ${S}/FindCoAP.cmake ${D}/$CMAKE_ROOT}/Modules
}
Thanks in advance.

To ship FindFoo.cmake with non-yet-cmake project
The ideal way is to update upstream project itself. So you will update your recipe and package FindFoo.cmake appropriately.
If you want to do it right now:
Add FindFoo.cmake to your layer (into the files directory next to your recipe).
Add that cmake file to SRC_URI (i.e. SRC_URI += "file://FindFoo.cmake").
Install it in do_install into the directory ${D}${datadir}/cmake/Modules/ for example.
Package it to the dev package by FILES_${PN}-dev variable (see example recipes below).
To use that cmake by other recipe
The usual way is to package .cmake files into the ${PN}-dev package. In your case, your application (which depends on the libcoap) will just set DEPENDS = "libcoap" and all the needed files (like headers, libraries and cmake file) will be copied (well, hardlinked) to the sysroot of your application.
CMake modules are packaged in various recipes for example:
libeigen
opencv
json-spirit
Your application is cmake based, so you will use inherit cmake in the recipe. Native module search path is set in cmake.bbclass.
(BTW, I do a build test of libcoap recipe from homeassistant layer and it worked, but obviously there is no cmake shipped.)

Related

How to include a library using CMAKE in a cross-platform way

I am trying to use the assimp library in a cross platform C++ project. I include the repo as a git submodule, so, effectively, if someone downloads my project they will also download the ASSIMP project.
After I go through the assimp build / CMAKE instructions and (on Linux) type make install and from then on in my project I can use:
target_link_libraries(${PROJECT_NAME} assimp)
However, there is no make install on Windows.
The only other way I have been able to include the library on Linux is to put (in my CmakeLists.txt file):
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/build/assimp/code/libassimp.so)
This is not cross platform as it hardcodes the name and location of the .so file which will not work on Windows.
How can I expose the library so that I can do something like target_link_libraries(${PROJECT_NAME} assimp) on all platforms?
My directory tree looks like:
- src
- include
- assimp
- bin
Where the assimp directory in the include directory is the git submodule
I think you're going about this the wrong way. You don't need to build assimp in a separate step from your project, and you don't need to make install to make it available.
There are a number of ways of handling third party dependencies in Cmake, since you've already chosen to submodule the assimp repository, we'll start there. Assuming assimp is located in the root of your repository in a directory called assimp/ this would be a barebones project including it:
cmake_minimum_required(VERSION 3.0)
project(Project myassimpproj)
# include your directories
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
# set any variables you might need to set for your app and assimp
set(BUILD_ASSIMP_TOOLS ON)
set(ASSIMP_BUILD_STATIC_LIB ON)
# add assimp source dir as a subdirectory, effectively making
# assimp's CMakeLists.txt part of your build
add_subdirectory(/path/to/assimp ${CMAKE_BINARY_DIR}/assimp)
add_executable(assimp_target main.cpp)
# be sure to link in assimp, use platform-agnostic syntax for the linker
target_link_libraries(assimp_target assimp)
There may be a better way of phrasing this using generator expressions syntax, but I haven't looked at assimp's CMakeLists.txt to know if it's supported (and this is a more generic way anyway.)
Not every project uses Cmake, so you may not be able to just add_subdirectory(). In those cases, you can effectively "fake" a user call to build them using their build commands on respective platforms. execute_process() runs a command at configure time add_custom_command() and add_custom_target() run commands at build time. You then create a fake target to make integration and cross your fingers they support Cmake someday.
You can also use the ExternalProject commands added to Cmake to create a custom target to drive download, update/patch, configure, build, install and test steps of an external project, but note that this solution and the next download the dependency rather than using the submodule'd source code.
Finally, I prefer to work with prebuilt dependencies, cuts down on build time, and they can be unit tested on their own outside of the project. Conan is an open source, decentralized and multi-platform package manager with very good support for C++ and almost transparent support for Cmake when used the right way. They have grown very stable in the last year. More information on how to use Conan with Cmake can be found here.

Creating a library in CMake depending on source files not available when generating build files

I have a CMake configuration file building two libraries:
a third-party library (here called ThirdPartyLib) containing a real-time OS / board support package from a supplier. It is built outside CMake using the autotools toolchain.
an extended version of the former library (here called ExtendedThirdPartyLib)
Unfortunately, some source code that I need (various tools) are not built in the ordinary build script for (1). Since I don't want to mess with the suppliers build script I want to add another library (2), building the missing files and thus extending the library from the supplier.
I want to able to do something like this in CMakeFiles.txt:
cmake_minimum_required(VERSION 3.2)
project(bsp)
include(ExternalProject)
ExternalProject_Add(
ThirdPartyLib
URL <http://some.url/bsp.tar.bz2
BUILD_COMMAND make -C ../external/ThirdPartyLib/src
)
set_target_properties(ThirdPartyLib PROPERTIES EXCLUDE_FROM_ALL TRUE)
add_library(ExtendedThirdPartyLib
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file1.c
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file2.c
)
add_dependencies(ExtendedThirdPartyLib ThirdPartyLib)
target_include_directories(ExtendedThirdPartyLib PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/some/path/include
)
target_link_libraries(ExtendedThirdPartyLib ThirdPartyLib)
The problem here is that the path to missing_file1.c and missing_file2.c are not valid when CMake is generating the build files (they are extracted from the tarball from the supplier). CMake exits with an error output saying: "Cannot find source file".
Is there a neat way to make this work? I.e. is it possible to convince CMake that certain non-existant input files will exist when building of the library begins? Or is there any other recommended way to solve this issue?
(I have temporary made local copies of the files I need to build from the suppliers tarball, but that is of course not a good solution. If those files are changed in future versions of the suppliers package and I forget to overwrite my local copies it could be a horrible mess...
Another "solution" would be to create a small makefile outside CMake and use another ExternalProject_Add in the CMakeFiles.txt somehow. But that's not a good solution either, e.g. if compile and linker flags are modified I need to remember to change the makefile too.)
Personally, I dislike the ExternalProject_Add command, because it does way too many things for my taste, but I've digressed.
What if you do something like this, where bar is simulating your ExtendedThirdPartyLib target, since it depends on generated files
cmake_minimum_required(VERSION 3.11)
project(lol C)
set(SOURCES lol.c) # only this file exists
add_library(lol ${SOURCES})
set(FOO_FILES "foo1.c" "foo2.c")
add_custom_command(OUTPUT ${FOO_FILES}
COMMAND ${CMAKE_COMMAND} -E touch ${FOO_FILES}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Creating ${FOO_FILES}"
VERBATIM)
add_custom_target(foo DEPENDS ${FOO_FILES})
add_library(bar ${FOO_FILES})
add_dependencies(bar foo)
target_link_libraries(lol bar)
The whole approach hinges on the fact that the method, where produced/generated files are procured, is explicitly defined via the custom command and associated custom target.
You should modify the custom command to extract the required files (e.g. could even call some external script) from the tarball (which might require downloading with curl or something similar).

Ninja equivalent of Make's "build from this directory down" feature (with CMake)?

When building a project using CMake and Make, you can execute make from a subdirectory of your build tree (i.e. from a directory below whatever directory contains your top-level Makefile), and make will (as far as I can tell) build all targets at or below that directory. This is because CMake generates a Makefile for every directory that contains targets, so when you're in a directory with targets, make finds the Makefile for building those targets.
When CMake generates Ninja files, however, it only generates one build.ninja file, which is at the top level of the build tree. So calling ninja from a directory other than the top-level directory fails (even the -f option doesn't work because ninja can't find the rules.ninja file).
Is there any way to emulate the "make-like" behavior of building targets at and below a directory? As far as I can tell, there are no Ninja targets that correspond to "all targets at and below a particular directory." (This could be emulated using phony targets named after each directory that depend on all targets at and below that directory, but CMake does not generate such targets by default.)
ninja <DIR>/all works with recent versions of Ninja (1.7.2). Version 1.3.4 does not allow this.
I could not find a reference to this on the manual. However, CMake has this documented here:
Recent versions of the ninja program can build the project through the “all” target. An “install” target is also provided.
For each subdirectory sub/dir of the project, additional targets are generated:
sub/dir/all
Depends on all targets required by the subdirectory.
sub/dir/install
Runs the install step in the subdirectory, if any.
sub/dir/test
Runs the test step in the subdirectory, if any.
sub/dir/package
Runs the package step in the subdirectory, if any.
This worked for me:
cd <build-root>
DIRECTORY=<path-relative-to-build-root>
ninja -t targets all | egrep "^${DIRECTORY}/" | egrep CXX_EXECUTABLE_LINKER | \
sed -e 's/:.*//g' | xargs ninja
ninja -t targets all - lists all targets (including target type)
egrep "^${DIRECTORY}/" - filters list of targets to only include those in desired directory
egrep CXX_EXECUTABLE_LINKER - limits the targets to just C++ executables. You can remove or tweak this to get the set of targets you're interested in.
sed -e 's/:.*//g' - removes the target type e.g. ": CXX_EXECUTABLE_LINKER"
xargs ninja - invokes ninja to build the targets
Good question. I would like to know the answer if you find it.
I am just in the process of transitioning to cmake+ninja myself.
I found that I could not create targets with the same name at different levels
(if there is a way I would be interested to know).
So I adopted a naming convention for different targets
E.g.
name - builds program or library
test.name - runs tests for the named program or library
doxygen.name - build doxygen for the named program or library
For deeper hierarchies you can do something like:
doxygen.subproject
doxygen.subproject.name
Using this pattern you can control precisely what is built but you have to issue the command from the top-level build directory.
I think after I get used to this I will find it more productive as there is no need to change directory before you build or run something and though there is sometimes a little extra typing required the shell history generally has it covered.
This is implemented under the hood by using add_custom_target() and adding appropriate dependencies. I use a macro to do this automatically so that
a macro "add_doxygen()" will add the doxygen target for the program and make the doxygen target at each higher level depend on it using add_dependencies().

Building .so module with autotools/libtool without .la and .a variants being installed

How to build and install a .so module with autotools/libtool without .la and .a libraries
being also installed into --prefix path?
Currently i am using following Makefile.am:
lib_LTLIBRARIES = libCurlDownloader.la
libCurlDownloader_la_SOURCES = Curl.cpp
libCurlDownloader_la_LDFLAGS = -module -avoid-version
It works, but in addition to libCurlDownloader.so it also installs libCurlDownloader.la and libCurlDownloader.a, what is undesirable.
Update #1
It is possible to make .a not be generated, by using either
./configure --disable-static
or
AC_ENABLE_SHARED(yes)
AC_ENABLE_STATIC(no)
in configure.ac
But it is still the question how to make .la not being installed into installation --prefix while having .so installed.
Update #2
It is possible to remove .la files from installation --prefix using
install-exec-hook: find $(DESTDIR)$(libdir) -type f -name \*.la -delete
You should not necessarily delete the .la files. The .la files contains information that is used in two situations:
Statically linking against the built library. When statically linking (i.e., .a with -static), there is no information about the dependencies of the library being linked, so libtool can use the information in the .la file to create an appropriate ld command referencing all the necessary dependencies. This is often more important on an environment like MinGW where the linker requires the same library specified multiples in a particular order to resolve recursive dependencies. This is only a concern if one intends to build a static binary.
Dynamically loading the build library on some platforms (i.e., with lt_dlopen if using libltdl). Similarly, on certain platforms, dependencies of the compile module are not encoded in the binary, so the .la file is needed so that lt_dlopen will find and load the correct dependencies. On ELF platforms (including Linux) and PE platforms (i.e., Windows), the dependencies are store in the library, so lt_dlopen will work without the .la files. The MachO format on MacOS can require .la files when building bundles.
The Debian/Ubuntu packagers have decided to exclude .la files from their packages because the second reason isn't appropriate on Linux and they would prefer you didn't build static binaries in the first place. On other platforms, which libtool is designed to support, the .la files may be needed for linking or running the program.
I stumbled upon this question because it used the term "module", which in automake/libtool speak is the term for a plugin. I have a plugin system in Finit, so I build my plugins with '-module' to avoid creating .a files. But I still get the .la files installed, which really is not even applicable in the '-module' case.
I've yet to find a documented way to skip .la files for plugins, but here is how I do it:
AM_LDFLAGS = -module -avoid-version -shared
pkglib_LTLIBRARIES = alsa-utils.la bootmisc.la
install-exec-hook:
#(cd $(DESTDIR)$(pkglibdir) && $(RM) $(pkglib_LTLIBRARIES))
To be clear, in my use-case there is nobody going to "link" against my plugin .so's, so the .la files really are of no use.

How to make CMake find google protobuf on windows?

I am using Google Protobuf with CMake. On Linux the Protobuf library is found by:
find_package( Protobuf REQUIRED )
CMake knows where to look for the library. How though do I get this to work in Windows? Is there an environment variable I should create, such as PROTOBUF_LIB? I have looked in FindProtobuf.cmake but cannot work out what is required.
I also struggled with this. To be more clear.
On Windows (7, similar on older windows):
Start → Control Panel → System → Advanced System Settings → Environment Variables
Then either on the top panel or the bottom panel (if you want it to apply to other users do it on the bottom), create two new variables. The 1st one is
CMAKE_INCLUDE_PATH which points at the bottom of your include path (should contain a "google" folder)
CMAKE_LIBRARY_PATH which should contain "libprotobuf" "libprotobuf-lite" "liteprotoc" .lib files.
After you create the variables press OK and then re-start cmake (or clean the cache).
Newest protobuf v3 have CMake support out of the box.
You could use protobuf repository as submodule and just use
add_subdiretory("third-party/protobuf/cmake")
to get all protobuf targets.
Then you can add dependency to protobuf with
target_link_libraries(YourLibrary libprotobuf libprotobuf-lite libprotoc)
Another possible way is available. With protobuf's CMake configuration you can build and install protobuf binaries once and use them across several projects in your development:
git clone https://github.com/google/protobuf.git
mkdir protobuf\tmp
cd protobuf\tmp
cmake ..\cmake
cmake --build .
cmake --build . --target install
Then you can use find_package with hint paths like
find_package(protobuf REQUIRED
HINTS
"C:/Program Files/protobuf"
"C:/Program Files (x86)/protobuf")
if (NOT PROTOBUF_FOUND)
message("protobuf not found")
return()
endif()
Hope this helps.
Protobuf on windows calls find_library which will search your PATH and LIB variables.
I found the way to use protobuf v2 with cmake on Windows and build it with your project settings. Please, try look to cmake-external-packages project and protobuf-v2 CMakeLists which do the job.
In fact, I wrote it because ExternalProject_Add is wrong (because does stuff in build phase instead of generation phase).
This CMakeLists.txt will download protobuf from protobuf's github releases, extract, and emit cmake targets which you should add reference to with target_link_libraries.
Use git-subtree, git-submodule or just copy this repository contents to your repository subfolder.
Then add packages you want to use with add_subdiretory. For protobuf, use:
add_subdirectory(path/to/cmake-external-packages/protobuf-v2)
Protobuf's includes will be copied to path/to/cmake-external-packages/include folder. You can customize its location in your top-level CMakeLists:
set (EXTERNAL_PACKAGES_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/third-party/include
CACHE STRING "Directory for third-party include files, where include folders will be copied")
include_directories(${EXTERNAL_PACKAGES_INCLUDE_DIR})
Just reference protobuf for your executable:
add_executable(your_exe ${your_exe_sources})
target_link_libraries(your_exe libprotobuf libprotobuf-lite libprotoc)
Hope this helps.