Skip to content

Commit 37fdd30

Browse files
authored
Detach bindgen for next release (#250)
1 parent 975af4e commit 37fdd30

File tree

3 files changed

+4
-282
lines changed

3 files changed

+4
-282
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,9 @@ jobs:
223223
if: matrix.config.skip-tests != 'true'
224224
run: |
225225
. ./ci-cargo.ps1
226-
ci-cargo test -vv --features use-bindgen,layout_tests $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET"
227-
ci-cargo test -vv --features use-bindgen,non-api,layout_tests $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET (with non-API)"
226+
# ci-cargo test -vv --features use-bindgen,layout_tests $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET"
227+
# ci-cargo test -vv --features use-bindgen,non-api,layout_tests $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET (with non-API)"
228+
ci-cargo test -vv $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET"
228229
env:
229230
RUST_TARGET: ${{ matrix.config.target }}
230231

@@ -233,7 +234,7 @@ jobs:
233234
id: build
234235
run: |
235236
. ./ci-cargo.ps1
236-
ci-cargo build -vv --features use-bindgen $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) -ActionName "Building for target: $env:RUST_TARGET"
237+
# ci-cargo build -vv --features use-bindgen $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) -ActionName "Building for target: $env:RUST_TARGET"
237238
env:
238239
LIBRSYS_BINDINGS_OUTPUT_PATH: generated_bindings
239240
RUST_TARGET: ${{ matrix.config.target }}

Cargo.toml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,6 @@ links = "R"
1616
documentation = "https://docs.rs/libR-sys/latest/libR_sys/"
1717
repository = "https://github.com/extendr/libR-sys"
1818

19-
[dependencies]
20-
21-
[build-dependencies]
22-
bindgen = { version = "0.69.4", optional = true, features = ["experimental"] }
23-
regex = { version = "*", optional = true, default-features = false }
24-
25-
[features]
26-
default = ["runtime"]
27-
# By default, we use pre-computed bindings that ship with the library. This may fail!
28-
# Turn on the 'use-bindgen' feature to generate bindings on the fly for your platform.
29-
use-bindgen = ["bindgen", "regex"]
30-
31-
# retain non-API bindings from R (requires build-time generation of bindings)
32-
non-api = ["use-bindgen"]
33-
34-
runtime = ["bindgen/runtime"]
35-
36-
# Enables generation of layout-tests in bindgen
37-
layout_tests = ["use-bindgen"]
38-
3919
[lib]
4020
# Some code comments on R's source code might be accidentally treated as Rust's
4121
# doc test. See https://github.com/extendr/libR-sys/issues/194 for the details.

build.rs

Lines changed: 0 additions & 259 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,10 @@ use std::os::unix::ffi::OsStrExt;
1313
#[cfg(target_family = "windows")]
1414
use std::os::windows::ffi::OsStringExt;
1515

16-
//
17-
// Environmental variables
18-
//
19-
2016
// The environmental variables that are usually set by R. These might be needed
2117
// to set manually if we compile libR-sys outside of an R session.
2218
//
2319
// c.f., https://stat.ethz.ch/R-manual/R-devel/library/base/html/EnvVar.html
24-
#[cfg(feature = "use-bindgen")]
25-
const ENVVAR_R_INCLUDE_DIR: &str = "R_INCLUDE_DIR";
2620
const ENVVAR_R_HOME: &str = "R_HOME";
2721

2822
// An R version (e.g., "4.1.2" or "4.2.0-devel"). When this is set, the actual R
@@ -31,24 +25,11 @@ const ENVVAR_R_HOME: &str = "R_HOME";
3125
const ENVVAR_R_VERSION: &str = "LIBRSYS_R_VERSION";
3226

3327
// A path to a dir containing pre-computed bindings (default: "bindings").
34-
#[cfg(not(feature = "use-bindgen"))]
3528
const ENVVAR_BINDINGS_PATH: &str = "LIBRSYS_BINDINGS_PATH";
3629

