#2 - Docker building for older glibc
This commit was merged in pull request #4.
This commit is contained in:
+1
-4
@@ -12,8 +12,5 @@ cmake-build-debug/
|
||||
compile_commands.json
|
||||
*.md
|
||||
|
||||
# cloned by FetchContent at configure time
|
||||
external/glm/
|
||||
external/entt/
|
||||
external/jolt/
|
||||
# cloned by FetchContent into the build tree at configure time
|
||||
external/tracy/
|
||||
|
||||
@@ -2,6 +2,7 @@ build/
|
||||
Debug/
|
||||
Release/
|
||||
Testing/
|
||||
dist/
|
||||
.cache
|
||||
imgui.ini
|
||||
|
||||
|
||||
@@ -41,3 +41,17 @@ Server is at `build/modules/client/mmo_server`
|
||||
Client is at `build/modules/client/mmo_client`
|
||||
|
||||
First run the server, so that client can connect. The server will try to use port 8080, but if it is already occupied, it will try use the next free higher one.
|
||||
|
||||
## Docker build
|
||||
|
||||
It also possible to build the project using Docker. Advantage is that you do not have to set up the environment. The current Dockerfile also uses older linux distribution to be backwards compatible with older systems. To build everything, build the Dockerfile at `docker/Dockerfile` with until target `builder`:
|
||||
|
||||
```
|
||||
$ docker build -f docker/Dockerfile --target builder -t tw_builder:jammy .
|
||||
```
|
||||
|
||||
To easily export the built executables, run the target `export` of the same Dockerfile.
|
||||
|
||||
```
|
||||
$ docker build -f docker/Dockerfile --target export --output type=local,dest=dist .
|
||||
```
|
||||
|
||||
+29
-5
@@ -24,13 +24,37 @@ services:
|
||||
depends_on:
|
||||
- timescaledb
|
||||
|
||||
zone-server:
|
||||
build: .
|
||||
# The zone server is not containerised yet: it needs libpqxx >= 7.7 for
|
||||
# pqxx::params and jammy carries 6.4.
|
||||
|
||||
# ── e2e harness ────────────────────────────────────────────────────────────
|
||||
# Opt in with `--profile e2e`, so a plain `docker compose up` still brings up
|
||||
# the database and dashboards on their own.
|
||||
#
|
||||
# docker compose --profile e2e up --build \
|
||||
# --abort-on-container-exit --exit-code-from chat-mock-client
|
||||
chat-server:
|
||||
profiles: [e2e]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
target: chat-server
|
||||
ports:
|
||||
- "8101:8101/udp"
|
||||
- "8102:8102/udp"
|
||||
- "8099:8099/udp"
|
||||
|
||||
chat-mock-client:
|
||||
profiles: [e2e]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
target: chat-mock-client
|
||||
# Address.hpp parses peers with inet_addr, which takes literal IPv4 only and
|
||||
# never consults DNS, so a service name is not addressable. Sharing the
|
||||
# server's network namespace makes its default 127.0.0.1 land on the server.
|
||||
network_mode: "service:chat-server"
|
||||
depends_on:
|
||||
- timescaledb
|
||||
- chat-server
|
||||
command: ["--channel", "1"]
|
||||
|
||||
volumes:
|
||||
timescaledb_data:
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
#
|
||||
# Builds against Ubuntu 22.04 (glibc 2.35) so the binaries also run on
|
||||
# distributions older than the developer machine. Two things make that work:
|
||||
#
|
||||
# * GCC 14 from the toolchain PPA — jammy's own gcc-11 has no <print>, which
|
||||
# eight translation units use.
|
||||
# * -static-libstdc++ -static-libgcc — the target then needs nothing newer
|
||||
# than the glibc of this base image.
|
||||
#
|
||||
# docker build -f docker/Dockerfile --target mock-client -t tw_mock_client .
|
||||
# docker build -f docker/Dockerfile --target test .
|
||||
# docker build -f docker/Dockerfile --target export --output type=local,dest=dist .
|
||||
|
||||
ARG UBUNTU_VERSION=22.04
|
||||
|
||||
# ── toolchain stage ──────────────────────────────────────────────────────────
|
||||
FROM ubuntu:${UBUNTU_VERSION} AS toolchain
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# jammy's Vulkan headers are 1.3.204, which predates the VkBufferUsageFlags2
|
||||
# constants the renderer uses, and its glslang predates the -gVS the shader
|
||||
# target passes. LunarG publishes current headers, loader and glslang for jammy,
|
||||
# and outranks the distro packages on version.
|
||||
ARG VULKAN_SDK_VERSION=1.4.313
|
||||
|
||||
# The apt lists are kept: conan installs the xorg and egl system packages itself
|
||||
# while resolving SDL.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
software-properties-common ca-certificates gnupg wget \
|
||||
&& add-apt-repository -y ppa:ubuntu-toolchain-r/test \
|
||||
&& wget -qO /etc/apt/trusted.gpg.d/lunarg.asc \
|
||||
https://packages.lunarg.com/lunarg-signing-key-pub.asc \
|
||||
&& wget -qO /etc/apt/sources.list.d/lunarg-vulkan.list \
|
||||
"https://packages.lunarg.com/vulkan/${VULKAN_SDK_VERSION}/lunarg-vulkan-${VULKAN_SDK_VERSION}-jammy.list" \
|
||||
&& apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc-14 g++-14 \
|
||||
git make ninja-build ccache pkg-config \
|
||||
python3-pip \
|
||||
vulkan-headers libvulkan-dev glslang-tools \
|
||||
libdecor-0-dev
|
||||
|
||||
# cmake comes from pip because jammy ships 3.22 and the project asks for 3.26.
|
||||
ARG CMAKE_VERSION=4.4.0
|
||||
ARG CONAN_VERSION=2.31.1
|
||||
RUN pip3 install --no-cache-dir "cmake==${CMAKE_VERSION}" "conan==${CONAN_VERSION}"
|
||||
|
||||
# Some autotools dependencies (flex, by way of SDL) run sub-configures that look
|
||||
# for an unsuffixed gcc/cc and ignore $CC, so 14 has to answer to the plain names.
|
||||
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 \
|
||||
--slave /usr/bin/g++ g++ /usr/bin/g++-14 \
|
||||
--slave /usr/bin/gcov gcov /usr/bin/gcov-14 \
|
||||
&& update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 100 \
|
||||
&& update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-14 100
|
||||
|
||||
ENV CC=gcc
|
||||
ENV CXX=g++
|
||||
|
||||
COPY docker/conan/jammy /etc/conan/jammy
|
||||
|
||||
# ── dependency stage ─────────────────────────────────────────────────────────
|
||||
# Kept apart from the build so that editing sources does not rebuild SDL and
|
||||
# protobuf. The conan cache lives in the image layer rather than a cache mount,
|
||||
# so the generators in /deps can never outlive the packages they point at.
|
||||
FROM toolchain AS deps
|
||||
|
||||
COPY conanfile.txt /src/conanfile.txt
|
||||
|
||||
RUN conan install /src/conanfile.txt \
|
||||
--profile:all=/etc/conan/jammy \
|
||||
--build=missing \
|
||||
--output-folder=/deps
|
||||
|
||||
# ── build stage ──────────────────────────────────────────────────────────────
|
||||
# The build tree is a cache mount, so an edit rebuilds only what it touched.
|
||||
# That is also why glm, entt and Jolt have to travel in the build context: their
|
||||
# FetchContent SOURCE_DIRs point into external/, while the stamps that record
|
||||
# them as populated live in the cached build tree. Dropping them from the
|
||||
# context would leave the stamps pointing at empty directories.
|
||||
FROM deps AS builder
|
||||
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
|
||||
# tw_server is absent on purpose: it needs libpqxx >= 7.7 for pqxx::params and
|
||||
# jammy carries 6.4.
|
||||
RUN --mount=type=cache,target=/build \
|
||||
--mount=type=cache,target=/root/.ccache \
|
||||
cmake -S /src -B /build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/deps/conan_toolchain.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DUSE_MOLD_LINKER=OFF \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-static-libstdc++ -static-libgcc" \
|
||||
&& cmake --build /build --target \
|
||||
tw_client \
|
||||
tw_mock_client \
|
||||
tw_chat_server_exe \
|
||||
tw_chat_mock_client \
|
||||
tw_message_protocol_tests \
|
||||
tw_metrics_tests \
|
||||
tw_network_tests \
|
||||
tw_peer_to_peer_tests \
|
||||
&& mkdir -p /out/bin /out/shaders \
|
||||
&& cp /build/modules/client/tw_client \
|
||||
/build/modules/mock_client/tw_mock_client \
|
||||
/build/modules/chat_service/chat_server_exe/tw_chat_server_exe \
|
||||
/build/modules/chat_service/chat_service/tests/chat_mock_client/tw_chat_mock_client \
|
||||
/out/bin/ \
|
||||
&& cp /build/modules/client/shaders/*.spirv /out/shaders/
|
||||
|
||||
# ── unit test stage ──────────────────────────────────────────────────────────
|
||||
# Catch2 suites only. The process harnesses below need peers and a network, so
|
||||
# they run as compose services instead.
|
||||
FROM builder AS test
|
||||
|
||||
RUN --mount=type=cache,target=/build \
|
||||
ctest --test-dir /build --output-on-failure
|
||||
|
||||
# ── client export stage ──────────────────────────────────────────────────────
|
||||
# Not runnable as a container; the client needs a GPU and a display. Extract it:
|
||||
# docker build -f docker/Dockerfile --target export --output type=local,dest=dist .
|
||||
FROM scratch AS export
|
||||
|
||||
COPY --from=builder /out/bin/tw_client /tw_client
|
||||
COPY --from=builder /out/shaders/ /shaders/
|
||||
|
||||
# ── runtime stages ───────────────────────────────────────────────────────────
|
||||
FROM gcr.io/distroless/base-debian12:nonroot AS mock-client
|
||||
|
||||
COPY --from=builder /out/bin/tw_mock_client /usr/local/bin/
|
||||
ENTRYPOINT ["/usr/local/bin/tw_mock_client"]
|
||||
|
||||
|
||||
FROM gcr.io/distroless/base-debian12:nonroot AS chat-server
|
||||
|
||||
COPY --from=builder /out/bin/tw_chat_server_exe /usr/local/bin/
|
||||
EXPOSE 8101/udp
|
||||
ENTRYPOINT ["/usr/local/bin/tw_chat_server_exe"]
|
||||
|
||||
|
||||
FROM gcr.io/distroless/base-debian12:nonroot AS chat-mock-client
|
||||
|
||||
COPY --from=builder /out/bin/tw_chat_mock_client /usr/local/bin/
|
||||
ENTRYPOINT ["/usr/local/bin/tw_chat_mock_client"]
|
||||
@@ -0,0 +1,14 @@
|
||||
[settings]
|
||||
arch=x86_64
|
||||
build_type=Release
|
||||
compiler=gcc
|
||||
compiler.version=14
|
||||
compiler.cppstd=gnu23
|
||||
compiler.libcxx=libstdc++11
|
||||
os=Linux
|
||||
|
||||
[conf]
|
||||
# xorg/system and egl/system resolve to apt packages; the build runs as root so
|
||||
# no sudo is available or needed.
|
||||
tools.system.package_manager:mode=install
|
||||
tools.system.package_manager:sudo=False
|
||||
Vendored
-1
@@ -51,7 +51,6 @@ add_definitions(
|
||||
)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
|
||||
@@ -49,7 +49,9 @@ Instance create_instance(const std::string& name, const lft::win::Window* window
|
||||
// required_extensions.push_back(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
|
||||
|
||||
std::vector<std::string> required_layers = {
|
||||
#if DEBUG
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <fmt/format.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
|
||||
namespace tw::net {
|
||||
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
#include <barrier>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <span>
|
||||
#include <iostream>
|
||||
|
||||
#include "UdpStream.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "quicr/QuicrConnection.hpp"
|
||||
#include "quicr/QuicrConnectionListener.hpp"
|
||||
#include "quicr/QuicrEndpoint.hpp"
|
||||
|
||||
TEST_CASE("Start two sockets and send message", "[udp]") {
|
||||
std::barrier create_sync_point(2);
|
||||
std::barrier send_sync_point(2);
|
||||
|
||||
const std::string message = "Hello, server!";
|
||||
|
||||
std::thread server_thread([&]() {
|
||||
auto r = tw::net::UdpStream::bind(tw::net::Address {"127.0.0.1", 6969});
|
||||
if(!r.has_value()) {
|
||||
std::cout << ("Failed to bind UDP stream: {}") << r.error().message() << std::endl;
|
||||
}
|
||||
|
||||
auto flag_result = r->set_non_blocking();
|
||||
|
||||
auto server = std::move(r.value());
|
||||
|
||||
create_sync_point.arrive_and_wait();
|
||||
send_sync_point.arrive_and_wait();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
std::vector<std::byte> buffer(1024);
|
||||
tw::net::Address from {{}, 0};
|
||||
auto read_result = server.read_into(std::span{buffer.data(), buffer.size()}, &from);
|
||||
|
||||
if(!read_result.has_value()) {
|
||||
spdlog::error("Failed to read from UDP stream: {}", read_result.error().message());
|
||||
}
|
||||
|
||||
// convert the buffer to a string
|
||||
std::string mesg(buffer.data(), buffer.data() + read_result.value());
|
||||
REQUIRE(mesg == message);
|
||||
});
|
||||
|
||||
std::thread client_thread([&]() {
|
||||
create_sync_point.arrive_and_wait();
|
||||
|
||||
auto connect_result = tw::net::UdpStream::to(tw::net::Address{"127.0.0.1", 6969});
|
||||
if(!connect_result.has_value()) {
|
||||
spdlog::error("Failed to bind UDP stream: {}", connect_result.error().mesg());
|
||||
}
|
||||
|
||||
auto client = std::move(connect_result.value());
|
||||
|
||||
std::string mesg = message;
|
||||
auto write_result = client.write(std::as_writable_bytes(std::span(mesg.begin(), mesg.end())));
|
||||
|
||||
if(!write_result.has_value() && write_result.value() == mesg.length()) {
|
||||
spdlog::error("Failed to write to UDP stream: {}", write_result.error().message());
|
||||
}
|
||||
|
||||
send_sync_point.arrive_and_wait();
|
||||
});
|
||||
|
||||
server_thread.join();
|
||||
client_thread.join();
|
||||
}
|
||||
|
||||
using namespace tw::net;
|
||||
using namespace tw::net::quicr;
|
||||
// #include <barrier>
|
||||
// #include <catch2/catch_test_macros.hpp>
|
||||
// #include <span>
|
||||
// #include <iostream>
|
||||
//
|
||||
// #include "UdpStream.hpp"
|
||||
// #include "Address.hpp"
|
||||
// #include "quicr/QuicrConnection.hpp"
|
||||
// #include "quicr/QuicrConnectionListener.hpp"
|
||||
// #include "quicr/QuicrEndpoint.hpp"
|
||||
//
|
||||
// TEST_CASE("Start two sockets and send message", "[udp]") {
|
||||
// std::barrier create_sync_point(2);
|
||||
// std::barrier send_sync_point(2);
|
||||
//
|
||||
// const std::string message = "Hello, server!";
|
||||
//
|
||||
// std::thread server_thread([&]() {
|
||||
// auto r = tw::net::UdpStream::bind(tw::net::Address {"127.0.0.1", 6969});
|
||||
// if(!r.has_value()) {
|
||||
// std::cout << ("Failed to bind UDP stream: {}") << r.error().message() << std::endl;
|
||||
// }
|
||||
//
|
||||
// auto flag_result = r->set_non_blocking();
|
||||
//
|
||||
// auto server = std::move(r.value());
|
||||
//
|
||||
// create_sync_point.arrive_and_wait();
|
||||
// send_sync_point.arrive_and_wait();
|
||||
//
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
//
|
||||
// std::vector<std::byte> buffer(1024);
|
||||
// tw::net::Address from {{}, 0};
|
||||
// auto read_result = server.read_into(std::span{buffer.data(), buffer.size()}, &from);
|
||||
//
|
||||
// if(!read_result.has_value()) {
|
||||
// spdlog::error("Failed to read from UDP stream: {}", read_result.error().message());
|
||||
// }
|
||||
//
|
||||
// // convert the buffer to a string
|
||||
// std::string mesg(buffer.data(), buffer.data() + read_result.value());
|
||||
// REQUIRE(mesg == message);
|
||||
// });
|
||||
//
|
||||
// std::thread client_thread([&]() {
|
||||
// create_sync_point.arrive_and_wait();
|
||||
//
|
||||
// auto connect_result = tw::net::UdpStream::to(tw::net::Address{"127.0.0.1", 6969});
|
||||
// if(!connect_result.has_value()) {
|
||||
// spdlog::error("Failed to bind UDP stream: {}", connect_result.error().mesg());
|
||||
// }
|
||||
//
|
||||
// auto client = std::move(connect_result.value());
|
||||
//
|
||||
// std::string mesg = message;
|
||||
// auto write_result = client.write(std::as_writable_bytes(std::span(mesg.begin(), mesg.end())));
|
||||
//
|
||||
// if(!write_result.has_value() && write_result.value() == mesg.length()) {
|
||||
// spdlog::error("Failed to write to UDP stream: {}", write_result.error().message());
|
||||
// }
|
||||
//
|
||||
// send_sync_point.arrive_and_wait();
|
||||
// });
|
||||
//
|
||||
// server_thread.join();
|
||||
// client_thread.join();
|
||||
// }
|
||||
//
|
||||
// using namespace tw::net;
|
||||
// using namespace tw::net::quicr;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "Serialization.hpp"
|
||||
#include "Serializers.hpp"
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
# Build the toolchain stage on its own and pass it back in to skip the apt step:
|
||||
# docker build -f modules/server/Dockerfile --target toolchain -t tw_toolchain .
|
||||
# docker build -f modules/server/Dockerfile --build-arg TOOLCHAIN_IMAGE=tw_toolchain .
|
||||
ARG TOOLCHAIN_IMAGE=toolchain
|
||||
|
||||
# ── toolchain stage ──────────────────────────────────────────────────────────
|
||||
FROM ubuntu:24.04 AS toolchain
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
clang \
|
||||
libstdc++-14-dev \
|
||||
cmake \
|
||||
ninja-build \
|
||||
mold \
|
||||
git \
|
||||
ca-certificates \
|
||||
pkg-config \
|
||||
glslang-tools \
|
||||
libvulkan-dev \
|
||||
libsdl2-dev \
|
||||
libprotobuf-dev protobuf-compiler \
|
||||
libpq-dev \
|
||||
libpqxx-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV CC=clang
|
||||
ENV CXX=clang++
|
||||
|
||||
# ── build stage ──────────────────────────────────────────────────────────────
|
||||
FROM ${TOOLCHAIN_IMAGE} AS builder
|
||||
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
|
||||
# glm, entt, Jolt, spdlog, expected, Catch2 and tracy are cloned at configure time
|
||||
RUN cmake -B /build -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||
&& cmake --build /build --target tw_server
|
||||
|
||||
# Stage the shared libraries the binary was linked against; the runtime image has
|
||||
# no package manager. glibc and its loader stay behind, they come with that image.
|
||||
RUN mkdir -p /rootfs/usr/lib/x86_64-linux-gnu \
|
||||
&& ldd /build/modules/server/tw_server \
|
||||
| awk '/=> \//{ print $3 }' \
|
||||
| grep -vE '/(libc|libm|libdl|libpthread|librt|libresolv|libanl)\.so' \
|
||||
| xargs -I{} cp -L {} /rootfs/usr/lib/x86_64-linux-gnu/
|
||||
|
||||
# ── runtime stage ─────────────────────────────────────────────────────────────
|
||||
FROM gcr.io/distroless/cc-debian13:nonroot
|
||||
|
||||
COPY --from=builder /rootfs/ /
|
||||
COPY --from=builder /build/modules/server/tw_server /usr/local/bin/tw_server
|
||||
|
||||
# player connections, zone-server peering
|
||||
EXPOSE 8101/udp 8102/udp
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/tw_server"]
|
||||
Reference in New Issue
Block a user