Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/build_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,16 @@ mod tests {
use super::*;
use crate::bridge::{Abi3Version, PyO3, PyO3Crate, find_bridge};
use crate::python_interpreter::InterpreterResolver;
use crate::test_utils::test_crate_path;
use crate::{BridgeModel, Target};
use cargo_metadata::MetadataCommand;
use insta::assert_snapshot;
use pretty_assertions::assert_eq;
use std::path::Path;

#[test]
fn test_find_bridge_pyo3() {
let pyo3_mixed = MetadataCommand::new()
.manifest_path(Path::new("test-crates/pyo3-mixed").join("Cargo.toml"))
.manifest_path(test_crate_path("pyo3-mixed").join("Cargo.toml"))
.exec()
.unwrap();

Expand All @@ -181,7 +181,7 @@ mod tests {
use crate::bridge::{PyO3Metadata, PyO3VersionMetadata};

let pyo3_pure = MetadataCommand::new()
.manifest_path(Path::new("test-crates/pyo3-pure").join("Cargo.toml"))
.manifest_path(test_crate_path("pyo3-pure").join("Cargo.toml"))
.exec()
.unwrap();

Expand All @@ -207,14 +207,14 @@ mod tests {
#[test]
fn test_find_bridge_pyo3_feature() {
let pyo3_pure = MetadataCommand::new()
.manifest_path(Path::new("test-crates/pyo3-feature").join("Cargo.toml"))
.manifest_path(test_crate_path("pyo3-feature").join("Cargo.toml"))
.exec()
.unwrap();

assert!(find_bridge(&pyo3_pure, None, None).is_err());

let pyo3_pure = MetadataCommand::new()
.manifest_path(Path::new("test-crates/pyo3-feature").join("Cargo.toml"))
.manifest_path(test_crate_path("pyo3-feature").join("Cargo.toml"))
.other_options(vec!["--features=pyo3".to_string()])
.exec()
.unwrap();
Expand All @@ -228,7 +228,7 @@ mod tests {
#[test]
fn test_find_bridge_cffi() {
let cffi_pure = MetadataCommand::new()
.manifest_path(Path::new("test-crates/cffi-pure").join("Cargo.toml"))
.manifest_path(test_crate_path("cffi-pure").join("Cargo.toml"))
.exec()
.unwrap();

Expand All @@ -247,7 +247,7 @@ mod tests {
#[test]
fn test_find_bridge_bin() {
let hello_world = MetadataCommand::new()
.manifest_path(Path::new("test-crates/hello-world").join("Cargo.toml"))
.manifest_path(test_crate_path("hello-world").join("Cargo.toml"))
.exec()
.unwrap();

Expand All @@ -263,7 +263,7 @@ mod tests {
assert!(find_bridge(&hello_world, Some("pyo3"), None).is_err());

let pyo3_bin = MetadataCommand::new()
.manifest_path(Path::new("test-crates/pyo3-bin").join("Cargo.toml"))
.manifest_path(test_crate_path("pyo3-bin").join("Cargo.toml"))
.exec()
.unwrap();
assert!(matches!(
Expand Down
39 changes: 39 additions & 0 deletions src/commands/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::commands::StripOption;
use crate::commands::utils::unpack_sdist_for_build;
use anyhow::Result;
use maturin::BuildOptions;

pub fn build(
mut build: BuildOptions,
release: bool,
strip_opt: StripOption,
sdist: bool,
pgo: bool,
) -> Result<()> {
let strip = strip_opt.strip;
// set profile to release if specified; `--release` and `--profile` are mutually exclusive
if release {
build.profile = Some("release".to_string());
}
// Keep tempdir alive for the duration of the build
let _sdist_tmp;
let sdist_pyproject_path;
if sdist {
let (_, unpacked) = unpack_sdist_for_build(&mut build, strip)?;
sdist_pyproject_path = unpacked.pyproject_toml_path.clone();
_sdist_tmp = Some(unpacked);
} else {
_sdist_tmp = None;
sdist_pyproject_path = None;
}
let build_context = build
.into_build_context()
.strip(strip)
.editable(false)
.pyproject_toml_path(sdist_pyproject_path)
.pgo(pgo)
.build()?;
let wheels = build_context.build_wheels()?;
assert!(!wheels.is_empty());
Ok(())
}
59 changes: 59 additions & 0 deletions src/commands/develop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use anyhow::{Context, Result, bail};
use maturin::{DevelopOptions, Target, develop};
use std::env;
use std::path::PathBuf;
use tracing::debug;

pub fn develop_cmd(develop_options: DevelopOptions) -> Result<()> {
let target = Target::from_target_triple(develop_options.cargo_options.target.as_ref())?;
let venv_dir = detect_venv(&target)?;
develop(develop_options, &venv_dir)?;
Ok(())
}

fn detect_venv(target: &Target) -> Result<PathBuf> {
match (env::var_os("VIRTUAL_ENV"), env::var_os("CONDA_PREFIX")) {
(Some(dir), None) => return Ok(PathBuf::from(dir)),
(None, Some(dir)) => return Ok(PathBuf::from(dir)),
(Some(venv), Some(conda)) if venv == conda => return Ok(PathBuf::from(venv)),
(Some(_), Some(_)) => {
bail!("Both VIRTUAL_ENV and CONDA_PREFIX are set. Please unset one of them")
}
(None, None) => {
// No env var, try finding .venv
}
};

let current_dir = env::current_dir().context("Failed to detect current directory ಠ_ಠ")?;
// .venv in the current or any parent directory
for dir in current_dir.ancestors() {
let dot_venv = dir.join(".venv");
if dot_venv.is_dir() {
if !dot_venv.join("pyvenv.cfg").is_file() {
bail!(
"Expected {} to be a virtual environment, but pyvenv.cfg is missing",
dot_venv.display()
);
}
let python = target.get_venv_python(&dot_venv);
if !python.is_file() {
bail!(
"Your virtualenv at {} is broken. It contains a pyvenv.cfg but no python at {}",
dot_venv.display(),
python.display()
);
}
debug!("Found a virtualenv named .venv at {}", dot_venv.display());
return Ok(dot_venv);
}
}

bail!(
"Couldn't find a virtualenv or conda environment, but you need one to use this command. \
For maturin to find your virtualenv you need to either set VIRTUAL_ENV (through activate), \
set CONDA_PREFIX (through conda activate) or have a virtualenv called .venv in the current \
or any parent folder. \
See https://virtualenv.pypa.io/en/latest/index.html on how to use virtualenv or \
use `maturin build` and `pip install <path/to/wheel>` instead."
)
}
104 changes: 104 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#[cfg(feature = "zig")]
use anyhow::Context;
use anyhow::Result;
#[cfg(feature = "schemars")]
use maturin::GenerateJsonSchemaOptions;
#[cfg(feature = "upload")]
use maturin::PublishOpt;
use maturin::{BridgeModel, Target, TargetTriple};
#[cfg(feature = "scaffolding")]
use maturin::{GenerateProjectOptions, ci::GenerateCI};
#[cfg(feature = "upload")]
use std::path::PathBuf;

pub mod build;
pub mod develop;
pub mod pep517;
#[cfg(feature = "upload")]
pub mod publish;
pub mod sdist;
pub mod utils;

/// Shared `--strip` CLI option used by multiple commands.
#[derive(Debug, clap::Args, Clone, Copy)]
pub struct StripOption {
/// Strip the library for minimum file size.
/// Can be set to `true` or `false`, or used as a flag (`--strip` implies `true`).
#[arg(
long,
env = "MATURIN_STRIP",
// `--strip` without a value is treated as `--strip true`
default_missing_value = "true",
num_args = 0..=1,
require_equals = false
)]
pub strip: Option<bool>,
}

/// Generate shell completions
#[cfg(feature = "cli-completion")]
pub fn completions(shell: clap_complete_command::Shell, cmd: &mut clap::Command) {
shell.generate(cmd, &mut std::io::stdout());
}

/// Search and list the available python installations
pub fn list_python(target: Option<TargetTriple>) -> Result<()> {
let found = if target.is_some() {
let target = Target::from_target_triple(target.as_ref())?;
maturin::PythonInterpreter::lookup_target(&target, None, None)
} else {
let target = Target::from_target_triple(None)?;
// We don't know the targeted bindings yet, so we use the most lenient
maturin::PythonInterpreter::find_all(&target, &BridgeModel::Cffi, None)?
};
eprintln!("🐍 {} python interpreter found:", found.len());
for interpreter in found {
eprintln!(" - {interpreter}");
}
Ok(())
}

/// Create a new cargo project in an existing directory
#[cfg(feature = "scaffolding")]
pub fn init_project(path: Option<String>, options: GenerateProjectOptions) -> Result<()> {
maturin::init_project(path, options)
}

/// Create a new cargo project
#[cfg(feature = "scaffolding")]
pub fn new_project(path: String, options: GenerateProjectOptions) -> Result<()> {
maturin::new_project(path, options)
}

/// Generate CI configuration
#[cfg(feature = "scaffolding")]
pub fn generate_ci(generate_ci: GenerateCI) -> Result<()> {
generate_ci.execute()
}

/// Generate the JSON schema for the `pyproject.toml` file.
#[cfg(feature = "schemars")]
pub fn generate_json_schema(args: GenerateJsonSchemaOptions) -> Result<()> {
maturin::generate_json_schema(args)
}

/// Upload python packages to pypi
#[cfg(feature = "upload")]
pub fn upload(mut publish: PublishOpt, files: Vec<PathBuf>) -> Result<()> {
if files.is_empty() {
eprintln!("⚠️ Warning: No files given, exiting.");
return Ok(());
}
publish.non_interactive_on_ci();

maturin::upload_ui(&files, &publish)?;
Ok(())
}

/// Zig linker wrapper
#[cfg(feature = "zig")]
pub fn zig(subcommand: cargo_zigbuild::Zig) -> Result<()> {
subcommand
.execute()
.context("Failed to run zig linker wrapper")
}
Loading
Loading