Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new sdist feature to download rust toolchain #2177

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
57f35c9
Initial draft to implement downloading rust toolchain during sdist
Owen-CH-Leung Aug 8, 2024
1ec7982
Enhance pep517 write-dist-info command to check for rust toolchain
Owen-CH-Leung Aug 13, 2024
6db3fdf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 13, 2024
2a721a7
Fix window command
Owen-CH-Leung Aug 13, 2024
7b76520
Merge remote-tracking branch 'origin/impl_sdist_feature_to_install_ru…
Owen-CH-Leung Aug 13, 2024
136fee2
Fix failing cargo fmt and ruff CI jobs
Owen-CH-Leung Aug 13, 2024
908bec0
Fix failing codespell CI jobs
Owen-CH-Leung Aug 13, 2024
c6c7d72
Initial draft to implement downloading rust toolchain during sdist
Owen-CH-Leung Aug 8, 2024
d45b609
Enhance pep517 write-dist-info command to check for rust toolchain
Owen-CH-Leung Aug 13, 2024
978677c
Fix window command
Owen-CH-Leung Aug 13, 2024
620c8f3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 13, 2024
22f5885
Fix failing cargo fmt and ruff CI jobs
Owen-CH-Leung Aug 13, 2024
e06953f
Fix failing codespell CI jobs
Owen-CH-Leung Aug 13, 2024
ec03119
Merge branch 'impl_sdist_feature_to_install_rust_toolchain' of https:…
Owen-CH-Leung Oct 19, 2024
cc62930
Add CI test to test pip install without toolchain
Owen-CH-Leung Oct 20, 2024
3c78b7c
Merge branch 'main' into impl_sdist_feature_to_install_rust_toolchain
Owen-CH-Leung Oct 20, 2024
bbea38e
Fix fmt & clippy CI job. Use upload & download artifact github action…
Owen-CH-Leung Oct 21, 2024
4b67e92
Fix pip install *.whl
Owen-CH-Leung Oct 21, 2024
0be8c05
Fix windows build
Owen-CH-Leung Oct 21, 2024
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
62 changes: 62 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,68 @@ jobs:
- repository: "oxigraph/oxigraph"
manifest-dir: "python"