37-
// A path to libclang toolchain. If this is set, the path is added to the
38-
// compiler arguments on executing bindgen.
39-
#[cfg(feature = "use-bindgen")]
40-
const ENVVAR_LIBCLANG_INCLUDE_PATH: &str = "LIBRSYS_LIBCLANG_INCLUDE_PATH";
41-
42-
// A path to an output dir of bindings in addition to the default "bindings"
43-
// dir. If this is set, generated bindings are also put there.
44-
#[cfg(feature = "use-bindgen")]
45-
const ENVVAR_BINDINGS_OUTPUT_PATH: &str = "LIBRSYS_BINDINGS_OUTPUT_PATH";
46-
4730
#[derive(Debug)]
4831
struct InstallationPaths {
4932
r_home: PathBuf,
50-
#[cfg(feature = "use-bindgen")]
51-
include: PathBuf,
5233
library: PathBuf,
5334
}
5435

@@ -209,47 +190,15 @@ fn get_r_library(r_home: &Path) -> PathBuf {
209190
}
210191
}
211192

212-
// Get the path to the R include directory either from an envvar or by executing the actual R binary.
213-
#[cfg(feature = "use-bindgen")]
214-
fn get_r_include(r_home: &Path, library: &Path) -> io::Result<PathBuf> {
215-
// If the environment variable R_INCLUDE_DIR is set we use it
216-
if let Some(include) = env::var_os(ENVVAR_R_INCLUDE_DIR) {
217-
return Ok(PathBuf::from(include));
218-
}
219-
220-
// Otherwise, we try to execute `R` to find the include dir. Here,
221-
// we're using the R home we found earlier, to make sure we're consistent.
222-
let r_binary = InstallationPaths {
223-
r_home: r_home.to_path_buf(),
224-
#[cfg(feature = "use-bindgen")]
225-
include: PathBuf::new(), // get_r_binary() doesn't use `include` so fill with an empty PathBuf.
226-
library: library.to_path_buf(),
227-
}
228-
.get_r_binary();
229-
230-
let rout = r_command(r_binary, r#"cat(normalizePath(R.home('include')))"#)?;
231-
if !rout.is_empty() {
232-
Ok(PathBuf::from(rout))
233-
} else {
234-
Err(Error::new(ErrorKind::Other, "Cannot find R include."))
235-
}
236-
}
237-
238193
fn probe_r_paths() -> io::Result<InstallationPaths> {
239194
// First we locate the R home
240195
let r_home = get_r_home()?;
241196

242197
// Now the library location. On Windows, it depends on the target architecture
243198
let library = get_r_library(&r_home);
244199

245-
// Finally the include location. It may or may not be located under R home
246-
#[cfg(feature = "use-bindgen")]
247-
let include = get_r_include(&r_home, &library)?;
248-
249200
Ok(InstallationPaths {
250201
r_home,
251-
#[cfg(feature = "use-bindgen")]
252-
include,
253202
library,
254203
})
255204
}
@@ -377,192 +326,6 @@ fn set_r_version_vars(ver: &RVersionInfo) {
377326
println!("cargo:r_version_devel={}", ver.devel); // Becomes DEP_R_R_VERSION_DEVEL for clients
378327
}
379328

380-
#[cfg(all(feature = "use-bindgen", not(feature = "non-api")))]
381-
fn get_non_api() -> std::collections::HashSet<String> {
382-
// Several non-APIs are required for extendr-engine, so we explicitly allow
383-
// these here. If extendr-engine (or other crate) requires more non-APIs,
384-
// add it here with caution.
385-
const REQUIRED_NON_API: [&str; 6] = [
386-
"R_CStackLimit",
387-
"R_CleanTempDir",
388-
"R_RunExitFinalizers",
389-
"Rf_endEmbeddedR",
390-
"Rf_initialize_R",
391-
"setup_Rmainloop",
392-
];
393-
394-
// nonAPI.txt is generated by
395-
// Rscript -e 'cat(tools:::nonAPI, sep = "\n")' | tr -d '\r' | sort -u | tee ./nonAPI.txt
396-
let non_api = include_str!("./nonAPI.txt")
397-
.lines()
398-
.filter(|e| !REQUIRED_NON_API.contains(e))
399-
.map(|s| s.to_string());
400-
401-
std::collections::HashSet::from_iter(non_api)
402-
}
403-
404-
#[cfg(feature = "use-bindgen")]
405-
/// Generate bindings by calling bindgen.
406-
fn generate_bindings(r_paths: &InstallationPaths, version_info: &RVersionInfo) {
407-
// The bindgen::Builder is the main entry point
408-
// to bindgen, and lets you build up options for
409-
// the resulting bindings.
410-
let mut bindgen_builder = bindgen::Builder::default();
411-
412-
#[cfg(all(feature = "use-bindgen", not(feature = "non-api")))]
413-
{
414-
let blocklist = get_non_api().into_iter().collect::<Vec<_>>().join("|");
415-
bindgen_builder = bindgen_builder.blocklist_item(blocklist);
416-
}
417-
418-
bindgen_builder = bindgen_builder
419-
.emit_diagnostics()
420-
.translate_enum_integer_types(true)
421-
.merge_extern_blocks(true)
422-
// The input header we would like to generate
423-
// bindings for.
424-
.header("wrapper.h")
425-
// Tell cargo to invalidate the built crate whenever any of the
426-
// included header files changed.
427-
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
428-
429-
// Use enum-definition of `SEXPTYPE`, as it is available and compatible
430-
bindgen_builder = bindgen_builder.clang_arg("-Denum_SEXPTYPE");
431-
432-
// Collect C-enums into idiomatic Rust-style enums
433-
bindgen_builder = bindgen_builder.default_enum_style(bindgen::EnumVariation::Rust {
434-
non_exhaustive: false,
435-
});
436-
437-
if cfg!(feature = "layout_tests") {
438-
bindgen_builder = bindgen_builder.layout_tests(true);
439-
} else {
440-
bindgen_builder = bindgen_builder.layout_tests(false);
441-
}
442-
443-
let target = env::var("TARGET").expect("Could not get the target triple");
444-
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
445-
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
446-
447-
println!(
448-
"Generating bindings for target: {target}, os: {target_os}, architecture: {target_arch}"
449-
);
450-
451-
let r_include_path = r_paths.include.display().to_string().replace(r"\", r"/");
452-
let r_include_path_escaped = regex::escape(&r_include_path);
453-
let r_include_path_escaped = format!("{r_include_path_escaped}.*");
454-
455-
// Point to the correct headers
456-
bindgen_builder =
457-
bindgen_builder.clang_args([format!("-I{r_include_path}",), format!("--target={target}")]);
458-
459-
// this effectively ignores all non-R headers from sneaking in
460-
bindgen_builder = bindgen_builder
461-
.allowlist_file(r_include_path_escaped)
462-
.allowlist_file(".*wrapper\\.h$");
463-
464-
// stops warning about ignored attributes,
465-
// e.g. ignores `__format__` attributes caused by `stdio.h`
466-
bindgen_builder = bindgen_builder.clang_arg("-Wno-ignored-attributes");
467-
468-
// allow injection of an alternative include path to libclang
469-
if let Some(alt_include) = env::var_os(ENVVAR_LIBCLANG_INCLUDE_PATH) {
470-
bindgen_builder =
471-
bindgen_builder.clang_arg(format!("-I{}", PathBuf::from(alt_include).display()));
472-
}
473-
474-
// Remove constants defined by C-headers as
475-
// there are rust equivalents for them.
476-
let bindgen_builder = bindgen_builder
477-
.blocklist_item("M_E")
478-
.blocklist_item("M_LOG2E")
479-
.blocklist_item("M_LOG10E")
480-
.blocklist_item("M_LN2")
481-
.blocklist_item("M_LN10")
482-
.blocklist_item("M_PI")
483-
.blocklist_item("M_PI_2")
484-
.blocklist_item("M_PI_4")
485-
.blocklist_item("M_1_PI")
486-
.blocklist_item("M_2_PI")
487-
.blocklist_item("M_2_SQRTPI")
488-
.blocklist_item("M_SQRT2")
489-
.blocklist_item("M_SQRT1_2")
490-
.blocklist_item("M_2PI")
491-
.blocklist_item("M_LOG10_2");
492-
493-
// `VECTOR_PTR` is deprecated, use `DATAPTR` and friends instead
494-
let bindgen_builder = bindgen_builder.blocklist_item("VECTOR_PTR");
495-
496-
// Remove all Fortran items, these are items with underscore _ postfix
497-
let bindgen_builder = bindgen_builder.blocklist_item("[A-Za-z_][A-Za-z0-9_]*[^_]_$");
498-
499-
// Ensure that `SEXPREC` is opaque to Rust
500-
let bindgen_builder = bindgen_builder.blocklist_item("SEXPREC");
501-
502-
// Replace `TYPEOF` definition with one that gives same type as `SEXPTYPE`.
503-
let bindgen_builder = bindgen_builder.blocklist_item("TYPEOF");
504-
505-
// Replace this with a function that handles enum-version of SEXPTYPE correctly
506-
let bindgen_builder = bindgen_builder.blocklist_item("Rf_isS4");
507-
508-
// Replace second arg with SEXPTYPE, see lib.rs
509-
let bindgen_builder = bindgen_builder.blocklist_type("R_altrep_Coerce_method_t");
510-
511-
// Extra, and unnecessary item being generated by bindgen
512-
let bindgen_builder = bindgen_builder.blocklist_item("Rcomplex__bindgen_ty_1");
513-
514-
// Finish the builder and generate the bindings.
515-
let bindings = bindgen_builder
516-
.raw_line(format!(
517-
"/* libR-sys version: {} */",
518-
env!("CARGO_PKG_VERSION")
519-
))
520-
.raw_line(format!(
521-
"/* bindgen clang version: {} */",
522-
bindgen::clang_version().full
523-
))
524-
.raw_line(format!("/* r version: {} */", version_info.full))
525-
.generate_comments(true)
526-
.parse_callbacks(Box::new(TrimCommentsCallbacks))
527-
.clang_arg("-fparse-all-comments")
528-
.generate()
529-
// Unwrap the Result and panic on failure.
530-
.expect("Unable to generate bindings");
531-
532-
// Write the bindings to the $OUT_DIR/bindings.rs file.
533-
let out_path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
534-
535-
bindings
536-
.write_to_file(out_path.join("bindings.rs"))
537-
.expect("Couldn't write bindings to default output path!");
538-
539-
// Also write the bindings to a folder specified by `LIBRSYS_BINDINGS_OUTPUT_PATH`, if defined
540-
if let Some(alt_target) = env::var_os(ENVVAR_BINDINGS_OUTPUT_PATH) {
541-
let out_path = PathBuf::from(alt_target);
542-
// if folder doesn't exist, try to create it
543-
if !out_path.exists() {
544-
fs::create_dir(&out_path).unwrap_or_else(|_| {
545-
panic!(
546-
"Couldn't create output directory for bindings: {}",
547-
out_path.display()
548-
)
549-
});
550-
}
551-
552-
let bindings_file_full = version_info.get_r_bindings_filename(&target_os, &target_arch);
553-
let out_file = out_path.join(bindings_file_full);
554-
555-
bindings
556-
.write_to_file(&out_file)
557-
.unwrap_or_else(|_| panic!("Couldn't write bindings: {}", out_file.display()));
558-
} else {
559-
println!(
560-
"cargo:warning=Couldn't write the bindings since `LIBRSYS_BINDINGS_OUTPUT_PATH` is not set."
561-
);
562-
}
563-
}
564-
565-
#[cfg(not(feature = "use-bindgen"))]
566329
/// Retrieve bindings from cache, if available. Errors out otherwise.
567330
fn retrieve_prebuild_bindings(version_info: &RVersionInfo) {
568331
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
@@ -599,25 +362,6 @@ fn retrieve_prebuild_bindings(version_info: &RVersionInfo) {
599362
println!("cargo:rerun-if-changed={}", from.display());
600363
}
601364

602-
/// Provide extra cleaning of the processed elements in the headers.
603-
#[cfg(feature = "use-bindgen")]
604-
#[derive(Debug)]
605-
struct TrimCommentsCallbacks;
606-
607-
#[cfg(feature = "use-bindgen")]
608-
impl bindgen::callbacks::ParseCallbacks for TrimCommentsCallbacks {
609-
fn process_comment(&self, comment: &str) -> Option<String> {
610-
// trim comments
611-
let comment = comment.trim();
612-
613-
// replace bare brackets in comments
614-
let comment = comment.replace('[', r"\[");
615-
let comment = comment.replace(']', r"\]");
616-
617-
Some(comment)
618-
}
619-
}
620-
621365
fn main() {
622366
let r_paths = probe_r_paths();
623367

@@ -652,8 +396,5 @@ fn main() {
652396
get_r_version(ENVVAR_R_VERSION, &r_paths).expect("Could not obtain R version");
653397
set_r_version_vars(&version_info);
654398

655-
#[cfg(feature = "use-bindgen")]
656-
generate_bindings(&r_paths, &version_info);
657-
#[cfg(not(feature = "use-bindgen"))]
658399
retrieve_prebuild_bindings(&version_info);
659400
}

0 commit comments

Comments
 (0)