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
43 changes: 42 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,54 @@ jobs:
run: cargo hack clippy --feature-powerset -- -D warnings

- name: Test
run: cargo hack test --feature-powerset
# `bundled` must stay enabled for every combination: it is what compiles
# and statically links the C++ ada sources. Without it (and with no
# external ada provided via ADA_LIB_DIR/ADA_LIB_NAME) the test binaries
# fail to link with `undefined symbol: ada_*`. `--features bundled` forces
# it into each powerset entry while still exercising the other features.
run: cargo hack test --feature-powerset --features bundled

- name: Check Documentation
env:
RUSTDOCFLAGS: '-D warnings'
run: cargo hack doc --feature-powerset

external-ada:
name: External ada (non-bundled)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: Swatinem/rust-cache@v2
with:
shared-key: external-ada
save-if: ${{ github.ref_name == 'main' }}

- run: rustup show

# Exercise the non-bundled linking path: instead of compiling the bundled
# C++ sources, build ada once as a standalone shared library and link the
# crate against it via ADA_LIB_DIR / ADA_LIB_NAME. This mirrors how a host
# build system (Bazel, CMake, a distro package) would provide ada. The
# shared library records its own libstdc++ dependency (DT_NEEDED), so the
# final Rust binary needs no extra C++ runtime link flags.
- name: Build standalone libada.so
run: |
c++ -std=c++20 -O2 -DADA_INCLUDE_URL_PATTERN=0 -Ideps -fPIC -shared \
deps/ada.cpp -o "$RUNNER_TEMP/libada.so"

# `bundled` is intentionally disabled (default-features off). `std` is
# re-enabled because dropping default features also drops it. The serde
# combination is run separately to cover the extra feature on this path.
- name: Test against external ada
env:
ADA_LIB_DIR: ${{ runner.temp }}
ADA_LIB_NAME: ada
LD_LIBRARY_PATH: ${{ runner.temp }}
run: |
cargo test --no-default-features --features std
cargo test --no-default-features --features serde

sanitizers:
name: AddressSanitizer + LeakSanitizer
runs-on: ubuntu-latest
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ path = "bench/wpt.rs"
harness = false

[features]
default = ["std"]
default = ["bundled", "std"]
# compile and statically link the bundled C++ `ada` sources. Enabled by
# default; disable (`default-features = false`) to link an externally provided
# ada instead (see `ADA_LIB_DIR` / `ADA_LIB_NAME` in build.rs).
bundled = []
# pass `cpp_set_stdlib("c++")` to `cc`
libcpp = []
# enables serde serialization/deserialization support
Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,44 @@ Enabling this feature and `std` would provide you both `Serialize` and `Deserial
**libcpp:** Build `ada-url` with `libc++`. This feature is disabled by default.
Enabling this feature without `libc++` installed would cause compile error.

**bundled:** Compile and statically link the bundled C++ `ada` sources. This
feature is **enabled by default**. See "Linking against an external ada" below
for how to disable it.

### Linking against an external ada

By default (the `bundled` feature) the build script compiles the bundled C++
`ada` sources and links them statically. If ada is already provided by your
host build system (e.g. Bazel, CMake, or a distribution package), compiling the
bundled copy would produce a second, duplicate copy of ada in the final binary.

Disable the `bundled` feature to skip the bundled build and link against an
external ada instead:

```toml
# Cargo.toml — re-enable `std` since disabling default features also drops it
ada-url = { version = "3", default-features = false, features = ["std"] }
```

When `bundled` is disabled, these optional environment variables configure how
the external ada is linked:

| Variable | Description |
| ------------- | -------------------------------------------------------------------------------------------------- |
| `ADA_LIB_DIR` | Optional directory added to the linker search path (`cargo:rustc-link-search=native`). |
| `ADA_LIB_NAME`| Optional library to link, passed verbatim to `cargo:rustc-link-lib` (e.g. `ada` or `static=ada`). |

If neither is provided, no link directive is emitted, leaving it to the host
build system to provide the ada symbols at final link time. The external ada
must expose the same C ABI (`ada_*`) and be ABI-compatible with this crate's
version.

```sh
# Link against a system-installed libada in /usr/local/lib
ADA_LIB_DIR=/usr/local/lib ADA_LIB_NAME=ada \
cargo build --no-default-features --features std
```

### Performance

Ada is fast. The benchmark below shows **3.49 times** faster URL parsing compared to `url`
Expand Down
36 changes: 36 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,42 @@ fn ndk_major_version(ndk_dir: &Path) -> u32 {
}

fn main() {
// By default the bundled C++ `ada` sources are compiled and linked
// statically (the `bundled` feature, enabled by default). Disabling the
// `bundled` feature (e.g. `default-features = false`) skips that compile and
// instead links against an externally provided ada library. This is useful
// when ada is already provided by the host build system (e.g. Bazel, CMake,
// or a distro package), where compiling the bundled copy would otherwise
// produce a second, duplicate copy of ada in the final binary.
//
// When the `bundled` feature is disabled, these optional environment
// variables configure how the external ada is linked:
//
// ADA_LIB_DIR directory added to the link search path.
// ADA_LIB_NAME library to link, passed verbatim to `cargo:rustc-link-lib`
// (e.g. `ada` or `static=ada`).
//
// If neither is provided, no link directive is emitted, leaving it to the
// host build system to provide the ada symbols at final link time. The
// external ada must expose the same C ABI (`ada_*`) and be ABI-compatible
// with this crate's version.
println!("cargo:rerun-if-env-changed=ADA_LIB_DIR");
println!("cargo:rerun-if-env-changed=ADA_LIB_NAME");
if !cfg!(feature = "bundled") {
if let Some(dir) = env::var_os("ADA_LIB_DIR") {
println!(
"cargo:rustc-link-search=native={}",
Path::new(&dir).display()
);
}
if let Ok(name) = env::var("ADA_LIB_NAME")
&& !name.is_empty()
{
println!("cargo:rustc-link-lib={name}");
}
return;
}

let target_str = env::var("TARGET").unwrap();
let target: Vec<String> = target_str.split('-').map(|s| s.into()).collect();
assert!(target.len() >= 2, "Failed to parse TARGET {}", target_str);
Expand Down
Loading