From 6cf9975e8478e59e6297b6cbaed9afe144a7308c Mon Sep 17 00:00:00 2001 From: talksik Date: Tue, 15 Jul 2025 08:46:45 -0700 Subject: [PATCH] add example with docs --- .gitignore | 2 ++ CMakeLists.txt | 39 ++++++++++++++++++++++++++++++ README.md | 39 ++++++++++++++++++++++++++++++ build.sh | 9 +++++++ cross-compile-for-ios.sh | 24 ++++++++++++++++++ fib.cpp | 18 ++++++++++++++ fib.h | 6 +++++ protocol/messenger/messenger.proto | 13 ++++++++++ vcpkg.json | 8 ++++++ 9 files changed, 158 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100755 build.sh create mode 100755 cross-compile-for-ios.sh create mode 100644 fib.cpp create mode 100644 fib.h create mode 100644 protocol/messenger/messenger.proto create mode 100644 vcpkg.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9785597 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c266b39 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.15) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +project(core CXX) + +find_package(fmt REQUIRED) +find_package(range-v3 REQUIRED) +find_package(cxxopts REQUIRED) +find_package(Protobuf CONFIG REQUIRED) + +set(CMAKE_CXX_STANDARD 17) + +add_library(core STATIC + "${CMAKE_CURRENT_SOURCE_DIR}/protocol/messenger/messenger.proto" + fib.cpp +) + +set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +target_include_directories(core + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + "$" +) + +target_link_libraries(core + PRIVATE + fmt::fmt + range-v3::range-v3 + cxxopts::cxxopts + PUBLIC + protobuf::libprotobuf +) + +protobuf_generate( + TARGET core + IMPORT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/protocol" + PROTOC_OUT_DIR "${PROTO_BINARY_DIR}" +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..a2d7210 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# Overview +This repository serves as a complex example of how C++ tooling and cross-compilation works. + +## The challenge +The variables at play: +* Protoc used to generate C++ files +* Compilation of the generated C++ files & other library/source files. +* Linking the correct protobuf runtime library for the target machine. + +`protoc` from any host machine can generate portable C++ files (.cpp and .h). However, the version of protoc that is used must match the _protobuf runtime library_ that is used. + +For example, the generated header will have something like this: +```cpp +#include "google/protobuf/runtime_version.h" +#if PROTOBUF_VERSION != 5029003 +#error "Protobuf C++ gencode is built with an incompatible version of" +#error "Protobuf C++ headers/runtime. See" +#error "https://protobuf.dev/support/cross-version-runtime-guarantee/#cpp" +#endif +``` + +If we are trying to build an application for arm64 from a x86_64 machine, the protobuf runtime library that we use _must_ be for that target (AND still match the protoc version that we used to generate the C++ files from `.proto`). This is why we use vcpkg. + + +See [here](https://claude.ai/share/c6c6394d-b9b4-41b4-9146-265682223174) for in-depth conversation with claude. + + +## The solution +```sh +./build.sh # build for applications meant only for the host machine +./cross-compile-for-ios.sh # uses ios/arm64 toolchain to compile all protoc generated C++ as well as other library files. More importantly, it also ensures that it compiles (or pulls from vcpkg cache) the correct protobuf runtime version for arm64. +``` + +Please look into the scripts to see how it all comes together. + +## Troubleshooting +I had an issue with vcpkg whereby the protobuf was conflicting between host machine one and the target machine one. So I had to add the cmake flag `-DVCPKG_BINARY_SOURCES=clear`. + +See [this issue](https://github.com/microsoft/vcpkg/issues/44517#issuecomment-2816462459) that I posted. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..c0c795d --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash -e + +rm -rf build +cmake -Bbuild -S. -GNinja \ + -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \ + -DVCPKG_BINARY_SOURCES=clear \ + -DVCPKG_INSTALL_OPTIONS="--debug" + +cmake --build build/ diff --git a/cross-compile-for-ios.sh b/cross-compile-for-ios.sh new file mode 100755 index 0000000..2b740e6 --- /dev/null +++ b/cross-compile-for-ios.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e + +echo "THIS SERVES AS AN EXAMPLE OF HOW TO CROSS COMPILE THIS FOR USE ON IOS" + +# NOTE: DVCPKG_TARGET_ARCHITECTURE is for a setting within the set DVCPKG_CHAINLOAD_TOOLCHAIN_FILE +# if using another toolchain or none at all, consider using the following native cmake variables +# - CMAKE_OSX_SYSROOT +# - CMAKE_OSX_DEPLOYMENT_TARGET +# - CMAKE_OSX_ARCHITECTURES +# - MACOSX_BUNDLE_GUI_IDENTIFIER +# - ... +# Qt has toolchain files that take care of these sorts of things, as does `vcpkg` as shown below + +rm -rf build +cmake -Bbuild -S. -GNinja \ + -DVCPKG_TARGET_TRIPLET=arm64-ios \ + -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \ + -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/toolchains/ios.cmake \ + -DVCPKG_TARGET_ARCHITECTURE=arm64 \ + -DVCPKG_BINARY_SOURCES=clear \ + -DVCPKG_INSTALL_OPTIONS="--debug" + + +cmake --build build/ diff --git a/fib.cpp b/fib.cpp new file mode 100644 index 0000000..9abb98a --- /dev/null +++ b/fib.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +namespace view = ranges::views; + +int fib(int x) { + int a = 0, b = 1; + + for (int it : view::repeat(0) | view::take(x)) { + (void)it; + int tmp = a; + a += b; + b = tmp; + } + + return a; +} diff --git a/fib.h b/fib.h new file mode 100644 index 0000000..32d5852 --- /dev/null +++ b/fib.h @@ -0,0 +1,6 @@ +#ifndef FIB_H +#define FIB_H + +int fib(int x); + +#endif // FIB_H diff --git a/protocol/messenger/messenger.proto b/protocol/messenger/messenger.proto new file mode 100644 index 0000000..a4058eb --- /dev/null +++ b/protocol/messenger/messenger.proto @@ -0,0 +1,13 @@ +syntax="proto3"; +package messenger; + +service HelloService { + rpc SayHello(SayHelloRequest) returns (SayHelloResponse) {} +} + +message SayHelloRequest { + string name = 1; +} +message SayHelloResponse { + string message = 1; +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..b908860 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,8 @@ +{ + "dependencies": [ + "cxxopts", + "fmt", + "range-v3", + "protobuf" + ] +}