Skip to content

Commit 6d147f4

Browse files
authored
Merge pull request #1823 from TheJJ/bump-cpp
add c++ module support (and c++26)
2 parents 8c7d630 + 7a17f63 commit 6d147f4

133 files changed

Lines changed: 2552 additions & 2074 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/macos.yml‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: macOS-CI
2+
on: [push, pull_request]
3+
4+
jobs:
5+
# https://docs.github.com/en/actions/reference/software-installed-on-github-hosted-runners
6+
# we install stuff not already there
7+
macos-build:
8+
runs-on: macos-latest
9+
env:
10+
UV_CACHE_DIR: ${{ github.workspace }}/../uv-cache
11+
steps:
12+
- name: Checkout sources
13+
uses: actions/checkout@v7
14+
15+
# ccache is incremental, so restore the newest entry and save an updated one per commit
16+
- name: Restore ccache dir
17+
uses: actions/cache/restore@v6
18+
id: cache-ccache
19+
with:
20+
path: ~/Library/Caches/ccache
21+
key: macos-ccache-${{ github.sha }}
22+
restore-keys: macos-ccache-
23+
# exact key on the workflow file: changes when the package list changes,
24+
# prefix fallback keeps the old downloads until then
25+
- name: Restore dependencies cache
26+
uses: actions/cache/restore@v6
27+
id: cache-deps
28+
with:
29+
path: |
30+
${{ env.UV_CACHE_DIR }}
31+
~/Library/Caches/Homebrew
32+
key: macos-deps-${{ hashFiles('.github/workflows/macos.yml', 'uv.lock') }}
33+
restore-keys: macos-deps-
34+
- name: Brew update-reset
35+
run: brew update-reset
36+
- name: Brew update
37+
run: brew update
38+
- name: Install dependencies
39+
# AppleClang is installed by default, but lacks C++ module support, see doc/build_instructions/macos.md
40+
run: |
41+
brew install --cask font-dejavu
42+
brew install ccache cmake eigen flex fontconfig freetype harfbuzz just libepoxy libogg libpng llvm make ninja opus opusfile python3 qt6 toml11 uv
43+
- name: Install Python dependencies
44+
run: uv sync --locked --python "$(brew --prefix)/bin/python3"
45+
- name: Configure
46+
# switch back to AppleClang once it supports C++ modules:
47+
# run: ./configure --compiler=clang --mode=release --ccache --download-nyan
48+
run: ./configure --compiler="$(brew --prefix llvm)/bin/clang++" --mode=release --ccache --download-nyan -- -DCMAKE_OSX_SYSROOT=macosx -DPython3_EXECUTABLE="$PWD/.venv/bin/python"
49+
- name: Build
50+
run: just build
51+
- name: Test
52+
run: just test
53+
# keep the caches even when the build or tests fail: the work done so far
54+
# is exactly what the next run wants to reuse
55+
- name: Save ccache dir
56+
if: always()
57+
uses: actions/cache/save@v6
58+
with:
59+
path: ~/Library/Caches/ccache
60+
key: ${{ steps.cache-ccache.outputs.cache-primary-key }}
61+
- name: Save dependencies cache
62+
if: always()
63+
uses: actions/cache/save@v6
64+
with:
65+
path: |
66+
${{ env.UV_CACHE_DIR }}
67+
~/Library/Caches/Homebrew
68+
key: ${{ steps.cache-deps.outputs.cache-primary-key }}

‎.github/workflows/macosx-ci.yml‎

Lines changed: 0 additions & 61 deletions
This file was deleted.

‎.github/workflows/ubuntu-26.04.yml‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
1-
name: Ubuntu 26.04 CI
1+
name: Ubuntu 26.10 CI
22

33
on: [push, workflow_dispatch, pull_request]
44

