Skip to content

Commit d848614

Browse files
sorafujitanikddnewton
authored andcommitted
Ship pregenerated bindings in ruby-prism-sys
Commit the bindgen output at src/bindings.rs so building the crate no longer requires bindgen or libclang. The committed file is generated with layout_tests(false) to stay portable across pointer widths. The new buildtime_bindgen feature restores build-time generation, and rake cargo:bindings regenerates the committed file.
1 parent 46d92d0 commit d848614

5 files changed

Lines changed: 12131 additions & 11 deletions

File tree

rakelib/cargo.rake

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ namespace :cargo do
3939
end
4040
end
4141

42+
desc "Regenerate the pregenerated bindings for ruby-prism-sys"
43+
task bindings: [:build] do
44+
gemspec = Gem::Specification.load("prism.gemspec")
45+
46+
generated_path = File.expand_path("../rust/target/pregenerated-bindings.rs", __dir__)
47+
mkdir_p(File.dirname(generated_path))
48+
49+
Dir.chdir("rust/ruby-prism-sys") do
50+
sh({ "PRISM_GENERATE_BINDINGS" => generated_path }, "cargo check --features buildtime_bindgen")
51+
end
52+
53+
generated = File.read(generated_path)
54+
bindgen_version = generated[/automatically generated by rust-bindgen (\S+)/, 1]
55+
raise "Could not find the bindgen version banner in the generated bindings" if bindgen_version.nil?
56+
57+
header = <<~HEADER
58+
/* Generated by `bundle exec rake cargo:bindings`. Do not edit by hand.
59+
* prism #{gemspec.version}, bindgen #{bindgen_version}, layout_tests(false)
60+
* so the file stays portable across pointer widths.
61+
*/
62+
HEADER
63+
64+
File.write("rust/ruby-prism-sys/src/bindings.rs", header + generated)
65+
end
66+
4267
desc "Run all cargo tests"
4368
task test: [:build] do
4469
CRATES.each do |crate|

rust/ruby-prism-sys/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ build = "build/main.rs"
2222
include = ["src/", "build/", "Cargo.toml", "Cargo.lock", "README.md", "vendor"]
2323

2424
[build-dependencies]
25-
bindgen = "0.72"
25+
bindgen = { version = "0.72", optional = true }
2626
cc = { version = "1.0", optional = true }
2727
shlex = "1.3"
2828

2929
[features]
3030
default = ["vendored"]
3131
vendored = ["dep:cc"]
32+
buildtime_bindgen = ["dep:bindgen"]

rust/ruby-prism-sys/build/main.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,30 @@ fn main() {
1919
// Add `[root]/build/` to the search paths, so it can find `libprism.a`.
2020
println!("cargo:rustc-link-search=native={}", ruby_build_path.to_str().unwrap());
2121

22-
// This is where the magic happens.
23-
let bindings = generate_bindings(&ruby_include_path, &clang_args);
24-
25-
// Write the bindings to file.
26-
write_bindings(&bindings);
22+
#[cfg(not(feature = "buildtime_bindgen"))]
23+
let _ = clang_args;
24+
25+
#[cfg(feature = "buildtime_bindgen")]
26+
{
27+
// This is where the magic happens.
28+
let bindings = generate_bindings(&ruby_include_path, &clang_args, true);
29+
30+
// Write the bindings to file.
31+
write_bindings(&bindings);
32+
33+
// The pregenerated copy omits layout tests, which hardcode the host
34+
// target's sizes and would break other pointer widths (e.g. wasm32).
35+
if let Some(path) = std::env::var_os("PRISM_GENERATE_BINDINGS") {
36+
generate_bindings(&ruby_include_path, &clang_args, false)
37+
.write_to_file(&path)
38+
.expect("Couldn't write pregenerated bindings!");
39+
}
40+
}
2741
}
2842

2943
fn emit_rerun_hints(ruby_include_path: &Path) {
3044
println!("cargo:rerun-if-env-changed=RUBY_PRISM_CFLAGS");
45+
println!("cargo:rerun-if-env-changed=PRISM_GENERATE_BINDINGS");
3146
println!("cargo:rerun-if-env-changed=PRISM_INCLUDE_DIR");
3247
println!("cargo:rerun-if-env-changed=PRISM_LIB_DIR");
3348
println!("cargo:rerun-if-changed={}", ruby_include_path.display());
@@ -73,9 +88,11 @@ fn cargo_manifest_path() -> PathBuf {
7388
PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap())
7489
}
7590

91+
#[cfg(feature = "buildtime_bindgen")]
7692
#[derive(Debug)]
7793
struct Callbacks;
7894

95+
#[cfg(feature = "buildtime_bindgen")]
7996
impl bindgen::callbacks::ParseCallbacks for Callbacks {
8097
/// Accept a string that is a comment on a node. Replace any internal code
8198
/// examples that are indented by 4 spaces with a fenced code block.
@@ -121,17 +138,19 @@ impl bindgen::callbacks::ParseCallbacks for Callbacks {
121138
///
122139
/// This method only generates code in memory here--it doesn't write it to file.
123140
///
124-
fn generate_bindings(ruby_include_path: &Path, clang_args: &[String]) -> bindgen::Bindings {
141+
#[cfg(feature = "buildtime_bindgen")]
142+
fn generate_bindings(ruby_include_path: &Path, clang_args: &[String], layout_tests: bool) -> bindgen::Bindings {
125143
bindgen::Builder::default()
126144
.derive_default(true)
145+
.formatter(bindgen::Formatter::Prettyplease)
127146
.generate_block(true)
128147
.generate_comments(true)
129148
.header(ruby_include_path.join("prism.h").to_str().unwrap())
130149
.clang_arg(format!("-I{}", ruby_include_path.to_str().unwrap()))
131150
.clang_arg("-fparse-all-comments")
132151
.clang_args(clang_args)
133152
.impl_debug(true)
134-
.layout_tests(true)
153+
.layout_tests(layout_tests)
135154
.merge_extern_blocks(true)
136155
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
137156
.parse_callbacks(Box::new(Callbacks))
@@ -226,6 +245,7 @@ fn generate_bindings(ruby_include_path: &Path, clang_args: &[String]) -> bindgen
226245

227246
/// Write the bindings to the `$OUT_DIR/bindings.rs` file. We'll pull these into
228247
/// the actual library in `src/lib.rs`.
248+
#[cfg(feature = "buildtime_bindgen")]
229249
fn write_bindings(bindings: &bindgen::Bindings) {
230250
let out_path = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
231251

0 commit comments

Comments
 (0)