Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
dc07e43
fix: Update C2PA version to 0.71.2 (#110)
tmathern Nov 14, 2025
40b91c9
fix: Version bump
tmathern Nov 20, 2025
ae61811
ci: Merge branch 'main' into vNext
tmathern Dec 1, 2025
0f8056c
Merge branch 'main' into vNext
tmathern Dec 2, 2025
0555506
Merge branch 'main' into vNext
tmathern Dec 2, 2025
1d182b2
chore: Update c2pa version to v0.72.1 (#115)
tmathern Dec 5, 2025
0e5e385
chore: Bump to c2pa-rs v0.73.0 (#116)
tmathern Dec 9, 2025
8190fa4
ci: Merge branch 'main' into vNext
tmathern Dec 16, 2025
f83191c
ci: Merge remote-tracking branch 'refs/remotes/origin/vNext' into vNext
tmathern Dec 16, 2025
000323c
chore: Update C2PA version to 0.73.1 (#117)
tmathern Dec 20, 2025
6dd299c
chore: Update C2PA version to 0.73.2 (#121)
tmathern Jan 5, 2026
1518622
Merge branch 'main' into vNext
tmathern Jan 6, 2026
9d9c055
Merge branch 'main' into vNext
tmathern Jan 7, 2026
550a083
fix: Update for move semantics (#123)
tmathern Jan 7, 2026
5b3ecd2
fix: Update for move semantics continu'd (#124)
tmathern Jan 9, 2026
f34b4d7
chore: Bump C2PA version to 0.74.0 (#125)
tmathern Jan 9, 2026
1a87403
fix: formatting in documentation comments (#126)
tmathern Jan 15, 2026
945c5da
chore: Update C2PA version to 0.75.0
tmathern Jan 15, 2026
9282940
chore: Update C2PA_VERSION to 0.75.2
tmathern Jan 15, 2026
43e18a8
Revert version bump until all things are published
tmathern Jan 15, 2026
5c3c2fa
Update C2PA version to 0.75.2 (#127)
tmathern Jan 15, 2026
85fcdd7
fix: Update C2PA version to 0.75.4 (#128)
tmathern Jan 17, 2026
ac15a27
chore: Update C2PA version to 0.75.6 (#129)
tmathern Jan 26, 2026
db5265e
fix: Update C2PA version to 0.75.7 (#130)
tmathern Jan 27, 2026
8ce6244
fix: Update C2PA version to 0.75.8
tmathern Jan 28, 2026
f66e567
feat: Allow building from source in CMakelist (#131)
tmathern Feb 2, 2026
ac9170c
chore: Bump c2pa-rs version to c2pa-v0.75.10
tmathern Feb 2, 2026
5a6efed
fix: Bump to c2pa-rs v0.75.16 (#140)
tmathern Feb 5, 2026
f723a08
fix: WinARM runner (#141)
tmathern Feb 6, 2026
5c85a14
Update CMakeLists.txt
tmathern Feb 6, 2026
b9ba075
fix: Bump to c2pa-rs v0.75.19
tmathern Feb 9, 2026
8480bbc
feat: Context APIs and the related Settings API (#137)
tmathern Feb 11, 2026
7ca21aa
fix: Clarifying comment
tmathern Feb 11, 2026
6a101aa
chore: Bump to c2pa-v0.75.21 (#152)
tmathern Feb 13, 2026
5f616e0
fix: Header docs (#150)
tmathern Feb 15, 2026
450cde4
Update c2pa.hpp
tmathern Feb 19, 2026
d815e8a
Update test.c
tmathern Feb 19, 2026
7640028
fix: Docs
Feb 20, 2026
42fba0f
ci: Merge remote-tracking branch 'refs/remotes/origin/vNext' into vNext
Feb 20, 2026
6d330fa
fix: Docs does not belong here
Feb 20, 2026
4708a3b
chore: Update C2PA version to 0.76.0 (#163)
tmathern Feb 24, 2026
3384873
fix: Builder::add_ingredient may fail with non-ascii characters in so…
tmathern Feb 25, 2026
6ee698e
docs: Context and Settings (#153)
tmathern Feb 26, 2026
9c6b2df
fix: Add a file size limit for settings file read (#162)
tmathern Feb 26, 2026
afe4dba
fix: Refactor (#165)
tmathern Feb 26, 2026
34ca574
docs: Working stores (#154)
tmathern Feb 27, 2026
b3623c7
ci: Merge branch 'main' into vNext
Feb 27, 2026
9dde5ab
docs: Re-Builder (#159)
tmathern Feb 27, 2026
71fab27
docs: Add draft release notes, some minor edits to other docs (#169)
crandmck Mar 3, 2026
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
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
os: [windows-latest, windows-11-arm, macos-latest, macos-15-intel, ubuntu-latest, ubuntu-22.04, ubuntu-22.04-arm, ubuntu-24.04-arm]

steps:
- name: Checkout repository
Expand All @@ -46,5 +46,10 @@ jobs:
run: |
which make
make --version
- name: Run tests

- name: Run tests (debug build)
run: make test

- name: Run tests (debug build, with sanitizers)
if: runner.os != 'Windows'
run: make test-san
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/build
/include/c2pa.h
/target

_deps
# CMake files
Expand All @@ -16,4 +16,4 @@ CMakeLists.txt.user
.vscode
# Mac OS X files
.DS_Store
.idea
.idea
66 changes: 62 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
cmake_minimum_required(VERSION 3.27)

# This is the version of this C++ project
project(c2pa-c VERSION 0.12.1)
project(c2pa-c VERSION 0.13.0)

# Set the version of the c2pa_rs library used here
set(C2PA_VERSION "0.66.0")
# Set the version of the c2pa_rs library used
set(C2PA_VERSION "0.76.0")

set(CMAKE_POLICY_DEFAULT_CMP0135 NEW)
set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.3")

if(MSVC)
Expand All @@ -46,6 +46,64 @@ else()
add_compile_options(-Wall -Wextra -Werror)
endif()

# Sanitizers (for debug/verification builds)
option(ENABLE_SANITIZERS "Enable AddressSanitizer, UndefinedBehaviorSanitizer, and LeakSanitizer" OFF)
option(ENABLE_MSAN "Enable MemorySanitizer (mutually exclusive with ASAN)" OFF)

if(ENABLE_SANITIZERS AND ENABLE_MSAN)
message(FATAL_ERROR "ENABLE_SANITIZERS and ENABLE_MSAN are mutually exclusive - choose one")
endif()

if(ENABLE_SANITIZERS)
if(MSVC)
message(WARNING "Sanitizers are not supported with MSVC in this configuration")
elseif(WIN32)
# MinGW on Windows doesn't have sanitizer libraries available
message(WARNING "Sanitizers are not available on Windows with MinGW - skipping sanitizer build")
else()
# AddressSanitizer, UndefinedBehaviorSanitizer, and LeakSanitizer for GCC/Clang
# Note: LeakSanitizer is not supported on macOS (both x86_64 and ARM64)
# On Linux, LeakSanitizer is integrated with AddressSanitizer
if(APPLE)
add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer -g)
add_link_options(-fsanitize=address,undefined)
message(STATUS "Sanitizers enabled on macOS: ASAN and UBSAN (LSan not supported on macOS)")
else()
add_compile_options(-fsanitize=address,undefined,leak -fno-omit-frame-pointer -g)
add_link_options(-fsanitize=address,undefined,leak)
message(STATUS "Sanitizers enabled: ASAN, UBSAN, and LSan")
endif()
# Allow macro redefinition for _FORTIFY_SOURCE which conflicts with sanitizers
add_compile_options(-Wno-macro-redefined)
endif()
endif()

if(ENABLE_MSAN)
if(NOT MSVC)
# MemorySanitizer for detecting uninitialized memory reads
# WARNING: Requires all dependencies to be built with MSan
add_compile_options(-fsanitize=memory -fno-omit-frame-pointer -g)
add_compile_options(-Wno-macro-redefined)
add_link_options(-fsanitize=memory)
message(STATUS "MemorySanitizer enabled (MSan)")
else()
message(WARNING "MemorySanitizer is not supported with MSVC")
endif()
endif()

# Code coverage instrumentation
option(ENABLE_COVERAGE "Enable code coverage instrumentation" OFF)

if(ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
add_compile_options(--coverage -fprofile-arcs -ftest-coverage)
add_link_options(--coverage)
message(STATUS "Code coverage enabled")
else()
message(WARNING "Code coverage is only supported with GCC or Clang")
endif()
endif()

enable_testing()

ADD_SUBDIRECTORY(src)
Expand Down
99 changes: 89 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,112 @@ BUILD_DIR = build
DEBUG_BUILD_DIR = build/debug
RELEASE_BUILD_DIR = build/release

# CMake options (can be overridden via environment)
CMAKE_OPTS :=
ifdef C2PA_BUILD_FROM_SOURCE
CMAKE_OPTS += -DC2PA_BUILD_FROM_SOURCE=$(C2PA_BUILD_FROM_SOURCE)
endif
ifdef C2PA_RS_PATH
CMAKE_OPTS += -DC2PA_RS_PATH=$(C2PA_RS_PATH)
endif

# Default target
all: test examples
all: clean test examples

clean:
rm -rf $(BUILD_DIR)

# Debug build
debug:
cmake -S . -B $(DEBUG_BUILD_DIR) -G "Ninja" -DCMAKE_BUILD_TYPE=Debug
cmake -S . -B $(DEBUG_BUILD_DIR) -G "Ninja" -DCMAKE_BUILD_TYPE=Debug $(CMAKE_OPTS)
cmake --build $(DEBUG_BUILD_DIR)

# Release build
release:
cmake -S . -B $(RELEASE_BUILD_DIR) -G "Ninja" -DCMAKE_BUILD_TYPE=Release
cmake -S . -B $(RELEASE_BUILD_DIR) -G "Ninja" -DCMAKE_BUILD_TYPE=Release $(CMAKE_OPTS)
cmake --build $(RELEASE_BUILD_DIR)

# Legacy cmake target (uses release build)
cmake: release

# Test targets
test: debug
test: clean debug
cd $(DEBUG_BUILD_DIR) && ctest --output-on-failure

test-release: release
test-release: clean release
cd $(RELEASE_BUILD_DIR) && ctest --output-on-failure

# Test with sanitizers (ASAN + UBSAN)
test-san: clean
cmake -S . -B $(DEBUG_BUILD_DIR) -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DENABLE_SANITIZERS=ON $(CMAKE_OPTS)
cmake --build $(DEBUG_BUILD_DIR)
cd $(DEBUG_BUILD_DIR) && ctest --output-on-failure

# Run only the C test (test.c)
test-c: clean release
@echo "Running C test only..."
ifeq ($(OS),Darwin)
DYLD_LIBRARY_PATH=$(RELEASE_BUILD_DIR)/tests:$$DYLD_LIBRARY_PATH ./$(RELEASE_BUILD_DIR)/tests/ctest
else
LD_LIBRARY_PATH=$(RELEASE_BUILD_DIR)/tests:$$LD_LIBRARY_PATH ./$(RELEASE_BUILD_DIR)/tests/ctest
endif

# Run only the C++ tests
test-cpp: clean release
@echo "Running C++ tests only..."
ifeq ($(OS),Darwin)
DYLD_LIBRARY_PATH=$(RELEASE_BUILD_DIR)/tests:$$DYLD_LIBRARY_PATH ./$(RELEASE_BUILD_DIR)/tests/c2pa_c_tests
else
LD_LIBRARY_PATH=$(RELEASE_BUILD_DIR)/tests:$$LD_LIBRARY_PATH ./$(RELEASE_BUILD_DIR)/tests/c2pa_c_tests
endif

# Run a single test by fully qualified name
# Like this: make run-single-test TEST=BuilderTest.MergingBuildersThenSignMerged
run-single-test: release
@if [ -z "$(TEST)" ]; then echo "Usage: make run-single-test TEST=SuiteName.TestName"; exit 1; fi
@echo "Running single test: $(TEST)"
ifeq ($(OS),Darwin)
DYLD_LIBRARY_PATH=$(RELEASE_BUILD_DIR)/tests:$$DYLD_LIBRARY_PATH ./$(RELEASE_BUILD_DIR)/tests/c2pa_c_tests --gtest_filter="$(TEST)"
else
LD_LIBRARY_PATH=$(RELEASE_BUILD_DIR)/tests:$$LD_LIBRARY_PATH ./$(RELEASE_BUILD_DIR)/tests/c2pa_c_tests --gtest_filter="$(TEST)"
endif

# Test with coverage reporting
# THis verifies necessary tooling for coverage check is also installed
test-coverage: clean
cmake -S . -B build/coverage -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON $(CMAKE_OPTS)
cmake --build build/coverage
cd build/coverage && ctest --output-on-failure
@echo ""
@echo "Generating coverage report..."
@lcov --capture --directory build/coverage --output-file build/coverage/coverage.info \
--ignore-errors mismatch,inconsistent,unsupported,format 2>&1 \
| grep -v "WARNING:" || { echo "Error: lcov capture failed"; exit 1; }
@lcov --remove build/coverage/coverage.info \
'/usr/*' '*/googletest/*' '*/json-src/*' '*/c2pa_prebuilt-src/*' '*/tests/*' \
--output-file build/coverage/coverage_filtered.info \
--ignore-errors unused,mismatch,inconsistent,format 2>&1 \
| grep -v "WARNING:" || { echo "Error: lcov filter failed"; exit 1; }
@echo ""
@echo "=== Coverage Summary ==="
@lcov --summary build/coverage/coverage_filtered.info \
--ignore-errors inconsistent,format 2>&1 \
| grep -E "(lines|functions|branches)" || true
@echo "========================"
@echo ""
@if command -v genhtml > /dev/null 2>&1; then \
genhtml build/coverage/coverage_filtered.info --output-directory build/coverage/html \
--ignore-errors inconsistent,corrupt,unsupported,format,category 2>&1 \
| grep -v "WARNING:" || true; \
if [ -f build/coverage/html/index.html ]; then \
echo "HTML report: build/coverage/html/index.html"; \
else \
echo "Warning: HTML report was not generated (genhtml may have failed)"; \
fi; \
else \
echo "Note: genhtml not found, skipping HTML report (install lcov for HTML reports)"; \
fi

# Demo targets
demo: release
cmake --build $(RELEASE_BUILD_DIR) --target demo
Expand All @@ -39,12 +122,8 @@ training: release

examples: training demo

clean:
rm -rf $(BUILD_DIR)

.PHONY: all debug release cmake test test-release demo training examples clean
.PHONY: all debug release cmake test test-release test-san test-c test-cpp test-coverage demo training examples clean

# Build C API docs with Doxygen
docs:
./scripts/generate_api_docs.sh

64 changes: 47 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ Although this library works for plain C applications, the documentation assumes
<div class="hide-doxygen" >
<div style={{display: 'none'}}>

For the best experience, read the docs on the [CAI Open Source SDK documentation website](https://opensource.contentauthenticity.org/docs/c2pa-c). If you want to view the documentation in GitHub, see:
For the best experience, read the docs on the [CAI Open Source SDK documentation website](https://opensource.contentauthenticity.org/docs/c2pa-c).

If you want to view the documentation in GitHub, see:
- [Using the C++ library](docs/usage.md)
- [Supported formats](https://github.com/contentauth/c2pa-rs/blob/crandmck/reorg-docs/docs/supported-formats.md)
- [Supported formats](https://github.com/contentauth/c2pa-rs/blob/main/docs/supported-formats.md)
- Configuring the SDK using [`Settings`](settings.md).
- [Using `Context`](context.md) to configure `Reader` `Builder`, and other aspects of the SDK.
- Using [working stores and archvies](working-stores.md).
- Using `Builder` and `Reader` together to [selectively construct a manifest by filtering actions and ingredients](selective-manifests.md).
- [Release notes](release-notes.md)

</div>
</div>

## Using c2pa_cpp
## Using c2pa_cpp

The recommended way to use this library in your own CMake project is with [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html):

Expand All @@ -34,45 +41,70 @@ add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE c2pa_cpp)
```

This will automatically fetch, build, and link the `c2pa_cpp` library and its dependencies.
This will automatically fetch, build, and link the `c2pa_cpp` library and its dependencies.

> **Note:**
> **Note:**
> This project uses pre-built dynamic libraries from the [c2pa-rs](https://github.com/contentauth/c2pa-rs) repository. It should select the correct library for your platform. If your platform is not supported, you can build your own library using the c2pa_rs repo.

### Example usage

See the [`examples/`](examples/) directory for sample applications that demonstrate how to use the library in practice.
See the [`examples/`](examples/) directory for sample applications that demonstrate how to use the library in practice.

## Development

This project has been tested on macOS and should also work on common Linux distributions.

You must install the [Ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) build system to run the unit tests.

You must install the [Ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) build system to run the unit tests.

### Building
### Building using pre-built C FFI libraries

Building the library requires [GNU make](https://www.gnu.org/software/make/), which is installed on most macOS systems.
Building the library holding the C++ SDK requires [GNU make](https://www.gnu.org/software/make/), which is installed on most macOS systems.

Enter this command to build the C library:
Enter this command to build the SDK:

```
make release
```

This will download the [pre-build libraries published with c2pa releases](https://github.com/contentauth/c2pa-rs/releases), build and link the C++ code.

The Makefile has a number of other targets; for example:
- `unit-tests` to run C++ unit tests
- `test` to run unit tests
- `examples` to build and run the C++ examples.
- `all` to run everything.
- `all` to build and run everything.

Results are saved in the `build` directory.

### Building using local sources

This project can also be built entirely from source (without pre-built library download), with the pre-requisite that you will also need [c2pa-rs](https://github.com/contentauth/c2pa-rs) on the local machine, as well as the [Rust toolchain](https://rust-lang.org/tools/install/).

To build in this case, the build scripts need to be able to locate the `c2pa-rs` sources as well as the library this builds for linking. This is done by setting environment variables in the terminal where the builds will run.

```sh
# Enable local c2pa-rs build
export C2PA_BUILD_FROM_SOURCE=ON

# If local build is enabled, set this environment variable to contain the path to c2pa-rs sources
export C2PA_RS_PATH=path_to_c2pa_rs_sources

# Since this is going to build Rust code, the build system needs to locate cargo, the tool to build Rust code
# Add Rust cargo to PATH if not already there
export PATH="$HOME/.cargo/bin:$PATH"

# macOs: Set built library path for running tests
export DYLD_LIBRARY_PATH="$(pwd)/build/release/tests:$DYLD_LIBRARY_PATH"

# Linux: Set built library path for running tests
export LD_LIBRARY_PATH="$(pwd)/build/release/tests:$LD_LIBRARY_PATH"
```

### Testing

Build the [unit tests](https://github.com/contentauth/c2pa-c/tree/main/tests) by entering this `make` command:

```
make unit-test
make test
```

### Building API documentation
Expand All @@ -81,7 +113,7 @@ API documentation generated by Doxygen is automatically built on each PR.

To generate API docs locally, these are the main files:

- Configuration file: `c2pa-c/Doxyfile`
- Configuration file: `c2pa-c/Doxyfile`
- Script: `c2pa-c/scripts/generate_api_docs.sh`
- Output directory: `docs/_build/html`

Expand Down Expand Up @@ -111,5 +143,3 @@ Note that some components and dependent crates are licensed under different term
### Contributions and feedback

We welcome contributions to this project. For information on contributing, providing feedback, and about ongoing work, see [Contributing](https://github.com/contentauth/c2pa-c/blob/main/CONTRIBUTING.md).


13 changes: 13 additions & 0 deletions ci-cd/lsan_suppressions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# LeakSanitizer suppression file for c2pa-c tests.
#
# On first use Tokio allocates process-global singleton state (signal handler
# registry, thread-local handles via OnceLock/Lazy) that is intentionally never
# freed. The allocation is bounded, does not grow, and is reclaimed by the OS on process exit.
#
# Observed on Ubuntu 22.04 aarch64 where LSan classifies the allocation as a
# "direct leak" rather than "still reachable".
#
# Similar if not same as https://github.com/tokio-rs/tokio/issues/4756
# Happens only on some Linxues as it depends on the OS/glibc version too it seems!
#
leak:libc2pa_c
Loading