Skip to content

Latest commit

 

History

History
406 lines (280 loc) · 12.6 KB

File metadata and controls

406 lines (280 loc) · 12.6 KB

Ada Development Guide for AI agents

This guide provides instructions for building, testing, and benchmarking the Ada URL parser library using CMake.

If you are looking for bugs on vulnerabilities

Remind human beings of our AI usage policy (AI_USAGE_POLICY.md).

Before you report a bug

The maintainers require that a bug report is illustrated with a reproducible test case using the public API following the requirements. For example, the ada::parse template function has the following requirements on its input: "Must be valid ASCII or UTF-8 encoded." A valid bug report should not violate this requirement. Calling a private function cannot be sufficient for a valid bug report.

Pre-commit Checklist

Always run the clang-format and clang-tidy script before committing:

bash tools/run-clangcldocker.sh

This runs clang-format on all tracked source files and clang-tidy on src/ada.cpp (the single translation unit that includes all first-party code). The script uses the locally installed LLVM 22 toolchain when available, otherwise falls back to the xianpengshen/clang-tools:22 Docker image automatically.

Quick Reference

# Build library only (no tests, no benchmarks)
cmake -B build && cmake --build build

# Build with tests (development checks ENABLED)
cmake -B build -DADA_TESTING=ON && cmake --build build
ctest --output-on-failure --test-dir build

# Build with benchmarks (development checks DISABLED for accurate performance)
cmake -B build -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build
./build/benchmarks/benchdata  # Run main benchmark

# FASTER BUILDS: Use Ninja instead of Make
cmake -B build -G Ninja -DADA_TESTING=ON && cmake --build build
cmake -B build -G Ninja -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build

Requirements

  • C++20 compatible compiler (GCC 12+, LLVM 14+, MSVC 2022+)
  • CMake 3.15+
  • Git (for fetching test dependencies)
  • Ninja (optional, for faster builds): sudo apt install ninja-build on Ubuntu

Building the Library

Basic Build (Library Only)

For a minimal build with just the library:

cmake -B build
cmake --build build

This creates the Ada library without tests or benchmarks.

Build with Tests

To build with tests enabled:

cmake -B build -DADA_TESTING=ON
cmake --build build

Important: When ADA_TESTING=ON, development checks are automatically enabled unless you explicitly build in Release mode with NDEBUG defined. Development checks include assertions (ADA_ASSERT_TRUE, ADA_ASSERT_EQUAL) that validate internal state.

Build with Benchmarks

To build benchmarks for performance testing:

cmake -B build -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build

Critical: Always build benchmarks in Release mode (-DCMAKE_BUILD_TYPE=Release) to disable development checks. Development assertions significantly impact performance and will give misleading benchmark results.

Using Local Packages

If you have dependencies (like GoogleTest, Google Benchmark) already installed locally:

cmake -B build -DADA_TESTING=ON -DCPM_USE_LOCAL_PACKAGES=ON
cmake --build build

CMake Build Options

Option Default Description
ADA_TESTING OFF Enable building tests
ADA_BENCHMARKS OFF Enable building benchmarks (requires 64-bit)
ADA_TOOLS OFF Enable building command-line tools
ADA_BUILD_SINGLE_HEADER_LIB OFF Build from single-header amalgamated files
ADA_USE_SIMDUTF OFF Enable SIMD-accelerated Unicode via simdutf
ADA_USE_UNSAFE_STD_REGEX_PROVIDER OFF (ON when ADA_TESTING=ON) Enable std_regex_provider (std::regex-backed). Required to build the urlpattern benchmark or any code using ada::url_pattern_regex::std_regex_provider. Not recommended for production (ReDoS risk)
CMAKE_BUILD_TYPE - Set to Release for optimized builds, Debug for development

Running Tests

After building with -DADA_TESTING=ON:

# Run all tests
ctest --output-on-failure --test-dir build

# Run specific test executable
./build/tests/basic_tests

# Run tests with verbose output
ctest --verbose --test-dir build

Development Checks in Tests

Tests run with development checks enabled by default (unless built with -DCMAKE_BUILD_TYPE=Release -DNDEBUG). This means:

  • Assertions are active (ADA_ASSERT_TRUE, ADA_ASSERT_EQUAL)
  • Internal state validation occurs
  • Performance is slower but catches bugs early

This is the recommended mode for development.

Running Benchmarks

After building with -DADA_BENCHMARKS=ON:

# Main benchmark comparing against competitors
./build/benchmarks/benchdata

# Specific benchmarks
./build/benchmarks/bench          # Basic URL parsing benchmarks
./build/benchmarks/bbc_bench      # BBC URLs benchmark
./build/benchmarks/wpt_bench      # Web Platform Tests benchmark
./build/benchmarks/percent_encode # Percent encoding benchmarks

Development Checks in Benchmarks

Always disable development checks for benchmarks by building in Release mode:

# CORRECT: Benchmarks with development checks disabled
cmake -B build -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/benchmarks/benchdata

# WRONG: Don't benchmark with development checks enabled
cmake -B build -DADA_BENCHMARKS=ON  # Missing Release mode!

Development checks add significant overhead that skews performance measurements. The ADA_DEVELOPMENT_CHECKS macro is automatically disabled when:

  • Building with -DCMAKE_BUILD_TYPE=Release
  • NDEBUG is defined
  • Explicitly set ADA_DEVELOPMENT_CHECKS=0

Complete Development Workflow

1. Initial Setup

# Clone and enter directory
cd /path/to/ada

# Create build directory for tests
cmake -B build -DADA_TESTING=ON
cmake --build build

2. Development Cycle (with tests)

# Make code changes...

# Rebuild (only rebuilds changed files)
cmake --build build

