diff --git a/conda_build/inspect_pkg.py b/conda_build/inspect_pkg.py index 0e33dac8b7..abd3953c9d 100644 --- a/conda_build/inspect_pkg.py +++ b/conda_build/inspect_pkg.py @@ -31,7 +31,7 @@ get_package_obj_files, get_untracked_obj_files, ) -from conda_build.os_utils.liefldd import codefile_type +from conda_build.os_utils.liefldd import codefile_class, machofile from conda_build.os_utils.macho import get_rpaths, human_filetype from conda_build.utils import ( comma_join, @@ -363,14 +363,16 @@ def inspect_objects(packages, prefix=sys.prefix, groupby="package"): info = [] for f in obj_files: - f_info = {} path = join(prefix, f) - filetype = codefile_type(path) - if filetype == "machofile": - f_info["filetype"] = human_filetype(path, None) - f_info["rpath"] = ":".join(get_rpaths(path)) - f_info["filename"] = f - info.append(f_info) + codefile = codefile_class(path) + if codefile == machofile: + info.append( + { + "filetype": human_filetype(path, None), + "rpath": ":".join(get_rpaths(path)), + "filename": f, + } + ) output_string += print_object_info(info, groupby) if hasattr(output_string, "decode"): diff --git a/conda_build/jinja_context.py b/conda_build/jinja_context.py index 61219be134..9d507e43a6 100644 --- a/conda_build/jinja_context.py +++ b/conda_build/jinja_context.py @@ -494,34 +494,42 @@ def native_compiler(language, config): return compiler -def compiler(language, config, permit_undefined_jinja=False): - """Support configuration of compilers. This is somewhat platform specific. +def _target(language, config, permit_undefined_jinja=False, component="compiler"): + """Support configuration of compilers/stdlib. This is somewhat platform specific. - Native compilers never list their host - it is always implied. Generally, they are + Native compilers/stdlib never list their host - it is always implied. Generally, they are metapackages, pointing at a package that does specify the host. These in turn may be metapackages, pointing at a package where the host is the same as the target (both being the native architecture). """ - compiler = native_compiler(language, config) + if component == "compiler": + package_prefix = native_compiler(language, config) + else: + package_prefix = language + version = None if config.variant: target_platform = config.variant.get("target_platform", config.subdir) - language_compiler_key = f"{language}_compiler" - # fall back to native if language-compiler is not explicitly set in variant - compiler = config.variant.get(language_compiler_key, compiler) - version = config.variant.get(language_compiler_key + "_version") + language_key = f"{language}_{component}" + # fall back to native if language-key is not explicitly set in variant + package_prefix = config.variant.get(language_key, package_prefix) + version = config.variant.get(language_key + "_version") else: target_platform = config.subdir - # support cross compilers. A cross-compiler package will have a name such as + # support cross components. A cross package will have a name such as # gcc_target # gcc_linux-cos6-64 - compiler = "_".join([compiler, target_platform]) + package = f"{package_prefix}_{target_platform}" if version: - compiler = " ".join((compiler, version)) - compiler = ensure_valid_spec(compiler, warn=False) - return compiler + package = f"{package} {version}" + package = ensure_valid_spec(package, warn=False) + return package + + +# ensure we have compiler in namespace +compiler = partial(_target, component="compiler") def ccache(method, config, permit_undefined_jinja=False): @@ -788,7 +796,16 @@ def context_processor( skip_build_id=skip_build_id, ), compiler=partial( - compiler, config=config, permit_undefined_jinja=permit_undefined_jinja + _target, + config=config, + permit_undefined_jinja=permit_undefined_jinja, + component="compiler", + ), + stdlib=partial( + _target, + config=config, + permit_undefined_jinja=permit_undefined_jinja, + component="stdlib", ), cdt=partial(cdt, config=config, permit_undefined_jinja=permit_undefined_jinja), ccache=partial( diff --git a/conda_build/os_utils/ldd.py b/conda_build/os_utils/ldd.py index 77daf4ab10..32eea125a2 100644 --- a/conda_build/os_utils/ldd.py +++ b/conda_build/os_utils/ldd.py @@ -8,12 +8,7 @@ from conda_build.conda_interface import linked_data, untracked from conda_build.os_utils.macho import otool -from conda_build.os_utils.pyldd import ( - codefile_class, - inspect_linkages, - is_codefile, - machofile, -) +from conda_build.os_utils.pyldd import codefile_class, inspect_linkages, machofile LDD_RE = re.compile(r"\s*(.*?)\s*=>\s*(.*?)\s*\(.*\)") LDD_NOT_FOUND_RE = re.compile(r"\s*(.*?)\s*=>\s*not found") @@ -118,7 +113,7 @@ def get_package_obj_files(dist, prefix): files = get_package_files(dist, prefix) for f in files: path = join(prefix, f) - if is_codefile(path): + if codefile_class(path): res.append(f) return res @@ -130,7 +125,7 @@ def get_untracked_obj_files(prefix): files = untracked(prefix) for f in files: path = join(prefix, f) - if is_codefile(path): + if codefile_class(path): res.append(f) return res diff --git a/conda_build/os_utils/liefldd.py b/conda_build/os_utils/liefldd.py index 2cf6ce92ad..26a768a4f6 100644 --- a/conda_build/os_utils/liefldd.py +++ b/conda_build/os_utils/liefldd.py @@ -1,9 +1,6 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause -try: - from collections.abc import Hashable -except ImportError: - from collections.abc import Hashable +from __future__ import annotations import hashlib import json @@ -11,34 +8,34 @@ import struct import sys import threading +from collections.abc import Hashable from fnmatch import fnmatch from functools import partial +from pathlib import Path from subprocess import PIPE, Popen +from ..deprecations import deprecated from .external import find_executable # lief cannot handle files it doesn't know about gracefully # TODO :: Remove all use of pyldd # Currently we verify the output of each against the other -from .pyldd import codefile_type as codefile_type_pyldd +from .pyldd import DLLfile, EXEfile, elffile, machofile +from .pyldd import codefile_type as _codefile_type from .pyldd import inspect_linkages as inspect_linkages_pyldd -codefile_type = codefile_type_pyldd -have_lief = False try: import lief lief.logging.disable() have_lief = True -except: - pass +except ImportError: + have_lief = False +@deprecated("3.28.0", "4.0.0", addendum="Use `isinstance(value, str)` instead.") def is_string(s): - try: - return isinstance(s, basestring) - except NameError: - return isinstance(s, str) + return isinstance(s, str) # Some functions can operate on either file names @@ -46,17 +43,16 @@ def is_string(s): # these are to be avoided, or if not avoided they # should be passed a binary when possible as that # will prevent having to parse it multiple times. -def ensure_binary(file): - if not is_string(file): +def ensure_binary(file: str | os.PathLike | Path | lief.Binary) -> lief.Binary | None: + if isinstance(file, lief.Binary): return file - else: - try: - if not os.path.exists(file): - return [] - return lief.parse(file) - except: - print(f"WARNING: liefldd: failed to ensure_binary({file})") - return None + elif not Path(file).exists(): + return None + try: + return lief.parse(str(file)) + except BaseException: + print(f"WARNING: liefldd: failed to ensure_binary({file})") + return None def nm(filename): @@ -77,25 +73,57 @@ def nm(filename): print("No symbols found") -def codefile_type_liefldd(file, skip_symlinks=True): - binary = ensure_binary(file) - result = None - if binary: - if binary.format == lief.EXE_FORMATS.PE: - if lief.PE.DLL_CHARACTERISTICS: - if binary.header.characteristics & lief.PE.HEADER_CHARACTERISTICS.DLL: - result = "DLLfile" - else: - result = "EXEfile" +if have_lief: + + def codefile_class( + path: str | os.PathLike | Path, + skip_symlinks: bool = False, + ) -> type[DLLfile | EXEfile | machofile | elffile] | None: + # same signature as conda.os_utils.pyldd.codefile_class + if not (binary := ensure_binary(path)): + return None + elif ( + binary.format == lief.EXE_FORMATS.PE + and lief.PE.HEADER_CHARACTERISTICS.DLL in binary.header.characteristics_list + ): + return DLLfile + elif binary.format == lief.EXE_FORMATS.PE: + return EXEfile elif binary.format == lief.EXE_FORMATS.MACHO: - result = "machofile" + return machofile elif binary.format == lief.EXE_FORMATS.ELF: - result = "elffile" - return result - + return elffile + else: + return None -if have_lief: - codefile_type = codefile_type_liefldd +else: + from .pyldd import codefile_class + + +@deprecated( + "3.28.0", + "4.0.0", + addendum="Use `conda_build.os_utils.liefldd.codefile_class` instead.", +) +def codefile_type_liefldd(*args, **kwargs) -> str | None: + codefile = codefile_class(*args, **kwargs) + return codefile.__name__ if codefile else None + + +deprecated.constant( + "3.28.0", + "4.0.0", + "codefile_type_pyldd", + _codefile_type, + addendum="Use `conda_build.os_utils.pyldd.codefile_class` instead.", +) +deprecated.constant( + "3.28.0", + "4.0.0", + "codefile_type", + _codefile_type, + addendum="Use `conda_build.os_utils.liefldd.codefile_class` instead.", +) def _trim_sysroot(sysroot): @@ -111,7 +139,9 @@ def get_libraries(file): if binary.format == lief.EXE_FORMATS.PE: result = binary.libraries else: - result = [lib if is_string(lib) else lib.name for lib in binary.libraries] + result = [ + lib if isinstance(lib, str) else lib.name for lib in binary.libraries + ] # LIEF returns LC_ID_DYLIB name @rpath/libbz2.dylib in binary.libraries. Strip that. binary_name = None if binary.format == lief.EXE_FORMATS.MACHO: @@ -505,7 +535,7 @@ def inspect_linkages_lief( while tmp_filename: if ( not parent_exe_dirname - and codefile_type(tmp_filename) == "EXEfile" + and codefile_class(tmp_filename) == EXEfile ): parent_exe_dirname = os.path.dirname(tmp_filename) tmp_filename = parents_by_filename[tmp_filename] @@ -595,7 +625,7 @@ def get_linkages( result_pyldd = [] debug = False if not have_lief or debug: - if codefile_type(filename) not in ("DLLfile", "EXEfile"): + if codefile_class(filename) not in (DLLfile, EXEfile): result_pyldd = inspect_linkages_pyldd( filename, resolve_filenames=resolve_filenames, @@ -607,7 +637,7 @@ def get_linkages( return result_pyldd else: print( - f"WARNING: failed to get_linkages, codefile_type('{filename}')={codefile_type(filename)}" + f"WARNING: failed to get_linkages, codefile_class('{filename}')={codefile_class(filename)}" ) return {} result_lief = inspect_linkages_lief( diff --git a/conda_build/os_utils/pyldd.py b/conda_build/os_utils/pyldd.py index 1e1cd4e4cc..42b89711ae 100644 --- a/conda_build/os_utils/pyldd.py +++ b/conda_build/os_utils/pyldd.py @@ -1,5 +1,7 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import argparse import glob import logging @@ -7,9 +9,12 @@ import re import struct import sys +from pathlib import Path from conda_build.utils import ensure_list, get_logger +from ..deprecations import deprecated + logging.basicConfig(level=logging.INFO) @@ -1028,46 +1033,60 @@ def codefile(file, arch="any", initial_rpaths_transitive=[]): return inscrutablefile(file, list(initial_rpaths_transitive)) -def codefile_class(filename, skip_symlinks=False): - if os.path.islink(filename): - if skip_symlinks: - return None - else: - filename = os.path.realpath(filename) - if os.path.isdir(filename): +def codefile_class( + path: str | os.PathLike | Path, + skip_symlinks: bool = False, +) -> type[DLLfile | EXEfile | machofile | elffile] | None: + # same signature as conda.os_utils.liefldd.codefile_class + path = Path(path) + if skip_symlinks and path.is_symlink(): return None - if filename.endswith((".dll", ".pyd")): + path = path.resolve() + + def _get_magic_bit(path: Path) -> bytes: + with path.open("rb") as handle: + bit = handle.read(4) + return struct.unpack(BIG_ENDIAN + "L", bit)[0] + + if path.is_dir(): + return None + elif path.suffix.lower() in (".dll", ".pyd"): return DLLfile - if filename.endswith(".exe"): + elif path.suffix.lower() == ".exe": return EXEfile - # Java .class files share 0xCAFEBABE with Mach-O FAT_MAGIC. - if filename.endswith(".class"): + elif path.suffix.lower() == ".class": + # Java .class files share 0xCAFEBABE with Mach-O FAT_MAGIC. return None - if not os.path.exists(filename) or os.path.getsize(filename) < 4: + elif not path.exists() or path.stat().st_size < 4: + return None + elif (magic := _get_magic_bit(path)) == ELF_HDR: + return elffile + elif magic in (FAT_MAGIC, MH_MAGIC, MH_CIGAM, MH_CIGAM_64): + return machofile + else: return None - with open(filename, "rb") as file: - (magic,) = struct.unpack(BIG_ENDIAN + "L", file.read(4)) - file.seek(0) - if magic in (FAT_MAGIC, MH_MAGIC, MH_CIGAM, MH_CIGAM_64): - return machofile - elif magic == ELF_HDR: - return elffile - return None -def is_codefile(filename, skip_symlinks=True): - klass = codefile_class(filename, skip_symlinks=skip_symlinks) - if not klass: - return False - return True +@deprecated( + "3.28.0", + "4.0.0", + addendum="Use `conda_build.os_utils.pyldd.codefile_class` instead.", +) +def is_codefile(path: str | os.PathLike | Path, skip_symlinks: bool = True) -> bool: + return bool(codefile_class(path, skip_symlinks=skip_symlinks)) -def codefile_type(filename, skip_symlinks=True): - "Returns None, 'machofile' or 'elffile'" - klass = codefile_class(filename, skip_symlinks=skip_symlinks) - if not klass: - return None - return klass.__name__ +@deprecated( + "3.28.0", + "4.0.0", + addendum="Use `conda_build.os_utils.pyldd.codefile_class` instead.", +) +def codefile_type( + path: str | os.PathLike | Path, + skip_symlinks: bool = True, +) -> str | None: + codefile = codefile_class(path, skip_symlinks=skip_symlinks) + return codefile.__name__ if codefile else None def _trim_sysroot(sysroot): diff --git a/conda_build/post.py b/conda_build/post.py index 5a8e5b439e..5cef3beed7 100644 --- a/conda_build/post.py +++ b/conda_build/post.py @@ -53,18 +53,24 @@ have_lief, set_rpath, ) -from conda_build.os_utils.pyldd import codefile_type +from conda_build.os_utils.pyldd import ( + DLLfile, + EXEfile, + codefile_class, + elffile, + machofile, +) filetypes_for_platform = { - "win": ("DLLfile", "EXEfile"), - "osx": ["machofile"], - "linux": ["elffile"], + "win": (DLLfile, EXEfile), + "osx": (machofile,), + "linux": (elffile,), } def fix_shebang(f, prefix, build_python, osx_is_app=False): path = join(prefix, f) - if codefile_type(path): + if codefile_class(path): return elif islink(path): return @@ -405,7 +411,7 @@ def osx_ch_link(path, link_dict, host_prefix, build_prefix, files): ".. seems to be linking to a compiler runtime, replacing build prefix with " "host prefix and" ) - if not codefile_type(link): + if not codefile_class(link): sys.exit( "Error: Compiler runtime library in build prefix not found in host prefix %s" % link @@ -841,7 +847,7 @@ def _collect_needed_dsos( sysroots = list(sysroots_files.keys())[0] for f in files: path = join(run_prefix, f) - if not codefile_type(path): + if not codefile_class(path): continue build_prefix = build_prefix.replace(os.sep, "/") run_prefix = run_prefix.replace(os.sep, "/") @@ -901,10 +907,9 @@ def _map_file_to_package( for subdir2, _, filez in os.walk(prefix): for file in filez: fp = join(subdir2, file) - dynamic_lib = ( - any(fnmatch(fp, ext) for ext in ("*.so*", "*.dylib*", "*.dll")) - and codefile_type(fp, skip_symlinks=False) is not None - ) + dynamic_lib = any( + fnmatch(fp, ext) for ext in ("*.so*", "*.dylib*", "*.dll") + ) and codefile_class(fp, skip_symlinks=False) static_lib = any(fnmatch(fp, ext) for ext in ("*.a", "*.lib")) # Looking at all the files is very slow. if not dynamic_lib and not static_lib: @@ -945,7 +950,7 @@ def _map_file_to_package( ) } all_lib_exports[prefix][rp_po] = exports - # Check codefile_type to filter out linker scripts. + # Check codefile_class to filter out linker scripts. if dynamic_lib: contains_dsos[prefix_owners[prefix][rp_po][0]] = True elif static_lib: @@ -1213,8 +1218,8 @@ def _show_linking_messages( ) for f in files: path = join(run_prefix, f) - filetype = codefile_type(path) - if not filetype or filetype not in filetypes_for_platform[subdir.split("-")[0]]: + codefile = codefile_class(path) + if codefile not in filetypes_for_platform[subdir.split("-")[0]]: continue warn_prelude = "WARNING ({},{})".format(pkg_name, f.replace(os.sep, "/")) err_prelude = " ERROR ({},{})".format(pkg_name, f.replace(os.sep, "/")) @@ -1312,15 +1317,15 @@ def check_overlinking_impl( files_to_inspect = [] filesu = [] - for f in files: - path = join(run_prefix, f) - filetype = codefile_type(path) - if filetype and filetype in filetypes_for_platform[subdir.split("-")[0]]: - files_to_inspect.append(f) - filesu.append(f.replace("\\", "/")) + for file in files: + path = join(run_prefix, file) + codefile = codefile_class(path) + if codefile in filetypes_for_platform[subdir.split("-")[0]]: + files_to_inspect.append(file) + filesu.append(file.replace("\\", "/")) if not files_to_inspect: - return dict() + return {} sysroot_substitution = "$SYSROOT" build_prefix_substitution = "$PATH" @@ -1629,18 +1634,18 @@ def post_process_shared_lib(m, f, files, host_prefix=None): if not host_prefix: host_prefix = m.config.host_prefix path = join(host_prefix, f) - codefile_t = codefile_type(path) - if not codefile_t or path.endswith(".debug"): + codefile = codefile_class(path) + if not codefile or path.endswith(".debug"): return rpaths = m.get_value("build/rpaths", ["lib"]) - if codefile_t == "elffile": + if codefile == elffile: mk_relative_linux( f, host_prefix, rpaths=rpaths, method=m.get_value("build/rpaths_patcher", None), ) - elif codefile_t == "machofile": + elif codefile == machofile: if m.config.host_platform != "osx": log = utils.get_logger(__name__) log.warn( @@ -1730,7 +1735,7 @@ def check_symlinks(files, prefix, croot): # symlinks to binaries outside of the same dir don't work. RPATH stuff gets confused # because ld.so follows symlinks in RPATHS # If condition exists, then copy the file rather than symlink it. - if not dirname(link_path) == dirname(real_link_path) and codefile_type(f): + if not dirname(link_path) == dirname(real_link_path) and codefile_class(f): os.remove(path) utils.copy_into(real_link_path, path) elif real_link_path.startswith(real_build_prefix): diff --git a/conda_build/render.py b/conda_build/render.py index 881898dc9d..fa428e07f6 100644 --- a/conda_build/render.py +++ b/conda_build/render.py @@ -384,7 +384,7 @@ def execute_download_actions(m, actions, env, package_subset=None, require_files with utils.LoggingContext(): pfe.execute() for pkg_dir in pkgs_dirs: - _loc = os.path.join(pkg_dir, index[pkg].fn) + _loc = os.path.join(pkg_dir, index.get(pkg, pkg).fn) if os.path.isfile(_loc): pkg_loc = _loc break diff --git a/conda_build/utils.py b/conda_build/utils.py index 321d3f0dd0..a76998de35 100644 --- a/conda_build/utils.py +++ b/conda_build/utils.py @@ -102,9 +102,10 @@ FileNotFoundError = FileNotFoundError on_win = sys.platform == "win32" +on_mac = sys.platform == "darwin" +on_linux = sys.platform == "linux" codec = getpreferredencoding() or "utf-8" -on_win = sys.platform == "win32" root_script_dir = os.path.join(root_dir, "Scripts" if on_win else "bin") mmap_MAP_PRIVATE = 0 if on_win else mmap.MAP_PRIVATE mmap_PROT_READ = 0 if on_win else mmap.PROT_READ diff --git a/docs/source/resources/compiler-tools.rst b/docs/source/resources/compiler-tools.rst index d206d1c947..d4832b5a0c 100644 --- a/docs/source/resources/compiler-tools.rst +++ b/docs/source/resources/compiler-tools.rst @@ -394,6 +394,71 @@ not available. You'd need to create a metapackage ``m2w64-gcc_win-64`` to point at the ``m2w64-gcc`` package, which does exist on the msys2 channel on `repo.anaconda.com `_. +Expressing the relation between compiler and its standard library +================================================================= + +For most languages, certainly for "c" and for "cxx", compiling any given +program *may* create a run-time dependence on symbols from the respective +standard library. For example, the standard library for C on linux is generally +``glibc``, and a core component of your operating system. Conda is not able to +change or supersede this library (it would be too risky to try to). A similar +situation exists on MacOS and on Windows. + +Compiler packages usually have two ways to deal with this dependence: + +* assume the package must be there (like ``glibc`` on linux). +* always add a run-time requirement on the respective stdlib (e.g. ``libcxx`` + on MacOS). + +However, even if we assume the package must be there, the information about the +``glibc`` version is still a highly relevant piece of information, which is +also why it is reflected in the ``__glibc`` +`virtual package `_. + +For example, newer packages may decide over time to increase the lowest version +of ``glibc`` that they support. We therefore need a way to express this +dependence in a way that conda will be able to understand, so that (in +conjunction with the ``__glibc`` virtual package) the environment resolver will +not consider those packages on machines whose ``glibc`` version is too old. + +The way to do this is to use the Jinja2 function ``{{ stdlib('c') }}``, which +matches ``{{ compiler('c') }}`` in as many ways as possible. Let's start again +with the ``conda_build_config.yaml``:: + + c_stdlib: + - sysroot # [linux] + - macosx_deployment_target # [osx] + c_stdlib_version: + - 2.17 # [linux] + - 10.13 # [osx] + +In the recipe we would then use:: + + requirements: + build: + - {{ compiler('c') }} + - {{ stdlib('c') }} + +This would then express that the resulting package requires ``sysroot ==2.17`` +(corresponds to ``glibc``) on linux and ``macosx_deployment_target ==10.13`` on +MacOS in the build environment, respectively. How this translates into a +run-time dependence can be defined in the metadata of the respective conda +(meta-)package which represents the standard library (i.e. those defined under +``c_stdlib`` above). + +In this example, ``sysroot 2.17`` would generate a run-export on +``__glibc >=2.17`` and ``macosx_deployment_target 10.13`` would similarly +generate ``__osx >=10.13``. This way, we enable packages to define their own +expectations about the standard library in a unified way, and without +implicitly depending on some global assumption about what the lower version +on a given platform must be. + +In principle, this facility would make it possible to also express the +dependence on separate stdlib implementations (like ``musl`` instead of +``glibc``), or to remove the need to assume that a C++ compiler always needs to +add a run-export on the C++ stdlib -- it could then be left up to packages +themselves whether they need ``{{ stdlib('cxx') }}`` or not. + Anaconda compilers implicitly add RPATH pointing to the conda environment ========================================================================= diff --git a/news/5037-conda-libmamba-solver-pins b/news/5037-conda-libmamba-solver-pins new file mode 100644 index 0000000000..d4044fac0f --- /dev/null +++ b/news/5037-conda-libmamba-solver-pins @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* Fallback to solved record filename to find the downloaded tarball in `get_upstream_pins`. (#4991 via #5037) + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/news/5040-codefile b/news/5040-codefile new file mode 100644 index 0000000000..c4f85ca7cf --- /dev/null +++ b/news/5040-codefile @@ -0,0 +1,21 @@ +### Enhancements + +* + +### Bug fixes + +* + +### Deprecations + +* Mark `conda_build.os_utils.pyldd.is_string` as pending deprecation. Use `isinstance(value, str)` instead. (#5040) +* Mark `conda_build.os_utils.pyldd.is_codefile` as pending deprecation. Use `conda_build.os_utils.pyldd.codefile_class` instead. (#5040) +* Mark `conda_build.os_utils.pyldd.codefile_type` as pending deprecation. Use `conda_build.os_utils.pyldd.codefile_class` instead. (#5040) + +### Docs + +* + +### Other + +* diff --git a/tests/data/ldd/clear.elf b/tests/data/ldd/clear.elf new file mode 100755 index 0000000000..52013aa3ee Binary files /dev/null and b/tests/data/ldd/clear.elf differ diff --git a/tests/data/ldd/clear.exe b/tests/data/ldd/clear.exe new file mode 100644 index 0000000000..bd7543feba Binary files /dev/null and b/tests/data/ldd/clear.exe differ diff --git a/tests/data/ldd/clear.macho b/tests/data/ldd/clear.macho new file mode 100755 index 0000000000..8de24d5608 Binary files /dev/null and b/tests/data/ldd/clear.macho differ diff --git a/tests/data/ldd/jansi.dll b/tests/data/ldd/jansi.dll new file mode 100755 index 0000000000..81433eef25 Binary files /dev/null and b/tests/data/ldd/jansi.dll differ diff --git a/tests/data/ldd/uuid.pyd b/tests/data/ldd/uuid.pyd new file mode 100644 index 0000000000..f99ad10b9b Binary files /dev/null and b/tests/data/ldd/uuid.pyd differ diff --git a/tests/os_utils/test_codefile.py b/tests/os_utils/test_codefile.py new file mode 100644 index 0000000000..a3e38342da --- /dev/null +++ b/tests/os_utils/test_codefile.py @@ -0,0 +1,40 @@ +# Copyright (C) 2014 Anaconda, Inc +# SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + +from pathlib import Path +from typing import Callable + +import pytest + +from conda_build.os_utils.liefldd import codefile_class as liefldd_codefile_class +from conda_build.os_utils.pyldd import DLLfile, EXEfile, elffile, machofile +from conda_build.os_utils.pyldd import codefile_class as pyldd_codefile_class + +LDD = Path(__file__).parent.parent / "data" / "ldd" + + +@pytest.mark.parametrize( + "path,expect", + [ + pytest.param(__file__, None, id="Unknown"), + pytest.param(LDD / "jansi.dll", DLLfile, id="DLL"), + pytest.param(LDD / "uuid.pyd", DLLfile, id="PYD"), + pytest.param(LDD / "clear.exe", EXEfile, id="EXE"), + pytest.param(LDD / "clear.macho", machofile, id="MACHO"), + pytest.param(LDD / "clear.elf", elffile, id="ELF"), + ], +) +@pytest.mark.parametrize( + "codefile_class", + [ + pytest.param(pyldd_codefile_class, id="pyldd"), + pytest.param(liefldd_codefile_class, id="liefldd"), + ], +) +def test_codefile_class( + path: str | Path, + expect: type[DLLfile | EXEfile | machofile | elffile] | None, + codefile_class: Callable, +): + assert codefile_class(path) == expect diff --git a/tests/test-recipes/metadata/_stdlib_jinja2/conda_build_config.yaml b/tests/test-recipes/metadata/_stdlib_jinja2/conda_build_config.yaml new file mode 100644 index 0000000000..a6ac88cd33 --- /dev/null +++ b/tests/test-recipes/metadata/_stdlib_jinja2/conda_build_config.yaml @@ -0,0 +1,8 @@ +c_stdlib: # [unix] + - sysroot # [linux] + - macosx_deployment_target # [osx] +c_stdlib_version: # [unix] + - 2.12 # [linux64] + - 2.17 # [aarch64 or ppc64le] + - 10.13 # [osx and x86_64] + - 11.0 # [osx and arm64] diff --git a/tests/test-recipes/metadata/_stdlib_jinja2/meta.yaml b/tests/test-recipes/metadata/_stdlib_jinja2/meta.yaml new file mode 100644 index 0000000000..c655aac2ca --- /dev/null +++ b/tests/test-recipes/metadata/_stdlib_jinja2/meta.yaml @@ -0,0 +1,9 @@ +package: + name: stdlib-test + version: 1.0 + +requirements: + host: + - {{ stdlib('c') }} + # - {{ stdlib('cxx') }} + # - {{ stdlib('fortran') }} diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 37319f0de4..e122b45b4b 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -223,6 +223,33 @@ def test_compiler_metadata_cross_compiler(): ) +@pytest.mark.parametrize( + "platform,arch,stdlibs", + [ + ("linux", "64", {"sysroot_linux-64 2.12.*"}), + ("linux", "aarch64", {"sysroot_linux-aarch64 2.17.*"}), + ("osx", "64", {"macosx_deployment_target_osx-64 10.13.*"}), + ("osx", "arm64", {"macosx_deployment_target_osx-arm64 11.0.*"}), + ], +) +def test_native_stdlib_metadata( + platform: str, arch: str, stdlibs: set[str], testing_config +): + testing_config.platform = platform + metadata = api.render( + os.path.join(metadata_dir, "_stdlib_jinja2"), + config=testing_config, + variants={"target_platform": f"{platform}-{arch}"}, + platform=platform, + arch=arch, + permit_unsatisfiable_variants=True, + finalize=False, + bypass_env_check=True, + python="3.11", # irrelevant + )[0][0] + assert stdlibs <= set(metadata.meta["requirements"]["host"]) + + def test_hash_build_id(testing_metadata): testing_metadata.config.variant["zlib"] = "1.2" testing_metadata.meta["requirements"]["host"] = ["zlib"]