Crosscompilation cannot find some libraries - c++

I am trying to cross compile a ROS package for a Jetson nano (aarch64) using my x86_64 PC. I am using the toolchain provided by nvidia (GCC Tool Chain for 64-bit BSP). I have been following this article for guidance
As the compiler needs binaries from the aarch64 system, I have mounted the nano's /usr/include into my build directory's usr/include. Here's my build directory hierarchy.
.
├── gcc-4.8.5-aarch64 #<cross compiler>
│ └── install
│ ├── aarch64-unknown-linux-gnu
│ ...
├── opt #<ROS source files from nano>
│ └── ros
│ └── melodic
├── src #<test c++ code in here>
│ └── test_cpp
│ └── src
└── usr
├── include #<nano's /usr/include mounted in here>
└── lib
└── aarch64-linux-gnu #<nano's /usr/lib/aarch64-linux-gnu mounted in here>
When compiling, I get errors saying the compiler cannot find crt1.o, crti.o and libpthread.so.0 libraries.
/home/teshan/xcompile/gcc-4.8.5-aarch64/install/bin/../lib/gcc/aarch64-unknown-linux-gnu/4.8.5/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find crt1.o: No such file or directory
/home/teshan/xcompile/gcc-4.8.5-aarch64/install/bin/../lib/gcc/aarch64-unknown-linux-gnu/4.8.5/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find crti.o: No such file or directory
/home/teshan/xcompile/gcc-4.8.5-aarch64/install/bin/../lib/gcc/aarch64-unknown-linux-gnu/4.8.5/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find /lib/aarch64-linux-gnu/libpthread.so.0 inside /home/teshan/xcompile
collect2: error: ld returned 1 exit status
Before mounting /usr/lib/aarch64-linux-gnu and /usr/include, I've had errors saying cannot find Boost libraries. They have disappeared now since the library files are available in the mounted directories.
However, I have the asked files crti.o and crt1.o in the mounted directories just like boost library files. So why can’t the compiler find them?
$ find -name crti.o -type f
./gcc-4.8.5-aarch64/install/aarch64-unknown-linux-gnu/sysroot/usr/lib/crti.o
./usr/lib/aarch64-linux-gnu/crti.o
$ find -name crt1.o -type f
./gcc-4.8.5-aarch64/install/aarch64-unknown-linux-gnu/sysroot/usr/lib/crt1.o
./usr/lib/aarch64-linux-gnu/crt1.o
I do not have libpthread.so.0 as asked but have libpthread.so. What can I do about this?
$ find -name libpthread.so* -type f
./gcc-4.8.5-aarch64/install/aarch64-unknown-linux-gnu/sysroot/usr/lib/libpthread.so
./usr/lib/aarch64-linux-gnu/libpthread.so
TL;DR
This is my build script
#!/bin/bash
PWD=$(pwd)
export LANG=C
source /opt/ros/melodic/setup.bash
catkin config --extend ${PWD}/opt/ros/melodic/
catkin build -j8 --cmake-args \
-DCMAKE_TOOLCHAIN_FILE=${PWD}/toolchain.cmake \
-DCMAKE_CROSS_COMPILE_PREFIX=${PWD} \
-DRT_LIBRARY=${PWD}/usr/lib/aarch64-linux-gnu/
Here's my toolchain.cmake
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_C_COMPILER ${NANO_ROOT_PATH}/gcc-4.8.5-aarch64/install/bin/aarch64-unknown-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER ${NANO_ROOT_PATH}/gcc-4.8.5-aarch64/install/bin/aarch64-unknown-linux-gnu-g++)
# Below call is necessary to avoid non-RT problem.
SET(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu)
SET(NANO_ROOT_PATH ${CMAKE_CURRENT_LIST_DIR})
SET(NANO_MELODIC_PATH ${NANO_ROOT_PATH}/opt/ros/melodic)
SET(CMAKE_FIND_ROOT_PATH ${NANO_ROOT_PATH} ${CATKIN_DEVEL_PREFIX})
SET(COMPILER_ROOT ${NANO_ROOT_PATH}/gcc-4.8.5-aarch64/install)
#Have to set this one to BOTH, to allow CMake to find rospack
#This set of variables controls whether the CMAKE_FIND_ROOT_PATH and CMAKE_SYSROOT are used for find_xxx() operations.
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
SET(CMAKE_PREFIX_PATH ${NANO_MELODIC_PATH} ${NANO_ROOT_PATH}/usr)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --sysroot=${NANO_ROOT_PATH}" CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --sysroot=${NANO_ROOT_PATH}" CACHE INTERNAL "" FORCE)
SET(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} --sysroot=${NANO_ROOT_PATH}" CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} --sysroot=${NANO_ROOT_PATH}" CACHE INTERNAL "" FORCE)
SET(LD_LIBRARY_PATH ${NANO_MELODIC_PATH}/lib)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
#Skip toolchain trying to build a test program first
SET(CMAKE_C_COMPILER_WORKS 1)
SET(CMAKE_CXX_COMPILER_WORKS 1)
set(BOOST_ROOT ${NANO_ROOT_PATH}/usr/include/boost)
set(COMPILER_SYSROOT ${COMPILER_ROOT}/aarch64-unknown-linux-gnu/sysroot)
include_directories(BEFORE SYSTEM ${COMPILER_SYSROOT}/usr/include/)

