Skip to content

Commit 258e198

Browse files
authored
Merge pull request #202 from davidhewitt/cache-cargo-metadata
perf: cache `cargo metadata` invocation to avoid multiple calls
2 parents a9cd47c + d358a94 commit 258e198

File tree

2 files changed

+30
-28
lines changed

2 files changed

+30
-28
lines changed

setuptools_rust/build.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -670,19 +670,10 @@ def _base_cargo_target_dir(ext: RustExtension) -> str:
670670
If --target is passed to cargo in the command line, the target directory
671671
will have the target appended as a child.
672672
"""
673-
metadata_command = [
674-
"cargo",
675-
"metadata",
676-
"--manifest-path",
677-
ext.path,
678-
"--format-version",
679-
"1",
680-
]
681-
metadata = json.loads(check_output(metadata_command))
682-
target_directory = metadata["target_directory"]
673+
target_directory = ext._metadata()["target_directory"]
683674
assert isinstance(
684675
target_directory, str
685-
), "expected cargo metadata to return a string target directory"
676+
), "expected cargo metadata to contain a string target directory"
686677
return target_directory
687678

688679

setuptools_rust/extension.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import subprocess
55
from distutils.errors import DistutilsSetupError
66
from enum import IntEnum, auto
7-
from typing import Dict, List, Optional, Union
7+
from typing import Any, Dict, List, NewType, Optional, Union
88

99
from semantic_version import SimpleSpec
1010
from typing_extensions import Literal
@@ -145,22 +145,13 @@ def __init__(
145145
path = os.path.relpath(path)
146146
self.path = path
147147

148+
self._cargo_metadata: Optional[_CargoMetadata] = None
149+
148150
def get_lib_name(self) -> str:
149151
"""Parse Cargo.toml to get the name of the shared library."""
150-
data = json.loads(
151-
subprocess.check_output(
152-
[
153-
"cargo",
154-
"metadata",
155-
"--manifest-path",
156-
self.path,
157-
"--format-version",
158-
"1",
159-
]
160-
)
161-
)
162-
root_key = data["resolve"]["root"]
163-
[pkg] = [p for p in data["packages"] if p["id"] == root_key]
152+
metadata = self._metadata()
153+
root_key = metadata["resolve"]["root"]
154+
[pkg] = [p for p in metadata["packages"] if p["id"] == root_key]
164155
name = pkg["targets"][0]["name"]
165156
assert isinstance(name, str)
166157
return re.sub(r"[./\\-]", "_", name)
@@ -190,13 +181,33 @@ def install_script(self, module_name: str, exe_path: str) -> None:
190181
dirname, executable = os.path.split(exe_path)
191182
file = os.path.join(dirname, "_gen_%s.py" % module_name)
192183
with open(file, "w") as f:
193-
f.write(TMPL.format(executable=repr(executable)))
184+
f.write(_TMPL.format(executable=repr(executable)))
185+
186+
def _metadata(self) -> "_CargoMetadata":
187+
"""Returns cargo metedata for this extension package.
188+
189+
Cached - will only execute cargo on first invocation.
190+
"""
191+
if self._cargo_metadata is None:
192+
metadata_command = [
193+
"cargo",
194+
"metadata",
195+
"--manifest-path",
196+
self.path,
197+
"--format-version",
198+
"1",
199+
]
200+
self._cargo_metadata = json.loads(subprocess.check_output(metadata_command))
201+
return self._cargo_metadata
194202

195203
def _uses_exec_binding(self) -> bool:
196204
return self.binding == Binding.Exec
197205

198206

199-
TMPL = """
207+
_CargoMetadata = NewType("_CargoMetadata", Dict[str, Any])
208+
209+
210+
_TMPL = """
200211
import os
201212
import sys
202213

0 commit comments

Comments
 (0)