55
jobs:
66
build:
77
runs-on: ubuntu-26.04
88
steps:
9-
- uses: actions/checkout@v4
9+
- uses: actions/checkout@v7
10+
- name: Restore ccache
11+
id: ccache-cache
12+
uses: actions/cache/restore@v6
13+
with:
14+
path: /tmp/ccache
15+
key: ccache-ubuntu-${{ github.sha }}
16+
restore-keys: ccache-ubuntu-
1017
- name: Restore devenv image
1118
id: devenv-cache
12-
uses: actions/cache@v4
19+
uses: actions/cache@v6
1320
with:
1421
path: /tmp/staging/devenv.tar.zst
1522
# rebuild the image only when its recipe changes
16-
key: devenv-image-${{ hashFiles('packaging/docker/devenv/Dockerfile.ubuntu.2604') }}
23+
key: devenv-image-${{ hashFiles('packaging/docker/devenv/ubuntu.dockerfile') }}
1724
- name: Build the Docker image
1825
if: steps.devenv-cache.outputs.cache-hit != 'true'
19-
run: sudo DOCKER_BUILDKIT=1 docker build ./packaging/docker/devenv --file ./packaging/docker/devenv/Dockerfile.ubuntu.2604 --tag openage-devenv:latest
26+
run: sudo DOCKER_BUILDKIT=1 docker build ./packaging/docker/devenv --file ./packaging/docker/devenv/ubuntu.dockerfile --tag openage-devenv:latest
2027
shell: bash
2128
- name: Save the Docker image
2229
if: steps.devenv-cache.outputs.cache-hit != 'true'
@@ -29,9 +36,12 @@ jobs:
2936
run: sudo docker load --input /tmp/staging/devenv.tar.zst
3037
shell: bash
3138
- name: Build openage
39+
shell: bash
3240
run: |
33-
# the image may come from cache, so refresh its packages before building
34-
sudo docker run --rm -v "$(pwd)":/mnt/openage -w /mnt/openage openage-devenv:latest bash <<'EOF'
41+
# the image may come from cache, so refresh its packages before building;
42+
# /tmp/ccache is mounted in so compilations are cached across runs;
43+
# -i feeds the heredoc to the container's bash (without it, stdin is not attached)
44+
sudo docker run --rm -i -v "$(pwd)":/mnt/openage -v /tmp/ccache:/ccache -e CCACHE_DIR=/ccache -w /mnt/openage openage-devenv:latest bash <<'EOF'
3545
set -e
3646
3747
apt-get update
@@ -42,8 +52,10 @@ jobs:
4252
4353
cmake \
4454
-DCMAKE_BUILD_TYPE=Release \
45-
-DCMAKE_C_COMPILER=$(which gcc) \
46-
-DCMAKE_CXX_COMPILER=$(which g++) \
55+
-DCMAKE_C_COMPILER=$(which gcc-16) \
56+
-DCMAKE_CXX_COMPILER=$(which g++-16) \
57+
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
58+
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
4759
-DCMAKE_CXX_FLAGS='' \
4860
-DCMAKE_EXE_LINKER_FLAGS='' \
4961
-DCMAKE_INSTALL_PREFIX=/usr/local \
@@ -67,3 +79,11 @@ jobs:
6779
6880
./run test -a
6981
EOF
82+
- name: Save ccache
83+
# keep the cache even when the build or tests fail: the objects
84+
# compiled so far are exactly what the next run wants to reuse
85+
if: always()
86+
uses: actions/cache/save@v6
87+
with:
88+
path: /tmp/ccache
89+
key: ${{ steps.ccache-cache.outputs.cache-primary-key }}

‎AGENTS.md‎

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AGENTS.md
22

3-
openage is a free-software RTS engine recreating Age of Empires II: `libopenage/` is the C++20 engine, `openage/` the Python package (converter, codegen, nyan integration), glued together by Cython
3+
openage is a free-software RTS engine recreating Age of Empires II: `libopenage/` is the C++26 engine, `openage/` the Python package (converter, codegen, nyan integration), glued together by Cython
44

55
## Reference docs
66

