diff --git a/Cargo.lock b/Cargo.lock index a6afb33b..c2901b09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -539,7 +539,7 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "huak" -version = "0.0.19" +version = "0.0.19-alpha1" dependencies = [ "clap", "clap_complete", diff --git a/crates/huak-cli/Cargo.toml b/crates/huak-cli/Cargo.toml index d0208190..fae9c567 100644 --- a/crates/huak-cli/Cargo.toml +++ b/crates/huak-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "huak" -version = "0.0.19" +version = "0.0.19-alpha1" description = "A Python package manager written in Rust and inspired by Cargo." repository = "https://github.com/cnpryer/huak.git" homepage = "https://github.com/cnpryer/huak.git" diff --git a/crates/huak-cli/tests/snapshots/r#mod__tests__help.snap b/crates/huak-cli/tests/snapshots/r#mod__tests__help.snap index 2961c953..2a4c0d28 100644 --- a/crates/huak-cli/tests/snapshots/r#mod__tests__help.snap +++ b/crates/huak-cli/tests/snapshots/r#mod__tests__help.snap @@ -1,5 +1,5 @@ --- -source: crates/huak_cli/tests/mod.rs +source: crates/huak-cli/tests/mod.rs info: program: huak args: @@ -29,6 +29,7 @@ Commands: remove Remove dependencies from the project run Run a command within the project's environment context test Test the project's Python code + toolchain Manage toolchains update Update the project's dependencies version Display the version of the project help Print this message or the help of the given subcommand(s) diff --git a/crates/huak-package-manager/src/error.rs b/crates/huak-package-manager/src/error.rs index af4cac34..91634f31 100644 --- a/crates/huak-package-manager/src/error.rs +++ b/crates/huak-package-manager/src/error.rs @@ -25,9 +25,9 @@ pub enum Error { #[error("a problem occurred resolving huak's home directory")] HuakHomeNotFound, #[error("a toolchain cannot be found")] - HuakToolchainNotFound, + ToolchainNotFound, #[error("{0}")] // See TODO note above. - HuakToolchainError(#[from] huak_toolchain::Error), + ToolchainError(#[from] huak_toolchain::Error), #[error("a toolchain already exists: {0}")] LocalToolchainExists(PathBuf), #[error("a problem with huak's internals occurred: {0}")] diff --git a/crates/huak-package-manager/src/ops/toolchain.rs b/crates/huak-package-manager/src/ops/toolchain.rs index 03268339..1fd48751 100644 --- a/crates/huak-package-manager/src/ops/toolchain.rs +++ b/crates/huak-package-manager/src/ops/toolchain.rs @@ -282,7 +282,7 @@ fn install(path: PathBuf, channel: Channel, config: &Config) -> HuakResult<()> { .is_err() { if let Err(e) = toolchain.register_tool_from_path(&path, "python", true) { - return Err(Error::HuakToolchainError(e)); + return Err(Error::ToolchainError(e)); } } diff --git a/crates/huak-package-manager/src/workspace.rs b/crates/huak-package-manager/src/workspace.rs index 3e70906a..a99c94f6 100644 --- a/crates/huak-package-manager/src/workspace.rs +++ b/crates/huak-package-manager/src/workspace.rs @@ -128,7 +128,7 @@ impl Workspace { /// 3. ~/.huak/settings.toml configuration pub fn resolve_local_toolchain(&self, channel: Option<&Channel>) -> HuakResult { let Some(it) = resolve_local_toolchain(self, channel) else { - return Err(Error::HuakToolchainNotFound); + return Err(Error::ToolchainNotFound); }; Ok(it) diff --git a/crates/huak-python-manager/src/lib.rs b/crates/huak-python-manager/src/lib.rs index c9d6d755..76a01a1b 100644 --- a/crates/huak-python-manager/src/lib.rs +++ b/crates/huak-python-manager/src/lib.rs @@ -38,7 +38,6 @@ //! ``` pub use crate::error::Error; -pub use crate::install::install_with_target; pub use crate::resolve::{ release_options_from_requested_version, resolve_release, ReleaseArchitecture, ReleaseBuildConfiguration, ReleaseKind, ReleaseOption, ReleaseOptions, ReleaseOs, @@ -46,6 +45,7 @@ pub use crate::resolve::{ }; pub use crate::version::Version; use install::download_release; +pub use install::install_with_target; pub use releases::Release; use std::path::Path; use tar::Archive; diff --git a/crates/huak-toolchain/src/install.rs b/crates/huak-toolchain/src/install.rs deleted file mode 100644 index 8dfce361..00000000 --- a/crates/huak-toolchain/src/install.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::{channel::DescriptorParts, Channel, Error, LocalToolchain}; -use huak_python_manager::{ - install_with_target as install_python_with_target, release_options_from_requested_version, - resolve_release as resolve_python_release, ReleaseOption, ReleaseOptions, RequestedVersion, - Strategy as PythonStrategy, -}; -#[cfg(unix)] -use std::os::unix::fs::symlink; -#[cfg(windows)] -use std::os::windows::fs::symlink_file; -use std::{ - env::consts::OS, - fs::{self, hard_link, read_link}, - path::{Path, PathBuf}, - str::FromStr, -}; - -pub fn install_toolchain_with_target( - toolchain: &LocalToolchain, - target: &PathBuf, -) -> Result<(), Error> { - if target.exists() { - Err(Error::LocalToolchainExistsError(target.clone())) - } else { - setup_toolchain(toolchain, target) - } -} - -fn setup_toolchain(toolchain: &LocalToolchain, path: &PathBuf) -> Result<(), Error> { - fs::create_dir_all(path)?; - - let downloads = path.join("downloads"); - fs::create_dir_all(&downloads)?; - - let bin = path.join("bin"); - fs::create_dir_all(&bin)?; - - // Get the path to the installed interpreter. - let py_path = maybe_exe( - downloads - .join("python") - .join("install") - .join(py_bin_name()) - .join("python3"), - ); - - install_python(toolchain, &downloads)?; - create_proxy_file(py_path, bin.join("python"))?; - - // TODO(cnpryer): Rest of tools - // todo!() - Ok(()) -} - -fn py_bin_name() -> &'static str { - #[cfg(unix)] - let name = "bin"; - - #[cfg(windows)] - let name = "Scripts"; - - name -} - -fn install_python(toolchain: &LocalToolchain, target: &PathBuf) -> Result<(), Error> { - let strategy = python_strategy_from_channel(toolchain.channel())?; - - let Some(release) = resolve_python_release(&strategy) else { - return Err(Error::PythonInstallationError(format!( - "could not resolve python with {strategy}" - ))); - }; - - Ok(install_python_with_target(&release, target)?) -} - -fn python_strategy_from_channel(channel: &Channel) -> Result { - let options = match channel { - Channel::Default => ReleaseOptions::default(), // TODO(cnpryer): Is there ever a case where channel default doesn't yield python default? - Channel::Version(version) => { - release_options_from_requested_version(RequestedVersion::from(*version))? - } - Channel::Descriptor(desc) => python_options_from_descriptor(desc)?, - }; - - Ok(PythonStrategy::Selection(options)) -} - -fn python_options_from_descriptor(desc: &DescriptorParts) -> Result { - let mut options = ReleaseOptions::default(); - - if let Some(kind) = desc.kind.as_ref() { - options.kind = ReleaseOption::from_str(kind).ok(); - } - - if let Some(version) = desc.version.as_ref() { - options.version = Some(ReleaseOption::from_str(&version.to_string())?); - } - - if let Some(architecture) = desc.architecture.as_ref() { - options.kind = ReleaseOption::from_str(architecture).ok(); - } - - if let Some(build_configuration) = desc.build_configuration.as_ref() { - options.kind = ReleaseOption::from_str(build_configuration).ok(); - } - - Ok(options) -} - -// TODO(cnpryer): -// - More robust support -// - Privileged action on windows https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links -// - Get file metadata for priv check (linux too) -// - Do symlinks not work on linux? -// - https://github.com/cnpryer/huak/issues/809 -// - check if file is already a link, if not attempt to make it one. -fn create_proxy_file>(original: T, link: T) -> Result<(), Error> { - let original = original.as_ref(); - let link = link.as_ref(); - - // If we can read the link we'll just make our own symlink of the original link's linked file - if let Ok(it) = read_link(original) { - // Do our best to get the path linked file - let p = if it.is_absolute() { - it - } else if let Some(parent) = original.parent() { - parent.join(it) - } else { - fs::canonicalize(it)? - }; - - // Attempt to create a symlink. If that doesn't work then we hardlink. If we can't hardlink we copy. - if try_symlink(p.as_path(), link).is_ok() || hard_link(p.as_path(), link).is_ok() { - Ok(()) - } else { - // Last resort is to copy the Python interpreter - println!( - " failed to link {} with {}", - link.display(), - original.display() - ); - println!(" copying {} to {}", original.display(), link.display()); - let _ = fs::copy(p.as_path(), link)?; - Ok(()) - } - } else if try_symlink(original, link).is_ok() || hard_link(original, link).is_ok() { - Ok(()) - } else { - todo!() - } -} - -fn try_symlink>(original: T, link: T) -> Result<(), Error> { - #[cfg(unix)] - let err = symlink(original, link); - - #[cfg(windows)] - let err = symlink_file(original, link); - - Ok(err?) -} - -fn maybe_exe(path: PathBuf) -> PathBuf { - if OS == "windows" && path.extension().map_or(false, |it| it == "exe") { - path.with_extension("exe") - } else { - path - } -} diff --git a/crates/huak-toolchain/src/lib.rs b/crates/huak-toolchain/src/lib.rs index 5df4ef74..5403ae9b 100644 --- a/crates/huak-toolchain/src/lib.rs +++ b/crates/huak-toolchain/src/lib.rs @@ -71,7 +71,6 @@ pub use channel::{Channel, DescriptorParts}; pub use error::Error; -pub use install::install_toolchain_with_target; use path::name_from_path; pub use resolve::{Entry, LocalToolchainResolver}; #[cfg(unix)] @@ -86,7 +85,6 @@ pub use tools::LocalTool; mod channel; mod error; -mod install; mod path; mod resolve; mod tools; diff --git a/pyproject.toml b/pyproject.toml index 42042103..2fa33466 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "huak" -version = "0.0.19" +version = "0.0.19a1" description = "A Python package manager written in Rust and inspired by Cargo." authors = [ {email = "cnpryer@gmail.com"},