diff --git a/crates/uv-build-backend/src/lib.rs b/crates/uv-build-backend/src/lib.rs index 634c7765b6d7..366cf43e6edb 100644 --- a/crates/uv-build-backend/src/lib.rs +++ b/crates/uv-build-backend/src/lib.rs @@ -4,14 +4,14 @@ use crate::metadata::{PyProjectToml, ValidationError}; use flate2::write::GzEncoder; use flate2::Compression; use fs_err::File; -use globset::GlobSetBuilder; +use globset::{Glob, GlobSetBuilder}; use itertools::Itertools; use sha2::{Digest, Sha256}; use std::fs::FileType; use std::io::{BufReader, Cursor, Read, Write}; use std::path::{Path, PathBuf, StripPrefixError}; use std::{io, mem}; -use tar::{Builder, EntryType, Header}; +use tar::{EntryType, Header}; use thiserror::Error; use tracing::{debug, trace}; use uv_distribution_filename::{SourceDistExtension, SourceDistFilename, WheelFilename}; @@ -54,8 +54,6 @@ pub enum Error { #[source] err: walkdir::Error, }, - #[error("Non-UTF-8 paths are not supported: `{}`", _0.user_display())] - NotUtf8Path(PathBuf), #[error("Failed to walk source tree")] StripPrefix(#[from] StripPrefixError), #[error("Unsupported file type {1:?}: `{}`", _0.user_display())] @@ -356,17 +354,16 @@ pub fn build_wheel( let relative_path = entry .path() .strip_prefix(&strip_root) - .expect("walkdir starts with root"); - let relative_path_str = relative_path - .to_str() - .ok_or_else(|| Error::NotUtf8Path(relative_path.to_path_buf()))?; + .expect("walkdir starts with root") + .user_display() + .to_string(); - debug!("Adding to wheel: `{relative_path_str}`"); + debug!("Adding to wheel: `{relative_path}`"); if entry.file_type().is_dir() { - wheel_writer.write_directory(relative_path_str)?; + wheel_writer.write_directory(&relative_path)?; } else if entry.file_type().is_file() { - wheel_writer.write_file(relative_path_str, entry.path())?; + wheel_writer.write_file(&relative_path, entry.path())?; } else { // TODO(konsti): We may want to support symlinks, there is support for installing them. return Err(Error::UnsupportedFileType( @@ -378,7 +375,80 @@ pub fn build_wheel( entry.path(); } - debug!("Adding metadata files to {}", wheel_path.user_display()); + if let Some(license_files) = &pyproject_toml.license_files() { + let license_files_globs: Vec<_> = license_files + .iter() + .map(|license_files| { + trace!("Including license files at: `{license_files}`"); + parse_portable_glob(license_files) + }) + .collect::>() + .map_err(|err| Error::PortableGlob { + field: "project.license-files".to_string(), + source: err, + })?; + let license_files_matcher = + GlobDirFilter::from_globs(&license_files_globs).map_err(|err| { + Error::GlobSetTooLarge { + field: "project.license-files".to_string(), + source: err, + } + })?; + + let license_dir = format!( + "{}-{}.dist-info/licenses/", + pyproject_toml.name().as_dist_info_name(), + pyproject_toml.version() + ); + + wheel_writer.write_directory(&license_dir)?; + + for entry in WalkDir::new(source_tree).into_iter().filter_entry(|entry| { + // TODO(konsti): This should be prettier. + let relative = entry + .path() + .strip_prefix(source_tree) + .expect("walkdir starts with root"); + + // Fast path: Don't descend into a directory that can't be included. + license_files_matcher.match_directory(relative) + }) { + let entry = entry.map_err(|err| Error::WalkDir { + root: source_tree.to_path_buf(), + err, + })?; + // TODO(konsti): This should be prettier. + let relative = entry + .path() + .strip_prefix(source_tree) + .expect("walkdir starts with root"); + + if !license_files_matcher.match_path(relative) { + trace!("Excluding {}", relative.user_display()); + continue; + }; + + let relative_licenses = Path::new(&license_dir) + .join(relative) + .portable_display() + .to_string(); + + if entry.file_type().is_dir() { + wheel_writer.write_directory(&relative_licenses)?; + } else if entry.file_type().is_file() { + debug!("Adding license file: `{}`", relative.user_display()); + wheel_writer.write_file(&relative_licenses, entry.path())?; + } else { + // TODO(konsti): We may want to support symlinks, there is support for installing them. + return Err(Error::UnsupportedFileType( + entry.path().to_path_buf(), + entry.file_type(), + )); + } + } + } + + debug!("Adding metadata files to: `{}`", wheel_path.user_display()); let dist_info_dir = write_dist_info( &mut wheel_writer, &pyproject_toml, @@ -449,28 +519,32 @@ pub fn build_source_dist( extension: SourceDistExtension::TarGz, }; - let top_level = format!("{}-{}", pyproject_toml.name(), pyproject_toml.version()); + let top_level = format!( + "{}-{}", + pyproject_toml.name().as_dist_info_name(), + pyproject_toml.version() + ); let source_dist_path = source_dist_directory.join(filename.to_string()); let tar_gz = File::create(&source_dist_path)?; let enc = GzEncoder::new(tar_gz, Compression::default()); let mut tar = tar::Builder::new(enc); - let metadata = pyproject_toml - .to_metadata(source_tree)? - .core_metadata_format(); + let metadata = pyproject_toml.to_metadata(source_tree)?; + let metadata_email = metadata.core_metadata_format(); let mut header = Header::new_gnu(); - header.set_size(metadata.bytes().len() as u64); + header.set_size(metadata_email.bytes().len() as u64); header.set_mode(0o644); header.set_cksum(); tar.append_data( &mut header, Path::new(&top_level).join("PKG-INFO"), - Cursor::new(metadata), + Cursor::new(metadata_email), ) .map_err(|err| Error::TarWrite(source_dist_path.clone(), err))?; + // The user (or default) includes let mut include_globs = Vec::new(); for include in settings.include { let glob = parse_portable_glob(&include).map_err(|err| Error::PortableGlob { @@ -479,6 +553,30 @@ pub fn build_source_dist( })?; include_globs.push(glob.clone()); } + + // Include the Readme + if let Some(readme) = pyproject_toml + .readme() + .as_ref() + .and_then(|readme| readme.path()) + { + trace!("Including readme at: `{}`", readme.user_display()); + include_globs.push( + Glob::new(&globset::escape(&readme.portable_display().to_string())) + .expect("escaped globset is parseable"), + ); + } + + // Include the license files + for license_files in pyproject_toml.license_files().into_iter().flatten() { + trace!("Including license files at: `{license_files}`"); + let glob = parse_portable_glob(license_files).map_err(|err| Error::PortableGlob { + field: "project.license-files".to_string(), + source: err, + })?; + include_globs.push(glob); + } + let include_matcher = GlobDirFilter::from_globs(&include_globs).map_err(|err| Error::GlobSetTooLarge { field: "tool.uv.source-dist.include".to_string(), @@ -549,7 +647,7 @@ pub fn build_source_dist( /// Add a file or a directory to a source distribution. fn add_source_dist_entry( - tar: &mut Builder>, + tar: &mut tar::Builder>, entry: &DirEntry, top_level: &str, source_dist_path: &Path, diff --git a/crates/uv-build-backend/src/metadata.rs b/crates/uv-build-backend/src/metadata.rs index 577a26d85871..c7c4e61584b8 100644 --- a/crates/uv-build-backend/src/metadata.rs +++ b/crates/uv-build-backend/src/metadata.rs @@ -76,6 +76,14 @@ impl PyProjectToml { Ok(toml::from_str(contents)?) } + pub(crate) fn readme(&self) -> Option<&Readme> { + self.project.readme.as_ref() + } + + pub(crate) fn license_files(&self) -> Option<&[String]> { + self.project.license_files.as_deref() + } + /// Warn if the `[build-system]` table looks suspicious. /// /// Example of a valid table: @@ -591,7 +599,7 @@ struct Project { /// . #[derive(Deserialize, Debug, Clone)] #[serde(untagged, rename_all = "kebab-case")] -enum Readme { +pub(crate) enum Readme { /// Relative path to the README. String(PathBuf), /// Relative path to the README. @@ -608,11 +616,22 @@ enum Readme { }, } +impl Readme { + /// If the readme is a file, return the path to the file. + pub(crate) fn path(&self) -> Option<&Path> { + match self { + Readme::String(path) => Some(path), + Readme::File { file, .. } => Some(file), + Readme::Text { .. } => None, + } + } +} + /// The optional `project.license` key in a pyproject.toml as specified in /// . #[derive(Deserialize, Debug, Clone)] #[serde(untagged)] -enum License { +pub(crate) enum License { /// An SPDX Expression. /// /// From the provisional PEP 639. @@ -639,7 +658,7 @@ enum License { deny_unknown_fields, expecting = "a table with 'name' and/or 'email' keys" )] -enum Contact { +pub(crate) enum Contact { /// TODO(konsti): RFC 822 validation. NameEmail { name: String, email: String }, /// TODO(konsti): RFC 822 validation. diff --git a/crates/uv-build-backend/src/tests.rs b/crates/uv-build-backend/src/tests.rs index 3b8469feea16..084493f5e9a7 100644 --- a/crates/uv-build-backend/src/tests.rs +++ b/crates/uv-build-backend/src/tests.rs @@ -1,5 +1,5 @@ use super::*; -use indoc::indoc; +use flate2::bufread::GzDecoder; use insta::assert_snapshot; use std::str::FromStr; use tempfile::TempDir; @@ -43,56 +43,6 @@ fn test_record() { "); } -/// Check that we write deterministic wheels. -#[test] -fn test_determinism() { - let built_by_uv = Path::new("../../scripts/packages/built-by-uv"); - let src = TempDir::new().unwrap(); - for dir in ["src", "tests", "data-dir"] { - copy_dir_all(built_by_uv.join(dir), src.path().join(dir)).unwrap(); - } - for dir in ["pyproject.toml", "README.md", "uv.lock"] { - fs_err::copy(built_by_uv.join(dir), src.path().join(dir)).unwrap(); - } - - let temp1 = TempDir::new().unwrap(); - build_wheel( - src.path(), - temp1.path(), - None, - WheelSettings::default(), - "1.0.0+test", - ) - .unwrap(); - - // Touch the file to check that we don't serialize the last modified date. - fs_err::write( - src.path().join("src/built_by_uv/__init__.py"), - indoc! {r#" - def greet() -> str: - return "Hello 👋" - "# - }, - ) - .unwrap(); - - let temp2 = TempDir::new().unwrap(); - build_wheel( - src.path(), - temp2.path(), - None, - WheelSettings::default(), - "1.0.0+test", - ) - .unwrap(); - - let wheel_filename = "built_by_uv-0.1.0-py3-none-any.whl"; - assert_eq!( - fs_err::read(temp1.path().join(wheel_filename)).unwrap(), - fs_err::read(temp2.path().join(wheel_filename)).unwrap() - ); -} - /// Snapshot all files from the prepare metadata hook. #[test] fn test_prepare_metadata() { @@ -125,12 +75,17 @@ fn test_prepare_metadata() { .path() .join("built_by_uv-0.1.0.dist-info/METADATA"); assert_snapshot!(fs_err::read_to_string(metadata_file).unwrap(), @r###" - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: built-by-uv Version: 0.1.0 Summary: A package to be built with the uv build backend that uses all features exposed by the build backend Requires-Dist: anyio>=4,<5 Requires-Python: >=3.12 + Description-Content-Type: text/markdown + + # built_by_uv + + A package to be built with the uv build backend that uses all features exposed by the build backend. "###); let record_file = metadata_dir @@ -138,7 +93,7 @@ fn test_prepare_metadata() { .join("built_by_uv-0.1.0.dist-info/RECORD"); assert_snapshot!(fs_err::read_to_string(record_file).unwrap(), @r###" built_by_uv-0.1.0.dist-info/WHEEL,sha256=3da1bfa0e8fd1b6cc246aa0b2b44a35815596c600cb485c39a6f8c106c3d5a8d,83 - built_by_uv-0.1.0.dist-info/METADATA,sha256=ec36b5ae8830bdd248e90aaf581483ffb057f9a2d0f41e19e585531e7d07c9dc,215 + built_by_uv-0.1.0.dist-info/METADATA,sha256=acb91f5a18cb53fa57b45eb4590ea13195a774c856a9dd8cf27cc5435d6451b6,372 built_by_uv-0.1.0.dist-info/RECORD,, "###); @@ -152,3 +107,136 @@ fn test_prepare_metadata() { Tag: py3-none-any "###); } + +/// Test that source tree -> source dist -> wheel includes the right files and is stable and +/// deterministic in dependent of the build path. +#[test] +fn built_by_uv_building() { + let built_by_uv = Path::new("../../scripts/packages/built-by-uv"); + let src = TempDir::new().unwrap(); + for dir in ["src", "tests", "data-dir", "third-party-licenses"] { + copy_dir_all(built_by_uv.join(dir), src.path().join(dir)).unwrap(); + } + for dir in [ + "pyproject.toml", + "README.md", + "uv.lock", + "LICENSE-APACHE", + "LICENSE-MIT", + ] { + fs_err::copy(built_by_uv.join(dir), src.path().join(dir)).unwrap(); + } + + // Build a wheel from the source tree + let direct_output_dir = TempDir::new().unwrap(); + build_wheel( + src.path(), + direct_output_dir.path(), + None, + WheelSettings::default(), + "1.0.0+test", + ) + .unwrap(); + + let wheel = zip::ZipArchive::new( + File::open( + direct_output_dir + .path() + .join("built_by_uv-0.1.0-py3-none-any.whl"), + ) + .unwrap(), + ) + .unwrap(); + let mut direct_wheel_contents: Vec<_> = wheel.file_names().collect(); + direct_wheel_contents.sort_unstable(); + + // Build a source dist from the source tree + let source_dist_dir = TempDir::new().unwrap(); + build_source_dist( + src.path(), + source_dist_dir.path(), + SourceDistSettings::default(), + "1.0.0+test", + ) + .unwrap(); + + // Build a wheel from the source dist + let sdist_tree = TempDir::new().unwrap(); + let source_dist_path = source_dist_dir.path().join("built_by_uv-0.1.0.tar.gz"); + let sdist_reader = BufReader::new(File::open(&source_dist_path).unwrap()); + let mut source_dist = tar::Archive::new(GzDecoder::new(sdist_reader)); + let mut source_dist_contents: Vec<_> = source_dist + .entries() + .unwrap() + .map(|entry| entry.unwrap().path().unwrap().to_str().unwrap().to_string()) + .collect(); + source_dist_contents.sort(); + // Reset the reader and unpack + let sdist_reader = BufReader::new(File::open(&source_dist_path).unwrap()); + let mut source_dist = tar::Archive::new(GzDecoder::new(sdist_reader)); + source_dist.unpack(sdist_tree.path()).unwrap(); + drop(source_dist_dir); + + let indirect_output_dir = TempDir::new().unwrap(); + build_wheel( + &sdist_tree.path().join("built_by_uv-0.1.0"), + indirect_output_dir.path(), + None, + WheelSettings::default(), + "1.0.0+test", + ) + .unwrap(); + + // Check that we write deterministic wheels. + let wheel_filename = "built_by_uv-0.1.0-py3-none-any.whl"; + assert_eq!( + fs_err::read(direct_output_dir.path().join(wheel_filename)).unwrap(), + fs_err::read(indirect_output_dir.path().join(wheel_filename)).unwrap() + ); + + // Check the contained files and directories + assert_snapshot!(source_dist_contents.iter().map(|path| path.replace('\\', "/")).join("\n"), @r" + built_by_uv-0.1.0/LICENSE-APACHE + built_by_uv-0.1.0/LICENSE-MIT + built_by_uv-0.1.0/PKG-INFO + built_by_uv-0.1.0/README.md + built_by_uv-0.1.0/pyproject.toml + built_by_uv-0.1.0/src/built_by_uv + built_by_uv-0.1.0/src/built_by_uv/__init__.py + built_by_uv-0.1.0/src/built_by_uv/arithmetic + built_by_uv-0.1.0/src/built_by_uv/arithmetic/__init__.py + built_by_uv-0.1.0/src/built_by_uv/arithmetic/circle.py + built_by_uv-0.1.0/src/built_by_uv/arithmetic/pi.txt + built_by_uv-0.1.0/third-party-licenses/PEP-401.txt + "); + + let wheel = zip::ZipArchive::new( + File::open( + indirect_output_dir + .path() + .join("built_by_uv-0.1.0-py3-none-any.whl"), + ) + .unwrap(), + ) + .unwrap(); + let mut indirect_wheel_contents: Vec<_> = wheel.file_names().collect(); + indirect_wheel_contents.sort_unstable(); + assert_eq!(indirect_wheel_contents, direct_wheel_contents); + + assert_snapshot!(indirect_wheel_contents.iter().map(|path| path.replace('\\', "/")).join("\n"), @r" + built_by_uv-0.1.0.dist-info/ + built_by_uv-0.1.0.dist-info/METADATA + built_by_uv-0.1.0.dist-info/RECORD + built_by_uv-0.1.0.dist-info/WHEEL + built_by_uv-0.1.0.dist-info/licenses/ + built_by_uv-0.1.0.dist-info/licenses/LICENSE-APACHE + built_by_uv-0.1.0.dist-info/licenses/LICENSE-MIT + built_by_uv-0.1.0.dist-info/licenses/third-party-licenses/PEP-401.txt + built_by_uv/ + built_by_uv/__init__.py + built_by_uv/arithmetic/ + built_by_uv/arithmetic/__init__.py + built_by_uv/arithmetic/circle.py + built_by_uv/arithmetic/pi.txt + "); +} diff --git a/crates/uv/tests/it/build_backend.rs b/crates/uv/tests/it/build_backend.rs index 213e4520b23e..36ac0cf8cee2 100644 --- a/crates/uv/tests/it/build_backend.rs +++ b/crates/uv/tests/it/build_backend.rs @@ -105,7 +105,7 @@ fn built_by_uv_direct() -> Result<()> { .build_backend() .arg("build-wheel") .arg(wheel_dir.path()) - .current_dir(sdist_tree.path().join("built-by-uv-0.1.0")), @r###" + .current_dir(sdist_tree.path().join("built_by_uv-0.1.0")), @r###" success: true exit_code: 0 ----- stdout ----- diff --git a/scripts/packages/built-by-uv/LICENSE-APACHE b/scripts/packages/built-by-uv/LICENSE-APACHE new file mode 100644 index 000000000000..f49a4e16e68b --- /dev/null +++ b/scripts/packages/built-by-uv/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/scripts/packages/built-by-uv/LICENSE-MIT b/scripts/packages/built-by-uv/LICENSE-MIT new file mode 100644 index 000000000000..ec2236bb8994 --- /dev/null +++ b/scripts/packages/built-by-uv/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Astral Software Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/packages/built-by-uv/pyproject.toml b/scripts/packages/built-by-uv/pyproject.toml index 22da4ec7ec60..b479ec718436 100644 --- a/scripts/packages/built-by-uv/pyproject.toml +++ b/scripts/packages/built-by-uv/pyproject.toml @@ -2,9 +2,10 @@ name = "built-by-uv" version = "0.1.0" description = "A package to be built with the uv build backend that uses all features exposed by the build backend" -#rreadme = "README.md" +readme = "README.md" requires-python = ">=3.12" dependencies = ["anyio>=4,<5"] +license-files = ["LICENSE*", "third-party-licenses/*"] [build-system] requires = ["uv>=0.4.15,<5"] diff --git a/scripts/packages/built-by-uv/third-party-licenses/PEP-401.txt b/scripts/packages/built-by-uv/third-party-licenses/PEP-401.txt new file mode 100644 index 000000000000..f77a7c7d228c --- /dev/null +++ b/scripts/packages/built-by-uv/third-party-licenses/PEP-401.txt @@ -0,0 +1,4 @@ +This document is the property of the Python Steering Union (not to +be confused with the Python Secret Underground, which emphatically +does not exist). We suppose it's okay for you to read this, but don't +even think about quoting, copying, modifying, or distributing it.