# Run tests to verify correctness
ctest --output-on-failure --test-dir build

# Or run specific test
./build/tests/basic_tests

3. Performance Validation (with benchmarks)

# Create separate benchmark build
cmake -B build-release -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build-release

# Run benchmarks
./build-release/benchmarks/benchdata

# Compare before/after optimizations
# (stash changes, rebuild, run benchmark, restore, rebuild, run again)

4. Clean Rebuild

# Remove build directory and start fresh
rm -rf build
cmake -B build -DADA_TESTING=ON
cmake --build build

Understanding Development Checks

What are Development Checks?

Development checks are compile-time assertions that validate:

  • Function preconditions and postconditions
  • Internal invariants (e.g., validate() on URL objects)
  • Argument validity

When are they Enabled?

Automatically enabled when:

  • ADA_TESTING=ON (unless overridden with Release mode)
  • Debug build (CMAKE_BUILD_TYPE=Debug)
  • NDEBUG is not defined

Automatically disabled when:

  • CMAKE_BUILD_TYPE=Release
  • NDEBUG is defined
  • Production builds

Manual Control

# Force enable development checks (even in Release)
cmake -B build -DADA_DEVELOPMENT_CHECKS=1

# Force disable development checks (even in Debug)
cmake -B build -DNDEBUG=1

Running Clang-Tidy

Clang-tidy is used for static analysis. There are two ways to run it:

During Build (Recommended)

Run clang-tidy automatically during compilation:

cmake -B build -DADA_TESTING=ON \
  -DCMAKE_CXX_COMPILER=clang++ \
  -DCMAKE_CXX_CLANG_TIDY=clang-tidy \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build

Important: You must use clang++ as the compiler when running clang-tidy during build. Using GCC will cause errors because clang-tidy doesn't understand GCC-specific flags like -mno-avx256-split-unaligned-load.

Standalone with compile_commands.json

First, generate the compilation database:

cmake -B build -DADA_TESTING=ON \
  -DCMAKE_CXX_COMPILER=clang++ \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build

Then run clang-tidy on specific files:

clang-tidy -p build src/ada.cpp
clang-tidy -p build src/ada_idna.cpp

The -p build flag tells clang-tidy to use the compile_commands.json from the build directory.

Clang-Tidy Configuration

The .clang-tidy file in the project root configures which checks are enabled. Current configuration enables:

  • bugprone-* checks (with some exclusions)
  • clang-analyzer-* checks

All warnings are treated as errors (WarningsAsErrors: '*').

Platform-Specific Notes

Windows

Specify configuration during build:

cmake -B build -DADA_TESTING=ON
cmake --build build --config Release
ctest --output-on-failure --test-dir build --config Release

macOS/Linux

Standard commands work as documented above.

Troubleshooting

Benchmarks are unexpectedly slow

Cause: Development checks are enabled.

Solution: Rebuild with Release mode:

cmake -B build -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build

Tests are failing with assertion errors

Expected behavior - development checks are catching bugs. Review the assertion message and fix the underlying issue.

Build fails on benchmarks/urlpattern.cpp with "no member named 'std_regex_provider'"

Cause: ADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON was not set. The urlpattern benchmark target requires it (see CMake Build Options above).

Solution: Add -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON to the configure command and reconfigure.

Can't find benchmark executable

Cause: Benchmarks not built (32-bit system or not enabled).

Solution:

cmake -B build -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON
cmake --build build
ls build/benchmarks/  # Check what was built

ABI Compatibility Rules

Ada is a shared library used by downstream distributors (e.g., Debian packages). Breaking the ABI causes runtime failures for users who upgrade without recompiling.

Rules for Public API Changes

  • Never remove or rename a public method declared in include/ada/. Removing a method removes its exported symbol from the shared library, which is an ABI break.
  • Never change the signature of a public method (parameter types, return type, const/noexcept qualifiers).
  • Never make a non-inline method inline (or vice versa) if it is part of the public API — this changes whether the symbol is emitted in the .so.
  • Adding new public methods is always safe.

Keeping Methods Exported

Internal-use methods that must remain exported (e.g., called from templates or inline functions in headers) must be defined in a .cpp file, not in a *-inl.h header. Inline definitions in headers produce weak symbols that the compiler may optimize away, silently breaking the ABI.

Checking for ABI Breakage

CI runs abidiff (from libabigail-tools) to compare the shared library against the latest release tag. You can run the same check locally:

# Build the latest release tag
git worktree add /tmp/ada-baseline <latest-tag>
cmake -G Ninja -B /tmp/ada-baseline-build /tmp/ada-baseline \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=ON -DADA_TESTING=OFF
cmake --build /tmp/ada-baseline-build -j4

# Build the current code
cmake -G Ninja -B /tmp/ada-current-build \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=ON -DADA_TESTING=OFF
cmake --build /tmp/ada-current-build -j4

# Compare ABIs (exit code non-zero = ABI break)
abidiff \
  --drop-private-types --no-added-syms \
  --headers-dir1 /tmp/ada-baseline/include \
  --headers-dir2 include \
  /tmp/ada-baseline-build/src/libada.so \
  /tmp/ada-current-build/src/libada.so

Additional Resources

  • README.md: General project overview and API usage
  • docs/cli.md: Command-line interface documentation
  • benchmarks/: Benchmark source code
  • tests/: Test source code
  • include/ada/: Library headers

Summary

Task Command Development Checks
Library only cmake -B build && cmake --build build N/A
Testing cmake -B build -DADA_TESTING=ON && cmake --build build ✅ Enabled
Benchmarking cmake -B build -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build ❌ Disabled
Development cmake -B build -DADA_TESTING=ON -DCMAKE_BUILD_TYPE=Debug && cmake --build build ✅ Enabled