Skip to content

Commit

Permalink
Added support for importing crates from cargo workspaces
Browse files Browse the repository at this point in the history
  • Loading branch information
mityax committed Jan 2, 2023
1 parent decaa5c commit 525f702
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 18 deletions.
6 changes: 6 additions & 0 deletions examples/test_workspace/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[workspace]

members = [
"crate_a",
"crate_b"
]
Empty file.
9 changes: 9 additions & 0 deletions examples/test_workspace/crate_a/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "crate_a"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
crate_b = { path = "../crate_b" }
9 changes: 9 additions & 0 deletions examples/test_workspace/crate_a/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// rustimport:pyo3

use crate_b::sum;
use pyo3::prelude::*;

#[pyfunction]
fn double(x: i32) -> i32 {
sum(x, x)
}
8 changes: 8 additions & 0 deletions examples/test_workspace/crate_b/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "crate_b"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
4 changes: 4 additions & 0 deletions examples/test_workspace/crate_b/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

pub fn sum(a: i32, b: i32) -> i32 {
a + b
}
68 changes: 50 additions & 18 deletions rustimport/importable.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import shutil
import sysconfig
import types
from functools import cached_property
from typing import Optional, List, Type

from rustimport import load, BuildError, settings
Expand All @@ -23,19 +24,19 @@ def __init__(self, path: str, fullname: Optional[str] = None):
self.fullname = fullname or os.path.splitext(os.path.basename(path))[0]

@property
def extension_path(self):
def extension_path(self) -> str:
return os.path.join(os.path.dirname(self.path), self.name) + get_extension_suffix()

@property
def build_tempdir(self):
def build_tempdir(self) -> str:
return os.path.join(settings.cache_dir, f'{self.fullname}-{hashlib.md5(self.path.encode()).hexdigest()}')

@property
def name(self):
def name(self) -> str:
return self.fullname.split('.')[-1]

@property
def dependencies(self):
def dependencies(self) -> List[str]:
return [self.path]

@classmethod
Expand Down Expand Up @@ -134,23 +135,44 @@ def build(self, release: bool = False):


class CrateImportable(Importable):
"""Importable allowing to import a whole rust crate directory."""
"""
Importable allowing to import a whole rust crate directory.
This importable also allows to import crates within a cargo
workspace – i.e. it handles the according dependencies.
"""

@property
def __crate_path(self):
def __crate_path(self) -> str:
return os.path.dirname(self.__manifest_path)

@property
def __manifest_path(self):
def __manifest_path(self) -> str:
return self.path if self.path.lower().endswith("/cargo.toml") else os.path.join(self.path, 'Cargo.toml')

@property
def dependencies(self):
@cached_property
def __workspace_path(self) -> Optional[str]:
"""Returns the path of the cargo workspace this crate belongs to, if there is any."""

p = self.__crate_path
while os.path.dirname(p) != os.path.sep: # loop through all parent directories...
p = os.path.dirname(p)

if os.path.isfile(os.path.join(p, "Cargo.toml")): # ... and check for a "Cargo.toml" file in each of them.
return p

return None

@cached_property
def dependencies(self) -> List[str]:
root_path = self.__workspace_path or self.__crate_path
src_path = os.path.join(self.__crate_path, 'src')

p = Preprocessor(os.path.join(src_path, 'lib.rs'), lib_name=self.name).process()

return [
os.path.join(self.__crate_path, '**/*.rs'),
os.path.join(self.__crate_path, '**/Cargo.*'),
os.path.join(root_path, '**/*.rs'),
os.path.join(root_path, '**/Cargo.*'),
*[os.path.join(src_path, d) for d in p.dependency_file_patterns],
]

Expand All @@ -167,11 +189,21 @@ def try_create(cls, path: str, fullname: Optional[str] = None, opt_in: bool = Tr
return CrateImportable(path=directory, fullname=fullname)

def build(self, release: bool = False):
output_path = os.path.join(self.build_tempdir, os.path.basename(self.__crate_path))
_logger.debug(f"Building in temporary directory {output_path}")
if self.__workspace_path is not None:
_logger.debug(f"The crate belongs to workspace {self.__workspace_path}")

root_output_path = os.path.join( # e.g. `/output/path/myworkspace` or `/output/path/mycrate` respectively
self.build_tempdir,
os.path.basename(self.__workspace_path or self.__crate_path)
)
crate_output_subdirectory = os.path.normpath(os.path.join( # e.g. `/output/path/myworkspace/mycrate` or `/output/path/mycrate`
root_output_path,
os.path.relpath(self.__crate_path, self.__workspace_path or self.__crate_path)
))
_logger.debug(f"Building in temporary directory {crate_output_subdirectory}")

os.makedirs(output_path, exist_ok=True)
shutil.copytree(self.__crate_path, output_path, dirs_exist_ok=True)
os.makedirs(root_output_path, exist_ok=True)
shutil.copytree(self.__workspace_path or self.__crate_path, root_output_path, dirs_exist_ok=True)

preprocessed = Preprocessor(
os.path.join(self.__crate_path, 'src/lib.rs'),
Expand All @@ -180,14 +212,14 @@ def build(self, release: bool = False):
).process()

if preprocessed.updated_source is not None:
with open(os.path.join(output_path, 'src/lib.rs'), 'wb') as f:
with open(os.path.join(crate_output_subdirectory, 'src/lib.rs'), 'wb') as f:
f.write(preprocessed.updated_source)

with open(os.path.join(output_path, 'Cargo.toml'), 'wb') as f:
with open(os.path.join(crate_output_subdirectory, 'Cargo.toml'), 'wb') as f:
f.write(preprocessed.cargo_manifest)

build_result = Cargo().build(
output_path,
crate_output_subdirectory,
destination_path=self.extension_path,
release=release,
additional_args=preprocessed.additional_cargo_args,
Expand Down

0 comments on commit 525f702

Please sign in to comment.