diff --git a/Cargo.lock b/Cargo.lock index 0dd230b58..ce7dbe514 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -642,11 +642,11 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys 0.5.0", ] [[package]] @@ -656,20 +656,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", - "windows-sys 0.48.0", + "redox_users 0.5.2", + "windows-sys 0.60.2", ] [[package]] @@ -1830,31 +1830,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" -[[package]] -name = "redox-pkg" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f49b2c29ae3c72ff3a8dbc1c5eefba9093a8c5ceaa8ca5292833816fe931e2" -dependencies = [ - "anyhow", - "indicatif", - "pkgar 0.1.18", - "pkgar-core 0.1.18", - "pkgar-keys 0.1.18", - "reqwest", - "serde", - "serde_derive", - "thiserror 1.0.69", - "toml 0.8.23", -] - [[package]] name = "redox-pkg" version = "0.2.8" -source = "git+https://gitlab.redox-os.org/redox-os/pkgutils#ea2641126c8b38d7b8997c5c7ebec74999fc9578" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d50a7ed267e236ce8bfa178bfd3fb7a39765689d037e51c57f36bad46f474fd" dependencies = [ "anyhow", "ignore", + "indicatif", "pkgar 0.1.18", "pkgar-core 0.1.18", "pkgar-keys 0.1.18", @@ -1886,7 +1870,7 @@ dependencies = [ "pkgar 0.1.19", "pkgar-core 0.1.19", "pkgar-keys 0.1.19", - "redox-pkg 0.2.8", + "redox-pkg", "redoxer", "regex", "serde", @@ -1913,7 +1897,7 @@ dependencies = [ "pkgar-core 0.1.18", "pkgar-keys 0.1.18", "rand 0.8.5", - "redox-pkg 0.2.5", + "redox-pkg", "redox_liner", "redox_syscall", "redoxfs", @@ -1965,18 +1949,29 @@ dependencies = [ ] [[package]] -name = "redoxer" -version = "0.2.53" +name = "redox_users" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5558ef5ce386b5a4b7d5276467d940023b31c5a85484a05ac35b31da4055d740" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "dirs 5.0.1", + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.12", +] + +[[package]] +name = "redoxer" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ead20eb76f54e16ecc3e678daca948d49497588a777148ac60d99a67dab8a2b0" +dependencies = [ + "dirs 6.0.0", "proc-mounts", "redox_installer", "redox_syscall", "redoxfs", "tempfile", - "toml 0.5.11", + "toml 0.9.5", ] [[package]] @@ -2274,6 +2269,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2547,15 +2551,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.8.23" @@ -2563,11 +2558,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit", ] +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -2577,6 +2587,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -2585,18 +2604,33 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + [[package]] name = "toml_write" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "tower" version = "0.5.2" @@ -2924,7 +2958,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -3001,15 +3035,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -3052,21 +3077,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -3105,12 +3115,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3129,12 +3133,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3153,12 +3151,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3189,12 +3181,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3213,12 +3199,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3237,12 +3217,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3261,12 +3235,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index b334424a6..e163cf5e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "redox_cookbook" version = "0.1.0" authors = ["Jeremy Soller "] -edition = "2018" +edition = "2024" default-run = "cook" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -28,7 +28,7 @@ pbr = "1.0.2" pkgar = { path = "pkgar/pkgar" } pkgar-core = { path = "pkgar/pkgar-core" } pkgar-keys = { path = "pkgar/pkgar-keys" } -redox-pkg = { git = "https://gitlab.redox-os.org/redox-os/pkgutils" } +redox-pkg = "0.2.8" redoxer = "0.2" regex = "1.11" serde = { version = "=1.0.197", features = ["derive"] } diff --git a/src/bin/cook.rs b/src/bin/cook.rs index 45787cc0c..7bc3502fe 100644 --- a/src/bin/cook.rs +++ b/src/bin/cook.rs @@ -1,629 +1,23 @@ -use cookbook::blake3::blake3_progress; -use cookbook::config::{init_config, translate_mirror}; -use cookbook::recipe::{AutoDeps, BuildKind, CookRecipe, Recipe, SourceRecipe}; +use cookbook::config::init_config; +use cookbook::cook::fetch::*; +use cookbook::cook::fs::*; +use cookbook::cook::script::SHARED_PRESCRIPT; +use cookbook::recipe::{AutoDeps, BuildKind, CookRecipe, Recipe}; use pkg::package::Package; -use pkg::{recipes, PackageName}; -use serde::Serialize; +use pkg::{PackageName, recipes}; use std::collections::VecDeque; use std::convert::TryInto; use std::{ collections::BTreeSet, env, fs, - io::{self, Write}, path::{Path, PathBuf}, - process::{self, Command, Stdio}, + process::{self, Command}, str, time::SystemTime, }; use termion::{color, style}; -use walkdir::{DirEntry, WalkDir}; -use cookbook::{is_redox, WALK_DEPTH}; - -fn remove_all(path: &Path) -> Result<(), String> { - if path.is_dir() { - fs::remove_dir_all(path) - } else { - fs::remove_file(path) - } - .map_err(|err| format!("failed to remove '{}': {}\n{:?}", path.display(), err, err)) -} - -fn create_dir(dir: &Path) -> Result<(), String> { - fs::create_dir(dir) - .map_err(|err| format!("failed to create '{}': {}\n{:?}", dir.display(), err, err)) -} - -fn create_dir_clean(dir: &Path) -> Result<(), String> { - if dir.is_dir() { - remove_all(dir)?; - } - create_dir(dir) -} - -fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { - fs::create_dir_all(&dst)?; - for entry in fs::read_dir(src)? { - let entry = entry?; - let ty = entry.file_type()?; - if ty.is_dir() { - copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; - } else { - fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; - } - } - Ok(()) -} - -fn symlink(original: impl AsRef, link: impl AsRef) -> Result<(), String> { - std::os::unix::fs::symlink(&original, &link).map_err(|err| { - format!( - "failed to symlink '{}' to '{}': {}\n{:?}", - original.as_ref().display(), - link.as_ref().display(), - err, - err - ) - }) -} - -fn modified(path: &Path) -> Result { - let metadata = fs::metadata(path).map_err(|err| { - format!( - "failed to get metadata of '{}': {}\n{:#?}", - path.display(), - err, - err - ) - })?; - metadata.modified().map_err(|err| { - format!( - "failed to get modified time of '{}': {}\n{:#?}", - path.display(), - err, - err - ) - }) -} - -fn modified_dir_inner bool>( - dir: &Path, - filter: F, -) -> io::Result { - let mut newest = fs::metadata(dir)?.modified()?; - for entry_res in WalkDir::new(dir).into_iter().filter_entry(filter) { - let entry = entry_res?; - let modified = entry.metadata()?.modified()?; - if modified > newest { - newest = modified; - } - } - Ok(newest) -} - -fn modified_dir(dir: &Path) -> Result { - modified_dir_inner(dir, |_| true).map_err(|err| { - format!( - "failed to get modified time of '{}': {}\n{:#?}", - dir.display(), - err, - err - ) - }) -} - -fn modified_dir_ignore_git(dir: &Path) -> Result { - modified_dir_inner(dir, |entry| { - entry - .file_name() - .to_str() - .map(|s| s != ".git") - .unwrap_or(true) - }) - .map_err(|err| { - format!( - "failed to get modified time of '{}': {}\n{:#?}", - dir.display(), - err, - err - ) - }) -} - -fn rename(src: &Path, dst: &Path) -> Result<(), String> { - fs::rename(src, dst).map_err(|err| { - format!( - "failed to rename '{}' to '{}': {}\n{:?}", - src.display(), - dst.display(), - err, - err - ) - }) -} - -fn run_command(mut command: process::Command) -> Result<(), String> { - let status = command - .status() - .map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?; - - if !status.success() { - return Err(format!( - "failed to run {:?}: exited with status {}", - command, status - )); - } - - Ok(()) -} - -fn run_command_stdin(mut command: process::Command, stdin_data: &[u8]) -> Result<(), String> { - command.stdin(Stdio::piped()); - - let mut child = command - .spawn() - .map_err(|err| format!("failed to spawn {:?}: {}\n{:#?}", command, err, err))?; - - if let Some(ref mut stdin) = child.stdin { - stdin.write_all(stdin_data).map_err(|err| { - format!( - "failed to write stdin of {:?}: {}\n{:#?}", - command, err, err - ) - })?; - } else { - return Err(format!("failed to find stdin of {:?}", command)); - } - - let status = child - .wait() - .map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?; - - if !status.success() { - return Err(format!( - "failed to run {:?}: exited with status {}", - command, status - )); - } - - Ok(()) -} - -fn serialize_and_write(file_path: &Path, content: &T) -> Result<(), String> { - let toml_content = toml::to_string(content).map_err(|err| { - format!( - "Failed to serialize content for '{}': {}", - file_path.display(), - err - ) - })?; - - fs::write(file_path, toml_content) - .map_err(|err| format!("Failed to write to file '{}': {}", file_path.display(), err))?; - Ok(()) -} - -static SHARED_PRESCRIPT: &str = r#" -# Build dynamically -function DYNAMIC_INIT { - COOKBOOK_AUTORECONF="autoreconf" - autotools_recursive_regenerate() { - for f in $(find . -name configure.ac -o -name configure.in -type f | sort); do - echo "* autotools regen in '$(dirname $f)'..." - ( cd "$(dirname "$f")" && "${COOKBOOK_AUTORECONF}" -fvi "$@" -I${COOKBOOK_HOST_SYSROOT}/share/aclocal ) - done - } - - if [ "${TARGET}" != "x86_64-unknown-redox" ] - then - echo "WARN: ${TARGET} does not support dynamic linking." >&2 - return - fi - - echo "DEBUG: Program is being compiled dynamically." - - COOKBOOK_CONFIGURE_FLAGS=( - --host="${GNU_TARGET}" - --prefix="/usr" - --enable-shared - --disable-static - ) - - COOKBOOK_CMAKE_FLAGS=( - -DBUILD_SHARED_LIBS=True - -DENABLE_SHARED=True - -DENABLE_STATIC=False - ) - - COOKBOOK_MESON_FLAGS=( - --buildtype release - --wrap-mode nofallback - --strip - -Ddefault_library=shared - -Dprefix=/usr - ) - - # TODO: check paths for spaces - export LDFLAGS="-Wl,-rpath-link,${COOKBOOK_SYSROOT}/lib -L${COOKBOOK_SYSROOT}/lib" - export RUSTFLAGS="-C target-feature=-crt-static" - export COOKBOOK_DYNAMIC=1 -} - -# Build both dynamically and statically -function DYNAMIC_STATIC_INIT { - DYNAMIC_INIT - if [ "${COOKBOOK_DYNAMIC}" == "1" ] - then - COOKBOOK_CONFIGURE_FLAGS=( - --host="${GNU_TARGET}" - --prefix="/usr" - --enable-shared - --enable-static - ) - - COOKBOOK_CMAKE_FLAGS=( - -DBUILD_SHARED_LIBS=True - -DENABLE_SHARED=True - -DENABLE_STATIC=True - ) - - COOKBOOK_MESON_FLAGS=( - --buildtype release - --wrap-mode nofallback - --strip - -Ddefault_library=both - -Dprefix=/usr - ) - fi -} - -function GNU_CONFIG_GET { - wget -O "$1" "https://gitlab.redox-os.org/redox-os/gnu-config/-/raw/master/config.sub?inline=false" -} -"#; - -fn fetch_offline(recipe_dir: &Path, source: &Option) -> Result { - let source_dir = recipe_dir.join("source"); - match source { - Some(SourceRecipe::SameAs { same_as: _ }) | Some(SourceRecipe::Path { path: _ }) | None => { - return fetch(recipe_dir, source); - } - Some(SourceRecipe::Git { - git: _, - upstream: _, - branch: _, - rev: _, - patches: _, - script: _, - shallow_clone: _, - }) - | Some(SourceRecipe::Tar { - tar: _, - blake3: _, - patches: _, - script: _, - }) => { - if !source_dir.is_dir() { - return Err(format!( - "'{dir}' is not exist and unable to continue in offline mode", - dir = source_dir.display(), - )); - } - } - } - - Ok(source_dir) -} - -fn fetch(recipe_dir: &Path, source: &Option) -> Result { - let source_dir = recipe_dir.join("source"); - match source { - Some(SourceRecipe::SameAs { same_as }) => { - if !source_dir.is_symlink() { - if source_dir.is_dir() { - return Err(format!( - "'{dir}' is a directory, but recipe indicated a symlink. \n\ - try removing '{dir}' if you haven't made any changes that would be lost", - dir = source_dir.display(), - )); - } - let original = Path::new(same_as).join("source"); - std::os::unix::fs::symlink(&original, &source_dir).map_err(|err| { - format!( - "failed to symlink '{}' to '{}': {}\n{:?}", - original.display(), - source_dir.display(), - err, - err - ) - })?; - } - } - Some(SourceRecipe::Path { path }) => { - if !source_dir.is_dir() || modified_dir(Path::new(path))? > modified_dir(&source_dir)? { - eprintln!("[DEBUG]: {} is newer than {}", path, source_dir.display()); - copy_dir_all(path, &source_dir).map_err(|e| { - format!( - "Couldn't copy source from {} to {}: {}", - path, - source_dir.display(), - e - ) - })?; - } - } - Some(SourceRecipe::Git { - git, - upstream, - branch, - rev, - patches, - script, - shallow_clone, - }) => { - //TODO: use libgit? - let shallow_clone = *shallow_clone == Some(true); - if !source_dir.is_dir() { - // Create source.tmp - let source_dir_tmp = recipe_dir.join("source.tmp"); - create_dir_clean(&source_dir_tmp)?; - - // Clone the repository to source.tmp - let mut command = Command::new("git"); - command - .arg("clone") - .arg("--recursive") - .arg(translate_mirror(git)); - if let Some(branch) = branch { - command.arg("--branch").arg(branch); - } - if shallow_clone { - command.arg("--depth").arg("1").arg("--shallow-submodules"); - } - command.arg(&source_dir_tmp); - run_command(command)?; - - // Move source.tmp to source atomically - rename(&source_dir_tmp, &source_dir)?; - } else if !shallow_clone { - // Don't let this code reset the origin for the cookbook repo - let source_git_dir = source_dir.join(".git"); - if !source_git_dir.is_dir() { - return Err(format!( - "'{}' is not a git repository, but recipe indicated git source", - source_dir.display(), - )); - } - - // Reset origin - let mut command = Command::new("git"); - command.arg("-C").arg(&source_dir); - command.arg("remote").arg("set-url").arg("origin").arg(git); - run_command(command)?; - - // Fetch origin - let mut command = Command::new("git"); - command.arg("-C").arg(&source_dir); - command.arg("fetch").arg("origin"); - run_command(command)?; - } - - if let Some(_upstream) = upstream { - //TODO: set upstream URL - // git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null || - // git remote add upstream "$GIT_UPSTREAM" - // git fetch upstream - } - - if let Some(rev) = rev { - // Check out specified revision - let mut command = Command::new("git"); - command.arg("-C").arg(&source_dir); - command.arg("checkout").arg(rev); - run_command(command)?; - } else if !shallow_clone && !is_redox() { - //TODO: complicated stuff to check and reset branch to origin - //TODO: redox can't undestand this (got exit status 1) - let mut command = Command::new("bash"); - command.arg("-c").arg( - r#" -ORIGIN_BRANCH="$(git branch --remotes | grep '^ origin/HEAD -> ' | cut -d ' ' -f 5-)" -if [ -n "$BRANCH" ] -then - ORIGIN_BRANCH="origin/$BRANCH" -fi - -if [ "$(git rev-parse HEAD)" != "$(git rev-parse $ORIGIN_BRANCH)" ] -then - git checkout -B "$(echo "$ORIGIN_BRANCH" | cut -d / -f 2-)" "$ORIGIN_BRANCH" -fi"#, - ); - if let Some(branch) = branch { - command.env("BRANCH", branch); - } - command.current_dir(&source_dir); - run_command(command)?; - } - - if !patches.is_empty() || script.is_some() { - // Hard reset - let mut command = Command::new("git"); - command.arg("-C").arg(&source_dir); - command.arg("reset").arg("--hard"); - run_command(command)?; - } - - if !shallow_clone { - // Sync submodules URL - let mut command = Command::new("git"); - command.arg("-C").arg(&source_dir); - command.arg("submodule").arg("sync").arg("--recursive"); - run_command(command)?; - - // Update submodules - let mut command = Command::new("git"); - command.arg("-C").arg(&source_dir); - command - .arg("submodule") - .arg("update") - .arg("--init") - .arg("--recursive"); - run_command(command)?; - } - - // Apply patches - for patch_name in patches { - let patch_file = recipe_dir.join(patch_name); - if !patch_file.is_file() { - return Err(format!( - "failed to find patch file '{}'", - patch_file.display() - )); - } - - let patch = fs::read_to_string(&patch_file).map_err(|err| { - format!( - "failed to read patch file '{}': {}\n{:#?}", - patch_file.display(), - err, - err - ) - })?; - - let mut command = Command::new("patch"); - command.arg("--forward"); - command.arg("--batch"); - command.arg("--directory").arg(&source_dir); - command.arg("--strip=1"); - run_command_stdin(command, patch.as_bytes())?; - } - - // Run source script - if let Some(script) = script { - let mut command = Command::new("bash"); - command.arg("-ex"); - command.current_dir(&source_dir); - run_command_stdin(command, format!("{SHARED_PRESCRIPT}\n{script}").as_bytes())?; - } - } - Some(SourceRecipe::Tar { - tar, - blake3, - patches, - script, - }) => { - if !source_dir.is_dir() { - // Download tar - //TODO: replace wget - let source_tar = recipe_dir.join("source.tar"); - if !source_tar.is_file() { - let source_tar_tmp = recipe_dir.join("source.tar.tmp"); - - let mut command = Command::new("wget"); - command.arg(translate_mirror(tar)); - command.arg("--continue").arg("-O").arg(&source_tar_tmp); - run_command(command)?; - - // Move source.tar.tmp to source.tar atomically - rename(&source_tar_tmp, &source_tar)?; - } - - // Calculate blake3 - let source_tar_blake3 = blake3_progress(&source_tar).map_err(|err| { - format!( - "failed to calculate blake3 of '{}': {}\n{:?}", - source_tar.display(), - err, - err - ) - })?; - if let Some(blake3) = blake3 { - // Check if it matches recipe - if &source_tar_blake3 != blake3 { - return Err(format!( - "calculated blake3 '{}' does not match recipe blake3 '{}'", - source_tar_blake3, blake3 - )); - } - } else { - //TODO: set blake3 hash on the recipe with something like "cook fix" - eprintln!( - "WARNING: set blake3 for '{}' to '{}'", - source_tar.display(), - source_tar_blake3 - ); - } - - // Create source.tmp - let source_dir_tmp = recipe_dir.join("source.tmp"); - create_dir_clean(&source_dir_tmp)?; - - // Extract tar to source.tmp - //TODO: use tar crate (how to deal with compression?) - let mut command = Command::new("tar"); - if is_redox() { - command.arg("xvf"); - } else { - command.arg("--extract"); - command.arg("--verbose"); - command.arg("--file"); - } - command.arg(&source_tar); - command.arg("--directory").arg(&source_dir_tmp); - command.arg("--strip-components").arg("1"); - run_command(command)?; - - // Apply patches - for patch_name in patches { - let patch_file = recipe_dir.join(patch_name); - if !patch_file.is_file() { - return Err(format!( - "failed to find patch file '{}'", - patch_file.display() - )); - } - - let patch = fs::read_to_string(&patch_file).map_err(|err| { - format!( - "failed to read patch file '{}': {}\n{:#?}", - patch_file.display(), - err, - err - ) - })?; - - let mut command = Command::new("patch"); - command.arg("--directory").arg(&source_dir_tmp); - command.arg("--strip=1"); - run_command_stdin(command, patch.as_bytes())?; - } - - // Run source script - if let Some(script) = script { - let mut command = Command::new("bash"); - command.arg("-ex"); - command.current_dir(&source_dir_tmp); - run_command_stdin(command, format!("{SHARED_PRESCRIPT}\n{script}").as_bytes())?; - } - - // Move source.tmp to source atomically - rename(&source_dir_tmp, &source_dir)?; - } - } - // Local Sources - None => { - if !source_dir.is_dir() { - eprintln!( - "WARNING: Recipe without source section expected source dir at '{}'", - source_dir.display(), - ); - create_dir(&source_dir)?; - } - } - } - - Ok(source_dir) -} +use cookbook::{WALK_DEPTH, is_redox}; fn auto_deps( stage_dir: &Path, diff --git a/src/bin/repo_builder.rs b/src/bin/repo_builder.rs index 8f1f0ac9c..a94123df1 100644 --- a/src/bin/repo_builder.rs +++ b/src/bin/repo_builder.rs @@ -1,5 +1,5 @@ use cookbook::WALK_DEPTH; -use pkg::{recipes, Package, PackageName}; +use pkg::{Package, PackageName, recipes}; use std::collections::{BTreeMap, HashMap}; use std::env; use std::fs::{self, File}; diff --git a/src/blake3.rs b/src/blake3.rs index 746b829d1..0ac193180 100644 --- a/src/blake3.rs +++ b/src/blake3.rs @@ -21,3 +21,11 @@ pub fn blake3_progress>(path: P) -> Result { Ok(res) } + +pub fn blake3_silent>(path: P) -> Result { + let mut f = fs::File::open(&path)?; + + let hash = Hasher::new().update_reader(&mut f)?.finalize(); + let res = format!("{}", hash.to_hex()); + Ok(res) +} diff --git a/src/cook.rs b/src/cook.rs new file mode 100644 index 000000000..e50800384 --- /dev/null +++ b/src/cook.rs @@ -0,0 +1,6 @@ +pub mod fetch; +pub mod fs; +pub mod script; +//TODO: Move rest of cook functions here in the next refactor +//pub mod build; +//pub mod package; diff --git a/src/cook/fetch.rs b/src/cook/fetch.rs new file mode 100644 index 000000000..21f45daa0 --- /dev/null +++ b/src/cook/fetch.rs @@ -0,0 +1,429 @@ +use crate::config::translate_mirror; +use crate::cook::fs::*; +use crate::cook::script::*; +use crate::is_redox; +use crate::recipe::Recipe; +use crate::{blake3, recipe::SourceRecipe}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +pub(crate) fn get_blake3(path: &PathBuf, show_progress: bool) -> Result { + if show_progress { + blake3::blake3_progress(&path) + } else { + blake3::blake3_silent(&path) + } + .map_err(|err| { + format!( + "failed to calculate blake3 of '{}': {}\n{:?}", + path.display(), + err, + err + ) + }) +} + +pub fn fetch_offline(recipe_dir: &Path, source: &Option) -> Result { + let source_dir = recipe_dir.join("source"); + match source { + Some(SourceRecipe::Path { path: _ }) | None => { + return fetch(recipe_dir, source); + } + Some(SourceRecipe::SameAs { same_as: _ }) => { + return fetch(recipe_dir, source); + } + Some(SourceRecipe::Git { + git: _, + upstream: _, + branch: _, + rev: _, + patches: _, + script: _, + shallow_clone: _, + }) => { + if !source_dir.is_dir() { + return Err(format!( + "'{dir}' is not exist and unable to continue in offline mode", + dir = source_dir.display(), + )); + } + } + Some(SourceRecipe::Tar { + tar: _, + blake3, + patches, + script, + }) => { + if !source_dir.is_dir() { + let source_tar = recipe_dir.join("source.tar"); + let source_tar_blake3 = get_blake3(&source_tar, true)?; + if source_tar.exists() { + if let Some(blake3) = blake3 { + if source_tar_blake3 != *blake3 { + return Err(format!( + "The downloaded tar blake3 '{source_tar_blake3}' is not equal to blake3 in recipe.toml." + )); + } + fetch_extract_tar(source_tar, &source_dir)?; + fetch_apply_patches(recipe_dir, patches, script, &source_dir)?; + } else { + // need to trust this tar file + return Err(format!( + "Please add blake3 = \"{source_tar_blake3}\" to '{recipe}'", + recipe = recipe_dir.join("recipe.toml").display(), + )); + } + } else { + return Err(format!( + "'{dir}' is not exist and unable to continue in offline mode", + dir = source_dir.display(), + )); + } + } + } + } + + Ok(source_dir) +} + +pub fn fetch(recipe_dir: &Path, source: &Option) -> Result { + let source_dir = recipe_dir.join("source"); + match source { + Some(SourceRecipe::SameAs { same_as }) => { + let (canon_dir, recipe) = fetch_resolve_canon(recipe_dir, same_as)?; + // recursively fetch + fetch(&canon_dir, &recipe.source)?; + fetch_make_symlink(&source_dir, same_as)?; + } + Some(SourceRecipe::Path { path }) => { + if !source_dir.is_dir() || modified_dir(Path::new(path))? > modified_dir(&source_dir)? { + eprintln!("[DEBUG]: {} is newer than {}", path, source_dir.display()); + copy_dir_all(path, &source_dir).map_err(|e| { + format!( + "Couldn't copy source from {} to {}: {}", + path, + source_dir.display(), + e + ) + })?; + } + } + Some(SourceRecipe::Git { + git, + upstream, + branch, + rev, + patches, + script, + shallow_clone, + }) => { + //TODO: use libgit? + let shallow_clone = *shallow_clone == Some(true); + if !source_dir.is_dir() { + // Create source.tmp + let source_dir_tmp = recipe_dir.join("source.tmp"); + create_dir_clean(&source_dir_tmp)?; + + // Clone the repository to source.tmp + let mut command = Command::new("git"); + command + .arg("clone") + .arg("--recursive") + .arg(translate_mirror(git)); + if let Some(branch) = branch { + command.arg("--branch").arg(branch); + } + if shallow_clone { + command.arg("--depth").arg("1").arg("--shallow-submodules"); + } + command.arg(&source_dir_tmp); + run_command(command)?; + + // Move source.tmp to source atomically + rename(&source_dir_tmp, &source_dir)?; + } else if !shallow_clone { + // Don't let this code reset the origin for the cookbook repo + let source_git_dir = source_dir.join(".git"); + if !source_git_dir.is_dir() { + return Err(format!( + "'{}' is not a git repository, but recipe indicated git source", + source_dir.display(), + )); + } + + // Reset origin + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("remote").arg("set-url").arg("origin").arg(git); + run_command(command)?; + + // Fetch origin + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("fetch").arg("origin"); + run_command(command)?; + } + + if let Some(_upstream) = upstream { + //TODO: set upstream URL + // git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null || + // git remote add upstream "$GIT_UPSTREAM" + // git fetch upstream + } + + if let Some(rev) = rev { + // Check out specified revision + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("checkout").arg(rev); + run_command(command)?; + } else if !shallow_clone && !is_redox() { + //TODO: complicated stuff to check and reset branch to origin + //TODO: redox can't undestand this (got exit status 1) + let mut command = Command::new("bash"); + command.arg("-c").arg(GIT_RESET_BRANCH); + if let Some(branch) = branch { + command.env("BRANCH", branch); + } + command.current_dir(&source_dir); + run_command(command)?; + } + + if !patches.is_empty() || script.is_some() { + // Hard reset + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("reset").arg("--hard"); + run_command(command)?; + } + + if !shallow_clone { + // Sync submodules URL + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("submodule").arg("sync").arg("--recursive"); + run_command(command)?; + + // Update submodules + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command + .arg("submodule") + .arg("update") + .arg("--init") + .arg("--recursive"); + run_command(command)?; + } + + fetch_apply_patches(recipe_dir, patches, script, &source_dir)?; + } + Some(SourceRecipe::Tar { + tar, + blake3, + patches, + script, + }) => { + let source_tar = recipe_dir.join("source.tar"); + let mut tar_updated = false; + while { + if !source_tar.is_file() { + tar_updated = true; + //TODO: replace wget + if !source_tar.is_file() { + let source_tar_tmp = recipe_dir.join("source.tar.tmp"); + + let mut command = Command::new("wget"); + command.arg(translate_mirror(tar)); + command.arg("--continue").arg("-O").arg(&source_tar_tmp); + run_command(command)?; + + // Move source.tar.tmp to source.tar atomically + rename(&source_tar_tmp, &source_tar)?; + } + } + let source_tar_blake3 = get_blake3(&source_tar, tar_updated)?; + if let Some(blake3) = blake3 { + if source_tar_blake3 != *blake3 { + if tar_updated { + return Err(format!( + "The downloaded tar blake3 '{source_tar_blake3}' is not equal to blake3 in recipe.toml" + )); + } else { + eprintln!("DEBUG: source tar blake3 is different and need redownload"); + remove_all(&source_tar)?; + } + true + } else { + false + } + } else { + //TODO: set blake3 hash on the recipe with something like "cook fix" + eprintln!( + "WARNING: set blake3 for '{}' to '{}'", + source_tar.display(), + source_tar_blake3 + ); + false + } + } {} + if source_dir.is_dir() { + if tar_updated || fetch_is_patches_newer(recipe_dir, patches, &source_dir)? { + eprintln!("DEBUG: source tar or patches is newer than the source directory"); + remove_all(&source_dir)? + } + } + if !source_dir.is_dir() { + // Create source.tmp + let source_dir_tmp = recipe_dir.join("source.tmp"); + create_dir_clean(&source_dir_tmp)?; + fetch_extract_tar(source_tar, &source_dir_tmp)?; + fetch_apply_patches(recipe_dir, patches, script, &source_dir_tmp)?; + + // Move source.tmp to source atomically + rename(&source_dir_tmp, &source_dir)?; + } + } + // Local Sources + None => { + if !source_dir.is_dir() { + eprintln!( + "WARNING: Recipe without source section expected source dir at '{}'", + source_dir.display(), + ); + create_dir(&source_dir)?; + } + } + } + + Ok(source_dir) +} + +pub(crate) fn fetch_make_symlink(source_dir: &PathBuf, same_as: &String) -> Result<(), String> { + let target_dir = Path::new(same_as).join("source"); + if !source_dir.is_symlink() { + if source_dir.is_dir() { + return Err(format!( + "'{dir}' is a directory, but recipe indicated a symlink. \n\ + try removing '{dir}' if you haven't made any changes that would be lost", + dir = source_dir.display(), + )); + } + std::os::unix::fs::symlink(&target_dir, source_dir).map_err(|err| { + format!( + "failed to symlink '{}' to '{}': {}\n{:?}", + target_dir.display(), + source_dir.display(), + err, + err + ) + })?; + } + Ok(()) +} + +pub(crate) fn fetch_resolve_canon( + recipe_dir: &Path, + same_as: &String, +) -> Result<(PathBuf, Recipe), String> { + let canon_dir = Path::new(recipe_dir).join(same_as); + if canon_dir + .to_str() + .unwrap() + .chars() + .filter(|c| *c == '/') + .count() + > 50 + { + return Err(format!("Infinite loop detected")); + } + if !canon_dir.exists() { + return Err(format!("'{dir}' is not exists.", dir = canon_dir.display())); + } + let recipe_path = canon_dir.join("recipe.toml"); + let recipe_str = fs::read_to_string(&recipe_path) + .map_err(|e| format!("unable to read {path}: {e}", path = recipe_path.display()))?; + let recipe: Recipe = toml::from_str(&recipe_str) + .map_err(|e| format!("Unable to parse {path}: {e}", path = recipe_path.display()))?; + Ok((canon_dir, recipe)) +} + +pub(crate) fn fetch_extract_tar( + source_tar: PathBuf, + source_dir_tmp: &PathBuf, +) -> Result<(), String> { + let mut command = Command::new("tar"); + if is_redox() { + command.arg("xvf"); + } else { + command.arg("--extract"); + command.arg("--verbose"); + command.arg("--file"); + } + command.arg(&source_tar); + command.arg("--directory").arg(source_dir_tmp); + command.arg("--strip-components").arg("1"); + run_command(command)?; + Ok(()) +} + +pub(crate) fn fetch_is_patches_newer( + recipe_dir: &Path, + patches: &Vec, + source_dir: &PathBuf, +) -> Result { + // don't check source files inside as it can be mixed with user patches + let source_time = modified(&source_dir)?; + for patch_name in patches { + let patch_file = recipe_dir.join(patch_name); + if !patch_file.is_file() { + return Err(format!( + "failed to find patch file '{}'", + patch_file.display() + )); + } + + let patch_time = modified(&patch_file)?; + if patch_time > source_time { + return Ok(true); + } + } + return Ok(false); +} + +pub(crate) fn fetch_apply_patches( + recipe_dir: &Path, + patches: &Vec, + script: &Option, + source_dir_tmp: &PathBuf, +) -> Result<(), String> { + for patch_name in patches { + let patch_file = recipe_dir.join(patch_name); + if !patch_file.is_file() { + return Err(format!( + "failed to find patch file '{}'", + patch_file.display() + )); + } + + let patch = fs::read_to_string(&patch_file).map_err(|err| { + format!( + "failed to read patch file '{}': {}\n{:#?}", + patch_file.display(), + err, + err + ) + })?; + + let mut command = Command::new("patch"); + command.arg("--directory").arg(source_dir_tmp); + command.arg("--strip=1"); + run_command_stdin(command, patch.as_bytes())?; + } + Ok(if let Some(script) = script { + let mut command = Command::new("bash"); + command.arg("-ex"); + command.current_dir(source_dir_tmp); + run_command_stdin(command, format!("{SHARED_PRESCRIPT}\n{script}").as_bytes())?; + }) +} diff --git a/src/cook/fs.rs b/src/cook/fs.rs new file mode 100644 index 000000000..0fa51ecfd --- /dev/null +++ b/src/cook/fs.rs @@ -0,0 +1,194 @@ +use serde::Serialize; +use std::{ + fs, + io::{self, Write}, + path::Path, + process::{self, Stdio}, + time::SystemTime, +}; +use walkdir::{DirEntry, WalkDir}; + +//TODO: pub(crate) for all of these functions + +pub fn remove_all(path: &Path) -> Result<(), String> { + if path.is_dir() { + fs::remove_dir_all(path) + } else { + fs::remove_file(path) + } + .map_err(|err| format!("failed to remove '{}': {}\n{:?}", path.display(), err, err)) +} + +pub fn create_dir(dir: &Path) -> Result<(), String> { + fs::create_dir(dir) + .map_err(|err| format!("failed to create '{}': {}\n{:?}", dir.display(), err, err)) +} + +pub fn create_dir_clean(dir: &Path) -> Result<(), String> { + if dir.is_dir() { + remove_all(dir)?; + } + create_dir(dir) +} + +pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + +pub fn symlink(original: impl AsRef, link: impl AsRef) -> Result<(), String> { + std::os::unix::fs::symlink(&original, &link).map_err(|err| { + format!( + "failed to symlink '{}' to '{}': {}\n{:?}", + original.as_ref().display(), + link.as_ref().display(), + err, + err + ) + }) +} + +pub fn modified(path: &Path) -> Result { + let metadata = fs::metadata(path).map_err(|err| { + format!( + "failed to get metadata of '{}': {}\n{:#?}", + path.display(), + err, + err + ) + })?; + metadata.modified().map_err(|err| { + format!( + "failed to get modified time of '{}': {}\n{:#?}", + path.display(), + err, + err + ) + }) +} + +pub fn modified_dir_inner bool>( + dir: &Path, + filter: F, +) -> io::Result { + let mut newest = fs::metadata(dir)?.modified()?; + for entry_res in WalkDir::new(dir).into_iter().filter_entry(filter) { + let entry = entry_res?; + let modified = entry.metadata()?.modified()?; + if modified > newest { + newest = modified; + } + } + Ok(newest) +} + +pub fn modified_dir(dir: &Path) -> Result { + modified_dir_inner(dir, |_| true).map_err(|err| { + format!( + "failed to get modified time of '{}': {}\n{:#?}", + dir.display(), + err, + err + ) + }) +} + +pub fn modified_dir_ignore_git(dir: &Path) -> Result { + modified_dir_inner(dir, |entry| { + entry + .file_name() + .to_str() + .map(|s| s != ".git") + .unwrap_or(true) + }) + .map_err(|err| { + format!( + "failed to get modified time of '{}': {}\n{:#?}", + dir.display(), + err, + err + ) + }) +} + +pub fn rename(src: &Path, dst: &Path) -> Result<(), String> { + fs::rename(src, dst).map_err(|err| { + format!( + "failed to rename '{}' to '{}': {}\n{:?}", + src.display(), + dst.display(), + err, + err + ) + }) +} + +pub fn run_command(mut command: process::Command) -> Result<(), String> { + let status = command + .status() + .map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?; + + if !status.success() { + return Err(format!( + "failed to run {:?}: exited with status {}", + command, status + )); + } + + Ok(()) +} + +pub fn run_command_stdin(mut command: process::Command, stdin_data: &[u8]) -> Result<(), String> { + command.stdin(Stdio::piped()); + + let mut child = command + .spawn() + .map_err(|err| format!("failed to spawn {:?}: {}\n{:#?}", command, err, err))?; + + if let Some(ref mut stdin) = child.stdin { + stdin.write_all(stdin_data).map_err(|err| { + format!( + "failed to write stdin of {:?}: {}\n{:#?}", + command, err, err + ) + })?; + } else { + return Err(format!("failed to find stdin of {:?}", command)); + } + + let status = child + .wait() + .map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?; + + if !status.success() { + return Err(format!( + "failed to run {:?}: exited with status {}", + command, status + )); + } + + Ok(()) +} + +pub fn serialize_and_write(file_path: &Path, content: &T) -> Result<(), String> { + let toml_content = toml::to_string(content).map_err(|err| { + format!( + "Failed to serialize content for '{}': {}", + file_path.display(), + err + ) + })?; + + fs::write(file_path, toml_content) + .map_err(|err| format!("Failed to write to file '{}': {}", file_path.display(), err))?; + Ok(()) +} diff --git a/src/cook/script.rs b/src/cook/script.rs new file mode 100644 index 000000000..aa531d699 --- /dev/null +++ b/src/cook/script.rs @@ -0,0 +1,91 @@ +//TODO: pub(crate) +pub static SHARED_PRESCRIPT: &str = r#" +# Build dynamically +function DYNAMIC_INIT { + COOKBOOK_AUTORECONF="autoreconf" + autotools_recursive_regenerate() { + for f in $(find . -name configure.ac -o -name configure.in -type f | sort); do + echo "* autotools regen in '$(dirname $f)'..." + ( cd "$(dirname "$f")" && "${COOKBOOK_AUTORECONF}" -fvi "$@" -I${COOKBOOK_HOST_SYSROOT}/share/aclocal ) + done + } + + if [ "${TARGET}" != "x86_64-unknown-redox" ] + then + echo "WARN: ${TARGET} does not support dynamic linking." >&2 + return + fi + + echo "DEBUG: Program is being compiled dynamically." + + COOKBOOK_CONFIGURE_FLAGS=( + --host="${GNU_TARGET}" + --prefix="/usr" + --enable-shared + --disable-static + ) + + COOKBOOK_CMAKE_FLAGS=( + -DBUILD_SHARED_LIBS=True + -DENABLE_SHARED=True + -DENABLE_STATIC=False + ) + + COOKBOOK_MESON_FLAGS=( + --buildtype release + --wrap-mode nofallback + --strip + -Ddefault_library=shared + -Dprefix=/usr + ) + + # TODO: check paths for spaces + export LDFLAGS="-Wl,-rpath-link,${COOKBOOK_SYSROOT}/lib -L${COOKBOOK_SYSROOT}/lib" + export RUSTFLAGS="-C target-feature=-crt-static" + export COOKBOOK_DYNAMIC=1 +} + +# Build both dynamically and statically +function DYNAMIC_STATIC_INIT { + DYNAMIC_INIT + if [ "${COOKBOOK_DYNAMIC}" == "1" ] + then + COOKBOOK_CONFIGURE_FLAGS=( + --host="${GNU_TARGET}" + --prefix="/usr" + --enable-shared + --enable-static + ) + + COOKBOOK_CMAKE_FLAGS=( + -DBUILD_SHARED_LIBS=True + -DENABLE_SHARED=True + -DENABLE_STATIC=True + ) + + COOKBOOK_MESON_FLAGS=( + --buildtype release + --wrap-mode nofallback + --strip + -Ddefault_library=both + -Dprefix=/usr + ) + fi +} + +function GNU_CONFIG_GET { + wget -O "$1" "https://gitlab.redox-os.org/redox-os/gnu-config/-/raw/master/config.sub?inline=false" +} +"#; + +pub(crate) static GIT_RESET_BRANCH: &str = r#" +ORIGIN_BRANCH="$(git branch --remotes | grep '^ origin/HEAD -> ' | cut -d ' ' -f 5-)" +if [ -n "$BRANCH" ] +then + ORIGIN_BRANCH="origin/$BRANCH" +fi + +if [ "$(git rev-parse HEAD)" != "$(git rev-parse $ORIGIN_BRANCH)" ] +then + git checkout -B "$(echo "$ORIGIN_BRANCH" | cut -d / -f 2-)" "$ORIGIN_BRANCH" +fi"#; diff --git a/src/lib.rs b/src/lib.rs index cfad4bc49..cef82ad06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod blake3; pub mod config; +pub mod cook; pub mod recipe; mod progress_bar; diff --git a/src/recipe.rs b/src/recipe.rs index 43b616575..ff71fd5b0 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -1,10 +1,10 @@ use std::{collections::BTreeSet, convert::TryInto, fs, path::PathBuf}; -use pkg::{package::PackageError, recipes, PackageName}; +use pkg::{PackageName, package::PackageError, recipes}; use regex::Regex; use serde::{ - de::{value::Error as DeError, Error as DeErrorT}, Deserialize, Serialize, + de::{Error as DeErrorT, value::Error as DeError}, }; use crate::WALK_DEPTH;