diff options
| author | Xavier Claessens <xclaessens@netflix.com> | 2025-06-08 12:33:20 -0400 |
|---|---|---|
| committer | Dylan Baker <dylan@pnwbakers.com> | 2025-10-14 09:35:56 -0700 |
| commit | 67c9ef74e0185a11dc94f076ba891232dfb18bfc (patch) | |
| tree | 1f00750b6086f027605c5be0177f02157503c24d | |
| parent | e4b178f494781620cb352c92bd3db04e262e2661 (diff) | |
| download | meson-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.py | 63 | ||||
| -rw-r--r-- | mesonbuild/wrap/wrap.py | 3 | ||||
| -rw-r--r-- | unittests/cargotests.py | 9 |
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('''\ |