@@ -11,25 +11,25 @@ Read the docs of the subsystem you touch; link to them instead of duplicating th
1111
- [doc/buildsystem.md](doc/buildsystem.md) — codegen → pxdgen → cythonize pipeline
1212
- [doc/code/architecture.md](doc/code/architecture.md) — subsystems and data flow, details in its sibling dirs (`event/`, `game_simulation/`, `input/`, `renderer/`, `pathfinding/`, `converter/`)
1313
- [doc/code/](doc/code/) — [curves](doc/code/curves.md), [exceptions](doc/code/exceptions.md), [logger](doc/code/logger.md), [pyinterface](doc/code/pyinterface.md), [testing](doc/code/testing.md)
14-
- [doc/code_style/](doc/code_style/) — C++ by example in [mom.h](doc/code_style/mom.h) and [mom.cpp](doc/code_style/mom.cpp), tabs indent and spaces align; Python is a PEP8 subset
14+
- [doc/code_style/](doc/code_style/) — C++ by example in [cat.cppm](doc/code_style/cat.cppm) and [cat.cpp](doc/code_style/cat.cpp), tabs indent and spaces align; Python is a PEP8 subset
1515
- [doc/nyan/](doc/nyan/), [doc/convert/](doc/convert/), [doc/media_convert.md](doc/media_convert.md) — game data and converter
1616
- [doc/development.md](doc/development.md) — code philosophy; [doc/contributing.md](doc/contributing.md) — PR workflow
1717

1818
## Build and verify
1919

20-
`./configure` creates the build dir in `.bin/`, symlinked as `bin/`; the root `Makefile` forwards there
20+
`./configure` creates the build dir in `.bin/`, symlinked as `bin/`; the root `justfile` forwards there
2121

2222
```sh
2323
./configure # e.g. --compiler=clang --mode=release --ccache --download-nyan
24-
make -j$(nproc) # build
25-
make run # run the game
26-
make tests # all C++/Python tests and doctests, as CI runs them
27-
make checkmerge # compliance checks, as CI runs them
28-
make doc # doxygen → bin/doc/html/index.html
24+
just build # build everything
25+
just run # run the game
26+
just tests # all C++/Python tests and doctests, as CI runs them
27+
just checkmerge # compliance checks, as CI runs them
28+
just doc # doxygen → bin/doc/html/index.html
2929
```
3030

3131
- Single test: `bin/run test NAME`, e.g. `openage::curve::tests::container`; `-l` lists all, `-d NAME ARGS` runs a demo, `-b NAME` a benchmark, `-a --have-assets` includes tests needing game assets
32-
- Checks on changed files only: `make checkuncommited` (sic; vs `HEAD`, new files count once `git add`ed) or `make checkchanged` (vs `origin/master`)
32+
- Checks on changed files only: `just checkuncommited` (sic; vs `HEAD`, new files count once `git add`ed) or `just checkchanged` (vs `origin/master`)
3333
- `checkfast`: authors, cppstyle, cython, headerguards, legal, filemodes, textfiles; `checkmerge` adds ruff (lint + format) and ty (type check), run through `uv run` (venv from pyproject.toml/uv.lock); `checkall` adds clang-tidy
3434
- Memory or threading changes: also run the tests in a build configured with `--sanitize=yes` (ASan+UBSan) or `--sanitize=thread`
3535
- CI also builds on macOS and Windows ([.github/workflows/](.github/workflows/)): keep code portable
@@ -48,11 +48,12 @@ make doc # doxygen → bin/doc/html/index.html
4848

4949
Non-obvious traps; add new ones here, verified and in one line
5050

