Building from Source

This guide covers building the GraphQLite SQLite extension and CLI from source, running tests, checking code quality, and installing the result for use with the Rust crate.

Prerequisites

ToolMinimum VersionmacOSLinux (Debian/Ubuntu)Windows (MSYS2)
GCC or Clang9 / 11Xcode CLI toolsbuild-essentialmingw-w64-x86_64-gcc
Bison3.0+brew install bisonbisonbison
Flex2.6+brew install flexflexflex
SQLite dev headers3.35+brew install sqlitelibsqlite3-devmingw-w64-x86_64-sqlite3
CUnit2.1+brew install cunitlibcunit1-dev(optional)
make4.0+Xcode CLI toolsmakemake

macOS

xcode-select --install
brew install bison flex sqlite cunit

# Homebrew Bison must precede the system Bison on PATH
export PATH="$(brew --prefix bison)/bin:$PATH"

Add the PATH export to your shell profile (~/.zshrc or ~/.bash_profile) to make it persistent.

Linux (Debian/Ubuntu)

sudo apt-get update
sudo apt-get install build-essential bison flex libsqlite3-dev libcunit1-dev

Windows (MSYS2)

pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-sqlite3 bison flex make

Run all commands from the MSYS2 MinGW 64-bit shell.

Clone and Build

git clone https://github.com/your-org/graphqlite
cd graphqlite
make extension

This produces:

PlatformOutput file
macOSbuild/graphqlite.dylib
Linuxbuild/graphqlite.so
Windowsbuild/graphqlite.dll

Debug vs. Release

The default build includes debug symbols and C assertions. For a production build:

make extension RELEASE=1

RELEASE=1 adds -O2 optimization and strips assertions. Always use release builds for benchmarking.

Build Targets

Run make help to see all available targets. The most commonly used ones are:

Core Targets

TargetDescription
make extensionBuild the SQLite extension (.dylib/.so/.dll)
make extension RELEASE=1Build optimized release extension
make graphqliteBuild the gqlite interactive CLI
make graphqlite RELEASE=1Build optimized release CLI
make allBuild everything (extension + CLI)

Test Targets

TargetDescription
make test unitRun CUnit unit tests (770 tests)
make test functionalRun SQL-based functional tests
make test pythonRun Python binding tests
make test rustBuild and run Rust binding tests
make test-allRun all test suites

Quality Targets

TargetDescription
make lintStrict C11 compliance check
make coverageBuild with coverage instrumentation and run unit tests
make performanceRun performance benchmarks
make performance-quickQuick performance smoke test

Install Targets

TargetDescription
make install-bundledCopy extension into the Rust crate's source tree

Running Tests

Unit Tests

make test unit

Builds and runs the CUnit test suite. Output shows pass/fail counts per suite. All 770 tests must pass on a clean build.

Functional Tests

make test functional

Runs SQL script tests against the built extension. Each test file is a .sql script in tests/functional/ that exercises a specific feature:

# Run a single functional test manually
sqlite3 :memory: < tests/functional/01_create_match.sql

Python Tests

make test python

Requires Python 3.8+ and pip install graphqlite[dev] (or install the dev dependencies from requirements-dev.txt).

Rust Tests

make test rust

This target first calls make install-bundled to copy the freshly-built extension into the Rust crate, then runs cargo test.

All Tests

make test-all

Runs unit, functional, CLI, and binding tests in sequence. All suites must pass before a release.

Linting

make lint

The lint target compiles all C source files with strict C11 flags:

-std=c11 -Wall -Wextra -Wpedantic -Werror -Wshadow -Wformat=2

Zero warnings are permitted (-Werror). Fix all lint warnings before submitting changes. The lint target does not link the extension; it only checks the source.

Coverage

make coverage

Builds the unit test runner with --coverage instrumentation (GCC gcov/lcov) and runs the tests. Coverage data appears in build/coverage/. Open build/coverage/index.html in a browser to browse line-by-line coverage.

Requirements: lcov must be installed (brew install lcov / sudo apt-get install lcov).

Performance Benchmarks

# Full benchmark suite
make performance

# Quick smoke test (faster, fewer iterations)
make performance-quick

# Extended benchmark with larger graphs
make performance-full

Benchmark results are printed to stdout. Run with RELEASE=1 for meaningful numbers:

make performance RELEASE=1

Installing for Rust Bundled Builds

The Rust crate defaults to the bundled-extension feature, which embeds the extension binary at compile time. After building from source, copy the extension into the Rust crate:

make install-bundled

This copies build/graphqlite.{dylib,so,dll} into the correct location inside bindings/rust/ so that cargo build picks it up. Run this after every source change when developing the Rust crate.

Platform-Specific Notes

macOS

  • The system bison (in /usr/bin) is version 2.x, which is too old. Always install via Homebrew and prepend it to PATH.

  • If you see library not loaded: libsqlite3.dylib when loading the built extension, set DYLD_LIBRARY_PATH:

    export DYLD_LIBRARY_PATH="$(brew --prefix sqlite)/lib:$DYLD_LIBRARY_PATH"
    
  • On Apple Silicon (M1/M2), the Homebrew prefix is /opt/homebrew rather than /usr/local.

Linux

  • The libcunit1-dev package is only needed for unit tests. The extension itself (make extension) does not require CUnit.

  • On systems without lcov, make coverage will fail at the report generation step. Install lcov or skip coverage.

  • On Alpine Linux (musl libc), replace apt-get with apk add:

    apk add build-base bison flex sqlite-dev
    

Windows (MSYS2)

  • All commands must be run from the MSYS2 MinGW 64-bit shell, not the default MSYS shell. The MinGW shell sets the correct compiler paths.
  • Python tests require a Windows Python 3.8+ installation accessible from the MSYS2 PATH.
  • The extension output is build/graphqlite.dll.

Build Directory Layout

After a full build:

build/
├── graphqlite.dylib        # SQLite extension (macOS)
├── gqlite                  # Interactive CLI binary
├── test_runner             # CUnit test runner binary
├── parser/                 # Compiled parser objects
├── transform/              # Compiled transform objects
├── executor/               # Compiled executor objects
└── coverage/               # Coverage HTML report (after make coverage)

Troubleshooting

bison: syntax error during build

The system Bison is too old. Confirm the version:

bison --version

If it shows 2.x, install Homebrew Bison (brew install bison) and prepend it to PATH.

flex: command not found

Install flex: brew install flex / sudo apt-get install flex.

cannot open shared object file: libcunit.so

CUnit is not installed or not on LD_LIBRARY_PATH. Install it (libcunit1-dev) or run make extension (which does not need CUnit) instead of make test unit.

Python tests fail with No module named graphqlite

Install the Python package in development mode:

pip install -e bindings/python/

or install it from PyPI and use GRAPHQLITE_EXTENSION_PATH to point to your locally built extension:

export GRAPHQLITE_EXTENSION_PATH=$(pwd)/build/graphqlite.dylib
make test python