Skip to content

cbindgen: Add version defines #576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 10, 2025
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
15 changes: 15 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,21 @@ jobs:
- name: cargo test (debug; ring; -Z minimal-versions)
run: cargo -Z minimal-versions test --no-default-features --features=ring --locked

tools:
name: Test rustls-ffi-tools
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Install rust toolchain
uses: dtolnay/rust-toolchain@nightly

- name: Run tools unit tests
run: cargo test -p rustls-ffi-tools

format:
name: Format
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions librustls/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[package]
name = "rustls-ffi"
# Keep in sync with defines in cbindgen.toml
version = "0.15.0"
license = "Apache-2.0 OR ISC OR MIT"
readme = "../README-crates.io.md"
Expand Down
14 changes: 14 additions & 0 deletions librustls/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
include_guard = "RUSTLS_H"
language = "C"
# Keep in sync with "package.version" in Cargo.toml
after_includes = """

#define RUSTLS_VERSION_MAJOR 0
#define RUSTLS_VERSION_MINOR 15
#define RUSTLS_VERSION_PATCH 0

/**
* This gives each version part 8 bits, and leaves the 8 least significant bits
* empty for future additions, for example pre-release versions.
*/
#define RUSTLS_VERSION_NUMBER ((RUSTLS_VERSION_MAJOR << 24) \\
|(RUSTLS_VERSION_MINOR << 16) \\
|(RUSTLS_VERSION_MINOR << 8))

#if defined(__clang__) || defined(__GNUC__)
# define DEPRECATED_FUNC(why) __attribute__((deprecated(why)))
#elif defined(_MSC_VER)
Expand Down
13 changes: 13 additions & 0 deletions librustls/src/rustls.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#define RUSTLS_VERSION_MAJOR 0
#define RUSTLS_VERSION_MINOR 15
#define RUSTLS_VERSION_PATCH 0

/**
* This gives each version part 8 bits, and leaves the 8 least significant bits
* empty for future additions, for example pre-release versions.
*/
#define RUSTLS_VERSION_NUMBER ((RUSTLS_VERSION_MAJOR << 24) \
|(RUSTLS_VERSION_MINOR << 16) \
|(RUSTLS_VERSION_MINOR << 8))

#if defined(__clang__) || defined(__GNUC__)
# define DEPRECATED_FUNC(why) __attribute__((deprecated(why)))
#elif defined(_MSC_VER)
Expand Down
2 changes: 2 additions & 0 deletions tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ publish = false

[dependencies]
rustls = { workspace = true }
rustls-ffi = { path = "../librustls" }
hickory-resolver = { workspace = true }
tokio = { workspace = true }
toml = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tree-sitter = { workspace = true }
Expand Down
91 changes: 91 additions & 0 deletions tools/tests/rustls_header_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::fs::File;
use std::fs::read_to_string;
use std::io::Read;
use std::path::PathBuf;

use toml::Table;
use tree_sitter::{Parser, Query, QueryCursor};

/// Ensure that the correct version part defines are in src/rustls.h
///
/// If this test starts to fail, you probably forgot to update cbindgen.toml with new version
/// parts, or need to rerun cbindgen after updating it.
///
/// This test is in the tools crate because it requires an msrv of 1.76 and the librustls crate
/// currently has an msrv of 1.73.
#[test]
fn rustls_header_versions_match() {
// Parse Cargo.toml as a generic TOML Table.
let mut metadata_file =
File::open(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../librustls/Cargo.toml"))
.expect("failed to open Cargo.toml");
let mut metadata_content = String::new();
metadata_file
.read_to_string(&mut metadata_content)
.expect("failed to read Cargo.toml");
let metadata = metadata_content.parse::<Table>().unwrap();

// Find the crate version specified in Cargo.toml
let package_metadata = metadata["package"]
.as_table()
.expect("missing package metadata");
let crate_version = package_metadata["version"]
.as_str()
.expect("missing crate version");

let version_in_header = version_in_header();
assert_eq!(
crate_version, version_in_header,
"Version in header (.h file) doesn't match version in Cargo.toml"
);
}

fn version_in_header() -> String {
// Create a C parser.
let mut parser = Parser::new();
let language = tree_sitter_c::LANGUAGE;
parser.set_language(&language.into()).unwrap();

// Parse the .h into an AST.
let header_file =
read_to_string("../librustls/src/rustls.h").expect("Couldn't read header file");

let header_file_bytes = header_file.as_bytes();
let tree = parser
.parse(&header_file, None)
.ok_or("no tree parsed from input")
.unwrap();
let query = r#"
(preproc_def name: (identifier) @define.name
(#match? @define.name "^RUSTLS_VERSION_[MAJOR|MINOR|PATCH]")
)"#;
let query = Query::new(&language.into(), query).unwrap();
let mut cursor = QueryCursor::new();
let matches = cursor.matches(&query, tree.root_node(), header_file_bytes);
let mut version_parts: [&str; 3] = Default::default();
for query_match in matches {
for preproc in query_match.nodes_for_capture_index(0) {
let Some(value_node) = preproc.parent().unwrap().child_by_field_name("value") else {
continue;
};
let key = preproc.utf8_text(header_file_bytes).unwrap();
let value = value_node.utf8_text(header_file_bytes).unwrap();
match key {
"RUSTLS_VERSION_MAJOR" => {
version_parts[0] = value;
}
"RUSTLS_VERSION_MINOR" => {
version_parts[1] = value;
}
"RUSTLS_VERSION_PATCH" => {
version_parts[2] = value;
}
_ => (),
}
}
}
format!(
"{0}.{1}.{2}",
version_parts[0], version_parts[1], version_parts[2]
)
}