use std::{env, fs};
use bindgen::callbacks::ParseCallbacks;
use camino::Utf8PathBuf;
#[derive(Debug)]
struct BashCallback;
impl ParseCallbacks for BashCallback {
fn item_name(&self, original_item_name: &str) -> Option<String> {
match original_item_name {
"word_desc" => Some("WordDesc".into()),
"WORD_DESC" => Some("WordDesc".into()),
"word_list" => Some("WordList".into()),
"WORD_LIST" => Some("WordList".into()),
"SHELL_VAR" => Some("ShellVar".into()),
"ARRAY" => Some("Array".into()),
"command" => Some("Command".into()),
"builtin" => Some("Builtin".into()),
"global_command" => Some("GLOBAL_COMMAND".into()),
"this_command_name" => Some("CURRENT_COMMAND".into()),
"temporary_env" => Some("TEMPORARY_ENV".into()),
"ifs_value" => Some("IFS".into()),
"shell_builtins" => Some("SHELL_BUILTINS".into()),
"num_shell_builtins" => Some("NUM_SHELL_BUILTINS".into()),
"subshell_level" => Some("SUBSHELL_LEVEL".into()),
"executing_builtin" => Some("BUILTIN_LEVEL".into()),
"restricted" => Some("RESTRICTED".into()),
"restricted_shell" => Some("RESTRICTED_SHELL".into()),
"dist_version" => Some("DIST_VERSION".into()),
"patch_level" => Some("PATCH_LEVEL".into()),
"get_minus_o_opts" => Some("get_set_options".into()),
_ => None,
}
}
}
fn main() {
let repo_dir = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let bash_repo_dir = repo_dir.join("bash");
let out_dir = Utf8PathBuf::from(env::var("OUT_DIR").unwrap());
let bash_out_dir = out_dir.join("bash");
let bash_build_dir = bash_out_dir.join("build");
fs::create_dir_all(&bash_out_dir).unwrap();
let mut bash = autotools::Config::new(&bash_repo_dir);
bash.make_args(vec![format!("-j{}", num_cpus::get())])
.out_dir(&bash_out_dir)
.forbid("--disable-shared")
.forbid("--enable-static")
.disable("readline", None)
.disable("history", None)
.disable("bang-history", None)
.disable("progcomp", None)
.without("bash-malloc", None)
.disable("mem-scramble", None)
.disable("net-redirections", None)
.disable("nls", None)
.enable("job-control", None)
.enable("restricted", None);
if cfg!(feature = "plugin") {
bash.make_target("pathnames.h").build();
} else {
bash.enable("library", None)
.make_target("libbash.a")
.build();
println!("cargo:rustc-link-search=native={bash_build_dir}");
println!("cargo:rustc-link-lib=static=bash");
}
let config_status = bash_out_dir.join("config.status");
if config_status.exists() {
fs::remove_file(config_status).expect("failed removing config.status");
}
#[rustfmt::skip]
let bindings = bindgen::Builder::default()
.clang_arg(format!("-I{bash_build_dir}"))
.clang_arg(format!("-I{bash_repo_dir}"))
.clang_arg(format!("-I{bash_repo_dir}/include"))
.clang_arg(format!("-I{bash_repo_dir}/builtins"))
.header("bash/builtins.h")
.allowlist_var("BUILTIN_ENABLED")
.allowlist_var("STATIC_BUILTIN")
.allowlist_var("ASSIGNMENT_BUILTIN")
.allowlist_var("LOCALVAR_BUILTIN")
.allowlist_var("num_shell_builtins")
.allowlist_var("shell_builtins")
.header("bash/shell.h")
.allowlist_function("bash_main")
.allowlist_function("lib_error_handlers")
.allowlist_function("lib_init")
.allowlist_function("lib_reset")
.allowlist_function("scallop_toggle_restricted")
.allowlist_function("set_shell_name")
.allowlist_var("shell_name")
.allowlist_var("dist_version")
.allowlist_var("patch_level")
.allowlist_var("EXECUTION_FAILURE")
.allowlist_var("EXECUTION_SUCCESS")
.allowlist_var("EX_LONGJMP")
.header("bash/builtins/common.h")
.allowlist_function("scallop_evalstring")
.allowlist_function("scallop_source_file")
.allowlist_function("register_builtins")
.allowlist_function("builtin_address_internal")
.allowlist_function("get_minus_o_opts")
.allowlist_function("get_shopt_options")
.allowlist_var("SEVAL_.*")
.header("bash/command.h")
.allowlist_type("word_desc")
.allowlist_type("word_list")
.allowlist_var("global_command")
.allowlist_function("copy_command")
.allowlist_var("CMD_.*")
.header("bash/execute_cmd.h")
.allowlist_var("this_command_name")
.allowlist_var("subshell_level")
.allowlist_var("executing_builtin")
.allowlist_function("execute_command")
.allowlist_function("scallop_execute_shell_function")
.header("bash/variables.h")
.allowlist_function("get_string_value")
.allowlist_function("bind_variable")
.allowlist_function("bind_global_variable")
.allowlist_function("unbind_variable")
.allowlist_function("check_unbind_variable")
.allowlist_function("find_function")
.allowlist_function("find_variable")
.allowlist_function("push_context")
.allowlist_function("pop_context")
.allowlist_var("temporary_env")
.allowlist_var("att_.*")
.header("bash/externs.h")
.allowlist_function("parse_command")
.allowlist_function("strvec_dispose")
.allowlist_function("strvec_to_word_list")
.header("bash/input.h")
.allowlist_function("with_input_from_string")
.allowlist_function("push_stream")
.allowlist_function("pop_stream")
.header("bash/dispose_cmd.h")
.allowlist_function("dispose_command")
.allowlist_function("dispose_words")
.header("bash/subst.h")
.allowlist_function("expand_string_to_string")
.allowlist_function("list_string")
.allowlist_var("ifs_value")
.allowlist_var("ASS_.*")
.header("bash/array.h")
.allowlist_function("array_to_argv")
.header("bash/flags.h")
.allowlist_var("restricted")
.allowlist_var("restricted_shell")
.header("bash/xmalloc.h")
.allowlist_function("xfree")
.parse_callbacks(Box::new(BashCallback))
.generate()
.expect("unable to generate bindings");
bindings
.write_to_file(out_dir.join("bash-bindings.rs"))
.expect("failed writing bindings");
println!("cargo:rerun-if-changed=bash");
}