Warning This is a beta release of the Viam C++ SDK. Stability is not guaranteed. Breaking changes are likely to occur, and occur often.
The viam-cpp-sdk
has a newly introduced CMake build system to
replace the existing Makefile infrastructure. This document explains
how to use the new infrastructure.
If you experience problems while following this guide, please see the
Limitations, Known Issues, and Troubleshooting
section at the bottom
to see if your issue (and hopefully a workaround) is covered there.
PLEASE NOTE: It is very likely that you will need to rebuild the
generated sources as part of building the SDK. Please see the
documentation on VIAMCPPSDK_USE_DYNAMIC_PROTOS
below, and the
additional comments about mismatched protobufs in the troubleshooting
guide, before continuing further.
The project depends on CMake >= 3.25, Boost >= 1.74, gRPC >= 1.30.2, protobuf >= 3.12.4, xtensor >= 0.24.3, abseil and on the transitive dependencies of those projects.
You will need to install these required dependencies before building the Viam C++ SDK, preferably by way of your system package manager:
- Debian:
apt-get install cmake build-essential libabsl-dev libboost-all-dev libgrpc++-dev libprotobuf-dev libxtensor-dev
- MacOS with Homebrew:
brew install abseil cmake boost grpc protobuf xtensor
There are also several optional or conditionally required dependencies:
- Debian:
apt-get install pkg-config ninja-build protobuf-compiler-grpc
- MacOS with Homebrew:
brew install pkg-config ninja buf
The pkg-config
dependency is needed if your local system does not
provide CMake find_package
support for one or more required
dependencies like gRPC
.
The ninja[-build]
dependency is only required if you want to use
Ninja as the target build system, so is entirely optional.
You will need protobuf-compiler-grpc
if you intend to do local
dynamic protobuf generation (see the relevant SDK options below) or if
your local gRPC version is too old to be used with buf.build
(effectively, older than gRPC 1.41).
Installing the buf
homebrew package will save you from needing to
download it as part of the build, which costs a little time on every
build invocation.
Note that Debian Bullseye does not ship a recent enough version of CMake by default, but it can be obtained from the Bullseye Backports repository:
apt-get install software-properties-common
apt-add-repository 'deb http://deb.debian.org/debian bullseye-backports main'
apt-get update
apt-get install -t bullseye-backports cmake
You can of course build and install any or all of these requirements manually rather than via your package manager, though this is likely to make it more difficult to correctly configure the build of the Viam C++ SDK.
Just clone https://github.com/viamrobotics/viam-cpp-sdk into a directory of your choice and checkout the branch or tag of interest.
This document doesn't intend to provide a complete guide for how CMake
is used, as that is a complex topic. Please consult the CMake
documentation for
specific details on how CMake works and how to invoke the tool. The
guidance here is intended to cover the common case builds of the
viam-cpp-sdk
.
CMake organizes out-of-tree builds. The first step is to create a
scratch directory where you want to keep the working state of your
build. A convention is to name this directory build
and to create it
under the root of the project. The viam-cpp-sdk
top-level
.gitignore
file already ignores such a directory, so we recommend
starting there:
git clone https://github.com/viamrobotics/viam-cpp-sdk
cd viam-cpp-sdk
mkdir build
cd build
CMake isn't itself a build system, it is a build system generator. Our next step is to generate the build system. In these examples we use Ninja, but you can also use Makefiles or many other build systems.
When invoking CMake, you pass the path to the root CMakeLists.txt
file for the project you wish to build. If you have opted to use an
in-tree build
directory per the above instructions, this is as
simple as:
cmake .. -G Ninja
If you encounter an error about an inability to link an OpenSSL target on MacOS, try setting your PKG_CONFIG_PATH to point to your Homebrew installation.
If all went well, you should now have a file named build.ninja
in
the build
directory created above. Note that git status
will not
show this, since the build
directory is ignored.
If you wish to use a different generator than Ninja, change the
argument to -G
above to the build system you wish to generate for.
To compile all targets in the SDK:
ninja all
To install the SDK:
ninja install
By default, the build is configured to install the SDK to the
install
subdirectory of the working directory, so if you have been
following the instructions above and the build worked, you should see
an installation tree under build/install
.
If the build did not succeed, please see the next section.
As of version 0.0.11 there is experimental support for building with
the conan
C++ package manager. This section will go into more detail
without assuming any familiarity with conan
, but please consult the
conan
docs for more info.
To use conan
, you'll need to have python
and pip
on your system if
you don't already. For example,
- Debian:
apt-get install python3 python3-pip
- MacOS with Homebrew:
brew install python
and then, optionally in a venv if you prefer,
pip install conan
conan profile detect
The second line detects a default conan
profile
encapsulating your build environment, which you can modify as needed.
Next note that there are two possible approaches here, which complement or supersede some of the other sections in this document. Namely, it is possible to
- use
conan
to build and package the SDK; OR - use
conan
to obtain the software prerequisites for the SDK.
Option 1. makes more sense for building against the
SDK as you would in module development.
There we use conan
to get the SDK's dependencies, and then to build,
install, and package the SDK, which can then be consumed by declaring it
as a conan
dependency.
Option 2. makes more sense for doing locally development on the SDK
while using conan
to get dependencies instead of your system package
manager. Note that Option 1 implies a superset of Option 2.
In either case, conan
will use the conanfile.py
.
Note that we build with offline_proto_generation=True
by default.
Here we use conan
to package and install the SDK so that we can build
against it by declaring it as a dependency in a
conanfile.txt
or a conanfile.py
.
To do this, call conan create .
from the project root. This will build
and test the viam-cpp-sdk
recipe, adding it into your local cache. See
the conan docs
for more info on conan create
, as we will omit any details about using
profiles, options, or settings to customize the build.
Once this is done, the viam-cpp-sdk
package is ready to be consumed.
The example projects show a minimal
conanfile.txt
. With
this conanfile.txt
in the same directory as your project's
CMakeLists.txt
, you can then do, for example,
conan install . --output-folder=build-conan --build=missing
cmake . --preset=conan-release
cmake --build --preset=conan-release -j 8
Note that this can be done with the same CMakeLists.txt
from the
example project: it is
agnostic of the use of conan
to package the SDK as opposed to the SDK
having been built and installed manually.
It is also possible to build using a conanfile.py
rather than a
conanfile.txt
, see again the conan
docs,
or look at the test_package/conanfile.py
which is the test package recipe.
Here we use conan
to grab the SDK dependencies, setting ourselves up
for local SDK development.
From the root of this repo, you can do
conan install . --output-folder=build-conan --build=missing
to install dependencies with conan
. And then
cmake . --preset conan-release
cmake --build --preset=conan-release -j
The conan install
sets up some
cmake-presets
which are used to resolve --preset conan-release
above. Among other
things, this tells CMake about the conan_toolchain.cmake
generated by
conan
, and runs CMake with certain environment variables set.
If you do not want to use cmake-presets, you can do
cd build-conan
source conanbuild.sh
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
and later call source deactivate_conanbuild.sh
. The conanbuild.sh
script does the same environment variable setting as the cmake-presets,
but it may be preferable if you would rather invoke your build system
directly.
PLEASE NOTE: Whether by cmake-presets or conanbuild.sh
, you MUST, one
way or another, make the PATH
entries set by conan
available to
CMake, because buf
requires that protoc
is available on PATH
. If
you do not do this then buf generate
will fail outright, or, if you
have a different version of protoc
available in your PATH
, it will
silently fail and later cause compilation failures due to protobuf
version mismatches.
A frequent cause of difficulty or failure is an inability for the
build to find a third-party dependency like gRPC
. If the build
fails, first check if you have installed the right dependencies per
the lists above. In some cases, you may find that a necessary direct
or transitive dependency has been installed, but to a location that
CMake does not search by default.
On Homebrew, OpenSSL does not get installed in the default search
paths because it is "keg-only". We can inform CMake of the actual
install location by customizing PKG_CONFIG_PATH
in the shell
environment before invoking CMake:
PKG_CONFIG_PATH="/opt/homebrew/opt/openssl@3/lib/pkgconfig" cmake ...
CMake also has its own intrinsic package search mechanism. If you have
dependencies installed to locations that are not part of CMake's
default search set, you can add them by configuring
CMAKE_PREFIX_PATH
as an argument to CMake. For instance, if you have
a personal tree of installed software in $HOME/opt
, you can inform
CMake that this path should be searched for packages by passing it as
a command line "define" to CMake:
cmake -DCMAKE_PREFIX_PATH=$HOME/opt ...
Please see
CMAKE_PREFIX_PATH
for more details on CMAKE_PREFIX_PATH
.
By default, the SDK will install into a subdirectory of the working
build directory. This is not appropriate for making the SDK available
for other projects. To install the SDK to a location of your choice
you should customize CMAKE_INSTALL_PREFIX
while invoking CMake
and
then re-run the install
task. For instance, to install the SDK to
/usr/local
:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ...
ninja install
By default, the SDK build will enforce compiler minima. If you must bypass this check for some reason, you may do so by disabling this default-on option:
cmake -DVIAMCPPSDK_ENFORCE_COMPILER_MINIMA=OFF ...
Because the checked-in copies of generated sources only work with a narrow range of gRPC versions, the SDK by default will dynamically regenerate proto code. If you wish to test against the checked-in copies or cannot regenerate for any reason, you can do so by disabling this flag:
cmake -DVIAMCPPSDK_USE_DYNAMIC_PROTOS=OFF ...
By default, the SDK will use local buf services and definitions to generate code. It is possible to instead rely on buf.build services to do code generation (note that this requies online access):
cmake -DVIAMCPPSDK_OFFLINE_PROTO_GENERATION=OFF ...
Note that this option only has an effect when
VIAMCPPSDK_USE_DYNAMIC_PROTOS=ON
.
The Viam C++ SDK installs support files for CMake's find_package
subsystem.
find_package(viam-cpp-sdk CONFIG REQUIRED COMPONENTS viamsdk)
target_link_libraries(mytarget viam-cpp-sdk::viamsdk)
Note that you will need to ensure that CMAKE_PREFIX_PATH
is set
appropriately in your project to include the path to the installed
Viam C++ SDK.
An example project using this technique is available under
examples/project/cmake
and it can be built with CMake in the regular
way. If you have allowed the SDK to install to the default
build/install
prefix, the example should build against your SDK
installation automatically. If you have customized the SDK
installation prefix by building it with a specific
CMAKE_INSTALL_PREFIX
that is not among CMake's default package
search list (e.g. $HOME/opt
), you will need to pass that information
along when building the example:
cd examples/project/cmake
mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=<path/to/which/the/sdk/was/installed> ...
Consuming the SDK via CMake is the preferred mechanism and likely to give the best results.
The Viam C++ SDK also installs .pc
files to introspect the SDK with
pkg-config
. An example project using make
and pkg-config
is
available under examples/project/pkg-config
. Much like with the need
to customize the CMake prefix path, you may need to customize
PKG_CONFIG_PATH
in order to find a Viam C++ SDK installation that is
not part of pkg-config
's default search list:
cd examples/project/pkg-config
PKG_CONFIG_PATH=<path/to/which/the/sdk/was/installed>/lib/pkgconfig make ...
It is of course possible to build against the Viam C++ SDK without
using either CMake support or pkg-config
by explicitly setting
toolchain include paths, library paths, and passing -lviamcpp
or
similar on the link line.
Sometimes when running ninja all
or ninja install
in a project
using -DVIAMCPPSDK_USE_DYNAMIC_PROTOS=ON
the build will fail because
generated sources have not yet been produced. If that happens you can
explicitly run the ninja generate-dynamic-protos
command to ensure
that they are pre-populated.
cmake -DVIAMCPPSDK_USE_DYNAMIC_PROTOS=ON ...
ninja all # FAILS!
ninja generate-dynamic-protos
nina all # OK!
If compilation fails with a wall of compiler-generated errors that includes text like:
error: incompatible with your Protocol Buffer headers
then there is likely a mismatch between your local protobuf/gRPC version and the version used to generate the checked-in files. Switch to using dynamic proto generation:
cmake ...
ninja all # FAILS, wall of compiler text with above message
cmake -DVIAMCPPSDK_USE_DYNAMIC_PROTOS=ON ...
ninja all # OK!
For some platforms (mostly macOS) the runtime and install name
properties are not correctly configured for the viam_rust_utils
library. This can lead to a runtime error where the viam_rust_utils
shared library is not found at program startup. If this happens, set
[DY]LD_LIBRARY_PATH
to point into the library directory of the
installation when running commands that require it:
cmake ...
ninja install
./build/install/bin/example_echo # FAILS: libviam_rust_utils not found
export DYLD_LIBRRAY_PATH=./build/install/lib
./build/install/bin/example_echo # OK!
If you are having trouble getting the SDK installed or your project built against it and running, please take advantage of our community resources.
Copyright 2023 Viam Inc.
Apache 2.0 - See LICENSE file