build-maturin-wheel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: dtolnay/rust-toolchain@stable
id: rustup
- name: Install python build tools
run: pip install setuptools wheel>=0.36.2 setuptools-rust>=1.4.0 tomli>=1.1.0
- name: Build maturin wheel
run: |
export MATURIN_SETUP_ARGS="--features rustls"
python setup.py bdist_wheel
- name: install maturin wheel
run: pip install --force-reinstall ./dist/*.whl
- name: test
run: maturin sdist --manifest-path ./test-crates/hello-world/Cargo.toml -o ./target/hello-world/sdist
- name: Upload sdist
id: upload-sdist
uses: actions/upload-artifact@v3
with:
name: hello-world-sdist
path: ./target/hello-world/sdist/*.tar.gz
- name: Upload wheels
id: upload-wheel
uses: actions/upload-artifact@v3
with:
name: maturin-wheel
path: ./dist/*.whl

test-pip-install-without-toolchain:
runs-on: ubuntu-latest
needs: [build-maturin-wheel]
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Download sdist from previous job
uses: actions/download-artifact@v3
with:
name: hello-world-sdist
path: ./target/hello-world/sdist
- name: Download wheel from previous job
uses: actions/download-artifact@v3
with:
name: maturin-wheel
path: ./dist/*.whl
- name: Install maturin whl
run: |
WHEEL_FILE=$(ls ./dist/*.whl)
pip install --force-reinstall "$WHEEL_FILE"
- name: pip install
run: |
export MATURIN_PEP517_ARGS="--verbose"
export RUST_LOG="debug"
pip install --no-build-isolation --verbose ./target/hello-world/sdist/hello_world-0.1.0.tar.gz

check:
name: Check ${{ matrix.platform.target }}
if: github.event_name != 'pull_request'
Expand Down
20 changes: 1 addition & 19 deletions maturin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import struct
import subprocess
import sys
from subprocess import SubprocessError
from typing import Any, Dict, Mapping, List, Optional

try:
Expand Down Expand Up @@ -165,23 +164,6 @@ def get_requires_for_build_sdist(config_settings: Optional[Mapping[str, Any]] =
def prepare_metadata_for_build_wheel(
metadata_directory: str, config_settings: Optional[Mapping[str, Any]] = None
) -> str:
print("Checking for Rust toolchain....")
is_cargo_installed = False
try:
output = subprocess.check_output(["cargo", "--version"]).decode("utf-8", "ignore")
if "cargo" in output:
is_cargo_installed = True
except (FileNotFoundError, SubprocessError):
pass

if not is_cargo_installed:
sys.stderr.write(
"\nCargo, the Rust package manager, is not installed or is not on PATH.\n"
"This package requires Rust and Cargo to compile extensions. Install it through\n"
"the system's package manager or via https://rustup.rs/\n\n"
)
sys.exit(1)

command = [
"maturin",
"pep517",
Expand All @@ -200,7 +182,7 @@ def prepare_metadata_for_build_wheel(

print("Running `{}`".format(" ".join(command)))
try:
_output = subprocess.check_output(command)
_output = subprocess.check_output(command, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
sys.stderr.write(f"Error running maturin: {e}\n")
sys.exit(1)
Expand Down
88 changes: 88 additions & 0 deletions src/build_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::project_layout::ProjectLayout;
use crate::python_interpreter::InterpreterKind;
use crate::source_distribution::source_distribution;
use crate::target::{Arch, Os};
#[cfg(feature = "upload")]
use crate::upload::http_agent;
use crate::{
compile, pyproject_toml::Format, BuildArtifact, Metadata23, ModuleWriter, PyProjectToml,
PythonInterpreter, Target,
Expand All @@ -28,8 +30,13 @@ use std::collections::{BTreeMap, HashSet};
use std::env;
use std::fmt::{Display, Formatter};
use std::io;
#[cfg(feature = "rustls")]
use std::io::copy;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
#[cfg(feature = "rustls")]
use tempfile::NamedTempFile;
use tracing::instrument;

/// The way the rust code is used in the wheel
Expand Down Expand Up @@ -1136,6 +1143,87 @@ impl BuildContext {
}
Ok(wheels)
}

/// Check if Rust toolchain is installed
pub fn is_toolchain_installed() -> bool {
let output = Command::new("cargo").arg("--version").output();
match output {
Ok(out) => out.status.success(),
Err(_) => false,
}
}

/// Downloads the rustup installer script and executes it to install rustup
///
/// Inspired by https://github.com/chriskuehl/rustenv
#[cfg(feature = "rustls")]
pub fn install_installer(rustup_home: &str, cargo_home: &str) -> Result<()> {
let mut tf = NamedTempFile::new()?;
let agent = http_agent()?;
let response = agent.get("https://sh.rustup.rs").call()?.into_string()?;

copy(&mut response.as_bytes(), &mut tf)?;

#[cfg(unix)]
{
Command::new("sh")
.arg(tf.path())
.arg("-y")
.arg("--default-toolchain")
.arg("none")
.env("RUSTUP_HOME", rustup_home)
.env("CARGO_HOME", cargo_home)
.status()?;
}

#[cfg(windows)]
{
Command::new("cmd")
.args(["/C", tf.path().to_str().unwrap()])
.args(vec!["-y", "--default-toolchain", "none"])
.env("RUSTUP_HOME", rustup_home)
.env("CARGO_HOME", cargo_home)
.status()?;
}

Ok(())
}

/// Refresh the current shell to include path for rust toolchain
pub fn install_toolchain(cargo_home: &str) -> Result<()> {
let current_path = env::var("PATH").unwrap_or_else(|_| String::from(""));

#[cfg(unix)]
{
let cargo_bin_path = format!("{}/bin", cargo_home);
let new_path = format!("{}:{}", cargo_bin_path, current_path);
env::set_var("PATH", &new_path);

let cargo_env_path = format!("{}/env", cargo_home);
Command::new("sh")
.arg("-c")
.arg(format!(". {}", cargo_env_path))
.status()?;

let rustup_command = format!("{}/bin/rustup", cargo_home);
Command::new(rustup_command)
.arg("default")
.arg("stable")
.status()?;
}

#[cfg(windows)]
{
let cargo_bin_path = format!("{}\\bin", cargo_home);
let new_path = format!("{};{}", cargo_bin_path, current_path);
env::set_var("PATH", &new_path);
Command::new("cmd")
.args(vec!["/C", "rustup default stable"])
.status()?;
}

Ok(())
}
}

/// Calculate the sha256 of a file
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub use crate::pyproject_toml::PyProjectToml;
pub use crate::python_interpreter::PythonInterpreter;
pub use crate::target::Target;
#[cfg(feature = "upload")]
pub use crate::upload::{upload, upload_ui, PublishOpt, Registry, UploadError};
pub use crate::upload::{upload_ui, PublishOpt, Registry, UploadError};
pub use auditwheel::PlatformTag;

mod auditwheel;
Expand Down
17 changes: 17 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ use cargo_zigbuild::Zig;
#[cfg(feature = "cli-completion")]
use clap::CommandFactory;
use clap::{Parser, Subcommand};
#[cfg(feature = "rustls")]
use dirs::home_dir;
#[cfg(feature = "rustls")]
use maturin::BuildContext;
#[cfg(feature = "scaffolding")]
use maturin::{ci::GenerateCI, init_project, new_project, GenerateProjectOptions};
use maturin::{
Expand Down Expand Up @@ -273,6 +277,19 @@ fn pep517(subcommand: Pep517Command) -> Result<()> {
strip,
} => {
assert_eq!(build_options.interpreter.len(), 1);
#[cfg(feature = "rustls")]
{
if !BuildContext::is_toolchain_installed() {
let home_dir = home_dir().context("Unable to get user home directory")?;
let home_dir_str = home_dir
.to_str()
.context("Unable to convert home directory string")?;
BuildContext::install_installer(home_dir_str, home_dir_str)
.context("Unable to install installer")?;
BuildContext::install_toolchain(home_dir_str)
.context("Unable to install rust toolchain")?;
}
}
let context = build_options.into_build_context(true, strip, false)?;

// Since afaik all other PEP 517 backends also return linux tagged wheels, we do so too
Expand Down
2 changes: 1 addition & 1 deletion src/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ fn http_agent() -> Result<ureq::Agent, UploadError> {

#[cfg(feature = "rustls")]
#[allow(clippy::result_large_err)]
fn http_agent() -> Result<ureq::Agent, UploadError> {
pub fn http_agent() -> Result<ureq::Agent, UploadError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a http_agent below that also need to be made public.

use std::sync::Arc;

let builder = ureq::builder().try_proxy_from_env(true);
Expand Down
Loading