diff --git a/conda_build/build.py b/conda_build/build.py index 4be112f57b..b074bf135a 100644 --- a/conda_build/build.py +++ b/conda_build/build.py @@ -89,6 +89,9 @@ set_language_env_vars, ) +if TYPE_CHECKING: + from typing import Iterable + if on_win: from . import windows @@ -3472,10 +3475,11 @@ def test( env["CONDA_BUILD_STATE"] = "TEST" if config.test_run_post: - from .utils import get_installed_packages - - installed = get_installed_packages(metadata.config.test_prefix) - files = installed[metadata.meta["package"]["name"]]["files"] + files = ( + PrefixData(metadata.config.test_prefix) + .get(metadata.meta["package"]["name"]) + .files + ) replacements = get_all_replacements(metadata.config) try_download(metadata, False, True) create_info_files(metadata, replacements, files, metadata.config.test_prefix) @@ -3578,7 +3582,7 @@ def tests_failed( _delegated_update_index( os.path.dirname(os.path.dirname(pkg)), verbose=config.debug, threads=1 ) - raise CondaBuildUserError("TESTS FAILED: " + os.path.basename(pkg)) + raise CondaBuildUserError(f"TESTS FAILED: {os.path.basename(pkg)}") @deprecated( diff --git a/conda_build/metadata.py b/conda_build/metadata.py index 94f7bd889f..1569d636a5 100644 --- a/conda_build/metadata.py +++ b/conda_build/metadata.py @@ -15,9 +15,11 @@ from os.path import isdir, isfile, join from typing import TYPE_CHECKING, NamedTuple, overload +import jinja2 import yaml from bs4 import UnicodeDammit from conda.base.context import locate_prefix_by_name +from conda.core.prefix_data import PrefixData from conda.gateways.disk.read import compute_sum from conda.models.match_spec import MatchSpec from frozendict import deepfreeze @@ -31,7 +33,6 @@ DependencyNeedsBuildingError, RecipeError, UnableToParse, - UnableToParseMissingJinja2, ) from .features import feature_list from .license_family import ensure_valid_license_family @@ -40,7 +41,6 @@ ensure_list, expand_globs, find_recipe, - get_installed_packages, insert_variant_versions, on_win, ) @@ -61,14 +61,6 @@ OutputDict = dict[str, Any] OutputTuple = tuple[OutputDict, "MetaData"] -try: - import yaml -except ImportError: - sys.exit( - "Error: could not import yaml (required to read meta.yaml " - "files of conda recipes)" - ) - try: Loader = yaml.CLoader except AttributeError: @@ -360,13 +352,6 @@ def yamlize(data): try: return yaml.load(data, Loader=StringifyNumbersLoader) except yaml.error.YAMLError as e: - if "{{" in data: - try: - import jinja2 - - jinja2 # Avoid pyflakes failure: 'jinja2' imported but unused - except ImportError: - raise UnableToParseMissingJinja2(original=e) print("Problematic recipe:", file=sys.stderr) print(data, file=sys.stderr) raise UnableToParse(original=e) @@ -739,12 +724,7 @@ def _git_clean(source_meta): If more than one field is used to specified, exit and complain. """ - git_rev_tags_old = ("git_branch", "git_tag") git_rev = "git_rev" - - git_rev_tags = (git_rev,) + git_rev_tags_old - has_rev_tags = tuple(bool(source_meta.get(tag, "")) for tag in git_rev_tags) - keys = [key for key in (git_rev, "git_branch", "git_tag") if key in source_meta] if not keys: # git_branch, git_tag, nor git_rev specified, return as-is @@ -754,14 +734,7 @@ def _git_clean(source_meta): # make a copy of the input so we have no side-effects ret_meta = source_meta.copy() - # loop over the old versions - for key, has in zip(git_rev_tags[1:], has_rev_tags[1:]): - # update if needed - if has: - ret_meta[git_rev_tags[0]] = ret_meta[key] - # and remove - ret_meta.pop(key, None) - + ret_meta[git_rev] = ret_meta.pop(keys[0]) return ret_meta @@ -868,17 +841,24 @@ def _get_env_path( ) -def _get_dependencies_from_environment(env_name_or_path): - path = _get_env_path(env_name_or_path) +def _get_dependencies_from_environment( + env_name_or_path: str | os.PathLike | Path, +) -> dict[str, dict[str, list[str]]]: # construct build requirements that replicate the given bootstrap environment # and concatenate them to the build requirements from the recipe - bootstrap_metadata = get_installed_packages(path) - bootstrap_requirements = [] - for package, data in bootstrap_metadata.items(): - bootstrap_requirements.append( - "{} {} {}".format(package, data["version"], data["build"]) - ) - return {"requirements": {"build": bootstrap_requirements}} + prefix = ( + env_name_or_path + if isdir(env_name_or_path) + else locate_prefix_by_name(env_name_or_path) + ) + return { + "requirements": { + "build": [ + f"{prec.name} {prec.version} {prec.build}" + for prec in PrefixData(prefix).iter_records() + ] + } + } def _toposort_outputs(output_tuples: list[OutputTuple]) -> list[OutputTuple]: @@ -1925,17 +1905,6 @@ def _get_contents( permit_undefined_jinja: If True, *any* use of undefined jinja variables will evaluate to an emtpy string, without emitting an error. """ - try: - import jinja2 - except ImportError: - print("There was an error importing jinja2.", file=sys.stderr) - print( - "Please run `conda install jinja2` to enable jinja template support", - file=sys.stderr, - ) # noqa - with open(self.meta_path) as fd: - return fd.read() - from .jinja_context import ( FilteredLoader, UndefinedNeverFail, diff --git a/conda_build/post.py b/conda_build/post.py index 6b10af2a78..fa18411a01 100644 --- a/conda_build/post.py +++ b/conda_build/post.py @@ -8,7 +8,6 @@ import re import shutil import stat -import sys import traceback from collections import OrderedDict, defaultdict from copy import copy @@ -43,7 +42,13 @@ from conda.models.records import PrefixRecord from . import utils -from .exceptions import OverDependingError, OverLinkingError, RunPathError +from .deprecations import deprecated +from .exceptions import ( + CondaBuildUserError, + OverDependingError, + OverLinkingError, + RunPathError, +) from .inspect_pkg import which_package from .os_utils import external, macho from .os_utils.liefldd import ( @@ -64,7 +69,7 @@ from .utils import on_mac, on_win, prefix_files if TYPE_CHECKING: - from typing import Literal + from typing import Iterable, Literal from .metadata import MetaData @@ -329,20 +334,26 @@ def compile_missing_pyc(files, cwd, python_exe, skip_compile_pyc=()): call(args + group, cwd=cwd) -def check_dist_info_version(name, version, files): - for f in files: - if f.endswith(".dist-info" + os.sep + "METADATA"): - f_lower = basename(dirname(f).lower()) - if f_lower.startswith(name + "-"): - f_lower, _, _ = f_lower.rpartition(".dist-info") - _, distname, f_lower = f_lower.rpartition(name + "-") - if distname == name and version != f_lower: - print( - f"ERROR: Top level dist-info version incorrect (is {f_lower}, should be {version})" - ) - sys.exit(1) - else: - return +def check_dist_info_version( + name: str, + version: str, + files: Iterable[str | os.PathLike | Path], +) -> None: + for file in map(Path, files): + if file.name != "METADATA": + continue + + dist = file.parent.name.lower() + if not (dist.startswith(f"{name}-") and dist.endswith(".dist-info")): + continue + + distversion = dist[len(name) + 1 : -10] # remove prefix & suffix + if version != distversion: + raise CondaBuildUserError( + f"Top level dist-info version incorrect (is {distversion}, should be {version})" + ) + else: + return def post_process( @@ -371,25 +382,35 @@ def post_process( check_dist_info_version(name, version, files) -def find_lib(link, prefix, files, path=None): +def find_lib( + link: str | os.PathLike | Path, + prefix: str | os.PathLike | Path, + files: Iterable[str | os.PathLike | Path], + path: str | os.PathLike | Path | None = None, +) -> str | None: + link = str(link) + prefix = str(prefix) + files = map(str, utils.ensure_list(files)) + path = str(path) if path else None + if link.startswith(prefix): link = normpath(link[len(prefix) + 1 :]) if not any(link == normpath(w) for w in files): - sys.exit(f"Error: Could not find {link}") + raise CondaBuildUserError(f"Could not find {link!r}") return link if link.startswith("/"): # but doesn't start with the build prefix - return + return None if link.startswith("@rpath/"): # Assume the rpath already points to lib, so there is no need to # change it. - return + return None if "/" not in link or link.startswith("@executable_path/"): link = basename(link) file_names = defaultdict(list) - for f in files: - file_names[basename(f)].append(f) + for file in files: + file_names[basename(file)].append(file) if link not in file_names: - sys.exit(f"Error: Could not find {link}") + raise CondaBuildUserError(f"Could not find {link!r}") if len(file_names[link]) > 1: if path and basename(path) == link: # The link is for the file itself, just use it @@ -397,20 +418,22 @@ def find_lib(link, prefix, files, path=None): # Allow for the possibility of the same library appearing in # multiple places. md5s = set() - for f in file_names[link]: - md5s.add(compute_sum(join(prefix, f), "md5")) + for file in file_names[link]: + md5s.add(compute_sum(join(prefix, file), "md5")) if len(md5s) > 1: - sys.exit( - f"Error: Found multiple instances of {link}: {file_names[link]}" + raise CondaBuildUserError( + f"Found multiple instances of {link!r}: {file_names[link]!r}" ) else: file_names[link].sort() print( - f"Found multiple instances of {link} ({file_names[link]}). " - "Choosing the first one." + f"Found multiple instances of {link!r}: {file_names[link]!r}. " + f"Choosing the first one." ) return file_names[link][0] - print(f"Don't know how to find {link}, skipping") + + print(f"Don't know how to find {link!r}, skipping") + return None def osx_ch_link(path, link_dict, host_prefix, build_prefix, files): @@ -423,8 +446,8 @@ def osx_ch_link(path, link_dict, host_prefix, build_prefix, files): "host prefix and" ) if not codefile_class(link, skip_symlinks=True): - sys.exit( - f"Error: Compiler runtime library in build prefix not found in host prefix {link}" + raise CondaBuildUserError( + f"Compiler runtime library in build prefix not found in host prefix {link}" ) else: print(f".. fixing linking of {link} in {path} instead") @@ -1262,6 +1285,7 @@ def _show_linking_messages( ) +@deprecated.argument("24.9", "24.11", "exception_on_error", addendum="Default to true.") def check_overlinking_impl( pkg_name: str, pkg_version: str, @@ -1279,7 +1303,6 @@ def check_overlinking_impl( error_overlinking, error_overdepending, verbose, - exception_on_error, files, bldpkgs_dirs, output_folder, @@ -1547,26 +1570,22 @@ def sysroot_matches_subdir(path): verbose=verbose, ) if len(errors): - if exception_on_error: - runpaths_errors = [ - error for error in errors if re.match(r".*runpaths.*found in.*", error) - ] - if len(runpaths_errors): - raise RunPathError(runpaths_errors) - overlinking_errors = [ - error - for error in errors - if re.match(r".*(overlinking|not found in|did not find).*", error) - ] - if len(overlinking_errors): - raise OverLinkingError(overlinking_errors) - overdepending_errors = [ - error for error in errors if "overdepending" in error - ] - if len(overdepending_errors): - raise OverDependingError(overdepending_errors) - else: - sys.exit(1) + if runpaths_errors := [ + error for error in errors if re.match(r".*runpaths.*found in.*", error) + ]: + raise RunPathError(runpaths_errors) + + if overlinking_errors := [ + error + for error in errors + if re.match(r".*(overlinking|not found in|did not find).*", error) + ]: + raise OverLinkingError(overlinking_errors) + + if overdepending_errors := [ + error for error in errors if "overdepending" in error + ]: + raise OverDependingError(overdepending_errors) if pkg_vendoring_key in vendoring_record: imports = vendoring_record[pkg_vendoring_key] @@ -1599,7 +1618,6 @@ def check_overlinking(m: MetaData, files, host_prefix=None): m.config.error_overlinking, m.config.error_overdepending, m.config.verbose, - True, files, m.config.bldpkgs_dir, m.config.output_folder, @@ -1762,7 +1780,7 @@ def post_build(m, files, build_python, host_prefix=None, is_already_linked=False def check_symlinks(files, prefix, croot): - msgs = [] + bad_symlinks = [] real_build_prefix = realpath(prefix) for f in files: path = join(real_build_prefix, f) @@ -1794,15 +1812,13 @@ def check_symlinks(files, prefix, croot): else: # Symlinks to absolute paths on the system (like /usr) are fine. if real_link_path.startswith(croot): - msgs.append( - f"{f} is a symlink to a path that may not " - f"exist after the build is completed ({link_path})" - ) + bad_symlinks.append(f" {f} → {link_path}") - if msgs: - for msg in msgs: - print(f"Error: {msg}", file=sys.stderr) - sys.exit(1) + if bad_symlinks: + raise CondaBuildUserError( + "Found symlinks to paths that may not exist after the build is completed:\n" + + "\n".join(bad_symlinks) + ) def make_hardlink_copy(path, prefix): diff --git a/tests/cli/test_main_inspect.py b/tests/cli/test_main_inspect.py index 83859bf441..f838f10e36 100644 --- a/tests/cli/test_main_inspect.py +++ b/tests/cli/test_main_inspect.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: BSD-3-Clause import os import re -import sys import pytest import yaml @@ -10,7 +9,7 @@ from conda_build import api from conda_build.cli import main_inspect from conda_build.exceptions import CondaBuildUserError -from conda_build.utils import on_win +from conda_build.utils import on_mac, on_win from ..utils import metadata_dir @@ -24,9 +23,11 @@ def test_inspect_linkages(testing_workdir, capfd): # get a package that has known object output args = ["linkages", "python"] if on_win: - with pytest.raises(CondaBuildUserError) as exc: + with pytest.raises( + CondaBuildUserError, + match=r"`conda inspect linkages` is only implemented on Linux and macOS", + ): main_inspect.execute(args) - assert "conda inspect linkages is only implemented in Linux and OS X" in exc else: main_inspect.execute(args) output, error = capfd.readouterr() @@ -36,10 +37,12 @@ def test_inspect_linkages(testing_workdir, capfd): def test_inspect_objects(testing_workdir, capfd): # get a package that has known object output args = ["objects", "python"] - if sys.platform != "darwin": - with pytest.raises(CondaBuildUserError) as exc: + if not on_mac: + with pytest.raises( + CondaBuildUserError, + match=r"`conda inspect objects` is only implemented on macOS", + ): main_inspect.execute(args) - assert "conda inspect objects is only implemented in OS X" in exc else: main_inspect.execute(args) output, error = capfd.readouterr() diff --git a/tests/conftest.py b/tests/conftest.py index cd66dddb97..af137195f0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,17 +1,18 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import os import subprocess import sys import tempfile from collections import defaultdict from pathlib import Path -from typing import Iterator +from typing import TYPE_CHECKING import pytest from conda.common.compat import on_mac, on_win from conda_index.api import update_index -from pytest import MonkeyPatch import conda_build import conda_build.config @@ -32,6 +33,17 @@ from conda_build.utils import check_call_env, copy_into, prepend_bin_path from conda_build.variants import get_default_variant +if TYPE_CHECKING: + from typing import Iterator + + from pytest import MonkeyPatch + + +pytest_plugins = ( + # Add testing fixtures and internal pytest plugins here + "conda.testing", +) + @pytest.hookimpl def pytest_report_header(config: pytest.Config): diff --git a/tests/test_api_build.py b/tests/test_api_build.py index 554bc452ae..41081b2abf 100644 --- a/tests/test_api_build.py +++ b/tests/test_api_build.py @@ -476,9 +476,9 @@ def test_cmake_generator(platform, target_compiler, testing_config): api.build(os.path.join(metadata_dir, "_cmake_generator"), config=testing_config) -@pytest.mark.skipif(sys.platform == "win32", reason="No windows symlinks") -def test_symlink_fail(testing_config): - with pytest.raises((SystemExit, FileNotFoundError)): +@pytest.mark.skipif(on_win, reason="No windows symlinks") +def test_symlink_fail(testing_config: MetaData) -> None: + with pytest.raises(CondaBuildUserError): api.build(os.path.join(fail_dir, "symlinks"), config=testing_config) @@ -544,11 +544,12 @@ def test_skip_existing_url(testing_metadata, testing_workdir, capfd): assert "are already built" in output -def test_failed_tests_exit_build(testing_config): - """https://github.com/conda/conda-build/issues/1112""" - with pytest.raises(CondaBuildUserError, match="TESTS FAILED"): +def test_failed_tests_exit_build(testing_config: Config) -> None: + # https://github.com/conda/conda-build/issues/1112 + with pytest.raises(CondaBuildUserError, match=r"TESTS FAILED"): api.build( - os.path.join(metadata_dir, "_test_failed_test_exits"), config=testing_config + os.path.join(metadata_dir, "_test_failed_test_exits"), + config=testing_config, ) diff --git a/tests/test_build.py b/tests/test_build.py index 52d1e5425b..8ddb38a915 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -22,9 +22,6 @@ from .utils import get_noarch_python_meta, metadata_dir, metadata_path -if TYPE_CHECKING: - from conda_build.config import Config - if TYPE_CHECKING: from pytest_mock import MockerFixture diff --git a/tests/test_inspect.py b/tests/test_inspect.py index 04acf2728b..8e84909e20 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -1,30 +1,29 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause import re -import sys +from contextlib import nullcontext import pytest +from conda.common.compat import on_mac, on_win from conda_build import api from conda_build.exceptions import CondaBuildUserError def test_inspect_linkages(): - if sys.platform == "win32": - with pytest.raises(CondaBuildUserError) as exc: - out_string = api.inspect_linkages("python") - assert "conda inspect linkages is only implemented in Linux and OS X" in exc - else: + with pytest.raises( + CondaBuildUserError, + match=r"`conda inspect linkages` is only implemented on Linux and macOS", + ) if on_win else nullcontext(): out_string = api.inspect_linkages("python") assert "libncursesw" in out_string def test_inspect_objects(): - if sys.platform != "darwin": - with pytest.raises(CondaBuildUserError) as exc: - out_string = api.inspect_objects("python") - assert "conda inspect objects is only implemented in OS X" in exc - else: + with pytest.raises( + CondaBuildUserError, + match=r"`conda inspect objects` is only implemented on macOS", + ) if not on_mac else nullcontext(): out_string = api.inspect_objects("python") assert re.search("rpath:.*@loader_path", out_string) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index b8dc9df8e4..972e1d210c 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -620,6 +620,9 @@ def test_parse_until_resolved(testing_metadata: MetaData, tmp_path: Path) -> Non with pytest.raises( CondaBuildUserError, - match=("Failed to render jinja template"), + match=( + rf"Failed to render jinja template in {recipe}:\n" + r"'UNDEFINED' is undefined" + ), ): testing_metadata.parse_until_resolved() diff --git a/tests/test_post.py b/tests/test_post.py index e0eb59237f..578a95d4d0 100644 --- a/tests/test_post.py +++ b/tests/test_post.py @@ -1,16 +1,20 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import json import logging import os import shutil import sys from pathlib import Path +from typing import TYPE_CHECKING import pytest import conda_build.utils from conda_build import api, post +from conda_build.exceptions import CondaBuildUserError from conda_build.utils import ( get_site_packages, on_linux, @@ -21,6 +25,10 @@ from .utils import add_mangling, metadata_dir, subpackage_path +if TYPE_CHECKING: + from conda.testing import PathFactoryFixture + from pytest import CaptureFixture + @pytest.mark.skipif( sys.version_info >= (3, 10), @@ -214,3 +222,123 @@ def test_rpath_symlink(mocker, testing_config): ) # Should only be called on the actual binary, not its symlinks. (once per variant) assert mk_relative.call_count == 2 + + +def test_check_dist_info_version(): + # package not installed + post.check_dist_info_version("name", "1.2.3", []) + + # package installed and version matches + post.check_dist_info_version("name", "1.2.3", ["name-1.2.3.dist-info/METADATA"]) + + # package installed and version does not match + with pytest.raises(CondaBuildUserError): + post.check_dist_info_version("name", "1.2.3", ["name-1.0.0.dist-info/METADATA"]) + + +def test_find_lib(capsys: CaptureFixture, tmp_path: Path) -> None: + (prefix := tmp_path / "prefix").mkdir() + (prefix / (file1 := (name := "name"))).write_text("content") + (prefix / (dirA := "dirA")).mkdir() + (prefix / (file2 := f"{dirA}{os.sep}{name}")).write_text("content") + (prefix / (dirB := "dirB")).mkdir() + (prefix / (file3 := f"{dirB}{os.sep}{name}")).write_text("other") + + (external := tmp_path / "external").mkdir() + + rpath = Path("@rpath") + executable_path = Path("@executable_path") + + # /prefix/missing is in prefix but isn't in file list (i.e., doesn't exist) + with pytest.raises(CondaBuildUserError, match=r"Could not find"): + post.find_lib(prefix / "missing", prefix, []) + + # /prefix/name is in prefix and is in file list (i.e., it exists) + assert post.find_lib(prefix / name, prefix, [file1]) == file1 + + # /external/name is not in prefix + assert post.find_lib(external / name, prefix, []) is None + + # @rpath/name paths are assumed to already point to a valid file + assert post.find_lib(rpath / name, prefix, []) is None + assert post.find_lib(rpath / "extra" / name, prefix, []) is None + + # name is in the file list + assert post.find_lib(name, prefix, [file1]) == file1 + assert post.find_lib(name, prefix, [file2]) == file2 + + # @executable_path/name is in the file list + assert post.find_lib(executable_path / name, prefix, [file1]) == file1 + assert post.find_lib(executable_path / name, prefix, [file2]) == file2 + + # @executable_path/extra/name is in the file list + # TODO: is this valid? + assert post.find_lib(executable_path / "extra" / name, prefix, [file1]) == file1 + assert post.find_lib(executable_path / "extra" / name, prefix, [file2]) == file2 + + # name matches multiples in the file list (and they're all the same file) + capsys.readouterr() # clear buffer + assert post.find_lib(name, prefix, [file1, file2]) == file2 + stdout, stderr = capsys.readouterr() + assert stdout == ( + f"Found multiple instances of {name!r}: {[file2, file1]!r}. " + f"Choosing the first one.\n" + ) + assert not stderr + + # name matches multiples in file list (and they're not the same file) + with pytest.raises(CondaBuildUserError, match=r"Found multiple instances"): + post.find_lib(name, prefix, [file1, file3]) + + # missing is not in the file list + with pytest.raises(CondaBuildUserError, match=r"Could not find"): + post.find_lib("missing", prefix, [file1, file2]) + + # name matches multiples in file list but an explicit path is given + assert post.find_lib(name, prefix, [file1, file2, file3], file1) == file1 + assert post.find_lib(name, prefix, [file1, file2, file3], file2) == file2 + assert post.find_lib(name, prefix, [file1, file2, file3], file3) == file3 + + # relative/name is not in prefix and doesn't match any of the files + capsys.readouterr() # clear buffer + assert post.find_lib(link := Path("relative", name), prefix, []) is None + stdout, stderr = capsys.readouterr() + assert stdout == f"Don't know how to find '{link}', skipping\n" + assert not stderr + + +def test_osx_ch_link_missing(path_factory: PathFactoryFixture): + path = path_factory() + host_prefix = path_factory() + build_prefix = path_factory() + + with pytest.raises( + CondaBuildUserError, + match="Compiler runtime library in build prefix not found in host prefix", + ): + post.osx_ch_link( + str(path), + {"name": str(build_prefix / "missing")}, + str(host_prefix), + str(build_prefix), + [], + ) + + +def test_check_symlinks_error(path_factory: PathFactoryFixture): + (prefix := path_factory()).mkdir() + (croot := path_factory()).mkdir() + + (real := croot / "real").touch() + (prefix / (link := "link")).symlink_to(real) + (prefix / (link2 := "link2")).symlink_to(real) + + with pytest.raises( + CondaBuildUserError, + match=( + r"Found symlinks to paths that may not exist after the build is completed:\n" + rf" link → {real}\n" + rf" link2 → {real}" + ), + ): + post.check_symlinks([link, link2], str(prefix), str(croot)) diff --git a/tests/test_utils.py b/tests/test_utils.py index 98733546b5..c96306a128 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -133,6 +133,10 @@ def test_filter_files(): "python.exe.conda_trash", "bla.dll.conda_trash_1", "bla.dll.conda_trash.conda_trash", + # ignore top-level conda-meta + "conda-meta", + "conda-meta/something", + "conda-meta-something", ] assert not utils.filter_files(files_list, "") @@ -150,6 +154,10 @@ def test_filter_files(): "another.lab", "miniconda_trashcan.py", "conda_trash_avoider.py", + # keep nested conda-meta + "something/conda-meta", + "something/conda-meta/something", + "something/conda-meta-something", ] assert len(utils.filter_files(files_list, "")) == len(files_list)