diff --git a/docs/guide/pyproject.md b/docs/guide/pyproject.md index 44317384a4..7c5a6bcc72 100644 --- a/docs/guide/pyproject.md +++ b/docs/guide/pyproject.md @@ -60,6 +60,20 @@ pulled in as indirect dependencies. These are added here automatically with `ry excluded-dependencies = ["cffi"] ``` +## `tool.rye.universal` + ++++ 0.36.0 + +When this flag is enabled all `lock` and `sync` operations in the project or workspace +operate as if `--universal` is passed. This means that the dependency resolver will +attempt to generate a resolution that's valid on all platforms, operating systems, and +architectures, rather than a resolution that's specific to the current platform. + +```toml +[tool.rye] +universal = true +``` + ## `tool.rye.generate-hashes` +++ 0.35.0 diff --git a/rye/src/cli/lock.rs b/rye/src/cli/lock.rs index 97f84f2eb2..31b3b16292 100644 --- a/rye/src/cli/lock.rs +++ b/rye/src/cli/lock.rs @@ -40,10 +40,13 @@ pub struct Args { /// Set to true to lock with hashes in the lockfile. #[arg(long)] generate_hashes: bool, + /// Use universal lock files. + #[arg(long)] + universal: bool, /// Reset prior lock options. #[arg(long)] reset: bool, - /// Use this pyproject.toml file + /// Use this pyproject.toml file. #[arg(long, value_name = "PYPROJECT_TOML")] pyproject: Option, } @@ -62,6 +65,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> { with_sources: cmd.with_sources, reset: cmd.reset, generate_hashes: cmd.generate_hashes, + universal: cmd.universal, }, pyproject: cmd.pyproject, keyring_provider: cmd.keyring_provider, diff --git a/rye/src/cli/sync.rs b/rye/src/cli/sync.rs index fc10379b98..e324d8ed2e 100644 --- a/rye/src/cli/sync.rs +++ b/rye/src/cli/sync.rs @@ -55,6 +55,9 @@ pub struct Args { /// Do not reuse (reset) prior lock options. #[arg(long)] reset: bool, + /// Use universal lock files + #[arg(long)] + universal: bool, } pub fn execute(cmd: Args) -> Result<(), Error> { @@ -78,6 +81,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> { with_sources: cmd.with_sources, reset: cmd.reset, generate_hashes: cmd.generate_hashes, + universal: cmd.universal, }, keyring_provider: cmd.keyring_provider, pyproject: cmd.pyproject, diff --git a/rye/src/lock.rs b/rye/src/lock.rs index 53eebb23b2..fc97bc9063 100644 --- a/rye/src/lock.rs +++ b/rye/src/lock.rs @@ -37,10 +37,12 @@ static REQUIREMENTS_HEADER: &str = r#"# generated by rye # all-features: {{ lock_options.all_features|tojson }} # with-sources: {{ lock_options.with_sources|tojson }} # generate-hashes: {{ lock_options.generate_hashes|tojson }} +# universal: {{ lock_options.universal|tojson }} "#; -static PARAM_RE: Lazy = - Lazy::new(|| Regex::new(r"^# (pre|features|all-features|with-sources):\s*(.*?)$").unwrap()); +static PARAM_RE: Lazy = Lazy::new(|| { + Regex::new(r"^# (pre|features|all-features|with-sources|universal):\s*(.*?)$").unwrap() +}); #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum LockMode { @@ -103,6 +105,8 @@ pub struct LockOptions { pub reset: bool, /// Generate hashes in the lock file. pub generate_hashes: bool, + /// Use universal lock files. + pub universal: bool, } impl LockOptions { @@ -141,6 +145,7 @@ impl LockOptions { "with-sources" => { rv.with_sources = rv.with_sources || serde_json::from_str(value)? } + "universal" => rv.universal = rv.universal || serde_json::from_str(value)?, _ => unreachable!(), } } @@ -445,6 +450,7 @@ fn generate_lockfile( upgrade, keyring_provider, lock_options.generate_hashes, + lock_options.universal, )?; } else { if keyring_provider != KeyringProvider::Disabled { diff --git a/rye/src/pyproject.rs b/rye/src/pyproject.rs index 600e02e606..1f0f527114 100644 --- a/rye/src/pyproject.rs +++ b/rye/src/pyproject.rs @@ -540,6 +540,11 @@ impl Workspace { generate_hashes(&self.doc) } + /// Should requirements.txt based locking be universal + pub fn universal(&self) -> bool { + universal(&self.doc) + } + /// Should requirements.txt based locking include a find-links reference? pub fn lock_with_sources(&self) -> bool { lock_with_sources(&self.doc) @@ -1019,6 +1024,14 @@ impl PyProject { } } + /// Should requirements.txt-based locking be universal? + pub fn universal(&self) -> bool { + match self.workspace { + Some(ref workspace) => workspace.universal(), + None => universal(&self.doc), + } + } + /// Should requirements.txt-based locking include a find-links reference? pub fn lock_with_sources(&self) -> bool { match self.workspace { @@ -1302,6 +1315,14 @@ fn generate_hashes(doc: &DocumentMut) -> bool { .unwrap_or(false) } +fn universal(doc: &DocumentMut) -> bool { + doc.get("tool") + .and_then(|x| x.get("rye")) + .and_then(|x| x.get("universal")) + .and_then(|x| x.as_bool()) + .unwrap_or(false) +} + fn lock_with_sources(doc: &DocumentMut) -> bool { doc.get("tool") .and_then(|x| x.get("rye")) diff --git a/rye/src/sync.rs b/rye/src/sync.rs index 9fb1d34413..2f4ef59d01 100644 --- a/rye/src/sync.rs +++ b/rye/src/sync.rs @@ -110,6 +110,11 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { cmd.lock_options.generate_hashes = true; } + // Turn on universal locking if the project demands it. + if pyproject.universal() { + cmd.lock_options.universal = true; + } + // Turn on locking with sources if the project demands it. if pyproject.lock_with_sources() { cmd.lock_options.with_sources = true; diff --git a/rye/src/uv.rs b/rye/src/uv.rs index f633cc061c..1eb48fb491 100644 --- a/rye/src/uv.rs +++ b/rye/src/uv.rs @@ -41,6 +41,7 @@ struct UvCompileOptions { pub no_header: bool, pub keyring_provider: KeyringProvider, pub generate_hashes: bool, + pub universal: bool, } impl UvCompileOptions { @@ -65,6 +66,10 @@ impl UvCompileOptions { cmd.arg("--exclude-newer").arg(dt); } + if self.universal { + cmd.arg("--universal"); + } + match self.upgrade { UvPackageUpgrade::All => { cmd.arg("--upgrade"); @@ -91,6 +96,7 @@ impl Default for UvCompileOptions { no_header: false, generate_hashes: false, keyring_provider: KeyringProvider::Disabled, + universal: false, } } } @@ -350,6 +356,7 @@ impl Uv { upgrade: UvPackageUpgrade, keyring_provider: KeyringProvider, generate_hashes: bool, + universal: bool, ) -> Result<(), Error> { let options = UvCompileOptions { allow_prerelease, @@ -359,6 +366,7 @@ impl Uv { no_header: true, generate_hashes, keyring_provider, + universal, }; let mut cmd = self.cmd(); @@ -608,6 +616,7 @@ impl UvWithVenv { no_header: true, generate_hashes: false, keyring_provider, + universal: false, }; cmd.arg("pip").arg("compile"); diff --git a/rye/tests/test_sync.rs b/rye/tests/test_sync.rs index c989c467b8..5bf4b8f64b 100644 --- a/rye/tests/test_sync.rs +++ b/rye/tests/test_sync.rs @@ -189,6 +189,7 @@ fn test_autosync_remember() { # all-features: true # with-sources: true # generate-hashes: false + # universal: false --index-url https://pypi.org/simple/ @@ -246,6 +247,7 @@ fn test_autosync_remember() { # all-features: true # with-sources: true # generate-hashes: false + # universal: false --index-url https://pypi.org/simple/