Skip to content

Commit f78507f

Browse files
authored
RSDK-8822: Whole archive linking (#306)
1 parent d8bee17 commit f78507f

File tree

7 files changed

+86
-34
lines changed

7 files changed

+86
-34
lines changed

.github/workflows/test.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ jobs:
1010
runs-on: ubuntu-latest
1111
container:
1212
image: ghcr.io/viamrobotics/canon:amd64
13+
strategy:
14+
matrix:
15+
include:
16+
- BUILD_SHARED: ON
17+
- BUILD_SHARED: OFF
1318
steps:
1419
- uses: actions/checkout@v4
1520
###########################################
@@ -18,4 +23,4 @@ jobs:
1823
- name: build-docker-test
1924
run: |
2025
docker build -t cpp . -f etc/docker/tests/Dockerfile.debian.bullseye
21-
docker run --rm -i -w /tmp cpp /bin/bash
26+
docker run -e BUILD_SHARED=${{ matrix.BUILD_SHARED }} --rm -i -w /tmp cpp /bin/bash

CMakeLists.txt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ project(viam-cpp-sdk
5151
#
5252
# Enabled by default so that we produce a
5353
# modern SDK, this option can be set to `OFF` to build a static
54-
# library. Note that this may result in non-functional examples.
54+
# library.
55+
# Note that the pkg-config files installed by the project do not work
56+
# for a static build; please use Conan for more robust pkg-config support.
5557
#
5658
option(BUILD_SHARED_LIBS "If enabled, build shared libraries" ON)
5759

@@ -129,14 +131,8 @@ option(VIAMCPPSDK_CLANG_TIDY "Run the clang-tidy linter" OFF)
129131

130132
# The following options are only defined if this project is not being included as a subproject
131133
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
132-
include(CMakeDependentOption)
133-
option(VIAMCPPSDK_BUILD_EXAMPLES "Build the example executables (requires building tests too)" ON)
134-
135-
# Note that the complex module example requires the testing library and adds its tests to the unit
136-
# test suite. Thus you can only disable tests if examples are also disabled.
137-
# The call below says don't give the user the option to disable tests unless examples are already
138-
# disabled, and default to building the tests in either case.
139-
cmake_dependent_option(VIAMCPPSDK_BUILD_TESTS "Build the unit test suite" ON "NOT VIAMCPPSDK_BUILD_EXAMPLES" ON)
134+
option(VIAMCPPSDK_BUILD_EXAMPLES "Build the example executables" ON)
135+
option(VIAMCPPSDK_BUILD_TESTS "Build the example executables" ON)
140136
endif()
141137

142138

conanfile.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from conan import ConanFile
22
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
33
from conan.tools.files import load
4+
from conan.tools.apple import is_apple_os
5+
import os
46
import re
57

68
class ViamCppSdkRecipe(ConanFile):
@@ -81,9 +83,10 @@ def package(self):
8183

8284
def package_info(self):
8385
self.cpp_info.components["viam_rust_utils"].libs = ["viam_rust_utils"]
86+
87+
self.cpp_info.components["viamsdk"].libs = ["viamsdk"]
8488

8589
for component in ["viamsdk", "viamapi"]:
86-
self.cpp_info.components[component].libs = [component]
8790
self.cpp_info.components[component].set_property("cmake_target_name", "viam-cpp-sdk::{}".format(component))
8891
self.cpp_info.components[component].set_property("pkg_config_name", "viam-cpp-sdk-lib{}".format(component))
8992
self.cpp_info.components[component].requires = ["grpc::grpc++", "protobuf::libprotobuf"]
@@ -95,6 +98,18 @@ def package_info(self):
9598

9699
self.cpp_info.components["viamapi"].includedirs.append("include/viam/api")
97100

101+
if self.options.shared:
102+
self.cpp_info.components["viamapi"].libs = ["viamapi"]
103+
else:
104+
lib_folder = os.path.join(self.package_folder, "lib")
105+
lib_fullpath = os.path.join(lib_folder, "libviamapi.a")
106+
if is_apple_os(self):
107+
whole_archive = f"-Wl,-force_load,{lib_fullpath}"
108+
else:
109+
whole_archive = f"-Wl,--push-state,--whole-archive,{lib_fullpath},--pop-state"
110+
self.cpp_info.components["viamapi"].exelinkflags.append(whole_archive)
111+
self.cpp_info.components["viamapi"].sharedlinkflags.append(whole_archive)
112+
98113
self.cpp_info.components["viamsdk"].requires.extend([
99114
"viamapi",
100115
"boost::headers",

etc/docker/tests/run_test.sh

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ mkdir build
55
cd build
66
cmake .. -G Ninja -DVIAMCPPSDK_USE_DYNAMIC_PROTOS=ON \
77
-DVIAMCPPSDK_OFFLINE_PROTO_GENERATION=ON \
8-
-DVIAMCPPSDK_SANITIZED_BUILD=ON \
9-
-DVIAMCPPSDK_CLANG_TIDY=ON
8+
-DVIAMCPPSDK_SANITIZED_BUILD=$BUILD_SHARED \
9+
-DVIAMCPPSDK_CLANG_TIDY=ON \
10+
-DBUILD_SHARED_LIBS=$BUILD_SHARED
11+
1012
ninja all
1113
ninja install
1214
INSTALL_DIR="$(pwd)/install"
@@ -20,15 +22,28 @@ popd
2022

2123
# Test that example_module builds and runs with the SDK install from above.
2224
# Check with both CMake and make/pkg-config that we can build the example
23-
# and have it exit with the expected error message.
25+
# and have it start listening.
26+
27+
run_module() {
28+
./example_module fake-socket-path > module-out-temp.txt &
29+
MODULE_PID=$!
30+
sleep 2
31+
kill ${MODULE_PID}
32+
grep 'Module listening' module-out-temp.txt
33+
return $?
34+
}
2435

2536
cd ../src/viam/examples/project
2637
pushd cmake
2738
cmake . -G Ninja # Just do an in-source build to save path fiddling
2839
ninja all
29-
[ $(./example_module 2>&1 | grep 'main failed with exception:' -c) = 1 ]
30-
popd
31-
pushd pkg-config
32-
PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig make all
33-
[ $(./example_module 2>&1 | grep 'main failed with exception:' -c) = 1 ]
40+
run_module
3441
popd
42+
43+
if [ ${BUILD_SHARED} = "ON" ]
44+
then
45+
pushd pkg-config
46+
PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig make all
47+
run_module
48+
popd
49+
fi

src/viam/examples/modules/complex/CMakeLists.txt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,13 @@ install(
140140
# writing their own modules. We are simply integrating tests from
141141
# `test_complex_module.cpp` into the Viam C++ SDK testing suite.
142142

143-
enable_testing()
144-
viamcppsdk_add_boost_test(test_complex_module.cpp)
145-
target_sources(test_complex_module
146-
PRIVATE
147-
gizmo/impl.cpp
148-
summation/impl.cpp
149-
)
150-
target_link_libraries(test_complex_module complex_module_sources)
143+
if (VIAMCPPSDK_BUILD_TESTS)
144+
enable_testing()
145+
viamcppsdk_add_boost_test(test_complex_module.cpp)
146+
target_sources(test_complex_module
147+
PRIVATE
148+
gizmo/impl.cpp
149+
summation/impl.cpp
150+
)
151+
target_link_libraries(test_complex_module complex_module_sources)
152+
endif()

src/viam/sdk/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,13 @@ target_link_directories(viamsdk
227227
"$<INSTALL_INTERFACE:${VIAMCPPSDK_PROTOBUF_LIBRARY_DIRS}>"
228228
)
229229

230+
if (BUILD_SHARED_LIBS)
231+
target_link_libraries(viamsdk PUBLIC viam-cpp-sdk::viamapi)
232+
else()
233+
target_link_libraries(viamsdk PUBLIC "$<LINK_LIBRARY:WHOLE_ARCHIVE,viam-cpp-sdk::viamapi>")
234+
endif()
235+
230236
target_link_libraries(viamsdk
231-
PUBLIC viam-cpp-sdk::viamapi
232237
PUBLIC ${VIAMCPPSDK_GRPCXX_LIBRARIES}
233238
PUBLIC ${VIAMCPPSDK_GRPC_LIBRARIES}
234239
PUBLIC ${VIAMCPPSDK_PROTOBUF_LIBRARIES}

test_package/conanfile.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import subprocess
12
import os
2-
from io import StringIO
33

44
from conan import ConanFile
55
from conan.errors import ConanException
@@ -23,8 +23,22 @@ def layout(self):
2323

2424
def test(self):
2525
if can_run(self):
26-
cmd = os.path.join(self.cpp.build.bindir, "example_module")
27-
stderr = StringIO()
28-
self.run(cmd, env='conanrun', stderr=stderr, ignore_errors=True)
29-
if "main failed with exception:" not in stderr.getvalue():
30-
raise ConanException("Unexpected error output from test")
26+
sock = "fake-socket-path"
27+
28+
cmd = os.path.join(self.cpp.build.bindir, f"example_module {sock}")
29+
30+
# the ConanFile run method is a wrapper around Popen, but it only returns the retcode.
31+
# A properly intialized module waits indefinitely on a signal, so we have to use Popen manually.
32+
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, text=True)
33+
34+
out = None
35+
36+
try:
37+
out = proc.communicate(timeout=2)[0]
38+
except subprocess.TimeoutExpired:
39+
proc.terminate()
40+
out = proc.communicate()[0]
41+
pass
42+
43+
if f"Module listening on {sock}" not in out:
44+
raise ConanException(f"Simple example failed to start module listening")

0 commit comments

Comments
 (0)