51-
- In-source builds are forbidden and poison the tree; `make cleaninsourcebuild` removes the traces
52-
- Run Python only via `bin/run ...` or `make run`, never `python3 -m openage` in the source tree: compiled Cython modules and `openage/config.py` live in `bin/`
53-
- Never edit files in `bin/`, they are generated: `config.*` from `*.in`, `*.gen.{h,cpp}` from `openage/codegen/` and `*.template`, `libopenage` `.pxd` files from `pxd:` annotations in C++ headers; codegen trouble: `make cleancodegen`
54-
- nyan is a separate project: build it and point to it with `-Dnyan_DIR=`, or `./configure --download-nyan`
55-
- C++20, but the minimum compilers are gcc/clang 10 and MSVC 16.8 (`buildsystem/CheckCompilerFeatures.cmake`): check support before using newer library features like `std::format`
51+
- In-source builds are forbidden and poison the tree; `just cleaninsourcebuild` removes the traces
52+
- Run Python only via `bin/run ...` or `just run`, never `python3 -m openage` in the source tree: compiled Cython modules and `openage/config.py` live in `bin/`
53+
- Never edit files in `bin/`, they are generated: `config.*` from `*.in`, `*.gen.{h,cpp}` from `openage/codegen/` and `*.template`, `libopenage` `.pxd` files from `pxd:` annotations in C++ headers; codegen trouble: `just cleancodegen`
54+
- Reconfigure with `./configure` or `cmake -B .bin/<config>`, never through the symlink (`cmake bin`): the changed build dir path rebuilds everything
55+
- Put `import`s after the last `#include` of a file (the `cppstyle` check enforces it): GCC rejects textual definitions after an import of the same entities; standard headers are exempt because GCC builds translate them into the `<bits/stdc++.h>` header unit of [StdHeaderUnit.cmake](buildsystem/StdHeaderUnit.cmake)
56+
- C++26, but the minimum compilers are gcc 16 and clang 21 (`buildsystem/CheckCompilerFeatures.cmake`): check support before using newer library features like `std::format`
5657

5758
## Working
5859

@@ -65,8 +66,8 @@ Non-obvious traps; add new ones here, verified and in one line
6566
- Keep diffs focused: no drive-by refactors, reformatting, renames or "improvements" outside the task
6667
- Fail hard: throw/raise on unexpected state instead of skipping, logging and continuing, or returning a default
6768
- Trust the code over comments and docs; fix stale comments in code you touch
68-
- Done means it builds, the relevant tests pass and `make checkmerge` is clean; state what you could not verify, e.g. without game assets or GPU
69-
- Scratch files go to `/tmp`, never into the tree
69+
- Done means it builds, the relevant tests pass and `just checkmerge` is clean; state what you could not verify, e.g. without game assets or GPU
70+
- Scratch files go to `/tmp/openage-agent`, never into the tree
7071

7172
## Code
7273

@@ -83,9 +84,10 @@ Non-obvious traps; add new ones here, verified and in one line
8384

8485
## C++
8586

86-
- Style by example ([mom.h](doc/code_style/mom.h), [mom.cpp](doc/code_style/mom.cpp)): `this->` for members, `{}` initialization, braces on every block, Doxygen API docs in the header, forward declarations in headers, sorted include groups
87-
- Every `.h` has a `.cpp` that includes it first, even if otherwise empty; header guard `#pragma once`
88-
- Register new `.cpp` files and directories in the local `CMakeLists.txt` (`add_sources(libopenage ...)`, `add_subdirectory`)
87+
- Style by example ([cat.cppm](doc/code_style/cat.cppm), [cat.cpp](doc/code_style/cat.cpp)): `this->` for members, `{}` initialization, braces on every block, Doxygen API docs on the exported interface, sorted include groups
88+
- New code is a C++ module: usually one `.cpp` holding `export module openage.dir.name;` with interface and implementation (like `libopenage/util/color.cpp`); split off a `.cppm` interface unit only when needed; `#include`s go in the global module fragment, `import`s after the module declaration
89+
- Legacy headers: every `.h` has a `.cpp` that includes it first, even if otherwise empty; header guard `#pragma once`
90+
- Register new `.cpp` files and directories in the local `CMakeLists.txt` (`add_sources(libopenage ...)`, module units after `MODULE`, `add_subdirectory`)
8991
- Integer types per [inttypes.md](doc/code_style/inttypes.md)
9092
- RAII and smart pointers, no owning raw `new`/`delete`
9193
- Log with `log::log(INFO << ...)`, throw `Error{MSG(err) << ...}` ([exceptions.md](doc/code/exceptions.md)); no `std::cout`/`printf`, no bare `std::` exceptions
@@ -106,7 +108,7 @@ Write tests where they catch real bugs; the right kind depends on the subject:
106108
- Data structures, algorithms, math, curves, events, pathfinding: unit tests of invariants and edge cases (empty, boundaries, overflow, equal timestamps, queries exactly at, before and after keyframes)
107109
- Parsers and converter logic: small hand-made inputs; gate tests needing game assets on `has_assets` in the test list
108110
- Small pure Python functions: doctests, which double as documentation
109-
- Rendering, GUI, input wiring and other interactive code: usually no automated test; verify with a demo or `make run` and say so
111+
- Rendering, GUI, input wiring and other interactive code: usually no automated test; verify with a demo or `just run` and say so
110112
- Performance claims: measure with a benchmark (`bin/run test -b NAME`)
111113
- Bug fixes: a regression test that fails without the fix, where the subject is testable as above
112114
- Test observable behavior and contracts, not implementation details; no tests that restate the code or depend on timing, network or user interaction
@@ -121,14 +123,15 @@ Mechanics ([testing.md](doc/code/testing.md)):
121123
## Writing docs
122124

