Skip to content

Commit

Permalink
Vendor all dependencies (crates) for Cargo
Browse files Browse the repository at this point in the history
Add logic to resolve each Cargo package by vendoring its dependencies
(crates) into the output directory using Cargo CLI. The important
option here is `--locked` that disabled any changes to Cargo.lock file.

To use vendored sources, `.cargo/config.toml` must contain the following
lines. See [1] for more details. Replace hardcoded directory path with a
placeholder for output directory specified by the users when running
`cachi2 inject-files ...`

```
[source.crates-io]
replace-with = "vendored-sources"

[source.vendored-sources]
directory = "${path-to-vendored-sources}"
```

---
[1]: https://crates.io/crates/cargo-vendor

Signed-off-by: Michal Šoltis <msoltis@redhat.com>
  • Loading branch information
slimreaper35 committed Jan 31, 2025
1 parent 784f121 commit 9e08277
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 0 deletions.
48 changes: 48 additions & 0 deletions cachi2/core/package_managers/cargo/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import logging
import textwrap

import tomlkit
from tomlkit.toml_file import TOMLFile

from cachi2.core.models.input import Request
from cachi2.core.models.output import Component, EnvironmentVariable, ProjectFile, RequestOutput
from cachi2.core.rooted_path import RootedPath
from cachi2.core.utils import run_cmd

log = logging.getLogger(__name__)


def fetch_cargo_source(request: Request) -> RequestOutput:
Expand All @@ -8,4 +18,42 @@ def fetch_cargo_source(request: Request) -> RequestOutput:
environment_variables: list[EnvironmentVariable] = []
project_files: list[ProjectFile] = []

for package in request.cargo_packages:
package_dir = request.source_dir.join_within_root(package.path)
components.extend(_resolve_cargo_package(package_dir, request.output_dir))
# cargo allows to specify configuration per-package
# https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure
project_files.append(_use_vendored_sources(package_dir))

return RequestOutput.from_obj_list(components, environment_variables, project_files)


def _resolve_cargo_package(package_dir: RootedPath, output_dir: RootedPath) -> list[Component]:
"""Resolve a single cargo package."""
vendor_dir = output_dir.join_within_root("deps/cargo")
cmd = ["cargo", "vendor", "--locked", str(vendor_dir)]
log.info("Vendoring crates at %s", package_dir)
run_cmd(cmd=cmd, params={"cwd": package_dir})
return []


def _use_vendored_sources(package_dir: RootedPath) -> ProjectFile:
"""Make sure cargo will use the vendored sources when building the project."""
cargo_config = package_dir.join_within_root(".cargo/config.toml")
cargo_config.path.parent.mkdir(parents=True, exist_ok=True)
cargo_config.path.touch(exist_ok=True)

template = """
[source.crates-io]
replace-with = "vendored-sources"
[source.vendored-sources]
directory = "${output_dir}/deps/cargo"
"""

toml_file = TOMLFile(cargo_config)
original_content = toml_file.read()
original_content.update(tomlkit.parse(textwrap.dedent(template)))
toml_file.write(original_content)

return ProjectFile(abspath=cargo_config.path, template=template)
Empty file.
42 changes: 42 additions & 0 deletions tests/unit/package_managers/cargo/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import tomlkit

from cachi2.core.package_managers.cargo.main import _use_vendored_sources
from cachi2.core.rooted_path import RootedPath


def test_use_vendored_sources_creates_config_file(rooted_tmp_path: RootedPath) -> None:
config_path = rooted_tmp_path.join_within_root(".cargo/config.toml")
config_path.path.parent.mkdir(parents=True)

project_file = _use_vendored_sources(rooted_tmp_path)

assert config_path.path.exists()
assert project_file.abspath == config_path.path

config_content = tomlkit.parse(config_path.path.read_text())

assert config_content.get("source") == {
"crates-io": {"replace-with": "vendored-sources"},
"vendored-sources": {"directory": "${output_dir}/deps/cargo"},
}


def test_use_vendored_sources_preserves_existing_config(rooted_tmp_path: RootedPath) -> None:
config_path = rooted_tmp_path.join_within_root(".cargo/config.toml")
config_path.path.parent.mkdir(parents=True)

some_toml = """
[build]
rustflags = ["-C", "target-cpu=native"]
"""
config_path.path.write_text(some_toml)

_use_vendored_sources(rooted_tmp_path)

config_content = tomlkit.parse(config_path.path.read_text())

assert config_content.get("build") == {"rustflags": ["-C", "target-cpu=native"]}
assert config_content.get("source") == {
"crates-io": {"replace-with": "vendored-sources"},
"vendored-sources": {"directory": "${output_dir}/deps/cargo"},
}

0 comments on commit 9e08277

Please sign in to comment.