diff --git a/resources/mock/dist/distribution-manifest.toml b/resources/mock/dist/distribution-manifest.toml deleted file mode 100644 index f78b579..0000000 --- a/resources/mock/dist/distribution-manifest.toml +++ /dev/null @@ -1,20 +0,0 @@ -[[packages]] -name = "Custom Rust Distribution" -version = "stable-1.81.0" -desc = "Just a customized bundle" -info = ''' -## Changelog -* Add Rust 1.81.0 toolchain -* Add mingw-w64 for Windows users that use GNU toolchain -''' -manifest-url = "{{SERVER}}/dist/stable-1.81.0.toml" - -[[packages]] -name = "Custom Rust Distribution" -version = "stable-1.82.0" -desc = "Just a customized bundle" -info = ''' -## Changelog -* Update Rust toolchain to 1.82.0 -''' -manifest-url = "{{SERVER}}/dist/stable-1.82.0.toml" diff --git a/resources/mock/dist/stable-1.81.0.toml b/resources/mock/dist/stable-1.81.0.toml deleted file mode 100644 index c5ffef3..0000000 --- a/resources/mock/dist/stable-1.81.0.toml +++ /dev/null @@ -1,24 +0,0 @@ -name = "Custom Rust Distribution" -version = "stable-1.81.0" - -[rust] -version = "1.81.0" -group = "Rust" -components = ["clippy", "rustfmt", "rust-src", "rust-docs"] -optional-components = ["llvm-tools", "rustc-dev"] - -[rust.profile] -name = "minimal" -verbose-name = "Basic" -description = "Basic set of tools to use Rust properly" - -[tools.descriptions] -llvm-tools = "Contains a collection of LLVM tools. Note that this component has not been stabilized and may change in the future and is provided as-is" -rustc-dev = "Contains the compiler as a library. Most users will not need this; it is only needed for development of tools that link to the compiler, such as making modifications to Clippy." -mingw64 = "(windows only) Requirement for Windows GNU toolchain" - -[tools.group] -Prerequisites = ["mingw64"] - -[tools.target.x86_64-pc-windows-gnu] -mingw64 = { required = true, url = "https://github.com/niXman/mingw-builds-binaries/releases/download/14.2.0-rt_v12-rev0/x86_64-14.2.0-release-posix-seh-ucrt-rt_v12-rev0.7z", version = "14.2.0-rt_v12-rev0" } diff --git a/resources/mock/dist/stable-1.82.0.toml b/resources/mock/dist/stable-1.82.0.toml deleted file mode 100644 index efb96ae..0000000 --- a/resources/mock/dist/stable-1.82.0.toml +++ /dev/null @@ -1,24 +0,0 @@ -name = "Custom Rust Distribution" -version = "stable-1.82.0" - -[rust] -version = "1.82.0" -group = "Rust" -components = ["clippy", "rustfmt", "rust-src", "rust-docs"] -optional-components = ["llvm-tools", "rustc-dev"] - -[rust.profile] -name = "minimal" -verbose-name = "Basic" -description = "Basic set of tools to use Rust properly" - -[tools.descriptions] -llvm-tools = "Contains a collection of LLVM tools. Note that this component has not been stabilized and may change in the future and is provided as-is" -rustc-dev = "Contains the compiler as a library. Most users will not need this; it is only needed for development of tools that link to the compiler, such as making modifications to Clippy." -mingw64 = "(windows only) Requirement for Windows GNU toolchain" - -[tools.group] -Prerequisites = ["mingw64"] - -[tools.target.x86_64-pc-windows-gnu] -mingw64 = { required = true, url = "https://github.com/niXman/mingw-builds-binaries/releases/download/14.2.0-rt_v12-rev0/x86_64-14.2.0-release-posix-seh-ucrt-rt_v12-rev0.7z", version = "14.2.0-rt_v12-rev0" } diff --git a/rim_dev/src/main.rs b/rim_dev/src/main.rs index 1769b13..b4bbfb8 100644 --- a/rim_dev/src/main.rs +++ b/rim_dev/src/main.rs @@ -3,15 +3,16 @@ extern crate rust_i18n; mod common; mod dist; -mod mocked_manager; +mod mocked; mod vendor; -use anyhow::{Context, Result}; +use anyhow::Result; use dist::{DistMode, DIST_HELP}; +use mocked::{installation, manager, server}; +use std::env; use std::io::{stdout, Write}; use std::path::PathBuf; -use std::process::{Command, ExitCode}; -use std::{env, fs}; +use std::process::ExitCode; use vendor::VENDOR_HELP; i18n!("../locales", fallback = "en"); @@ -51,33 +52,16 @@ impl DevCmd { match self { Self::Dist { mode, binary_only } => dist::dist(*mode, *binary_only)?, Self::RunManager { no_gui, args } => { - let mut cargo_args = if *no_gui { - ["run", "--"].to_vec() - } else { - ["tauri", "dev", "--"].to_vec() - }; - cargo_args.extend(args.iter().map(|s| s.as_str())); - - gen_mocked_files()?; + // a mocked server is needed to run most of function in manager + server::generate()?; + + // generate a fake manager binary with higher version so we + // can test the self update. if args.iter().any(|arg| arg == "update") { - mocked_manager::generate()?; + manager::generate()?; } - let mut mock_dir = - PathBuf::from(env!("CARGO_MANIFEST_DIR")).with_file_name("resources"); - mock_dir.push("mock"); - let mocked_server = url::Url::from_directory_path(&mock_dir).unwrap_or_else(|_| { - panic!("path {} cannot be converted to URL", mock_dir.display()) - }); - let status = Command::new("cargo") - .args(cargo_args) - .env("MODE", "manager") - .env("RIM_DIST_SERVER", mocked_server.as_str()) - .status()?; - println!( - "\nmanager exited with status code: {}", - status.code().unwrap_or(-1) - ); + installation::generate_and_run_manager(*no_gui, args)?; } Self::Vendor => vendor::vendor()?, } @@ -85,45 +69,8 @@ impl DevCmd { } } -fn current_exe() -> Result { - env::current_exe().context("failed to get the path of current binary") -} - -/// Generate mocked `.fingerprint`, and `toolset-manifest` files when running with `run-manager` -fn gen_mocked_files() -> Result<()> { - let cur_exe = current_exe()?; - // safe to unwrap, always have parent dir - let debug_dir = cur_exe.parent().unwrap(); - // Note: there is a `.fingerprint` folder generated by cargo, don't touch it - let fingerprint_path = debug_dir.join(".fingerprint.toml"); - // don't write if the path already exists - if !fingerprint_path.exists() { - fs::write( - fingerprint_path, - format!( - " -name = 'Custom Rust Distribution' -version = 'stable-1.80.1' -root = '{0}' - -[rust] -version = '1.80.1' -components = [\"llvm-tools\", \"rustc-dev\"] - -[tools.mingw64] -kind = 'dir-with-bin' -version = '13.0.0' -paths = ['{0}/tools/mingw64']", - debug_dir.display() - ), - )?; - } - - let manifest = include_str!("../../resources/toolset_manifest.toml"); - let manifest_path = debug_dir.join("toolset-manifest.toml"); - fs::write(manifest_path, manifest)?; - - Ok(()) +fn current_exe() -> PathBuf { + env::current_exe().expect("failed to get the path of current binary") } fn main() -> Result { diff --git a/rim_dev/src/mocked/installation.rs b/rim_dev/src/mocked/installation.rs new file mode 100644 index 0000000..add26f7 --- /dev/null +++ b/rim_dev/src/mocked/installation.rs @@ -0,0 +1,107 @@ +//! Module to create a fake installation root, useful to test the `manager` utilities. + +use super::TOOLKIT_NAME; +use anyhow::{bail, Result}; +use std::{env::consts::EXE_SUFFIX, fs, path::PathBuf, process::Command}; + +struct FakeInstallation { + manager_bin: Option, +} + +impl FakeInstallation { + fn new() -> Self { + Self { manager_bin: None } + } + + fn fingerprint_content(&self, ver: &str) -> String { + format!( + " +name = '{TOOLKIT_NAME}' +version = 'stable-{ver}' +root = '{0}' + +[rust] +version = '{ver}' +components = [\"llvm-tools\", \"rustc-dev\"] + +[tools.mingw64] +kind = 'dir-with-bin' +version = '13.0.0' +paths = ['{0}/tools/mingw64'] +", + super::install_dir().display() + ) + } + + fn generate_manager_bin(&mut self, no_gui: bool, args: &[String]) -> Result<()> { + let mut cargo_args = if no_gui { + ["build", "--"].to_vec() + } else { + ["tauri", "build", "--debug", "-b", "none", "--"].to_vec() + }; + cargo_args.extend(args.iter().map(|s| s.as_str())); + + // build rim + let build_status = Command::new("cargo").args(cargo_args).status()?; + if !build_status.success() { + bail!("failed to build manager binary"); + } + + // make a copy of rim as manager binary to the fake installation root + let (src_bin, dest_bin) = if no_gui { + ( + format!("rim-cli{EXE_SUFFIX}"), + format!("manager-cli{EXE_SUFFIX}"), + ) + } else { + ( + format!("rim-gui{EXE_SUFFIX}"), + format!("manager{EXE_SUFFIX}"), + ) + }; + let build_artifact = super::debug_dir().join(src_bin); + let dest_path = super::install_dir().join(dest_bin); + fs::copy(build_artifact, &dest_path)?; + + self.manager_bin = Some(dest_path); + + Ok(()) + } + + fn generate_meta_files(&self) -> Result<()> { + let fingerprint_path = super::install_dir().join(".fingerprint.toml"); + let manifest_path = super::install_dir().join("toolset-manifest.toml"); + + // don't write if the path already exists + if !fingerprint_path.exists() { + fs::write(fingerprint_path, self.fingerprint_content("1.0.0"))?; + } + let manifest = include_str!("../../../resources/toolset_manifest.toml"); + if !manifest_path.exists() { + fs::write(manifest_path, manifest)?; + } + + Ok(()) + } +} + +pub(crate) fn generate_and_run_manager(no_gui: bool, args: &[String]) -> Result<()> { + let mut fake = FakeInstallation::new(); + fake.generate_meta_files()?; + fake.generate_manager_bin(no_gui, args)?; + + // `fake.manager_bin` cannot be none if the previous `generate_manager_bin` + // succeeded, so it's safe to unwrap + let manager = &fake.manager_bin.unwrap(); + + let mocked_dist_server = super::server_dir_url(); + // run the manager copy + let status = Command::new(manager) + .env("RIM_DIST_SERVER", mocked_dist_server.as_str()) + .status()?; + println!( + "\nmanager exited with status code: {}", + status.code().unwrap_or(-1) + ); + Ok(()) +} diff --git a/rim_dev/src/mocked_manager.rs b/rim_dev/src/mocked/manager.rs similarity index 86% rename from rim_dev/src/mocked_manager.rs rename to rim_dev/src/mocked/manager.rs index 8c6772c..ddf18cb 100644 --- a/rim_dev/src/mocked_manager.rs +++ b/rim_dev/src/mocked/manager.rs @@ -1,13 +1,5 @@ use anyhow::Result; -use std::{ - env::consts::EXE_SUFFIX, - fs, - path::{Path, PathBuf}, - process::Command, - sync::OnceLock, -}; - -static MANAGER_DIR: OnceLock = OnceLock::new(); +use std::{env::consts::EXE_SUFFIX, fs, path::PathBuf, process::Command}; struct FakeRim { main_rs: String, @@ -63,7 +55,7 @@ edition = \"2021\" let mut binary_path = temp_dir.join("target"); binary_path.push("release"); binary_path.push(format!("rim{}", std::env::consts::EXE_SUFFIX)); - let mut dest_dir = manager_dir().join("archive"); + let mut dest_dir = super::manager_dir().join("archive"); dest_dir.push(&self.version); dest_dir.push(env!("TARGET")); fs::create_dir_all(&dest_dir)?; @@ -77,18 +69,9 @@ edition = \"2021\" } } -fn manager_dir() -> &'static Path { - MANAGER_DIR.get_or_init(|| { - let mut m_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).with_file_name("resources"); - m_dir.push("mock"); - m_dir.push("manager"); - m_dir - }) -} - /// Generate a `release.toml` for self update, that the version will always be newer. fn gen_release_toml(version: &str) -> Result<()> { - let release_toml = manager_dir().join("release.toml"); + let release_toml = super::manager_dir().join("release.toml"); let desired_content = format!("version = '{version}'"); fs::write(release_toml, desired_content)?; diff --git a/rim_dev/src/mocked/mod.rs b/rim_dev/src/mocked/mod.rs new file mode 100644 index 0000000..460e8cc --- /dev/null +++ b/rim_dev/src/mocked/mod.rs @@ -0,0 +1,78 @@ +use std::{ + fs, + path::{Path, PathBuf}, + sync::OnceLock, +}; + +use url::Url; + +pub mod installation; +pub mod manager; +pub mod server; + +const TOOLKIT_NAME: &str = "Custom Rust Distribution"; + +// TODO: (?) we need a `rim_utils`, +// then we can expose `rim::core::directories::get_path_and_create` macro and use it instead + +fn debug_dir() -> &'static Path { + static DEBUG_DIR: OnceLock = OnceLock::new(); + DEBUG_DIR.get_or_init(|| { + // safe to unwrap, binary file always have a parent dir + crate::current_exe().parent().unwrap().to_path_buf() + }) +} + +fn mocked_dir() -> &'static Path { + static MOCKED_DIR: OnceLock = OnceLock::new(); + MOCKED_DIR.get_or_init(|| { + let dir = debug_dir().join("mocked"); + fs::create_dir_all(&dir) + .unwrap_or_else(|_| panic!("unable to create mocked dir at {}", dir.display())); + dir + }) +} + +fn install_dir() -> &'static Path { + static INSTALL_DIR: OnceLock = OnceLock::new(); + INSTALL_DIR.get_or_init(|| { + let dir = mocked_dir().join("installation"); + fs::create_dir_all(&dir) + .unwrap_or_else(|_| panic!("unable to create mocked install dir at {}", dir.display())); + dir + }) +} + +fn server_dir() -> &'static Path { + static SERVER_DIR: OnceLock = OnceLock::new(); + SERVER_DIR.get_or_init(|| { + let dir = mocked_dir().join("server"); + fs::create_dir_all(&dir) + .unwrap_or_else(|_| panic!("unable to create mocked server dir at {}", dir.display())); + dir + }) +} + +fn server_dir_url() -> Url { + let mocked_dist_dir = server_dir(); + Url::from_directory_path(mocked_dist_dir).unwrap_or_else(|_| { + panic!( + "path {} cannot be converted to URL", + mocked_dist_dir.display() + ) + }) +} + +fn manager_dir() -> &'static Path { + static MANAGER_DIR: OnceLock = OnceLock::new(); + MANAGER_DIR.get_or_init(|| { + let dir = server_dir().join("manager"); + fs::create_dir_all(&dir).unwrap_or_else(|_| { + panic!( + "unable to create mocked manager dist dir at {}", + dir.display() + ) + }); + dir + }) +} diff --git a/rim_dev/src/mocked/server.rs b/rim_dev/src/mocked/server.rs new file mode 100644 index 0000000..1a27a17 --- /dev/null +++ b/rim_dev/src/mocked/server.rs @@ -0,0 +1,90 @@ +use super::TOOLKIT_NAME; +use anyhow::{Context, Result}; +use std::{fs, path::PathBuf}; + +/// The versions to write to +static VERSIONS: &[&str] = &["1.80.0", "1.80.1", "1.81.0", "1.82.0"]; + +struct FakeServer { + dist_dir: PathBuf, +} + +impl FakeServer { + fn new() -> Self { + let dist_dir = super::server_dir().join("dist"); + fs::create_dir_all(&dist_dir) + .unwrap_or_else(|_| panic!("unable to create mocked dist dir")); + Self { dist_dir } + } + + fn gen_dist_manifest(&self) -> Result<()> { + let server_url = super::server_dir_url(); + let dist_manifest_content_for = |ver: &str| -> String { + format!( + " +[[packages]] +name = \"{TOOLKIT_NAME}\" +version = \"stable-{ver}\" +desc = \"This is is generated for testing purpose\" +info = ''' +- A fake toolkit with fake info that is generated by rim-dev +''' +manifest-url = \"{}/dist/stable-{ver}.toml\" +", + server_url.as_str() + ) + }; + + let mut full_content = String::new(); + for ver in VERSIONS { + full_content.push_str(&dist_manifest_content_for(ver)); + } + + let dist_manifest = self.dist_dir.join("distribution-manifest.toml"); + fs::write(dist_manifest, full_content).context("unable to create dist-manifest") + } + + fn gen_toolset_manifests(&self) -> Result<()> { + let toolset_manifest_for = |ver: &str| -> String { + format!( + " +name = \"{TOOLKIT_NAME}\" +version = \"stable-{ver}\" + +[rust] +version = \"{ver}\" +group = \"Rust\" +components = [\"clippy\", \"rustfmt\", \"rust-src\", \"rust-docs\"] +optional-components = [\"llvm-tools\", \"rustc-dev\", \"rust-analyzer\"] + +[rust.profile] +name = \"minimal\" +verbose-name = \"Basic\" +description = \"Basic set of tools to use Rust properly\" + +[tools.descriptions] +llvm-tools = \"llvm-tools\" +rustc-dev = \"rustc-dev\" +rust-analyzer = \"rust-analyzer\" +", + ) + }; + + for ver in VERSIONS { + let content = toolset_manifest_for(ver); + // the name should match the ones in `gen_dist_manifest` + let filename = format!("stable-{ver}.toml"); + let dest = self.dist_dir.join(filename); + fs::write(dest, content)?; + } + + Ok(()) + } +} + +pub(crate) fn generate() -> Result<()> { + let mocked = FakeServer::new(); + mocked.gen_toolset_manifests()?; + mocked.gen_dist_manifest()?; + Ok(()) +} diff --git a/src/core/toolkit.rs b/src/core/toolkit.rs index 1fdae17..6ae9067 100644 --- a/src/core/toolkit.rs +++ b/src/core/toolkit.rs @@ -111,15 +111,6 @@ fn toolkits_from_server() -> Result<&'static [Toolkit]> { false, )?; - // process place-holder text in debug mode - #[cfg(debug_assertions)] - { - use std::fs; - let mut dist_m_content = fs::read_to_string(dist_m_file.path())?; - dist_m_content = dist_m_content.replace("{{SERVER}}", Url::parse(dist_server)?.as_str()); - fs::write(dist_m_file.path(), dist_m_content)?; - } - // load dist "pacakges" then convert them into `toolkit`s let packages = DistManifest::load(dist_m_file.path())?.packages; let cached =