123125
- Update `doc/` when behavior, interfaces or file formats change
126+
- Write token-lean docs => write concisely and avoid verbosity
124127
- Write for the reader, not as a record of decisions or attempts
125128
- Markdown: one sentence per line
126129

127130
## Commits
128131

129132
- Message `subsystem: summary`, e.g. `convert: ...`, `renderer: ...`; the body explains what and why, concisely
130133
- One logical change per commit; squash fixups before review ([contributing.md](doc/contributing.md#rebasing))
131-
- New source files start with `// Copyright <year> the openage authors. See copying.md for legal info.` (`#` in Python and CMake) and have mode 0644; don't bump years in existing headers
134+
- New source files start with `// Copyright <year>+ the openage authors. See copying.md for legal info.` (`#` in Python and CMake) and have mode 0644; don't bump years in existing headers
132135
- Commit authors must be listed in [copying.md](copying.md) (authors check)
133136
- Never commit generated files, build output, converted assets or scratch files
134137
- Never push, force-push, open or comment on PRs without explicit consent

‎CMakeLists.txt‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2013-2023 the openage authors. See copying.md for legal info.
22

3-
# >=3.16 finding numpy with the findpython3 module
4-
cmake_minimum_required(VERSION 3.16)
3+
# >=3.28 for c++ module support
4+
cmake_minimum_required(VERSION 3.28)
55

66

77
# main build configuration file
@@ -34,7 +34,7 @@ If it doesn't, consider reporting the issue, or ask for help:
3434
project(openage CXX)
3535

3636
# C++ standard requirement
37-
set(CMAKE_CXX_STANDARD 20)
37+
set(CMAKE_CXX_STANDARD 26)
3838
set(CMAKE_CXX_STANDARD_REQUIRED ON)
3939

4040
# Python and Cython requirements
@@ -51,6 +51,7 @@ foreach(pol
5151
CMP0094 # take the first satisfying Python version
5252
CMP0082 # run add_subdirectory() in the declaration order
5353
CMP0102 # Don't create empty cache entries
54+
CMP0177 # normalize install() destination paths
5455
)
5556
if (POLICY ${pol})
5657
cmake_policy(SET ${pol} NEW)
@@ -133,8 +134,7 @@ if(ENABLE_CCACHE)
133134
find_program(CCACHE_FOUND "ccache")
134135

135136
if(CCACHE_FOUND)
136-
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
137-
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
137+
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_FOUND}")
138138
else()
139139
message(FATAL_ERROR "ccache not found, but you requested it")
140140
endif(CCACHE_FOUND)
@@ -177,6 +177,7 @@ include(HandlePythonOptions)
177177
include(CheckRuntimeDependencies)
178178
include(DetectProjectVersion)
179179
include(DependencyFetch)
180+
include(StdHeaderUnit)
180181
include(FindPackageHandleStandardArgs)
181182

182183
# include build configuration modules

0 commit comments

Comments
 (0)