Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Byte-exact test data: a CRLF checkout would change parser input,
# golden output, and fixture checksums.
tests/fixtures/** -text
tests/golden/** -text
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ body:
attributes:
label: Environment
placeholder: |
- spc-cpp: v0.4.1 (or commit)
- spc-cpp: vX.Y.Z or commit
- OS: Ubuntu 24.04 / macOS 26
- Compiler: GCC 13.3 / Clang 18 / Apple Clang 17
- libcurl: 8.5.0
Expand Down
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
if it were the whole buffer, and a view that ran to the end of its buffer
was read one byte past it. Clients are unaffected, since they parse the
strings they own.
- The `ErrorCode` comments in `error.hpp` now agree with the README: a bad
ArcGIS layer id is `InvalidRequest`, HTTP 503 is `RateLimited`, and
`ParseError` means the body was not JSON.

## [0.4.1] - 2026-09-25

Expand All @@ -54,8 +57,7 @@ uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Fixed

- On macOS, any `CMAKE_OSX_DEPLOYMENT_TARGET` below 26.0 failed to compile,
and a default build would not start on a Mac older than macOS 26.
- On macOS, any `CMAKE_OSX_DEPLOYMENT_TARGET` below 26.0 failed to compile.
Floating-point `std::from_chars` was used whenever libc++ was version 20 or
newer, but Apple's libc++ provides it only from macOS 26. Older targets now
use the stream parser, so the earliest supported macOS is 13.4.
Expand Down
16 changes: 10 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ git clone https://github.com/Reddimus/spc-cpp.git
cd spc-cpp

# Ubuntu 24.04 (its packaged CMake is too old)
sudo apt install build-essential clang-format libcurl4-openssl-dev pipx
pipx install cmake
# macOS (Xcode or its command-line tools provide Apple Clang and libcurl)
sudo apt install build-essential clang-format libcurl4-openssl-dev
sudo snap install cmake --classic
# macOS (Xcode or its command-line tools provide Apple Clang and libcurl).
# CI formats with clang-format 18; open a new shell after ensurepath.
brew install cmake pipx
pipx ensurepath
pipx install clang-format==18.1.8

make test
Expand Down Expand Up @@ -103,7 +105,9 @@ a case there, or a place in its list of fixtures no parser reads. When a
change to parsed output is intended, rewrite the files and review the diff:

```bash
cmake --build build
SPC_UPDATE_GOLDEN=1 ./build/tests/spc_tests --gtest_filter='Parsers/Golden.*'
git status --short tests/golden # new cases are untracked files
git diff tests/golden
```

Expand All @@ -116,8 +120,8 @@ build, and runs it. `BENCH_ARGS` passes flags through, for example
Each benchmark also reports the heap use of one call: `allocs`,
`alloc_bytes`, `peak_bytes`, and `retained_bytes`, the memory the result
keeps. They repeat exactly from run to run, so compare them directly. On
macOS, `instructions` per iteration varies by less than 0.1% between runs,
even on a busy machine, which makes it the steadiest measure of CPU work.
macOS, `instructions` per iteration moves by under 1% between runs, even on
a busy machine, which makes it the steadiest measure of CPU work.
Timings are noisy: to compare two builds, run each with repetitions and
random interleaving, then compare the results with Google Benchmark's
`compare.py`, which needs numpy and scipy:
Expand Down Expand Up @@ -146,7 +150,7 @@ python3 build-bench/_deps/benchmark-src/tools/compare.py benchmarks \
(CMake reads them from there), move `[Unreleased]` to
`[X.Y.Z] - YYYY-MM-DD` in `CHANGELOG.md`, and update the `GIT_TAG` and
`find_package` versions in `README.md`. `make test-consumers` fails if
the README's `find_package` version does not match.
either README version does not match.
2. After it merges, tag `main` and push the tag:
`git tag vX.Y.Z && git push origin vX.Y.Z`.
3. The release workflow checks that the tag matches `version.hpp`, runs
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ int main() {

## Add it to your project

- CMake 3.31 or newer. Ubuntu 24.04 ships 3.28, so run `pipx install cmake`.
- CMake 3.31 or newer. Ubuntu 24.04 ships 3.28, so run
`sudo snap install cmake --classic`.
- GCC 13+, Clang 18+ with libc++ or 19+ with libstdc++, or Apple Clang 17+.
- libcurl 7.85 or newer with its headers: `libcurl4-openssl-dev` on Ubuntu,
built into macOS.
Expand Down
8 changes: 5 additions & 3 deletions include/spc/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ enum class ErrorCode {
NetworkError,
/// HTTP 429 or 503, or the SDK's own rate limit.
RateLimited,
/// HTTP 5xx, or an ArcGIS query that never finished paging.
/// HTTP 5xx other than 503, or an ArcGIS query that never finished paging.
ServerError,
/// A real fault: a bad URL, a retired product, or a wrong ArcGIS layer.
/// A URL or ArcGIS service that no longer exists: a bug to report. A bad
/// layer id is `InvalidRequest`.
NotFound,
/// SPC has not issued this product right now; its static feeds answer
/// HTTP 404. This is normal (day 1 probabilities overnight, for example),
Expand All @@ -29,7 +30,8 @@ enum class ErrorCode {
/// HTTP 400, an ArcGIS error such as an invalid parameter, or a response
/// larger than `ClientConfig::max_response_bytes`.
InvalidRequest,
/// The response was not the expected JSON.
/// The body was not valid JSON. Valid JSON without the expected fields
/// parses to an empty result.
ParseError,
Unknown
};
Expand Down
6 changes: 6 additions & 0 deletions tests/consumer/fetchcontent/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ endif()

add_executable(spc_fetchcontent_consumer ../main.cpp)
target_link_libraries(spc_fetchcontent_consumer PRIVATE spc::spc)

# tools/test_consumers.sh passes the README's quick start, to compile only.
if(SPC_QUICK_START)
add_executable(spc_quick_start "${SPC_QUICK_START}")
target_link_libraries(spc_quick_start PRIVATE spc::spc)
endif()
26 changes: 22 additions & 4 deletions tools/test_consumers.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
#!/usr/bin/env bash
# Build and run a tiny consumer against an installed spc-cpp and against a
# FetchContent checkout. The installed consumer asks for the version the
# README's find_package() line documents.
# README's find_package() line documents, and the FetchContent build also
# compiles the README's quick start. The README must pin the current version.
set -euo pipefail

source_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
scratch_dir=$(mktemp -d "${TMPDIR:-/tmp}/spc-consumer.XXXXXX")
trap 'rm -rf "$scratch_dir"' EXIT
readme="$source_dir/README.md"

documented_version=$(sed -nE 's/^find_package\(spc ([0-9.]+) REQUIRED\)$/\1/p' "$source_dir/README.md")
documented_version=$(sed -nE 's/^find_package\(spc ([0-9.]+) REQUIRED\)$/\1/p' "$readme")
if [ -z "$documented_version" ]; then
echo "README.md has no 'find_package(spc X.Y REQUIRED)' line" >&2
exit 1
fi

version=$(sed -nE 's/^#define SPC_VERSION_(MAJOR|MINOR|PATCH) ([0-9]+)$/\2/p' \
"$source_dir/include/spc/version.hpp" | paste -sd . -)
if ! grep -qx " GIT_TAG v$version" "$readme"; then
echo "README.md's FetchContent snippet must pin GIT_TAG v$version" >&2
exit 1
fi

# The first C++ block, the quick start. It needs the network, so it only
# compiles.
quick_start="$scratch_dir/quick_start.cpp"
awk '/^```cpp$/ { inside = 1; next } inside && /^```$/ { exit } inside' "$readme" >"$quick_start"
if ! grep -q 'int main' "$quick_start"; then
echo "README.md's first C++ block is not a whole program" >&2
exit 1
fi

cmake -S "$source_dir" -B "$scratch_dir/sdk" -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$scratch_dir/prefix" -DSPC_BUILD_TESTS=OFF -DSPC_BUILD_EXAMPLES=OFF
cmake --build "$scratch_dir/sdk" --parallel
Expand All @@ -30,8 +48,8 @@ cmake --build "$scratch_dir/installed" --parallel
"$scratch_dir/installed/spc_installed_consumer"

cmake -S "$source_dir/tests/consumer/fetchcontent" -B "$scratch_dir/fetchcontent" \
-DSPC_SOURCE_DIR="$source_dir"
-DSPC_SOURCE_DIR="$source_dir" -DSPC_QUICK_START="$quick_start"
cmake --build "$scratch_dir/fetchcontent" --parallel
"$scratch_dir/fetchcontent/spc_fetchcontent_consumer"

echo "consumer checks passed (find_package(spc $documented_version))"
echo "consumer checks passed (v$version, find_package(spc $documented_version))"
Loading