Related

How to include library from sibling directory in CMakeLists.txt for compilation

I am working on a C project as of recently and want to learn how to use CMake properly.
The project consists of the following directory structure (as of now):
.
└── system
├── collections
│ ├── bin
│ ├── build
│ ├── includes
│ └── src
└── private
└── corelib
├── bin
├── build
├── includes
└── src
Every directory including 'bin' sub-directories is a separate library. They contain a CMakeLists.txt each.
The plan is to link the libraries in such a way that, during development, no manual recompilation of 'corelib' should be required to receive updated code from it, while also ensuring that dependencies would be resolved once all libraries get compiled as SHARED libraries and put in a place such as 'usr/local/lib' or similar.
I have a dependency of library 'corelib' in library 'collections'.
Trying to resolve said dependency, I have come up with the following CMakeLists.txt in 'collections':
cmake_minimum_required(VERSION 3.0.0)
project(collections VERSION 0.1.0 LANGUAGES C)
set(LIBRARY_OUTPUT_PATH ../bin)
add_subdirectory(../private/corelib ${LIBRARY_OUTPUT_PATH})
include_directories(./includes)
aux_source_directory(./src SOURCES)
add_library(collections SHARED ${SOURCES} main.c)
However, this does not produce the result I am looking for, as I get the following output on build:
[main] Building folder: collections
[build] Starting build
[proc] Executing command: /usr/bin/cmake --build /home/codeuntu/Repositories/netcore-c/src/system/collections/build --config Debug --target all -j 6 --
[build] gmake[1]: *** No rule to make target '../bin/all', needed by 'all'. Stop.
[build] gmake[1]: *** Waiting for unfinished jobs....
[build] Consolidate compiler generated dependencies of target collections
[build] [ 50%] Built target collections
[build] gmake: *** [Makefile:91: all] Error 2
[build] Build finished with exit code 2
It seems this is the wrong way to go about it. Any help is greatly appreciated.
This is the CMakeLists.txt for 'corelib':
cmake_minimum_required(VERSION 3.0.0)
project(corelib VERSION 0.1.0 LANGUAGES C)
include_directories(./includes)
aux_source_directory(./src SOURCES)
set(LIBRARY_OUTPUT_PATH ../bin)
add_library(corelib SHARED ${SOURCES} main.c)
Binary directory has to be a subdirectory of current dir, it can't be above ../bin. Use:
add_subdirectory(../private/corelib some_unique_name)
Overall, let's fix some issues. A more advanced CMake might look like this:
# system/CmakeLists.txt
add_subdirectory(private EXCLUDE_FROM_ALL)
add_subdirectory(collections)
# system/collections/CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(collections VERSION 0.1.0 LANGUAGES C)
file(GLOB_RECURSE srcs *.c *.h)
add_library(collections ${srcs})
# Use only target_* intefaces
target_include_directories(collections PUBLIC
./includes
)
target_link_libraries(collections PRIVATE
corelib
)
# system/private/CMakeLists.txt
add_subdirectory(corelib)
# system/private/corelib/CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(corelib VERSION 0.1.0 LANGUAGES C)
file(GLOB_RECURSE srcs *.c *.h)
add_library(corelib ${srcs})
target_include_directorieS(corelib PUBLIC ./includes)
# system/CMakePresets.json
{
... see documentation ...
"configurePresets": [
{
...
"cacheVariables": {
"BUILD_SHARED_LIBS": "1",
"ARCHIVE_OUTPUT_DIRECTORY": "${binaryDir}/bin",
"LIBRARY_OUTPUT_DIRECTORY": "${binaryDir}/bin",
"RUNTIME_OUTPUT_DIRECTORY": "${binaryDir}/bin"
}
}
I.e. overall, I do not think every project inside system wants to compile his own separate instance of corelib, rather one corelib should be shared. Just add corelib once, from anywhere. Note that it doesn't have to be in order - you can target_link_libraries on targets before they are defined.

How to get coverage for tests with CMake and Catch2

I'm trying to print coverage with lcov on a C++ project that is using Catch2 for tests. I'm able to run my tests and get results. However, I'm unable to get any coverage. This is the error that is shown.
Capturing coverage data from .
Found gcov version: 9.3.0
Using intermediate gcov format
Scanning . for .gcda files ...
geninfo: WARNING: no .gcda files found in . - skipping!
Finished .info-file creation
Combining tracefiles.
Reading tracefile coverage.base
lcov: ERROR: no valid records found in tracefile coverage.base
My current toolchain is WSL. I'm using Conan for dependency management. The solution has the following structure:
my project/
├─ build/
│ ├─ build files
├─ core/
│ ├─ library files
├─ main/
│ ├─ main runtime
├─ tests/
│ ├─ test runtime/
├─ CMakeLists.txt
Each folder has it's CMakeLists.txt file and is identified as a target. I'm also using this CMake Module to register a target for coverage.
My root CMakeLists.txt looks like this:
cmake_minimum_required(VERSION 3.16)
project(my-project)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "-O0")
include(build/conanbuildinfo.cmake)
conan_basic_setup()
add_subdirectory(core)
option(BUILD_TESTING "Builds only the test executable." OFF)
option(CODE_COVERAGE "Collect coverage from test library" OFF)
if(BUILD_TESTING)
enable_testing()
add_subdirectory(tests)
add_test(NAME project-tests COMMAND ./bin/tests)
if(CODE_COVERAGE)
include(CodeCoverage.cmake)
append_coverage_compiler_flags()
setup_target_for_coverage_lcov(NAME coverage EXECUTABLE ./bin/tests BASE_DIRECTORY ../coverage)
endif()
else()
add_subdirectory(main)
endif()
To get my coverage, I'm using the following commands (on build/).
cmake .. -DCODE_COVERAGE=ON -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug
make
make coverage
From what I understand, it seems to be missing some files necessary for coverage information, but I don't know how to make them. From what I've looked online, I have all the necessary compiler flags. I can't see what is wrong/missing in here.
I believe you forgot to add appropriate flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")

CMake ExternalProject does not unpack dependencies

I am building an application which uses tinyxml2 and few other dependencies (namely Irrlicht and IrrKlang) that I provide as .zip files in the Dependency subdirectory of my project:
.
├── CMakeLists.txt
├── Dependencies
│   ├── irrKlang-1.5.0.zip
│   ├── irrKlang-32bit-1.5.0.zip
│   ├── irrKlang-64bit-1.5.0.zip
│   ├── irrlicht-1.8.4.zip
│   └── tinyxml2-master.zip
├── Editor
│   ├── CMakeLists.txt
│   └── Sources
│   └── main.cpp
└── Game
   ├── CMakeLists.txt
   └── Sources
   └── main.cpp
NOTE: for a reference, full sources are available on GitHub, here I cut some corners to make the question shorter.
The top-level CMakeFiles.txt is set up is:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(shoot-them VERSION 1.0.1 LANGUAGES CXX)
include(ExternalProject)
# few platform-specific variables like MAKE_COMMAND, PLATFORM_ARCH, etc.
# libraries (see below)
add_subdirectory(Game)
add_subdirectory(Editor
Both Irrlicht and IrrKlang come with pre-built libraries for Windows for x86, but not for Windows x64 and not for OSX. Hence I add it as a dependency like this (using the if(NOT IRRLICHT_LIBRARY_PATH) just to separate the code into a block:
if(NOT IRRLICHT_LIBRARY_PATH)
ExternalProject_Add(irrlicht-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
PREFIX Dependencies/irrlicht
SOURCE_SUBDIR source/Irrlicht
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(irrlicht-dep SOURCE_DIR)
set(IRRLICHT_PATH ${SOURCE_DIR})
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
endif()
I follow the same principles for IrrKlang. But since tinyxml2 comes as a header and a source file and it comes packed with CMakeLists.txt, I just include it like this:
ExternalProject_Add(tinyxml2-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/tinyxml2-master.zip
PREFIX Dependencies/tinyxml2
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(tinyxml2-dep SOURCE_DIR)
set(TINYXML2_PATH ${SOURCE_DIR})
add_subdirectory(${TINYXML2_PATH})
I define both Game and Editor sub-projects as follows:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(Editor VERSION 1.0.1 LANGUAGES CXX)
set(EXECUTABLE_NAME Editor)
set(SOURCES Sources/main.cpp)
# platform-specific variables
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
add_dependencies(${EXECUTABLE_NAME} tinyxml2 irrlicht)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2 irrlicht)
if(NOT APPLE)
# Copy libraries' DLLs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:tinyxml2> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
endif()
# TODO: copy DLLs, not LIBs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:irrlicht> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
The way I use ExternalProject specifically is because
I wanted to fix the versions I build my project with
I did not want to pollute my system with installing the libraries (hence no INSTALL steps, although I may completely misunderstand that concept)
I do not depend on 3rd party repositories being dead (hence shipping the ZIP files with the sources)
I do not rely on unbelievably outdated (and thus potentially non-working) find modules for CMake
To be fair: I am not aware of any best practices of building projects with CMake, so I might very well be completely wrong about all of the above, so please do correct me.
When I build this project in Visual Studio 2019 on Windows, it works like a charm. But whenever I try building the thing on OSX, I get failures:
none of the dependencies gets even unpacked
(because of p.1) the ${TINYXML2_DIR} is never set
(because of p.2) the tinyxml2 directory could not be found and thus added via add_subdirectory()
(because of p.3) the $<TARGET_FILE:tinyxml2> expression does not evaluate
(as a global consequence) the project does not build
The way I build project is rather simple:
cmake -Bbuild -H. && cmake --build build
What am I doing wrong?
Also, what is the right way to handle 3rd party dependencies with CMake?
I am very well aware that CMake is technically just a makefile (roughly speaking, since it is different for every build toolchain) generator, so my question is more about how do I tell CMake to generate the correct build files for each type of dependency that should be built with my project (pre-built, build from sources with CMake, build from sources with a custom command). I thought ExternalProject is supposed to handle just that, but apparently something went horribly wrong along the way.
I have played with both a solution suggested by #Mizux in their comment and had some success with two different approaches.
1. vcpkg
This is arguably the easier of the two. It requires vcpkg installed.
See this commit for example.
Create a manifest file, vcpkg.json in the project root directory, listing all the dependencies used by the project:
{
"name": "PROJECT_NAME",
"version-string": "0.1.0",
"dependencies": [
"irrlicht",
"tinyxml2"
]
}
You can also use CLI to generate the manifest by using vcpkg install command.
Use the find_package from CMake to link libraries to each target - in the child CMakeLists.txt:
find_package(irrlicht CONFIG REQUIRED)
find_package(tinyxml2 CONFIG REQUIRED)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2::tinyxml2 Irrlicht)
Important: when configuring the project, pass the path to the vcpkg CMake module:
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=[vcpkg root]/scripts/buildsystems/vcpkg.cmake
2. FetchContent + ExternalProject
Since my project depends on both a library that ships with CMakeLists.txt and a library that does not, this is a nice example.
See this commit for example.
First, as #Mizux mentioned, FetchContent works at configure time - it downloads and unpacks the dependency when you configure the project (call cmake -S . -B build). Then, since irrlicht does not ship with CMakeLists.txt, you either use ExternalProject_Add to build it with custom command (make in my case) or add it as a sub-directory to the project.
FetchContent part:
include(FetchContent)
FetchContent_Declare(irrlicht
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
)
FetchContent_GetProperties(irrlicht)
if(NOT irrlicht_POPULATED)
FetchContent_Populate(irrlicht)
endif()
set(IRRLICHT_PATH ${irrlicht_SOURCE_DIR})
ExternalProject part:
include(ExternalProject)
if(NOT WIN32)
ExternalProject_Add(irrlicht-dep
SOURCE_DIR "${IRRLICHT_PATH}/source/Irrlicht"
BUILD_IN_SOURCE true
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
)
endif()
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
Note the important bits in the ExternalProject configuration:
irrlicht_SOURCE_DIR variable is populated by FetchContent
SOURCE_DIR "${irrlicht_SOURCE_DIR}/source/Irrlicht" - tells ExternalProject where the unpacked sources are; if this directory is not empty (which it should be not, since FetchContent should have populated it at project configuration time), ExternalProject will skip the download phase
BUILD_IN_SOURCE true - builds from within the source directory
CONFIGURE_COMMAND "" - skips the dependency configuration phase, so not cmake will be executed for the dependency
BUILD_COMMAND "make" - uses a specific command to build the dependency from sources
if(NOT WIN32) - only uses ExternalProject to build the dependency; since Irrlicht comes with pre-built libraries for Win32, this should build the thing for the other platforms (including Win64)

add_custom_target not executing with multiple executable in a CMakeLists file

I got a situation where another Makefile is executed from a CMakeLists.txt file.
Here is my root directory structure.
.
├── client.cpp
├── CMakeLists.txt
├── proto_lib
│   ├── Makefile
│   └── math.proto
└── server.cpp
This is a simple grpc test code where I am trying to compile a proto files using a Makefile create a static library. And then link that library to the final targets in the CMakefile. Makefile is working fine. CMakefile is also working fine with one executable.
Here is the CMakefile:
cmake_minimum_required(VERSION 3.5)
project(client-server)
message(STATUS "Compiling Network Function : ${PROJECT_NAME}")
set(CMAKE_CXX_STANDARD 11)
message(STATUS "Project Directory: ${PROJECT_SOURCE_DIR}")
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIB_FILE ${CMAKE_CURRENT_SOURCE_DIR}/proto_lib/libmylib.a)
add_custom_target(libmylib.a ALL
COMMAND make
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/proto_lib
COMMENT "grpc headers and libraries are generating..."
)
add_executable(server server.cpp)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -g")
set(BASIC_LIBRARIES "-lpthread")
set(CMAKE_LINK_WHAT_YOU_USE ON)
set(LIBDIR "/usr/local/lib")
find_library(proto_location NAMES protobuf PATHS ${LIBDIR})
find_library(grpc_location NAMES grpc++ PATHS ${LIBDIR})
find_library(grpc_reflection_location NAMES grpc++_reflection PATHS ${LIBDIR})
find_library(dl_location NAMES dl PATHS ${LIBDIR})
find_library(grpc_location2 NAMES grpc PATHS ${LIBDIR})
if(proto_location)
message(STATUS "protobuf library found at " ${proto_location})
endif()
if(grpc_location)
message(STATUS "grpc++ found at " ${grpc_location})
endif()
if(grpc_reflection_location)
message(STATUS "grpc_reflection found at " ${grpc_reflection_location})
endif()
if(dl_location)
message(STATUS "dl found at " ${dl_location})
endif()
if(grpc_location2)
message(STATUS "grpc found at " ${grpc_location2})
endif()
target_link_libraries(server ${proto_location}
${LIB_FILE}
${grpc_location}
${grpc_reflection_location}
${dl_location}
${grpc_location2}
${BASIC_LIBRARIES}
)
When I enter the command make after CMake . in the root directory.
it starts with following output...
Scanning dependencies of target libmylib.a
[ 33%] grpc headers and libraries are generating...
[ 33%] Built target libmylib.a
etc.. etc..
and it succeeds.(The internal Makefile creates header files and static objects using proto and grpc libraries. After that server.cpp can include the headers from inside the proto_lib/ folder and CMake build succeeds)
But when I add one more executable at the end of CMakeLists.txt,
add_executable(client client.cpp)
target_link_libraries(client ${proto_location}
${LIB_FILE}
${grpc_location}
${grpc_reflection_location}
${dl_location}
${grpc_location2}
${BASIC_LIBRARIES}
)
and then cmake . && make
does not start with internal Makefile build. Instead, it starts with client build.
Scanning dependencies of target client
[ 20%] Building CXX object CMakeFiles/client.dir/client.cpp.o
/home/deka/Academic/gemini/learning/client_server_async/advanced1/client.cpp:10:10: fatal error: proto_lib/math.grpc.pb.h: No such file or directory
#include "proto_lib/math.grpc.pb.h"
Because proto_lib/Makefile is not executed first in this case therefore math.grpc.pb.h is not generated and that is what the above error is showing.
My question is: Why adding one more executable does not trigger the internal Makefile build at the start?
Thanks!

Unable to use locally installed Protocol Buffers inside ROS

I have installed Protocol Buffers locally. Below is the directory structure of ROS package:
.
├── CMakeLists.txt
├── package.xml
├── include
│   ├── addressbook.pb.cc
│   ├── addressbook.pb.h
│   └── addressbook.proto
├── lib
│   └── protobuf-3.5.0
└── src
└── main.cpp
Protocol Buffers was installed locally by using ./configure --prefix=$PWD inside lib/protobuf-3.5.0 directory.
While compiling the ROS package using catkin_make, it seems referring to an old installation of Protocol Buffers and showing version incompatibility errors.
[ 0%] Building CXX object local_protobuf_ros_example/CMakeFiles/addressbook_protobuf.dir/include/addressbook.pb.cc.o
In file included from /home/bgplvm/ros_ws/src/local_protobuf_ros_example/include/addressbook.pb.cc:4:0:
/home/bgplvm/ros_ws/src/local_protobuf_ros_example/include/addressbook.pb.h:12:2: error: #error This file was generated by a newer version of protoc which is
#error This file was generated by a newer version of protoc which is
^
/home/bgplvm/ros_ws/src/local_protobuf_ros_example/include/addressbook.pb.h:13:2: error: #error incompatible with your Protocol Buffer headers. Please update
#error incompatible with your Protocol Buffer headers. Please update
^
/home/bgplvm/ros_ws/src/local_protobuf_ros_example/include/addressbook.pb.h:14:2: error: #error your headers.
#error your headers.
^
In file included from /home/bgplvm/ros_ws/src/local_protobuf_ros_example/include/addressbook.pb.cc:4:0:
/home/bgplvm/ros_ws/src/local_protobuf_ros_example/include/addressbook.pb.h:23:35: fatal error: google/protobuf/arena.h: No such file or directory
#include <google/protobuf/arena.h>
^
compilation terminated.
Although, I put message(STATUS "Using Protocol Buffers ${Protobuf_VERSION}") statement inside CMakeLists.txt and found that it is using Protocol Buffers 3.5.0. See below the CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.3)
project(local_protobuf_ros_example)
find_package(catkin REQUIRED COMPONENTS roscpp)
set(PROTOBUF_FOLDER ${PROJECT_SOURCE_DIR}/lib/protobuf-3.5.0)
set(CMAKE_PREFIX_PATH
${CMAKE_PREFIX_PATH}
"${PROTOBUF_FOLDER}/cmake/build/release/lib/x86_64-linux-gnu/cmake/protobuf"
)
find_package(Protobuf CONFIG REQUIRED)
message(STATUS "Using Protocol Buffers ${Protobuf_VERSION}")
catkin_package()
include_directories(include ${catkin_INCLUDE_DIRS} ${PROTOBUF_INCLUDE_DIRS})
add_library(addressbook_protobuf include/addressbook.pb.cc)
add_executable(main src/main.cpp)
target_link_libraries(main ${catkin_LIBRARIES} addressbook_protobuf ${PROTOBUF_LIBRARIES})
What is missing here? Since I am referring to the local installation, it should work without showing errors. Isn't it?
Your set of CMAKE_PREFIX_PATH is incorrect. It should point to the prefix in which protobuf was installed, not the location of the cmake config file for the package.
So try changing:
set(CMAKE_PREFIX_PATH
${CMAKE_PREFIX_PATH}
${PROTOBUF_FOLDER}/cmake/build/release/lib/x86_64-linux-gnu/cmake/protobuf
)
To this:
set(CMAKE_PREFIX_PATH
${CMAKE_PREFIX_PATH}
${PROTOBUF_FOLDER}
)
That is, assuming ${PROTOBUF_FOLDER} points to the same location given to the --prefix argument of ./configure --prefix=<INSTALL_PREFIX>
See the search paths documentation for find_package here. (about half way down the page)
I believe if you were on Windows what you had would have worked as <prefix>/ is one of the search paths for that platform. But on Unix based OS'es you have these as the search paths (per the documentation on above link):
<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/
<prefix>/(lib/<arch>|lib|share)/<name>*/
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/
You also have to be careful about case sensitivity here. Note that in the above paths the last path element they search is <name>*. From what I can see in your question it looks like protobuf installs itself under the name of
'protobuf', but your find_package call is asking for 'Protobuf'. So, try also changing your call to:
find_package(protobuf CONFIG REQUIRED)
And finally, as far as I can tell, protobuf doesn't install CMake configs when doing a build via ./configure .... To get the CMake configs installed I had to build via CMake by doing:
cd protobuf-3.5/cmake
cmake -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> .
make
make install
I found a workaround to use locally installed Protocol Buffers.
I just need to set following two variables to locally installed Protocol Buffers:
Protobuf_INCLUDE_DIRS
Protobuf_LIBRARIES
Please see the CMakeLists.txt below:
cmake_minimum_required(VERSION 2.8.3)
project(local_protobuf_ros_example)
find_package(catkin REQUIRED COMPONENTS roscpp)
set(PROTOBUF_FOLDER ${PROJECT_SOURCE_DIR}/lib/protobuf-3.5.0)
set(Protobuf_INCLUDE_DIRS ${PROTOBUF_FOLDER}/include)
set(Protobuf_LIBRARIES ${PROTOBUF_FOLDER}/lib/libprotobuf.so)
catkin_package()
include_directories(include ${catkin_INCLUDE_DIRS} ${Protobuf_INCLUDE_DIRS})
add_library(addressbook_protobuf include/addressbook.pb.cc)
add_executable(main src/main.cpp)
target_link_libraries(main ${catkin_LIBRARIES} addressbook_protobuf ${Protobuf_LIBRARIES})
It works as of now. However, I am not sure, if it is a good practice or not. Anyone having a better solution is most welcome.