diff --git a/CHANGELOG.md b/CHANGELOG.md index a9c69ba8..bfd302a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added - The metadata repositories can now contain source files. - Support for unpacking bzip2 file formats. +- The `msvc` requirement now supports specifiying version bounds using `msvc@`. ### Changes - The test environment now uses a fresh environment for each test and can be used in parallel. diff --git a/docs/metadata.md b/docs/metadata.md index 6e76187e..07ce3541 100644 --- a/docs/metadata.md +++ b/docs/metadata.md @@ -182,9 +182,9 @@ Targets are specified as `[targets.]`, where bounds specify the supporte ### Available requirements All available requirements for use with the `build_requirements` and `test_requirements` fields. Requirements are things that need to be available on the system before building or testing, they need to be installed by the user manually. -| Field | Explanation | -| ------ | ------------------------------------------------------------------------------------------------------------- | -| `msvc` | The Microsoft Visual C++ toolchain, automatically adds `PACKIT_VS_PATH`, `PACKIT_VCVARSALL`, `PACKIT_VCVARSALL_ARCH` and `PACKIT_MSVC_VERSION` to the build environment. Find out more [here](./build-env.md#windows-msvc) (Windows only) | +| Field | Explanation | +| ----------------------- | ------------------------------------------------------------------------------------------ | +| `msvc@` | The Microsoft Visual C++ toolchain, automatically adds `PACKIT_VS_PATH`, `PACKIT_VCVARSALL`, `PACKIT_VCVARSALL_ARCH` and `PACKIT_MSVC_VERSION` to the build environment. Find out more [here](./build-env.md#windows-msvc) (Windows only) | ## `prebuilds.toml` The package version directory can contain an optional `prebuilds.toml` file. This file describes which prebuilds can be generated for the package version. If the file is not available, a default list of prebuilds is assumed, consisting of a prebuild for each target architecture that is supported by the package. diff --git a/src/builder/build_env.rs b/src/builder/build_env.rs index a3c056bc..5e344dd1 100644 --- a/src/builder/build_env.rs +++ b/src/builder/build_env.rs @@ -230,7 +230,7 @@ impl<'a> BuildEnv<'a> { for requirement in requirements { match requirement { - Requirement::Msvc => { + Requirement::Msvc { .. } => { // Detect MSVC toolchain let Some(msvc) = tool_detection::detect_msvc()? else { return Err(BuildEnvError::ToolNotFound("msvc".into())); diff --git a/src/repositories/types/requirement.rs b/src/repositories/types/requirement.rs index 4e2aaae4..4065bbab 100644 --- a/src/repositories/types/requirement.rs +++ b/src/repositories/types/requirement.rs @@ -4,36 +4,57 @@ use std::{fmt::Display, str::FromStr}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::platforms::tool_detection; +use crate::{ + installer::types::{VersionError, VersionIntervals}, + platforms::tool_detection, +}; /// Errors that occur when parsing requirements. #[derive(Error, Debug)] pub enum RequirementError { #[error("Requirement '{0}' is unknown")] InvalidRequirement(String), + + #[error("Expected a version, because '@' was used")] + ExpectedVersion, + + #[error("Invalid version")] + VersionError(#[from] VersionError), } /// Represents requirements of a build or test process of a package. #[derive(Clone, Debug)] pub enum Requirement { - Msvc, + Msvc { + version_intervals: VersionIntervals, + }, } impl FromStr for Requirement { type Err = RequirementError; fn from_str(string: &str) -> Result { - match string { - "msvc" => Ok(Self::Msvc), - _ => Err(RequirementError::InvalidRequirement(string.into())), + // Check MSVC + if string == "msvc" || string.starts_with("msvc@") { + // Parse out the version + let version = match string.split_once('@') { + Some((_, "")) => return Err(RequirementError::ExpectedVersion), + Some((_, version)) => version, + None => "", + }; + + let version_intervals = VersionIntervals::from_str(version)?; + return Ok(Self::Msvc { version_intervals }); } + + Err(RequirementError::InvalidRequirement(string.into())) } } impl Display for Requirement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Requirement::Msvc => write!(f, "msvc"), + Requirement::Msvc { version_intervals } => write!(f, "msvc@{version_intervals}"), } } } @@ -63,14 +84,22 @@ impl Requirement { /// Returns true if the requirement is satisfied, false otherwise. pub fn is_satisfied(&self) -> tool_detection::error::Result { match self { - Requirement::Msvc => Ok(tool_detection::detect_msvc()?.is_some()), + Requirement::Msvc { version_intervals } => { + let Some(msvc) = tool_detection::detect_msvc()? else { + return Ok(false); + }; + + Ok(version_intervals.covers(msvc.get_version())) + }, } } /// Gets the message that should be shown when the requirement is not satisfied. pub fn get_not_satisfied_message(&self) -> &str { match self { - Requirement::Msvc => "Microsoft Visual C++ cannot be found, please install it from the Microsoft website first", + Requirement::Msvc { .. } => { + "The required Microsoft Visual C++ cannot be found, please install it from the Microsoft website first" + }, } } }