@@ -30,6 +30,83 @@ Cflags: -I${{includedir}}
3030 out_file. write_all ( output. as_bytes ( ) ) . unwrap ( ) ;
3131}
3232
33+ /// Returns true if cargo resolved `boring` to a 4.x version.
34+ ///
35+ /// Walks up from `OUT_DIR` looking for a `Cargo.lock`, then scans it
36+ /// for the `boring` package. We use `Cargo.lock` rather than shelling
37+ /// out to `cargo metadata` because (a) the lockfile is guaranteed to
38+ /// exist at this point in the build, (b) parsing it is cheap and has
39+ /// no extra dependencies, and (c) it avoids re-entering cargo from a
40+ /// build script.
41+ fn detect_boring_v4 ( ) -> bool {
42+ let Some ( lockfile) = find_cargo_lock ( ) else {
43+ // No lockfile (shouldn't happen in normal cargo builds, but
44+ // be conservative). Assume 5.x — the default and forward-
45+ // looking version. Downstream can fix this by generating a
46+ // lockfile (`cargo generate-lockfile`).
47+ println ! (
48+ "cargo:warning=quiche: Cargo.lock not found; assuming boring 5.x"
49+ ) ;
50+ return false ;
51+ } ;
52+
53+ println ! ( "cargo:rerun-if-changed={}" , lockfile. display( ) ) ;
54+
55+ let contents = match std:: fs:: read_to_string ( & lockfile) {
56+ Ok ( s) => s,
57+ Err ( e) => {
58+ println ! (
59+ "cargo:warning=quiche: failed to read {}: {e}; assuming boring 5.x" ,
60+ lockfile. display( ) ,
61+ ) ;
62+ return false ;
63+ } ,
64+ } ;
65+
66+ // The lockfile is TOML but a regex-light scan is enough: find a
67+ // `[[package]]` whose `name = "boring"` (not "boring-sys") and
68+ // read its `version`.
69+ let mut in_boring = false ;
70+ for line in contents. lines ( ) {
71+ let line = line. trim ( ) ;
72+ if line == "[[package]]" {
73+ in_boring = false ;
74+ continue ;
75+ }
76+ if line == "name = \" boring\" " {
77+ in_boring = true ;
78+ continue ;
79+ }
80+ if in_boring {
81+ if let Some ( rest) = line. strip_prefix ( "version = \" " ) {
82+ let version = rest. trim_end_matches ( '"' ) ;
83+ let major = version. split ( '.' ) . next ( ) . unwrap_or ( "" ) ;
84+ return major == "4" ;
85+ }
86+ }
87+ }
88+
89+ // `boring` not present in the lockfile (e.g.
90+ // `boringssl-boring-crate` is off). Doesn't matter what we return
91+ // since the `cfg` won't be observed.
92+ false
93+ }
94+
95+ fn find_cargo_lock ( ) -> Option < std:: path:: PathBuf > {
96+ // Start from `CARGO_MANIFEST_DIR` and walk up. Cargo guarantees
97+ // the lockfile lives at the workspace root, which is an ancestor
98+ // of the manifest dir.
99+ let manifest_dir =
100+ std:: path:: PathBuf :: from ( std:: env:: var_os ( "CARGO_MANIFEST_DIR" ) ?) ;
101+ for dir in manifest_dir. ancestors ( ) {
102+ let candidate = dir. join ( "Cargo.lock" ) ;
103+ if candidate. is_file ( ) {
104+ return Some ( candidate) ;
105+ }
106+ }
107+ None
108+ }
109+
33110fn target_dir_path ( ) -> std:: path:: PathBuf {
34111 let out_dir = std:: env:: var ( "OUT_DIR" ) . unwrap ( ) ;
35112 let out_dir = std:: path:: Path :: new ( & out_dir) ;
@@ -44,7 +121,18 @@ fn target_dir_path() -> std::path::PathBuf {
44121}
45122
46123fn main ( ) {
124+ // Emit `cfg(boring_v4)` if boring version 4.x is detected. This is used to
125+ // pick which APIs to expect and to guide test expectations. (Larger post
126+ // quantum key shares are enabled by default in boring 5.x but not boring
127+ // 4.x.)
128+ //
129+ // The cfg is always registered (even when the backend feature is
130+ // off) so rustc doesn't warn about unknown cfg names.
131+ println ! ( "cargo::rustc-check-cfg=cfg(boring_v4)" ) ;
47132 if cfg ! ( feature = "boringssl-boring-crate" ) {
133+ if detect_boring_v4 ( ) {
134+ println ! ( "cargo:rustc-cfg=boring_v4" ) ;
135+ }
48136 println ! ( "cargo:rustc-link-lib=static=ssl" ) ;
49137 println ! ( "cargo:rustc-link-lib=static=crypto" ) ;
50138 }
0 commit comments