From 167d83ca4521e7e604747142d2a2b79ecb3f7677 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Fri, 14 May 2021 21:36:50 -0600 Subject: provide stdlib --- Cargo.toml | 2 ++ build.rs | 21 +++++++++++++++++++++ src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 53943d1..d4f9960 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,9 @@ categories = ["external-ffi-bindings"] links = "tcl" [dependencies] +flate2 = "1.0.20" libc = "0.2.94" +tar = "0.4.33" [build-dependencies] bindgen = "0.58.1" diff --git a/build.rs b/build.rs index acbf264..8baa99f 100644 --- a/build.rs +++ b/build.rs @@ -199,6 +199,7 @@ fn main() { generate_bindings(target.as_str(), host.as_str(), include_paths.as_slice()); link_tcl(target_os); + compress_stdlib(tcl_lib_path.join("tcl8.6")); println!("cargo:rerun-if-changed=build.rs"); } @@ -250,6 +251,26 @@ fn generate_bindings(target: &str, host: &str, headers_paths: &[String]) { .expect("Couldn't write bindings!"); } +fn compress_stdlib(stdlib_root: PathBuf) { + let out_dir = env::var("OUT_DIR").unwrap(); + let tcl_stdlib_archive_path = Path::new(&out_dir).join("stdlib.tar.gz"); + let writer = flate2::write::GzEncoder::new( + fs::File::create(tcl_stdlib_archive_path).unwrap(), + flate2::Compression::best(), + ); + let mut ar = tar::Builder::new(writer); + + for child in stdlib_root.read_dir().unwrap() { + let child = child.unwrap(); + if child.path().is_dir() { + ar.append_dir_all(child.file_name(), child.path()).unwrap(); + } else { + ar.append_path_with_name(child.path(), child.file_name()) + .unwrap(); + } + } +} + fn get_os_from_triple(triple: &str) -> Option<&str> { triple.splitn(3, "-").nth(2) } diff --git a/src/lib.rs b/src/lib.rs index d403fbc..a1dfc93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,38 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +mod stdlib { + use std::env::current_exe; + use std::io; + use std::path::{Path, PathBuf}; + + const STDLIB_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/stdlib.tar.gz")); + + /// Guess where tcl will look for the standard library (from the directory containing the current executable, ../lib/tcl8.6/). + pub fn guess_expected_stdlib_target() -> PathBuf { + let exe = current_exe().unwrap(); + let exe_parent = exe.parent().unwrap(); + let exe_parent_parent = exe_parent.parent().unwrap(); + let exe_parent_parent_lib = exe_parent_parent.join("lib"); + let expected_stdlib_path = exe_parent_parent_lib.join("tcl8.6"); + expected_stdlib_path + } + + /// Extract the built-in standard library to the given path, ignoring if already exists. + pub fn extract_stdlib_to(target: impl AsRef) -> io::Result<()> { + let reader = flate2::read::GzDecoder::new(STDLIB_DATA); + let mut ar = tar::Archive::new(reader); + ar.set_overwrite(false); + let result = ar.unpack(target); + match result { + Err(e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()), + x => x, + } + } +} + +pub use stdlib::*; + #[cfg(test)] mod tests { use super::*; @@ -12,6 +44,7 @@ mod tests { #[test] fn two_plus_two() { unsafe { + extract_stdlib_to(guess_expected_stdlib_target()).unwrap(); let interp = Tcl_CreateInterp(); let init_status = Tcl_Init(interp); if init_status == TCL_ERROR as i32 { -- cgit v1.2.3