mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Add templates for popular build backends (#7857)
Co-authored-by: konstin <konstin@mailbox.org>
This commit is contained in:
parent
ea0c32df8c
commit
319c0183c6
9 changed files with 1063 additions and 53 deletions
|
@ -12,7 +12,7 @@ use url::Url;
|
|||
use uv_cache::CacheArgs;
|
||||
use uv_configuration::{
|
||||
ConfigSettingEntry, ExportFormat, IndexStrategy, KeyringProviderType, PackageNameSpecifier,
|
||||
TargetTriple, TrustedHost, TrustedPublishing, VersionControlSystem,
|
||||
ProjectBuildBackend, TargetTriple, TrustedHost, TrustedPublishing, VersionControlSystem,
|
||||
};
|
||||
use uv_distribution_types::{Index, IndexUrl, Origin, PipExtraIndex, PipFindLinks, PipIndex};
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
|
@ -2525,6 +2525,10 @@ pub struct InitArgs {
|
|||
#[arg(long, value_enum, conflicts_with = "script")]
|
||||
pub vcs: Option<VersionControlSystem>,
|
||||
|
||||
/// Initialize a build-backend of choice for the project.
|
||||
#[arg(long, value_enum, conflicts_with_all=["script", "no_package"])]
|
||||
pub build_backend: Option<ProjectBuildBackend>,
|
||||
|
||||
/// Do not create a `README.md` file.
|
||||
#[arg(long)]
|
||||
pub no_readme: bool,
|
||||
|
|
|
@ -14,6 +14,7 @@ pub use name_specifiers::*;
|
|||
pub use overrides::*;
|
||||
pub use package_options::*;
|
||||
pub use preview::*;
|
||||
pub use project_build_backend::*;
|
||||
pub use sources::*;
|
||||
pub use target_triple::*;
|
||||
pub use trusted_host::*;
|
||||
|
@ -36,6 +37,7 @@ mod name_specifiers;
|
|||
mod overrides;
|
||||
mod package_options;
|
||||
mod preview;
|
||||
mod project_build_backend;
|
||||
mod sources;
|
||||
mod target_triple;
|
||||
mod trusted_host;
|
||||
|
|
20
crates/uv-configuration/src/project_build_backend.rs
Normal file
20
crates/uv-configuration/src/project_build_backend.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
/// Available project build backends for use in `pyproject.toml`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum ProjectBuildBackend {
|
||||
#[default]
|
||||
/// Use [hatchling](https://pypi.org/project/hatchling) as the project build backend.
|
||||
Hatch,
|
||||
/// Use [flit-core](https://pypi.org/project/flit-core) as the project build backend.
|
||||
Flit,
|
||||
/// Use [pdm-backend](https://pypi.org/project/pdm-backend) as the project build backend.
|
||||
PDM,
|
||||
/// Use [setuptools](https://pypi.org/project/setuptools) as the project build backend.
|
||||
Setuptools,
|
||||
/// Use [maturin](https://pypi.org/project/maturin) as the project build backend.
|
||||
Maturin,
|
||||
/// Use [scikit-build-core](https://pypi.org/project/scikit-build-core) as the project build backend.
|
||||
Scikit,
|
||||
}
|
|
@ -9,7 +9,7 @@ use tracing::{debug, warn};
|
|||
use uv_cache::Cache;
|
||||
use uv_cli::AuthorFrom;
|
||||
use uv_client::{BaseClientBuilder, Connectivity};
|
||||
use uv_configuration::{VersionControlError, VersionControlSystem};
|
||||
use uv_configuration::{ProjectBuildBackend, VersionControlError, VersionControlSystem};
|
||||
use uv_fs::{Simplified, CWD};
|
||||
use uv_git::GIT;
|
||||
use uv_pep440::Version;
|
||||
|
@ -38,6 +38,7 @@ pub(crate) async fn init(
|
|||
package: bool,
|
||||
init_kind: InitKind,
|
||||
vcs: Option<VersionControlSystem>,
|
||||
build_backend: Option<ProjectBuildBackend>,
|
||||
no_readme: bool,
|
||||
author_from: Option<AuthorFrom>,
|
||||
no_pin_python: bool,
|
||||
|
@ -115,6 +116,7 @@ pub(crate) async fn init(
|
|||
package,
|
||||
project_kind,
|
||||
vcs,
|
||||
build_backend,
|
||||
no_readme,
|
||||
author_from,
|
||||
no_pin_python,
|
||||
|
@ -246,6 +248,7 @@ async fn init_project(
|
|||
package: bool,
|
||||
project_kind: InitProjectKind,
|
||||
vcs: Option<VersionControlSystem>,
|
||||
build_backend: Option<ProjectBuildBackend>,
|
||||
no_readme: bool,
|
||||
author_from: Option<AuthorFrom>,
|
||||
no_pin_python: bool,
|
||||
|
@ -486,6 +489,7 @@ async fn init_project(
|
|||
&requires_python,
|
||||
python_request.as_ref(),
|
||||
vcs,
|
||||
build_backend,
|
||||
author_from,
|
||||
no_readme,
|
||||
package,
|
||||
|
@ -576,6 +580,7 @@ impl InitProjectKind {
|
|||
requires_python: &RequiresPython,
|
||||
python_request: Option<&PythonRequest>,
|
||||
vcs: Option<VersionControlSystem>,
|
||||
build_backend: Option<ProjectBuildBackend>,
|
||||
author_from: Option<AuthorFrom>,
|
||||
no_readme: bool,
|
||||
package: bool,
|
||||
|
@ -588,6 +593,7 @@ impl InitProjectKind {
|
|||
requires_python,
|
||||
python_request,
|
||||
vcs,
|
||||
build_backend,
|
||||
author_from,
|
||||
no_readme,
|
||||
package,
|
||||
|
@ -601,6 +607,7 @@ impl InitProjectKind {
|
|||
requires_python,
|
||||
python_request,
|
||||
vcs,
|
||||
build_backend,
|
||||
author_from,
|
||||
no_readme,
|
||||
package,
|
||||
|
@ -618,6 +625,7 @@ impl InitProjectKind {
|
|||
requires_python: &RequiresPython,
|
||||
python_request: Option<&PythonRequest>,
|
||||
vcs: Option<VersionControlSystem>,
|
||||
build_backend: Option<ProjectBuildBackend>,
|
||||
author_from: Option<AuthorFrom>,
|
||||
no_readme: bool,
|
||||
package: bool,
|
||||
|
@ -644,25 +652,13 @@ impl InitProjectKind {
|
|||
pyproject.push_str(&pyproject_project_scripts(name, name.as_str(), "main"));
|
||||
|
||||
// Add a build system
|
||||
let build_backend = build_backend.unwrap_or_default();
|
||||
pyproject.push('\n');
|
||||
pyproject.push_str(pyproject_build_system());
|
||||
}
|
||||
pyproject.push_str(&pyproject_build_system(name, build_backend));
|
||||
pyproject_build_backend_prerequisites(name, path, build_backend)?;
|
||||
|
||||
// Create the source structure.
|
||||
if package {
|
||||
// Create `src/{name}/__init__.py`, if it doesn't exist already.
|
||||
let src_dir = path.join("src").join(&*name.as_dist_info_name());
|
||||
let init_py = src_dir.join("__init__.py");
|
||||
if !init_py.try_exists()? {
|
||||
fs_err::create_dir_all(&src_dir)?;
|
||||
fs_err::write(
|
||||
init_py,
|
||||
indoc::formatdoc! {r#"
|
||||
def main() -> None:
|
||||
print("Hello from {name}!")
|
||||
"#},
|
||||
)?;
|
||||
}
|
||||
// Generate `src` files
|
||||
generate_package_scripts(name, path, build_backend, false)?;
|
||||
} else {
|
||||
// Create `hello.py` if it doesn't exist
|
||||
// TODO(zanieb): Only create `hello.py` if there are no other Python files?
|
||||
|
@ -710,6 +706,7 @@ impl InitProjectKind {
|
|||
requires_python: &RequiresPython,
|
||||
python_request: Option<&PythonRequest>,
|
||||
vcs: Option<VersionControlSystem>,
|
||||
build_backend: Option<ProjectBuildBackend>,
|
||||
author_from: Option<AuthorFrom>,
|
||||
no_readme: bool,
|
||||
package: bool,
|
||||
|
@ -726,31 +723,15 @@ impl InitProjectKind {
|
|||
let mut pyproject = pyproject_project(name, requires_python, author.as_ref(), no_readme);
|
||||
|
||||
// Always include a build system if the project is packaged.
|
||||
let build_backend = build_backend.unwrap_or_default();
|
||||
pyproject.push('\n');
|
||||
pyproject.push_str(pyproject_build_system());
|
||||
pyproject.push_str(&pyproject_build_system(name, build_backend));
|
||||
pyproject_build_backend_prerequisites(name, path, build_backend)?;
|
||||
|
||||
fs_err::write(path.join("pyproject.toml"), pyproject)?;
|
||||
|
||||
// Create `src/{name}/__init__.py`, if it doesn't exist already.
|
||||
let src_dir = path.join("src").join(&*name.as_dist_info_name());
|
||||
fs_err::create_dir_all(&src_dir)?;
|
||||
|
||||
let init_py = src_dir.join("__init__.py");
|
||||
if !init_py.try_exists()? {
|
||||
fs_err::write(
|
||||
init_py,
|
||||
indoc::formatdoc! {r#"
|
||||
def hello() -> str:
|
||||
return "Hello from {name}!"
|
||||
"#},
|
||||
)?;
|
||||
}
|
||||
|
||||
// Create a `py.typed` file
|
||||
let py_typed = src_dir.join("py.typed");
|
||||
if !py_typed.try_exists()? {
|
||||
fs_err::write(py_typed, "")?;
|
||||
}
|
||||
// Generate `src` files
|
||||
generate_package_scripts(name, path, build_backend, true)?;
|
||||
|
||||
// Write .python-version if it doesn't exist.
|
||||
if let Some(python_request) = python_request {
|
||||
|
@ -807,18 +788,63 @@ fn pyproject_project(
|
|||
dependencies = []
|
||||
"#,
|
||||
readme = if no_readme { "" } else { "\nreadme = \"README.md\"" },
|
||||
authors = author.map_or_else(String::new, |author| format!("\nauthors = [\n {} \n]", author.to_toml_string())),
|
||||
authors = author.map_or_else(String::new, |author| format!("\nauthors = [\n {}\n]", author.to_toml_string())),
|
||||
requires_python = requires_python.specifiers(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the `[build-system]` section of a `pyproject.toml`.
|
||||
fn pyproject_build_system() -> &'static str {
|
||||
indoc::indoc! {r#"
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
"#}
|
||||
/// Generate the `[tool.]` section of a `pyproject.toml` where applicable.
|
||||
fn pyproject_build_system(package: &PackageName, build_backend: ProjectBuildBackend) -> String {
|
||||
let module_name = package.as_dist_info_name();
|
||||
match build_backend {
|
||||
// Pure-python backends
|
||||
ProjectBuildBackend::Hatch => indoc::indoc! {r#"
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
"#}
|
||||
.to_string(),
|
||||
ProjectBuildBackend::Flit => indoc::indoc! {r#"
|
||||
[build-system]
|
||||
requires = ["flit_core>=3.2,<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
"#}
|
||||
.to_string(),
|
||||
ProjectBuildBackend::PDM => indoc::indoc! {r#"
|
||||
[build-system]
|
||||
requires = ["pdm-backend"]
|
||||
build-backend = "pdm.backend"
|
||||
"#}
|
||||
.to_string(),
|
||||
ProjectBuildBackend::Setuptools => indoc::indoc! {r#"
|
||||
[build-system]
|
||||
requires = ["setuptools>=61"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#}
|
||||
.to_string(),
|
||||
// Binary build backends
|
||||
ProjectBuildBackend::Maturin => indoc::formatdoc! {r#"
|
||||
[tool.maturin]
|
||||
module-name = "{module_name}._core"
|
||||
python-packages = ["{module_name}"]
|
||||
python-source = "src"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
build-backend = "maturin"
|
||||
"#},
|
||||
ProjectBuildBackend::Scikit => indoc::indoc! {r#"
|
||||
[tool.scikit-build]
|
||||
minimum-version = "build-system.requires"
|
||||
build-dir = "build/{wheel_tag}"
|
||||
|
||||
[build-system]
|
||||
requires = ["scikit-build-core>=0.10", "pybind11"]
|
||||
build-backend = "scikit_build_core.build"
|
||||
"#}
|
||||
.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the `[project.scripts]` section of a `pyproject.toml`.
|
||||
|
@ -830,6 +856,198 @@ fn pyproject_project_scripts(package: &PackageName, executable_name: &str, targe
|
|||
"#}
|
||||
}
|
||||
|
||||
/// Generate additional files as needed for specific build backends.
|
||||
fn pyproject_build_backend_prerequisites(
|
||||
package: &PackageName,
|
||||
path: &Path,
|
||||
build_backend: ProjectBuildBackend,
|
||||
) -> Result<()> {
|
||||
let module_name = package.as_dist_info_name();
|
||||
match build_backend {
|
||||
ProjectBuildBackend::Maturin => {
|
||||
// Generate Cargo.toml
|
||||
let build_file = path.join("Cargo.toml");
|
||||
if !build_file.try_exists()? {
|
||||
fs_err::write(
|
||||
build_file,
|
||||
indoc::formatdoc! {r#"
|
||||
[package]
|
||||
name = "{module_name}"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "_core"
|
||||
# "cdylib" is necessary to produce a shared library for Python to import from.
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
# "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so)
|
||||
# "abi3-py39" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.9
|
||||
pyo3 = {{ version = "0.22.4", features = ["extension-module", "abi3-py39"] }}
|
||||
"#},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
ProjectBuildBackend::Scikit => {
|
||||
// Generate CMakeLists.txt
|
||||
let build_file = path.join("CMakeLists.txt");
|
||||
if !build_file.try_exists()? {
|
||||
fs_err::write(
|
||||
build_file,
|
||||
indoc::formatdoc! {r#"
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(${{SKBUILD_PROJECT_NAME}} LANGUAGES CXX)
|
||||
|
||||
set(PYBIND11_FINDPYTHON ON)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
|
||||
pybind11_add_module(_core MODULE src/main.cpp)
|
||||
install(TARGETS _core DESTINATION ${{SKBUILD_PROJECT_NAME}})
|
||||
"#},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate startup scripts for a package-based application or library.
|
||||
fn generate_package_scripts(
|
||||
package: &PackageName,
|
||||
path: &Path,
|
||||
build_backend: ProjectBuildBackend,
|
||||
is_lib: bool,
|
||||
) -> Result<()> {
|
||||
let module_name = package.as_dist_info_name();
|
||||
|
||||
let src_dir = path.join("src");
|
||||
let pkg_dir = src_dir.join(&*module_name);
|
||||
fs_err::create_dir_all(&pkg_dir)?;
|
||||
|
||||
// Python script for pure-python packaged apps or libs
|
||||
let pure_python_script = if is_lib {
|
||||
indoc::formatdoc! {r#"
|
||||
def hello() -> str:
|
||||
return "Hello from {package}!"
|
||||
"#}
|
||||
} else {
|
||||
indoc::formatdoc! {r#"
|
||||
def main() -> None:
|
||||
print("Hello from {package}!")
|
||||
"#}
|
||||
};
|
||||
|
||||
// Python script for binary-based packaged apps or libs
|
||||
let binary_call_script = if is_lib {
|
||||
indoc::formatdoc! {r#"
|
||||
from {module_name}._core import hello_from_bin
|
||||
|
||||
def hello() -> str:
|
||||
return hello_from_bin()
|
||||
"#}
|
||||
} else {
|
||||
indoc::formatdoc! {r#"
|
||||
from {module_name}._core import hello_from_bin
|
||||
|
||||
def main() -> None:
|
||||
print(hello_from_bin())
|
||||
"#}
|
||||
};
|
||||
|
||||
// .pyi file for binary script
|
||||
let pyi_contents = indoc::indoc! {r"
|
||||
from __future__ import annotations
|
||||
|
||||
def hello_from_bin() -> str: ...
|
||||
"};
|
||||
|
||||
let package_script = match build_backend {
|
||||
ProjectBuildBackend::Maturin => {
|
||||
// Generate lib.rs
|
||||
let native_src = src_dir.join("lib.rs");
|
||||
if !native_src.try_exists()? {
|
||||
fs_err::write(
|
||||
native_src,
|
||||
indoc::formatdoc! {r#"
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn hello_from_bin() -> String {{
|
||||
return "Hello from {package}!".to_string();
|
||||
}}
|
||||
|
||||
/// A Python module implemented in Rust. The name of this function must match
|
||||
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
|
||||
/// import the module.
|
||||
#[pymodule]
|
||||
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {{
|
||||
m.add_function(wrap_pyfunction!(hello_from_bin, m)?)?;
|
||||
Ok(())
|
||||
}}
|
||||
"#},
|
||||
)?;
|
||||
}
|
||||
// Generate .pyi file
|
||||
let pyi_file = pkg_dir.join("_core.pyi");
|
||||
if !pyi_file.try_exists()? {
|
||||
fs_err::write(pyi_file, pyi_contents)?;
|
||||
};
|
||||
// Return python script calling binary
|
||||
binary_call_script
|
||||
}
|
||||
ProjectBuildBackend::Scikit => {
|
||||
// Generate main.cpp
|
||||
let native_src = src_dir.join("main.cpp");
|
||||
if !native_src.try_exists()? {
|
||||
fs_err::write(
|
||||
native_src,
|
||||
indoc::formatdoc! {r#"
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
std::string hello_from_bin() {{ return "Hello from {package}!"; }}
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(_core, m) {{
|
||||
m.doc() = "pybind11 hello module";
|
||||
|
||||
m.def("hello_from_bin", &hello_from_bin, R"pbdoc(
|
||||
A function that returns a Hello string.
|
||||
)pbdoc");
|
||||
}}
|
||||
"#},
|
||||
)?;
|
||||
}
|
||||
// Generate .pyi file
|
||||
let pyi_file = pkg_dir.join("_core.pyi");
|
||||
if !pyi_file.try_exists()? {
|
||||
fs_err::write(pyi_file, pyi_contents)?;
|
||||
};
|
||||
// Return python script calling binary
|
||||
binary_call_script
|
||||
}
|
||||
_ => pure_python_script,
|
||||
};
|
||||
|
||||
// Create `src/{name}/__init__.py`, if it doesn't exist already.
|
||||
let init_py = pkg_dir.join("__init__.py");
|
||||
if !init_py.try_exists()? {
|
||||
fs_err::write(init_py, package_script)?;
|
||||
}
|
||||
|
||||
// Create `src/{name}/py.typed`, if it doesn't exist already.
|
||||
if is_lib {
|
||||
let py_typed = pkg_dir.join("py.typed");
|
||||
if !py_typed.try_exists()? {
|
||||
fs_err::write(py_typed, "")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize the version control system at the given path.
|
||||
fn init_vcs(path: &Path, vcs: Option<VersionControlSystem>) -> Result<()> {
|
||||
// Detect any existing version control system.
|
||||
|
|
|
@ -1217,6 +1217,7 @@ async fn run_project(
|
|||
args.package,
|
||||
args.kind,
|
||||
args.vcs,
|
||||
args.build_backend,
|
||||
args.no_readme,
|
||||
args.author_from,
|
||||
args.no_pin_python,
|
||||
|
|
|
@ -21,8 +21,8 @@ use uv_client::Connectivity;
|
|||
use uv_configuration::{
|
||||
BuildOptions, Concurrency, ConfigSettings, DevMode, EditableMode, ExportFormat,
|
||||
ExtrasSpecification, HashCheckingMode, IndexStrategy, InstallOptions, KeyringProviderType,
|
||||
NoBinary, NoBuild, PreviewMode, Reinstall, SourceStrategy, TargetTriple, TrustedHost,
|
||||
TrustedPublishing, Upgrade, VersionControlSystem,
|
||||
NoBinary, NoBuild, PreviewMode, ProjectBuildBackend, Reinstall, SourceStrategy, TargetTriple,
|
||||
TrustedHost, TrustedPublishing, Upgrade, VersionControlSystem,
|
||||
};
|
||||
use uv_distribution_types::{DependencyMetadata, Index, IndexLocations};
|
||||
use uv_install_wheel::linker::LinkMode;
|
||||
|
@ -164,6 +164,7 @@ pub(crate) struct InitSettings {
|
|||
pub(crate) package: bool,
|
||||
pub(crate) kind: InitKind,
|
||||
pub(crate) vcs: Option<VersionControlSystem>,
|
||||
pub(crate) build_backend: Option<ProjectBuildBackend>,
|
||||
pub(crate) no_readme: bool,
|
||||
pub(crate) author_from: Option<AuthorFrom>,
|
||||
pub(crate) no_pin_python: bool,
|
||||
|
@ -185,6 +186,7 @@ impl InitSettings {
|
|||
lib,
|
||||
script,
|
||||
vcs,
|
||||
build_backend,
|
||||
no_readme,
|
||||
author_from,
|
||||
no_pin_python,
|
||||
|
@ -208,6 +210,7 @@ impl InitSettings {
|
|||
package,
|
||||
kind,
|
||||
vcs,
|
||||
build_backend,
|
||||
no_readme,
|
||||
author_from,
|
||||
no_pin_python,
|
||||
|
|
|
@ -2316,7 +2316,7 @@ fn init_with_author() {
|
|||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{ name = "Alice", email = "alice@example.com" }
|
||||
{ name = "Alice", email = "alice@example.com" }
|
||||
]
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
@ -2338,7 +2338,7 @@ fn init_with_author() {
|
|||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{ name = "Alice", email = "alice@example.com" }
|
||||
{ name = "Alice", email = "alice@example.com" }
|
||||
]
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
@ -2380,3 +2380,692 @@ fn init_with_author() {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Run `uv init --app --package --build-backend flit` to create a packaged application project
|
||||
#[test]
|
||||
fn init_application_package_flit() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let child = context.temp_dir.child("foo");
|
||||
child.create_dir_all()?;
|
||||
|
||||
let pyproject_toml = child.join("pyproject.toml");
|
||||
let init_py = child.join("src").join("foo").join("__init__.py");
|
||||
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--app").arg("--package").arg("--build-backend").arg("flit"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Initialized project `foo`
|
||||
"###);
|
||||
|
||||
let pyproject = fs_err::read_to_string(&pyproject_toml)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject, @r###"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[project.scripts]
|
||||
foo = "foo:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["flit_core>=3.2,<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let init = fs_err::read_to_string(init_py)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
init, @r###"
|
||||
def main() -> None:
|
||||
print("Hello from foo!")
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
uv_snapshot!(context.filters(), context.run().current_dir(&child).env_remove("VIRTUAL_ENV").arg("foo"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Hello from foo!
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Creating virtual environment at: .venv
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ foo==0.1.0 (from file://[TEMP_DIR]/foo)
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run `uv init --lib --build-backend flit` to create an library project
|
||||
#[test]
|
||||
fn init_library_flit() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let child = context.temp_dir.child("foo");
|
||||
child.create_dir_all()?;
|
||||
|
||||
let pyproject_toml = child.join("pyproject.toml");
|
||||
let init_py = child.join("src").join("foo").join("__init__.py");
|
||||
let py_typed = child.join("src").join("foo").join("py.typed");
|
||||
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--lib").arg("--build-backend").arg("flit"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Initialized project `foo`
|
||||
"###);
|
||||
|
||||
let pyproject = fs_err::read_to_string(&pyproject_toml)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject, @r###"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[build-system]
|
||||
requires = ["flit_core>=3.2,<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let init = fs_err::read_to_string(init_py)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
init, @r###"
|
||||
def hello() -> str:
|
||||
return "Hello from foo!"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let py_typed = fs_err::read_to_string(py_typed)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
py_typed, @""
|
||||
);
|
||||
});
|
||||
|
||||
uv_snapshot!(context.filters(), context.run().current_dir(&child).env_remove("VIRTUAL_ENV").arg("python").arg("-c").arg("import foo; print(foo.hello())"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Hello from foo!
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Creating virtual environment at: .venv
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ foo==0.1.0 (from file://[TEMP_DIR]/foo)
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run `uv init --app --package --build-backend maturin` to create a packaged application project
|
||||
#[test]
|
||||
fn init_app_build_backend_maturin() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let child = context.temp_dir.child("foo");
|
||||
child.create_dir_all()?;
|
||||
|
||||
let pyproject_toml = child.join("pyproject.toml");
|
||||
let init_py = child.join("src").join("foo").join("__init__.py");
|
||||
let pyi_file = child.join("src").join("foo").join("_core.pyi");
|
||||
let lib_core = child.join("src").join("lib.rs");
|
||||
let build_file = child.join("Cargo.toml");
|
||||
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--app").arg("--package").arg("--build-backend").arg("maturin"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Initialized project `foo`
|
||||
"###);
|
||||
|
||||
let pyproject = fs_err::read_to_string(&pyproject_toml)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject, @r###"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[project.scripts]
|
||||
foo = "foo:main"
|
||||
|
||||
[tool.maturin]
|
||||
module-name = "foo._core"
|
||||
python-packages = ["foo"]
|
||||
python-source = "src"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
build-backend = "maturin"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let init = fs_err::read_to_string(init_py)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
init, @r###"
|
||||
from foo._core import hello_from_bin
|
||||
|
||||
def main() -> None:
|
||||
print(hello_from_bin())
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let pyi_contents = fs_err::read_to_string(pyi_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyi_contents, @r###"
|
||||
from __future__ import annotations
|
||||
|
||||
def hello_from_bin() -> str: ...
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let lib_core_contents = fs_err::read_to_string(lib_core)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
lib_core_contents, @r###"
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn hello_from_bin() -> String {
|
||||
return "Hello from foo!".to_string();
|
||||
}
|
||||
|
||||
/// A Python module implemented in Rust. The name of this function must match
|
||||
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
|
||||
/// import the module.
|
||||
#[pymodule]
|
||||
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(hello_from_bin, m)?)?;
|
||||
Ok(())
|
||||
}
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let build_file_contents = fs_err::read_to_string(build_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
build_file_contents, @r###"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "_core"
|
||||
# "cdylib" is necessary to produce a shared library for Python to import from.
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
# "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so)
|
||||
# "abi3-py39" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.9
|
||||
pyo3 = { version = "0.22.4", features = ["extension-module", "abi3-py39"] }
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
uv_snapshot!(context.filters(), context.run().current_dir(&child).env_remove("VIRTUAL_ENV").arg("foo"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Hello from foo!
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Creating virtual environment at: .venv
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ foo==0.1.0 (from file://[TEMP_DIR]/foo)
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run `uv init --app --package --build-backend scikit` to create a packaged application project
|
||||
#[test]
|
||||
fn init_app_build_backend_scikit() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let child = context.temp_dir.child("foo");
|
||||
child.create_dir_all()?;
|
||||
|
||||
let pyproject_toml = child.join("pyproject.toml");
|
||||
let init_py = child.join("src").join("foo").join("__init__.py");
|
||||
let pyi_file = child.join("src").join("foo").join("_core.pyi");
|
||||
let lib_core = child.join("src").join("main.cpp");
|
||||
let build_file = child.join("CMakeLists.txt");
|
||||
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--app").arg("--package").arg("--build-backend").arg("scikit"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Initialized project `foo`
|
||||
"###);
|
||||
|
||||
let pyproject = fs_err::read_to_string(&pyproject_toml)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject, @r###"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[project.scripts]
|
||||
foo = "foo:main"
|
||||
|
||||
[tool.scikit-build]
|
||||
minimum-version = "build-system.requires"
|
||||
build-dir = "build/{wheel_tag}"
|
||||
|
||||
[build-system]
|
||||
requires = ["scikit-build-core>=0.10", "pybind11"]
|
||||
build-backend = "scikit_build_core.build"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let init = fs_err::read_to_string(init_py)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
init, @r###"
|
||||
from foo._core import hello_from_bin
|
||||
|
||||
def main() -> None:
|
||||
print(hello_from_bin())
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let pyi_contents = fs_err::read_to_string(pyi_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyi_contents, @r###"
|
||||
from __future__ import annotations
|
||||
|
||||
def hello_from_bin() -> str: ...
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let lib_core_contents = fs_err::read_to_string(lib_core)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
lib_core_contents, @r###"
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
std::string hello_from_bin() { return "Hello from foo!"; }
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(_core, m) {
|
||||
m.doc() = "pybind11 hello module";
|
||||
|
||||
m.def("hello_from_bin", &hello_from_bin, R"pbdoc(
|
||||
A function that returns a Hello string.
|
||||
)pbdoc");
|
||||
}
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let build_file_contents = fs_err::read_to_string(build_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
build_file_contents, @r###"
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
|
||||
|
||||
set(PYBIND11_FINDPYTHON ON)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
|
||||
pybind11_add_module(_core MODULE src/main.cpp)
|
||||
install(TARGETS _core DESTINATION ${SKBUILD_PROJECT_NAME})
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
// We do not test with uv run since it would otherwise require specific CXX build tooling
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run `uv init --lib --build-backend maturin` to create a packaged application project
|
||||
#[test]
|
||||
fn init_lib_build_backend_maturin() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let child = context.temp_dir.child("foo");
|
||||
child.create_dir_all()?;
|
||||
|
||||
let pyproject_toml = child.join("pyproject.toml");
|
||||
let init_py = child.join("src").join("foo").join("__init__.py");
|
||||
let pyi_file = child.join("src").join("foo").join("_core.pyi");
|
||||
let lib_core = child.join("src").join("lib.rs");
|
||||
let build_file = child.join("Cargo.toml");
|
||||
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--lib").arg("--build-backend").arg("maturin"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Initialized project `foo`
|
||||
"###);
|
||||
|
||||
let pyproject = fs_err::read_to_string(&pyproject_toml)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject, @r###"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[tool.maturin]
|
||||
module-name = "foo._core"
|
||||
python-packages = ["foo"]
|
||||
python-source = "src"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
build-backend = "maturin"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let init = fs_err::read_to_string(init_py)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
init, @r###"
|
||||
from foo._core import hello_from_bin
|
||||
|
||||
def hello() -> str:
|
||||
return hello_from_bin()
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let pyi_contents = fs_err::read_to_string(pyi_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyi_contents, @r###"
|
||||
from __future__ import annotations
|
||||
|
||||
def hello_from_bin() -> str: ...
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let lib_core_contents = fs_err::read_to_string(lib_core)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
lib_core_contents, @r###"
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn hello_from_bin() -> String {
|
||||
return "Hello from foo!".to_string();
|
||||
}
|
||||
|
||||
/// A Python module implemented in Rust. The name of this function must match
|
||||
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
|
||||
/// import the module.
|
||||
#[pymodule]
|
||||
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(hello_from_bin, m)?)?;
|
||||
Ok(())
|
||||
}
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let build_file_contents = fs_err::read_to_string(build_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
build_file_contents, @r###"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "_core"
|
||||
# "cdylib" is necessary to produce a shared library for Python to import from.
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
# "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so)
|
||||
# "abi3-py39" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.9
|
||||
pyo3 = { version = "0.22.4", features = ["extension-module", "abi3-py39"] }
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
uv_snapshot!(context.filters(), context.run().current_dir(&child).env_remove("VIRTUAL_ENV").arg("python").arg("-c").arg("import foo; print(foo.hello())"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Hello from foo!
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Creating virtual environment at: .venv
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ foo==0.1.0 (from file://[TEMP_DIR]/foo)
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run `uv init --lib --build-backend scikit` to create a packaged application project
|
||||
#[test]
|
||||
fn init_lib_build_backend_scikit() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let child = context.temp_dir.child("foo");
|
||||
child.create_dir_all()?;
|
||||
|
||||
let pyproject_toml = child.join("pyproject.toml");
|
||||
let init_py = child.join("src").join("foo").join("__init__.py");
|
||||
let pyi_file = child.join("src").join("foo").join("_core.pyi");
|
||||
let lib_core = child.join("src").join("main.cpp");
|
||||
let build_file = child.join("CMakeLists.txt");
|
||||
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--lib").arg("--build-backend").arg("scikit"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Initialized project `foo`
|
||||
"###);
|
||||
|
||||
let pyproject = fs_err::read_to_string(&pyproject_toml)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject, @r###"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[tool.scikit-build]
|
||||
minimum-version = "build-system.requires"
|
||||
build-dir = "build/{wheel_tag}"
|
||||
|
||||
[build-system]
|
||||
requires = ["scikit-build-core>=0.10", "pybind11"]
|
||||
build-backend = "scikit_build_core.build"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let init = fs_err::read_to_string(init_py)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
init, @r###"
|
||||
from foo._core import hello_from_bin
|
||||
|
||||
def hello() -> str:
|
||||
return hello_from_bin()
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let pyi_contents = fs_err::read_to_string(pyi_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyi_contents, @r###"
|
||||
from __future__ import annotations
|
||||
|
||||
def hello_from_bin() -> str: ...
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let lib_core_contents = fs_err::read_to_string(lib_core)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
lib_core_contents, @r###"
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
std::string hello_from_bin() { return "Hello from foo!"; }
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(_core, m) {
|
||||
m.doc() = "pybind11 hello module";
|
||||
|
||||
m.def("hello_from_bin", &hello_from_bin, R"pbdoc(
|
||||
A function that returns a Hello string.
|
||||
)pbdoc");
|
||||
}
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
let build_file_contents = fs_err::read_to_string(build_file)?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
build_file_contents, @r###"
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
|
||||
|
||||
set(PYBIND11_FINDPYTHON ON)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
|
||||
pybind11_add_module(_core MODULE src/main.cpp)
|
||||
install(TARGETS _core DESTINATION ${SKBUILD_PROJECT_NAME})
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
// We do not test with uv run since it would otherwise require specific CXX build tooling
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -203,10 +203,41 @@ def hello() -> str:
|
|||
And you can import and execute it using `uv run`:
|
||||
|
||||
```console
|
||||
$ uv run python -c "import example_lib; print(example_lib.hello())"
|
||||
$ uv run --directory example-lib python -c "import example_lib; print(example_lib.hello())"
|
||||
Hello from example-lib!
|
||||
```
|
||||
|
||||
You can select a different build backend template by using `--build-backend` with `hatchling`,
|
||||
`flit-core`, `pdm-backend`, `setuptools`, `maturin`, or `scikit-build-core`.
|
||||
|
||||
```console
|
||||
$ uv init --lib --build-backend maturin example-lib
|
||||
$ tree example-lib
|
||||
example-lib
|
||||
├── .python-version
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
└── src
|
||||
├── lib.rs
|
||||
└── example_lib
|
||||
├── py.typed
|
||||
├── __init__.py
|
||||
└── _core.pyi
|
||||
```
|
||||
|
||||
And you can import and execute it using `uv run`:
|
||||
|
||||
```console
|
||||
$ uv run --directory example-lib python -c "import example_lib; print(example_lib.hello())"
|
||||
Hello from example-lib!
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
Changes to `lib.rs` or `main.cpp` will require running `--reinstall` when using binary build
|
||||
backends such as `maturin` and `scikit-build-core`.
|
||||
|
||||
### Packaged applications
|
||||
|
||||
The `--package` flag can be passed to `uv init` to create a distributable application, e.g., if you
|
||||
|
@ -257,7 +288,7 @@ build-backend = "hatchling.build"
|
|||
Which can be executed with `uv run`:
|
||||
|
||||
```console
|
||||
$ uv run example-packaged-app
|
||||
$ uv run --directory example-packaged-app example-packaged-app
|
||||
Hello from example-packaged-app!
|
||||
```
|
||||
|
||||
|
@ -267,6 +298,31 @@ Hello from example-packaged-app!
|
|||
However, this may require changes to the project directory structure, depending on the build
|
||||
backend.
|
||||
|
||||
In addition, you can further customize the build backend of a packaged application by specifying
|
||||
`--build-backend` including binary build backends such as `maturin`.
|
||||
|
||||
```console
|
||||
$ uv init --app --package --build-backend maturin example-packaged-app
|
||||
$ tree example-packaged-app
|
||||
example-packaged-app
|
||||
├── .python-version
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
└── src
|
||||
├── lib.rs
|
||||
└── example_packaged_app
|
||||
├── __init__.py
|
||||
└── _core.pyi
|
||||
```
|
||||
|
||||
Which can also be executed with `uv run`:
|
||||
|
||||
```console
|
||||
$ uv run --directory example-packaged-app example-packaged-app
|
||||
Hello from example-packaged-app!
|
||||
```
|
||||
|
||||
## Project environments
|
||||
|
||||
When working on a project with uv, uv will create a virtual environment as needed. While some uv
|
||||
|
|
|
@ -467,6 +467,23 @@ uv init [OPTIONS] [PATH]
|
|||
|
||||
<li><code>none</code>: Do not infer the author information</li>
|
||||
</ul>
|
||||
</dd><dt><code>--build-backend</code> <i>build-backend</i></dt><dd><p>Initialize a build-backend of choice for the project</p>
|
||||
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>hatch</code>: Use <a href='https://pypi.org/project/hatchling'>hatchling</a> as the project build backend</li>
|
||||
|
||||
<li><code>flit</code>: Use <a href='https://pypi.org/project/flit-core'>flit-core</a> as the project build backend</li>
|
||||
|
||||
<li><code>pdm</code>: Use <a href='https://pypi.org/project/pdm-backend'>pdm-backend</a> as the project build backend</li>
|
||||
|
||||
<li><code>setuptools</code>: Use <a href='https://pypi.org/project/setuptools'>setuptools</a> as the project build backend</li>
|
||||
|
||||
<li><code>maturin</code>: Use <a href='https://pypi.org/project/maturin'>maturin</a> as the project build backend</li>
|
||||
|
||||
<li><code>scikit</code>: Use <a href='https://pypi.org/project/scikit-build-core'>scikit-build-core</a> as the project build backend</li>
|
||||
</ul>
|
||||
</dd><dt><code>--cache-dir</code> <i>cache-dir</i></dt><dd><p>Path to the cache directory.</p>
|
||||
|
||||
<p>Defaults to <code>$HOME/Library/Caches/uv</code> on macOS, <code>$XDG_CACHE_HOME/uv</code> or <code>$HOME/.cache/uv</code> on Linux, and <code>%LOCALAPPDATA%\uv\cache</code> on Windows.</p>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue