-
Notifications
You must be signed in to change notification settings - Fork 37
CommonAPI C D Bus in 10 minutes
Step 1: Preparation / Prerequisites
Step 2: Build the CommonAPI Runtime Library
Step 3: Build the CommonAPI D-Bus Runtime Library
Step 4: Write the Franca file and generate code
Step 5: Write the client and the service application
Step 6: Build and run
The following description is tested with CommonAPI 3.2.3 and assumes that you use a standard Linux distribution (I tested it with Ubuntu 22.04) and that you have installed git and (CMake >=3.13). Please note that the code generators need the java 8 runtime environment.
You may also want to install the following packages (debian/ubuntu names)
cmake cmake-qt-gui libexpat-dev expat default-jre
Start with fetching the source code of CommonAPI:
$ git clone https://github.com/GENIVI/capicxx-core-runtime.git
Cloning into 'capicxx-core-runtime'...
remote: Enumerating objects: 5182, done.
remote: Counting objects: 100% (35/35), done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 5182 (delta 3), reused 26 (delta 2), pack-reused 5147
Receiving objects: 100% (5182/5182), 4.01 MiB | 1.94 MiB/s, done.
Resolving deltas: 100% (2539/2539), done.
The CommonAPI runtime library can now be built without any other dependencies:
$ cd capicxx-core-runtime/
$ ls
AUTHORS Android.bp Android.mk CHANGES CMakeLists.txt CommonAPI.pc.in INSTALL LICENSE README.md cmake commonapi.spec.in docx doxygen.in include libcommonapi.yaml src
<.>/capicxx-core-runtime$ cmake -Bbuild -DCMAKE_INSTALL_PREFIX=../install_folder .
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Project name: libcommonapi
-- This is CMake for Common API C++ Version 3.2.3.
-- CMAKE_INSTALL_PREFIX set to: install_folder
-- BUILD_SHARED_LIBS is set to value: ON
-- MAX_LOG_LEVEL is set to value: DEBUG
-- Setting build type to 'RelWithDebInfo' as none was specified.
-- USE_FILE is set to value: OFF
-- USE_CONSOLE is set to value: OFF
-- RPM packet version set to r0
-- Build type: RelWithDebInfo
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.2")
-- Checking for module 'automotive-dlt >= 2.11'
-- Found automotive-dlt , version 2.18.4
-- Found Doxygen: /usr/bin/doxygen (found version "1.9.1") found components: doxygen dot
-- asciidoc found
-- Configuring done
-- Generating done
-- Build files have been written to: ./build
<.>/capicxx-core-runtime$ cmake --build build/ --target install
[ 9%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/Address.cpp.o
[ 18%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/CallInfo.cpp.o
[ 27%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/ContainerUtils.cpp.o
[ 36%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/IniFileReader.cpp.o
[ 45%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/Logger.cpp.o
[ 54%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/MainLoopContext.cpp.o
[ 63%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/Proxy.cpp.o
[ 72%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/ProxyManager.cpp.o
[ 81%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/Runtime.cpp.o
[ 90%] Building CXX object CMakeFiles/CommonAPI.dir/src/CommonAPI/Utils.cpp.o
[100%] Linking C shared library libCommonAPI.so
[100%] Built target CommonAPI
Install the project...
-- Install configuration: "RelWithDebInfo"
-- Installing: /home/COVESA/install_folder/lib/libCommonAPI.so.3.2.3
-- Set runtime path of "/home/COVESA/install_folder/lib/libCommonAPI.so.3.2.3" to ""
-- Installing: /home/COVESA/install_folder/lib/libCommonAPI.so
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Address.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Attribute.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/AttributeExtension.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/ByteBuffer.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/CallInfo.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/CommonAPI.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Config.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/ContainerUtils.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Deployable.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Deployment.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Enumeration.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Event.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Export.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/AttributeCacheExtension.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Factory.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/IniFileReader.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/InputStream.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Logger.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/MainLoopContext.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/OutputStream.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Proxy.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/ProxyManager.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/RangedInteger.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Runtime.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/SerializableArguments.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Struct.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Stub.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/TypeOutputStream.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Types.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Utils.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Variant.hpp
-- Installing: /home/COVESA/install_folder/include/CommonAPI-3.2/CommonAPI/Version.hpp
-- Installing: /home/COVESA/install_folder/lib/cmake/CommonAPI-3.2.3/CommonAPIConfig.cmake
-- Installing: /home/COVESA/install_folder/lib/cmake/CommonAPI-3.2.3/CommonAPIConfigVersion.cmake
-- Installing: /home/COVESA/install_folder/lib/cmake/CommonAPI-3.2.3/CommonAPITargets.cmake
-- Installing: /home/COVESA/install_folder/lib/cmake/CommonAPI-3.2.3/CommonAPITargets-relwithdebinfo.cmake
-- Installing: /home/COVESA/install_folder/lib/pkgconfig/CommonAPI.pc
<.>/capicxx-core-runtime$ cd ..
The output in your console should be similar to the extract of my output that it is the window above. Do not doubt when you see in your results that Doxygen or asciidoc is not found; it is only needed for the generation of the documentation. The package 'automotive-dlt' is only needed in case you want to get DLT log messages. As shown in the last two lines you should find libCommonAPI.so in your build directory as well as in the install_folder.
Start again with cloning the source code of CommonAPI-D-Bus:
$ git clone https://github.com/GENIVI/capicxx-dbus-runtime.git
Cloning into 'capicxx-dbus-runtime'...
remote: Enumerating objects: 5108, done.
remote: Counting objects: 100% (110/110), done.
remote: Compressing objects: 100% (85/85), done.
remote: Total 5108 (delta 34), reused 84 (delta 17), pack-reused 4998
Receiving objects: 100% (5108/5108), 1.46 MiB | 2.23 MiB/s, done.
Resolving deltas: 100% (3734/3734), done.
The D-Bus runtime library cannot be built without further preparations. The reason is that CommonAPI-D-Bus doesn't use the standard libdbus library but needs a patched version. That means that you must first download, patch and build libdbus before the CommonAPI D-Bus runtime can be built.
➡️It is recommended NOT TO USE ❌️ "sudo" when following the tutorials, especially for DBus, except when using "apt-get". In case Ubuntu stops booting up, check in the journalctl, if you have any error messages related to dbus, example: "Failed to start D-Bus System Message Bus."
Subject : A start job for unit dbus.service has failed
▶️ Solution : Go into recovery mode, then root shell and then run the following command to purge the libdbus you've accidentally installed with "sudo make install": find /usr/local -iname 'dbus' -exec rm -rv {}IF the above recovery mode does not work, then, the last option would be to reinstall UBUNTU
Let's start (3 of 10 minutes have already passed :simple_smile:). Get and unpack the actual libdbus library:
$ wget -N https://dbus.freedesktop.org/releases/dbus/dbus-1.12.16.tar.gz
$ tar -xf dbus-1.12.16.tar.gz
I discarded the output in the console. Now apply the patches in the CommonAPI D-Bus source directory, e.g. for the patch capi-dbus-add-send-with-reply-set-notify.patch it looks like:
$ for patch in capicxx-dbus-runtime/src/dbus-patches/*.patch; do patch -d dbus-1.12.16/ -p1 <"$patch"; done
patching file dbus-1.pc.in
Hunk #1 succeeded at 19 (offset 1 line).
patching file dbus/dbus-connection.c
Hunk #1 succeeded at 3501 (offset 19 lines).
patching file dbus/dbus-connection.h
Hunk #1 succeeded at 230 (offset 1 line).
patching file dbus/dbus-message.c
patching file dbus/dbus-message.h
patching file dbus/dbus-string.c
patching file dbus/dbus-string.h
patching file dbus/dbus-connection.c
patching file dbus/dbus-connection.c
Reversed (or previously applied) patch detected! Assume -R? [n] y
Hunk #1 succeeded at 2527 (offset 2 lines).
patching file dbus/dbus-connection.c
Hunk #1 succeeded at 3737 (offset 28 lines).
Hunk #2 succeeded at 6528 (offset 85 lines).
❗ Please check if all these patches could be applied successfully. I tested it with D-Bus 1.6.x, 1.8.x, 1.10.x and 1.12.x. For other versions I cannot guarantee that the patches work.
Now build libdbus with autotools and check if the so library has been created in the .libs-directory:
$ env -C dbus-1.12.16 ./configure --prefix=$(realpath install_folder)
$ make -C dbus-1.12.16/dbus/ install
$ make -C dbus-1.12.16/ install-pkgconfigDATA
❗ I recommend to build libdbus with autotools. Otherwise it is possible that you miss some necessary files when you try to build CommonAPI C++ D-Bus.
Now it is possible to build the CommonAPI runtime library. Please note that you only use the uninstalled versions of CommonAPI and libdbus. First add the path to your D-Bus directory to the actual PKG_CONFIG_PATH environment variable, start CMake and call make as usual:
<.>/capicxx-dbus-runtime/build$ export PKG_CONFIG_PATH="<my-dbus-path>/dbus-1.12.16"
<.>/capicxx-dbus-runtime/build$ cmake -Bbuild -DCMAKE_INSTALL_PREFIX=../install_folder -DCMAKE_PREFIX_PATH=../install_folder -DPKG_CONFIG_USE_CMAKE_PREFIX_PATH=ON -DUSE_INSTALLED_DBUS=OFF -DDBus1_DIR=../dbus-1.12.16/cmake .
<.>/capicxx-dbus-runtime/build$ cmake --build build --target install
<.>/capicxx-dbus-runtime/build$ cd ..
If everything was successful (it should be!) you should find libCommonAPI-DBus.so in your build directory. Make sure that there are no empty spaces in the path of your working directory.
After all these preparations we start seriously to write a true CommonAPI application (more precisely two applications). We want to write one service which offers the method sayHello and one client which calls this method. For CommonAPI services the description of offered interfaces is done by means of Franca IDL. Don't worry if you don't know Franca IDL, you will learn it very fast.
First create some directories and open a new empty ASCII file with the name HelloWorld.fidl:
$ mkdir project
$ cd project/
<.>/project$ mkdir fidl
<.>/project$ cd fidl
<.>/project/fidl$ vi HelloWorld.fidl
Now create the following interface:
package commonapi.examples
interface HelloWorld {
version { major 0 minor 1 }
method sayHello {
in {
String name
}
out {
String message
}
}
}
Ready! A service which instantiates the interface HelloWorld provides the function sayHello which can be called.
The next step is to generate code. For that we need the code generators. The source code of the code generators is available in the tools repositories. You could now clone the repository and build the generators yourself. This is not particularly difficult, but requires the installation of Maven, which is not familiar to everyone. Therefore it is the fastest way to download the built code generator as binary from github. There are four versions (Linux, Windows and 64bit variants). Type uname -m if you don't know if you have a 32bit or 64bit version of Linux (you get i686 or x86_64). For the further description we assume that you have the 32bit version of Linux.
Copy the zip-files into a directory (e.g. cgen) and unpack them. Then you should find the executables of the code generators in cgen/commonapi-generator, cgen/commonapi_dbus_generator and cgen/commonapi_someip_generator, respectively.
<.>/project/fidl$ cd ..
<.>/project$ mkdir cgen
<.>/project$ cd cgen/
<.>/project/cgen$ wget https://github.com/GENIVI/capicxx-core-tools/releases/download/3.2.14/commonapi_core_generator.zip
<.>/project/cgen$ unzip commonapi_core_generator.zip -d commonapi_core_generator/
<.>/project/cgen$ wget https://github.com/GENIVI/capicxx-dbus-tools/releases/download/3.2.14/commonapi_dbus_generator.zip
<.>/project/cgen$ unzip commonapi_dbus_generator.zip -d commonapi_dbus_generator/
<.>/project/cgen$ wget https://github.com/GENIVI/capicxx-someip-tools/releases/download/3.2.14/commonapi_someip_generator.zip
<.>/project/cgen$ unzip commonapi_someip_generator.zip -d commonapi_someip_generator/
<.>/project/cgen$ cd ..
Finally generate code (CommonAPI code with the commonapi-generator and CommonAPI D-Bus code with the commonapi-dbus-generator; the SOME/IP code generator is not needed at the moment, we assume that the cgen directory is in your project directory):
<.>/project$ ./cgen/commonapi_core_generator/commonapi-core-generator-linux-x86 -d src-gen/core -sk ./fidl/HelloWorld.fidl
<.>/project$ ./cgen/commonapi_dbus_generator/commonapi-dbus-generator-linux-x86 -d src-gen/dbus ./fidl/HelloWorld.fidl
If everything worked, the generated code will be in the new directory src-gen. The option -sk generates a default implementation of your interface instance in the service.
Now we can start to write the Hello World application. Create a new subdirectory src in the project directory and change to it. There create 4 files: The client code HelloWorldClient.cpp, one file for the main-function of the service HelloWorldService.cpp and 2 files (header and source) for the implementation of the generated skeleton for the stub (we call it HelloWorldStubImpl.hpp and HelloWorldStubImpl.cpp).
Let's begin with the client. Create a new file HelloWorldClient.cpp with the editor of your choice and type:
// HelloWorldClient.cpp
#include <iostream>
#include <string>
#include <thread>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <CommonAPI/CommonAPI.hpp>
#include <v0/commonapi/examples/HelloWorldProxy.hpp>
using namespace v0::commonapi::examples;
int main() {
CommonAPI::Runtime::setProperty("LogContext", "E01C");
CommonAPI::Runtime::setProperty("LogApplication", "E01C");
CommonAPI::Runtime::setProperty("LibraryBase", "HelloWorld");
std::shared_ptr < CommonAPI::Runtime > runtime = CommonAPI::Runtime::get();
std::string domain = "local";
std::string instance = "commonapi.examples.HelloWorld";
std::string connection = "client-sample";
std::shared_ptr<HelloWorldProxy<>> myProxy = runtime->buildProxy<HelloWorldProxy>(domain,
instance, connection);
std::cout << "Checking availability!" << std::endl;
while (!myProxy->isAvailable())
std::this_thread::sleep_for(std::chrono::microseconds(10));
std::cout << "Available..." << std::endl;
const std::string name = "World";
CommonAPI::CallStatus callStatus;
std::string returnMessage;
CommonAPI::CallInfo info(1000);
info.sender_ = 1234;
while (true) {
myProxy->sayHello(name, callStatus, returnMessage, &info);
if (callStatus != CommonAPI::CallStatus::SUCCESS) {
std::cerr << "Remote call failed!\n";
return -1;
}
info.timeout_ = info.timeout_ + 1000;
std::cout << "Got message: '" << returnMessage << "'\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return 0;
}
At the beginning of each CommonAPI application it is necessary to get a pointer to the generic runtime object. The runtime is necessary to create proxies and stubs. A client application has to call functions of an instance of an interface in the service application. In order to be able to call these functions we must build a proxy for this interface in the client. The interface name is the template parameter of the buildProxy method; furthermore we build the proxy for a certain instance; the instance name is the second parameter in the buildProxy method. In principle there is the possibility to distinguish between instances in different so-call domains (first parameter), but we don't want to discuss this in depth at the moment and take always the domain "local".
The proxy provides the API function isAvailable; if the we start the service first then isAvailable returns always true. It is also possible to register a callback which is called when the service becomes available; but we try to keep it here as simple as possible.
Now we call the function sayHello which we have defined in our fidl-file. The function has one in-parameter (string) and one out-parameter (also string). Have a look into HelloWorldProxy.hpp at src-gen/v1/commonapi to get the information how exactly the function sayHello must be called. Here it is important to know that it is possible to call the synchronous variant of this function (what we do here) or to call the asynchronous variant (sayHelloAsync) which is slightly more complicated. One return value is the so-called CallStatus, which gives us the information if the call was successful or not. Again, to keep it simple we do not check the CallStatus and hope that everything worked fine.
We continue now to write the service. Create a new file HelloWorldService.cpp:
// HelloWorldService.cpp
#include <iostream>
#include <thread>
#include <CommonAPI/CommonAPI.hpp>
#include "HelloWorldStubImpl.hpp"
using namespace std;
int main() {
CommonAPI::Runtime::setProperty("LogContext", "E01S");
CommonAPI::Runtime::setProperty("LogApplication", "E01S");
CommonAPI::Runtime::setProperty("LibraryBase", "HelloWorld");
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
std::string domain = "local";
std::string instance = "commonapi.examples.HelloWorld";
std::string connection = "service-sample";
std::shared_ptr<HelloWorldStubImpl> myService = std::make_shared<HelloWorldStubImpl>();
bool successfullyRegistered = runtime->registerService(domain, instance, myService, connection);
while (!successfullyRegistered) {
std::cout << "Register Service failed, trying again in 100 milliseconds..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
successfullyRegistered = runtime->registerService(domain, instance, myService, connection);
}
std::cout << "Successfully Registered Service!" << std::endl;
while (true) {
std::cout << "Waiting for calls... (Abort with CTRL+C)" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(60));
}
return 0;
}
The main function of the service is even simpler as the main function of the client because the implementation of the interface functions is in the stub implementation. Again we need the pointer to the runtime environment; then we instantiate our implementation of the stub and register this instance of the interface by calling registerService with an instance name. The service shall run forever and answer to function calls until it becomes killed; therefore we need the while loop at the end of the main function.
At the end we need the stub implementation; we realize it by creating a stub-implementation class which is inherited from the stub-default implementation. The header file HelloWorldStubImpl.hpp is:
// HelloWorldStubImpl.hpp
#ifndef HELLOWORLDSTUBIMPL_H_
#define HELLOWORLDSTUBIMPL_H_
#include <CommonAPI/CommonAPI.hpp>
#include <v0/commonapi/examples/HelloWorldStubDefault.hpp>
class HelloWorldStubImpl: public v0_1::commonapi::examples::HelloWorldStubDefault {
public:
HelloWorldStubImpl();
virtual ~HelloWorldStubImpl();
virtual void sayHello(const std::shared_ptr<CommonAPI::ClientId> _client, std::string _name, sayHelloReply_t _return);
};
#endif /* HELLOWORLDSTUBIMPL_H_ */
The implementation is in the cpp-file HelloWorldStubImpl.cpp:
// HelloWorldStubImpl.cpp
#include "HelloWorldStubImpl.hpp"
HelloWorldStubImpl::HelloWorldStubImpl() {
}
HelloWorldStubImpl::~HelloWorldStubImpl() {
}
void HelloWorldStubImpl::sayHello(const std::shared_ptr<CommonAPI::ClientId> _client,
std::string _name, sayHelloReply_t _reply) {
std::stringstream messageStream;
messageStream << "Hello " << _name << "!";
std::cout << "sayHello('" << _name << "'): '" << messageStream.str() << "'\n";
_reply(messageStream.str());
};
If the function sayHello is called it gets the name (which is supposed to be the name of the developer of this application) and returns it with an added "Hello" in front. The return parameter is not directly the string as it is defined in the fidl-file; it is a standard function object with the return parameters as in-parameters. The reason for this is to provide the possibility to answer not synchronously in the implementation of this function but to delegate the answer to a different thread.
Since you must have installed CMake in order to build the CommonAPI runtime libraries, the fastest way to compile and build our applications is to write a simple CMake file. Create a new file CMakeLists.txt directly in the project directory:
cmake_minimum_required(VERSION 3.13)
set(PRJ_NAME HelloWorld)
set(CMAKE_VERBOSE_MAKEFILE on)
OPTION(USE_FILE "Set to OFF to disable file logging" OFF )
message(STATUS "USE_FILE is set to value: ${USE_FILE}")
OPTION(USE_CONSOLE "Set to OFF to disable console logging" OFF )
message(STATUS "USE_CONSOLE is set to value: ${USE_CONSOLE}")
IF(USE_FILE)
add_definitions(-DUSE_FILE)
ENDIF(USE_FILE)
IF(USE_CONSOLE)
add_definitions(-DUSE_CONSOLE)
ENDIF(USE_CONSOLE)
SET(MAX_LOG_LEVEL "DEBUG" CACHE STRING "maximum log level")
message(STATUS "MAX_LOG_LEVEL is set to value: ${MAX_LOG_LEVEL}")
add_definitions(-DCOMMONAPI_LOGLEVEL=COMMONAPI_LOGLEVEL_${MAX_LOG_LEVEL})
if (MSVC)
# Visual C++ is not always sure whether he is really C++
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /EHsc /wd\\\"4503\\\"")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd\\\"4503\\\"")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Wall -O0 -std=c++11 -D_GLIBCXX_USE_NANOSLEEP -DLINUX")
endif()
message(STATUS "Compiler options: ${CMAKE_CXX_FLAGS}")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
"Choose the type of build, options are: Debug Release." FORCE)
endif(NOT CMAKE_BUILD_TYPE)
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
OPTION(USE_INSTALLED_COMMONAPI "Set to OFF to use the local (build tree) version of CommonAPI" ON)
message(STATUS "USE_INSTALLED_COMMONAPI is set to value: ${USE_INSTALLED_COMMONAPI}")
if ("${USE_INSTALLED_COMMONAPI}" STREQUAL "ON")
FIND_PACKAGE(CommonAPI 3.2.0 REQUIRED CONFIG NO_CMAKE_PACKAGE_REGISTRY)
FIND_PACKAGE(CommonAPI-DBus 3.2.0 REQUIRED CONFIG NO_CMAKE_PACKAGE_REGISTRY)
else()
FIND_PACKAGE(CommonAPI 3.2.0 REQUIRED CONFIG NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
FIND_PACKAGE(CommonAPI-DBus 3.2.0 REQUIRED CONFIG NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
endif()
message(STATUS "CommonAPI_CONSIDERED_CONFIGS: ${CommonAPI_CONSIDERED_CONFIGS}")
message(STATUS "COMMONAPI_INCLUDE_DIRS: ${COMMONAPI_INCLUDE_DIRS}")
message(STATUS "CommonAPI-DBus_CONSIDERED_CONFIGS: ${CommonAPI-DBus_CONSIDERED_CONFIGS}")
message(STATUS "COMMONAPI_DBUS_INCLUDE_DIRS: ${COMMONAPI_DBUS_INCLUDE_DIRS}")
# CommonAPI
include(FindPkgConfig)
###############################################################################
# find DBus by using the 'pkg-config' tool
if (MSVC)
#Not beautiful, but it works
if (DBus_DIR)
if (DBus_BUILD_DIR)
set(DBus_INCLUDE_DIRS "${DBus_DIR};${DBus_BUILD_DIR};")
else ()
message (FATAL_ERROR "DBus_BUILD_DIR not set! Cannot continue.")
endif ()
else()
message (FATAL_ERROR "DBus_DIR not set! Cannot continue.")
endif ()
else()
pkg_check_modules(DBus REQUIRED dbus-1>=1.4)
endif()
# Source Files
set(PRJ_SRC_PATH src)
set(PRJ_SRC_GEN_PATH src-gen)
set(PRJ_SRC_GEN_COMMONAPI_PATH ${PRJ_SRC_GEN_PATH}/core/v0/commonapi/examples)
set(PRJ_SRC_GEN_COMMONAPI_DBUS_PATH ${PRJ_SRC_GEN_PATH}/dbus/v0/commonapi/examples)
set(PRJ_NAME_CLIENT ${PRJ_NAME}Client)
set(PRJ_NAME_SERVICE ${PRJ_NAME}Service)
# Application
FILE(GLOB PRJ_PROXY_GEN_SRCS ${PRJ_SRC_GEN_COMMONAPI_PATH}/*Proxy.cpp)
FILE(GLOB PRJ_STUB_GEN_SRCS ${PRJ_SRC_GEN_COMMONAPI_PATH}/*Stub*.cpp)
FILE(GLOB PRJ_STUB_IMPL_SRCS ${PRJ_SRC_COMMONAPI_PATH}/*Stub*.cpp)
set(PRJ_CLIENT_SRCS ${PRJ_SRC_PATH}/${PRJ_NAME_CLIENT}.cpp ${PRJ_PROXY_GEN_SRCS})
set(PRJ_SERVICE_SRCS ${PRJ_SRC_PATH}/${PRJ_NAME_SERVICE}.cpp ${PRJ_SRC_PATH}/${PRJ_NAME}StubImpl.cpp ${PRJ_STUB_GEN_SRCS} ${PRJ_STUB_IMPL_SRCS})
# Boost
find_package( Boost 1.54 COMPONENTS system thread log REQUIRED )
include_directories( ${Boost_INCLUDE_DIR} )
# DBus library
FILE(GLOB PRJ_DBUS_LIB_SRCS ${PRJ_SRC_GEN_COMMONAPI_DBUS_PATH}/*cpp)
# Paths
OPTION(USE_INSTALLED_DBUS "Set to OFF to use the local (patched) version of dbus" ON)
message(STATUS "USE_INSTALLED_DBUS is set to value: ${USE_INSTALLED_DBUS}")
include_directories(
src-gen/core
src-gen/dbus
${COMMONAPI_INCLUDE_DIRS}
${COMMONAPI_DBUS_INCLUDE_DIRS}
${DBus_INCLUDE_DIRS}
)
if ("${USE_INSTALLED_DBUS}" STREQUAL "ON")
link_directories(
${COMMONAPI_LIBDIR}
${COMMONAPI_DBUS_LIBDIR}
${DBus_LIBRARY_DIRS}
${Boost_LIBRARY_DIR}
)
else()
link_directories(
${COMMONAPI_LIBDIR}
${COMMONAPI_DBUS_LIBDIR}
${DBus_INCLUDE_DIRS}/dbus/.libs
${Boost_LIBRARY_DIR}
)
endif()
if (MSVC)
set(LINK_LIBRARIES CommonAPI)
else()
set(LINK_LIBRARIES -Wl,--as-needed CommonAPI)
endif()
# Build Client
add_executable(${PRJ_NAME_CLIENT} ${PRJ_CLIENT_SRCS})
target_link_libraries(${PRJ_NAME_CLIENT} ${LINK_LIBRARIES})
# Build service
add_executable(${PRJ_NAME_SERVICE} ${PRJ_SERVICE_SRCS})
target_link_libraries(${PRJ_NAME_SERVICE} ${LINK_LIBRARIES})
# Build DBus library
add_library (${PRJ_NAME}-dbus SHARED ${PRJ_DBUS_LIB_SRCS})
target_link_libraries(${PRJ_NAME}-dbus CommonAPI-DBus)
As include paths we need the include directories of CommonAPI and bindings and D-Bus; the directory of the generated code must be also added. We link everything together and do not discuss here questions concerning the configuration with different bindings. Therefore we tell CMake where to find the CommonAPI libraries and libdbus (set the environment variable to the right values or replace them with the absolute paths on your machine). At the end we build two executables, one for the service and one for the client.
Now call CMake and make (remember that we created the build directory before):
<.>/project$ cmake -Bbuild -DCMAKE_PREFIX_PATH=$(realpath ../install_folder) -DPKG_CONFIG_USE_CMAKE_PREFIX_PATH=ON .
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- USE_FILE is set to value: OFF
-- USE_CONSOLE is set to value: OFF
-- MAX_LOG_LEVEL is set to value: DEBUG
-- Compiler options: -pthread -Wall -O0 -std=c++11 -D_GLIBCXX_USE_NANOSLEEP -DLINUX
-- Build type: Debug
-- USE_INSTALLED_COMMONAPI is set to value: ON
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.2")
-- Found DBus1: <.>/install_folder/lib/libdbus-1.so
-- CommonAPI_CONSIDERED_CONFIGS: <.>/install_folder/lib/cmake/CommonAPI-3.2.3/CommonAPIConfig.cmake
-- COMMONAPI_INCLUDE_DIRS: <.>/install_folder/include/CommonAPI-3.2
-- CommonAPI-DBus_CONSIDERED_CONFIGS: <.>/install_folder/lib/cmake/CommonAPI-DBus-3.2.3/CommonAPI-DBusConfig.cmake
-- COMMONAPI_DBUS_INCLUDE_DIRS: <.>/install_folder/include/CommonAPI-3.2
-- Checking for module 'dbus-1>=1.4'
-- Found dbus-1, version 1.12.16
-- Found Boost: /usr/lib/x86_64-linux-gnu/cmake/Boost-1.74.0/BoostConfig.cmake (found suitable version "1.74.0", minimum required is "1.54") found components: system thread log
-- USE_INSTALLED_DBUS is set to value: ON
-- Configuring done
-- Generating done
-- Build files have been written to: <.>/project/build
<.>/project$ cmake --build build
Scanning dependencies of target HelloWorldClient
[ 11%] Building CXX object CMakeFiles/HelloWorldClient.dir/src/HelloWorldClient.cpp.o
[ 22%] Linking CXX executable HelloWorldClient
[ 22%] Built target HelloWorldClient
[ 33%] Building CXX object CMakeFiles/HelloWorldService.dir/src/HelloWorldService.cpp.o
[ 44%] Building CXX object CMakeFiles/HelloWorldService.dir/src/HelloWorldStubImpl.cpp.o
[ 55%] Linking CXX executable HelloWorldService
[ 55%] Built target HelloWorldService
[ 66%] Building CXX object CMakeFiles/HelloWorld-dbus.dir/src-gen/dbus/v1/commonapi/HelloWorldDBusDeployment.cpp.o
[ 77%] Building CXX object CMakeFiles/HelloWorld-dbus.dir/src-gen/dbus/v1/commonapi/HelloWorldDBusProxy.cpp.o
[ 88%] Building CXX object CMakeFiles/HelloWorld-dbus.dir/src-gen/dbus/v1/commonapi/HelloWorldDBusStubAdapter.cpp.o
[100%] Linking CXX shared library libHelloWorld-dbus.so
[100%] Built target HelloWorld-dbus
Your output should look similar. In the build directory there should be two executables now: HelloWorldClient and HelloWorldService.
Start them in two different terminals and they should communicate with each other using dbus.
<.>/project$ LD_LIBRARY_PATH=../install_folder/lib:$PWD/build build/HelloWorldService
[CAPI][INFO] Loading configuration file '/etc/commonapi.ini'
[CAPI][INFO] Using default binding 'dbus'
[CAPI][INFO] Using default shared library folder '/usr/local/lib/commonapi'
Successfully Registered Service!
Waiting for calls... (Abort with CTRL+C)
sayHello('World'): 'Hello World!'
sayHello('World'): 'Hello World!'
<.>/project$ LD_LIBRARY_PATH=../install_folder/lib:$PWD/build build/HelloWorldClient
[CAPI][INFO] Loading configuration file '/etc/commonapi.ini'
[CAPI][INFO] Using default binding 'dbus'
[CAPI][INFO] Using default shared library folder '/usr/local/lib/commonapi'
[CAPI][INFO] subscribeAvailabilityListener service: commonapi.examples.HelloWorld.v0_1_commonapi.examples.HelloWorld objectPath: /commonapi/examples/HelloWorld interface: commonapi.examples.HelloWorld.v0_1
Checking availability!
Available...
Got message: 'Hello World!'
Got message: 'Hello World!'