summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xclaessens@netflix.com>2025-06-08 12:33:20 -0400
committerDylan Baker <dylan@pnwbakers.com>2025-10-14 09:35:56 -0700
commit67c9ef74e0185a11dc94f076ba891232dfb18bfc (patch)
tree1f00750b6086f027605c5be0177f02157503c24d
parente4b178f494781620cb352c92bd3db04e262e2661 (diff)
downloadmeson-67c9ef74e0185a11dc94f076ba891232dfb18bfc.tar.gz
cargo: When loading wraps multiple packages can have the same URL
This avoids cloning the same repo multiple times, instead a single wrap can provide multiple cargo dependencies.
-rw-r--r--mesonbuild/cargo/interpreter.py63
-rw-r--r--mesonbuild/wrap/wrap.py3
-rw-r--r--unittests/cargotests.py9
3 files changed, 51 insertions, 24 deletions
diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py
index c48efd6c5..82d2b5836 100644
--- a/mesonbuild/cargo/interpreter.py
+++ b/mesonbuild/cargo/interpreter.py
@@ -448,21 +448,41 @@ class Interpreter:
]
+def _parse_git_url(url: str, branch: T.Optional[str] = None) -> T.Tuple[str, str, str]:
+ if url.startswith('git+'):
+ url = url[4:]
+ parts = urllib.parse.urlparse(url)
+ query = urllib.parse.parse_qs(parts.query)
+ query_branch = query['branch'][0] if 'branch' in query else ''
+ branch = branch or query_branch
+ revision = parts.fragment or branch
+ directory = os.path.basename(parts.path)
+ if directory.endswith('.git'):
+ directory = directory[:-4]
+ if branch:
+ directory += f'-{branch}'
+ url = urllib.parse.urlunparse(parts._replace(params='', query='', fragment=''))
+ return url, revision, directory
+
+
def load_wraps(source_dir: str, subproject_dir: str) -> T.List[PackageDefinition]:
""" Convert Cargo.lock into a list of wraps """
- wraps: T.List[PackageDefinition] = []
+ # Map directory -> PackageDefinition, to avoid duplicates. Multiple packages
+ # can have the same source URL, in that case we have a single wrap that
+ # provides multiple dependency names.
+ wraps: T.Dict[str, PackageDefinition] = {}
filename = os.path.join(source_dir, 'Cargo.lock')
if os.path.exists(filename):
try:
toml = load_toml(filename)
except TomlImplementationMissing as e:
mlog.warning('Failed to load Cargo.lock:', str(e), fatal=False)
- return wraps
+ return []
raw_cargolock = T.cast('raw.CargoLock', toml)
cargolock = CargoLock.from_raw(raw_cargolock)
for package in cargolock.package:
- subp_name = _dependency_name(package.name, version.api(package.version))
+ meson_depname = _dependency_name(package.name, version.api(package.version))
if package.source is None:
# This is project's package, or one of its workspace members.
pass
@@ -472,25 +492,24 @@ def load_wraps(source_dir: str, subproject_dir: str) -> T.List[PackageDefinition
checksum = cargolock.metadata[f'checksum {package.name} {package.version} ({package.source})']
url = f'https://crates.io/api/v1/crates/{package.name}/{package.version}/download'
directory = f'{package.name}-{package.version}'
- wraps.append(PackageDefinition.from_values(subp_name, subproject_dir, 'file', {
- 'directory': directory,
- 'source_url': url,
- 'source_filename': f'{directory}.tar.gz',
- 'source_hash': checksum,
- 'method': 'cargo',
- }))
+ if directory not in wraps:
+ wraps[directory] = PackageDefinition.from_values(meson_depname, subproject_dir, 'file', {
+ 'directory': directory,
+ 'source_url': url,
+ 'source_filename': f'{directory}.tar.gz',
+ 'source_hash': checksum,
+ 'method': 'cargo',
+ })
+ wraps[directory].add_provided_dep(meson_depname)
elif package.source.startswith('git+'):
- parts = urllib.parse.urlparse(package.source[4:])
- query = urllib.parse.parse_qs(parts.query)
- branch = query['branch'][0] if 'branch' in query else ''
- revision = parts.fragment or branch
- url = urllib.parse.urlunparse(parts._replace(params='', query='', fragment=''))
- wraps.append(PackageDefinition.from_values(subp_name, subproject_dir, 'git', {
- 'directory': package.name,
- 'url': url,
- 'revision': revision,
- 'method': 'cargo',
- }))
+ url, revision, directory = _parse_git_url(package.source)
+ if directory not in wraps:
+ wraps[directory] = PackageDefinition.from_values(directory, subproject_dir, 'git', {
+ 'url': url,
+ 'revision': revision,
+ 'method': 'cargo',
+ })
+ wraps[directory].add_provided_dep(meson_depname)
else:
mlog.warning(f'Unsupported source URL in {filename}: {package.source}')
- return wraps
+ return list(wraps.values())
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index f8ed5e0fe..7273e77f4 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -318,6 +318,9 @@ class PackageDefinition:
with open(self.get_hashfile(subproject_directory), 'w', encoding='utf-8') as file:
file.write(self.wrapfile_hash + '\n')
+ def add_provided_dep(self, name: str) -> None:
+ self.provided_deps[name] = None
+
def get_directory(subdir_root: str, packagename: str) -> str:
fname = os.path.join(subdir_root, packagename + '.wrap')
if os.path.isfile(fname):
diff --git a/unittests/cargotests.py b/unittests/cargotests.py
index 7c09ab938..96a7b81ee 100644
--- a/unittests/cargotests.py
+++ b/unittests/cargotests.py
@@ -193,6 +193,10 @@ class CargoLockTest(unittest.TestCase):
name = "bar"
version = "0.1"
source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#23c5599424cc75ec66618891c915d9f490f6e4c2"
+ [[package]]
+ name = "member"
+ version = "0.1"
+ source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#23c5599424cc75ec66618891c915d9f490f6e4c2"
'''))
wraps = load_wraps(tmpdir, 'subprojects')
self.assertEqual(len(wraps), 2)
@@ -202,12 +206,13 @@ class CargoLockTest(unittest.TestCase):
self.assertEqual(wraps[0].get('method'), 'cargo')
self.assertEqual(wraps[0].get('source_url'), 'https://crates.io/api/v1/crates/foo/0.1/download')
self.assertEqual(wraps[0].get('source_hash'), '8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb')
- self.assertEqual(wraps[1].name, 'bar-0.1-rs')
- self.assertEqual(wraps[1].directory, 'bar')
+ self.assertEqual(wraps[1].name, 'gtk-rs-core-0.19')
+ self.assertEqual(wraps[1].directory, 'gtk-rs-core-0.19')
self.assertEqual(wraps[1].type, 'git')
self.assertEqual(wraps[1].get('method'), 'cargo')
self.assertEqual(wraps[1].get('url'), 'https://github.com/gtk-rs/gtk-rs-core')
self.assertEqual(wraps[1].get('revision'), '23c5599424cc75ec66618891c915d9f490f6e4c2')
+ self.assertEqual(list(wraps[1].provided_deps), ['gtk-rs-core-0.19', 'bar-0.1-rs', 'member-0.1-rs'])
class CargoTomlTest(unittest.TestCase):
CARGO_TOML_1 = textwrap.dedent('''\