def apply_dep_update(recipe_dir, dep_comparison, update_python=False): """Upodate a recipe given a dependency comparison. Parameters ---------- recipe_dir : str The path to the recipe dir. dep_comparison : dict The dependency comparison. update_python : bool, optional If True, update python deps. Default is False. Returns ------- update_deps : bool True if deps were updated, False otherwise. """ recipe_pth = os.path.join(recipe_dir, "meta.yaml") with open(recipe_pth) as fp: lines = fp.readlines() if _ok_for_dep_updates(lines) and any( len(dep_comparison.get(s, {}).get("df_minus_cf", set())) > 0 for s in SECTIONS_TO_UPDATE): recipe = CondaMetaYAML("".join(lines)) updated_deps = _update_sec_deps( recipe, dep_comparison, SECTIONS_TO_UPDATE, ) if updated_deps: with open(recipe_pth, "w") as fp: recipe.dump(fp)
def _scrape_license_string(pkg): d = {} if pkg.startswith("r-"): pkg = pkg[2:] LOGGER.info("LICENSE running cran skeleton for pkg %s" % pkg) with tempfile.TemporaryDirectory() as tmpdir, indir(tmpdir): subprocess.run( [ "conda", "skeleton", "cran", "--allow-archived", "--use-noarch-generic", pkg, ], check=True, ) with open("r-%s/meta.yaml" % pkg) as fp: in_about = False meta_yaml = [] for line in fp.readlines(): if line.startswith("about:"): in_about = True elif line.startswith("extra:"): in_about = False if in_about: meta_yaml.append(line) if line.startswith("# License:"): d["cran_license"] = line[len("# License:"):].strip() cmeta = CondaMetaYAML("".join(meta_yaml)) d["license_file"] = [ lf for lf in cmeta.meta.get("about", {}).get("license_file", []) ] if len(d["license_file"]) == 0: d["license_file"] = None if "cran_license" in d: spdx = _munge_licenses([d["cran_license"]]) if "(Restricts use)" in cmeta.meta.get("about", {}).get("license", ""): if len(spdx) > 1: spdx = ["("] + spdx + [")", " AND ", "LicenseRef-RestrictsUse"] else: spdx = spdx + [" AND ", "LicenseRef-RestrictsUse"] d["spdx_license"] = "".join(spdx) else: d["spdx_license"] = None if d: return d else: return None
def _do_r_license_munging(pkg, recipe_dir): try: d = _scrape_license_string(pkg) LOGGER.info("LICENSE R package license data: %s" % d) with open(os.path.join(recipe_dir, "meta.yaml")) as fp: cmeta = CondaMetaYAML(fp.read()) if d["license_file"] is not None: cmeta.meta["about"]["license_file"] = d["license_file"] if d["spdx_license"] is not None: cmeta.meta["about"]["license"] = d["spdx_license"] elif d["cran_license"] is not None: cmeta.meta["about"]["license"] = d["cran_license"] with open(os.path.join(recipe_dir, "meta.yaml"), "w") as fp: cmeta.dump(fp) except Exception as e: LOGGER.info("LICENSE R license ERROR: %s" % repr(e)) pass
def test_update_run_deps(): with open( os.path.join(os.path.dirname(__file__), "test_yaml", "depfinder.json"), ) as f: attrs = load(f) d, rs = get_grayskull_comparison(attrs) lines = attrs["raw_meta_yaml"].splitlines() lines = [ln + "\n" for ln in lines] recipe = CondaMetaYAML("".join(lines)) updated_deps = _update_sec_deps(recipe, d, ["host", "run"], update_python=False) print("\n" + recipe.dumps()) assert not updated_deps assert "python <3.9" in recipe.dumps() updated_deps = _update_sec_deps(recipe, d, ["host", "run"], update_python=True) print("\n" + recipe.dumps()) assert updated_deps assert "python >=3.6" in recipe.dumps()
def test_parsing(add_extra_req): meta_yaml = """\ {% set name = 'val1' %} # [py2k] {% set name = 'val2' %}#[py3k and win] {% set version = '4.5.6' %} {% set major_ver = version.split('.')[0] %} {% set bad_ver = bad_version.split('.')[0] %} {% set vmajor,vminor,vpatch = version.split('.') %} {% set crazy_string1 = ">=5,<7" ~ version %} {% set crazy_string2 = '"' ~ version %} {% set crazy_string3 = "'" ~ version %} {% set crazy_string4 = "|" ~ version %} {% set build = 0 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5#[py3k and win] build: number: 10 """ if add_extra_req: meta_yaml += """\ requirements: host: - blah <{{ blarg }} """ meta_yaml_canonical = """\ {% set name = "val1" %} # [py2k] {% set name = "val2" %} # [py3k and win] {% set version = "4.5.6" %} {% set major_ver = version.split('.')[0] %} {% set bad_ver = bad_version.split('.')[0] %} {% set vmajor,vminor,vpatch = version.split('.') %} {% set crazy_string1 = ">=5,<7" ~ version %} {% set crazy_string2 = '"' ~ version %} {% set crazy_string3 = "'" ~ version %} {% set crazy_string4 = "|" ~ version %} {% set build = 0 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5 # [py3k and win] build: number: 10 """ if add_extra_req: meta_yaml_canonical += """\ requirements: host: - blah <{{ blarg }} """ cm = CondaMetaYAML(meta_yaml) # check the jinja2 keys assert cm.jinja2_vars["name__###conda-selector###__py2k"] == "val1" assert cm.jinja2_vars["name__###conda-selector###__py3k and win"] == "val2" assert cm.jinja2_vars["version"] == "4.5.6" # check jinja2 expressions assert cm.jinja2_exprs[ "major_ver"] == "{% set major_ver = version.split('.')[0] %}" assert cm.jinja2_exprs[ "bad_ver"] == "{% set bad_ver = bad_version.split('.')[0] %}" assert (cm.jinja2_exprs["vmajor"] == "{% set vmajor,vminor,vpatch = version.split('.') %}") assert (cm.jinja2_exprs["vminor"] == "{% set vmajor,vminor,vpatch = version.split('.') %}") assert (cm.jinja2_exprs["vpatch"] == "{% set vmajor,vminor,vpatch = version.split('.') %}") assert (cm.jinja2_exprs["crazy_string1"] == '{% set crazy_string1 = ">=5,<7" ~ version %}') assert (cm.jinja2_exprs["crazy_string2"] == "{% set crazy_string2 = '\"' ~ version %}") assert (cm.jinja2_exprs["crazy_string3"] == '{% set crazy_string3 = "\'" ~ version %}') assert cm.jinja2_exprs[ "crazy_string4"] == '{% set crazy_string4 = "|" ~ version %}' # check it when we eval jinja2_exprs_evaled = cm.eval_jinja2_exprs(cm.jinja2_vars) assert jinja2_exprs_evaled["major_ver"] == "4" assert jinja2_exprs_evaled["vmajor"] == "4" assert jinja2_exprs_evaled["vminor"] == "5" assert jinja2_exprs_evaled["vpatch"] == "6" assert jinja2_exprs_evaled["crazy_string1"] == ">=5,<74.5.6" assert jinja2_exprs_evaled["crazy_string2"] == '"4.5.6' assert jinja2_exprs_evaled["crazy_string3"] == "'4.5.6" assert jinja2_exprs_evaled["crazy_string4"] == "|4.5.6" # check selectors assert cm.meta["source"]["sha256__###conda-selector###__py2k"] == 1 assert cm.meta["source"]["sha256__###conda-selector###__py3k and win"] == 5 # check other keys assert cm.meta["build"]["number"] == 10 assert cm.meta["package"]["name"] == "{{ name|lower }}" assert cm.meta["source"]["url"] == "foobar" if add_extra_req: assert cm.meta["requirements"]["host"][0] == "blah <{{ blarg }}" s = io.StringIO() cm.dump(s) s.seek(0) assert meta_yaml_canonical == s.read() # now add stuff and test outputs cm.jinja2_vars["foo"] = "bar" cm.jinja2_vars["xfoo__###conda-selector###__win or osx"] = 10 cm.jinja2_vars["build"] = 100 cm.meta["about"] = 10 cm.meta["extra__###conda-selector###__win"] = "blah" cm.meta["extra__###conda-selector###__not win"] = "not_win_blah" s = io.StringIO() cm.dump(s) s.seek(0) new_meta_yaml = s.read() true_new_meta_yaml = """\ {% set foo = "bar" %} {% set xfoo = 10 %} # [win or osx] {% set name = "val1" %} # [py2k] {% set name = "val2" %} # [py3k and win] {% set version = "4.5.6" %} {% set major_ver = version.split('.')[0] %} {% set bad_ver = bad_version.split('.')[0] %} {% set vmajor,vminor,vpatch = version.split('.') %} {% set crazy_string1 = ">=5,<7" ~ version %} {% set crazy_string2 = '"' ~ version %} {% set crazy_string3 = "'" ~ version %} {% set crazy_string4 = "|" ~ version %} {% set build = 100 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5 # [py3k and win] build: number: 10 """ if add_extra_req: true_new_meta_yaml += """\ requirements: host: - blah <{{ blarg }} """ true_new_meta_yaml += """\ about: 10 extra: blah # [win] extra: not_win_blah # [not win] """ assert new_meta_yaml == true_new_meta_yaml
def migrate( self, recipe_dir: str, attrs: "AttrsTypedDict", hash_type: str = "sha256", **kwargs: Any, ) -> "MigrationUidTypedDict": errors = set() version = attrs["new_version"] # record the attempt if "new_version_attempts" not in attrs: attrs["new_version_attempts"] = {} if version not in attrs["new_version_attempts"]: attrs["new_version_attempts"][version] = 0 attrs["new_version_attempts"][version] += 1 if "new_version_errors" not in attrs: attrs["new_version_errors"] = {} if not isinstance(version, str): errors.add( "the version '%s' is not a string and must be for the bot" % version, ) attrs["new_version_errors"][version] = _fmt_error_message( errors, version) return {} try: with open(os.path.join(recipe_dir, "meta.yaml"), "r") as fp: cmeta = CondaMetaYAML(fp.read()) except Exception as e: attrs["new_version_errors"][ version] = "We found a problem parsing the recipe: \n\n" + str( e) return {} # cache round-tripped yaml for testing later s = io.StringIO() cmeta.dump(s) s.seek(0) old_meta_yaml = s.read() # if is a git url, then we error if _recipe_has_git_url(cmeta): logger.critical("Migrations do not work on `git_url`s!") errors.add("migrations do not work on `git_url`s") attrs["new_version_errors"][version] = _fmt_error_message( errors, version) return {} # mangle the version if it is R r_url = False for src_key in _gen_key_selector(cmeta.meta, "source"): r_url |= _has_r_url(cmeta.meta[src_key]) for key, val in cmeta.jinja2_vars.items(): if isinstance(val, str): r_url |= _is_r_url(val) if r_url: version = version.replace("_", "-") # replace the version if "version" in cmeta.jinja2_vars: # cache old version for testing later old_version = cmeta.jinja2_vars["version"] cmeta.jinja2_vars["version"] = version else: logger.critical( "Migrations do not work on versions not specified with jinja2!", ) errors.add( "migrations do not work on versions not specified with jinja2") attrs["new_version_errors"][version] = _fmt_error_message( errors, version) return {} if len(list(_gen_key_selector(cmeta.meta, "source"))) > 0: did_update = True for src_key in _gen_key_selector(cmeta.meta, "source"): if isinstance(cmeta.meta[src_key], collections.abc.MutableSequence): for src in cmeta.meta[src_key]: _did_update, _errors = _try_to_update_version( cmeta, src, hash_type, ) did_update &= _did_update errors |= _errors else: _did_update, _errors = _try_to_update_version( cmeta, cmeta.meta[src_key], hash_type, ) did_update &= _did_update errors |= _errors else: did_update = False errors.add("no source sections found in the recipe") if did_update: # if the yaml did not change, then we did not migrate actually cmeta.jinja2_vars["version"] = old_version s = io.StringIO() cmeta.dump(s) s.seek(0) still_the_same = s.read() == old_meta_yaml cmeta.jinja2_vars["version"] = version # put back version if still_the_same and old_version != version: did_update = False errors.add( "recipe did not appear to change even " "though the bot said it should have", ) logger.critical( "Recipe did not change in version migration " "but the code indicates an update was done!", ) if did_update: with indir(recipe_dir): with open("meta.yaml", "w") as fp: cmeta.dump(fp) self.set_build_number("meta.yaml") return super().migrate(recipe_dir, attrs) else: logger.critical("Recipe did not change in version migration!") attrs["new_version_errors"][version] = _fmt_error_message( errors, version) return {}
def migrate( self, recipe_dir: str, attrs: "AttrsTypedDict", hash_type: str = "sha256", **kwargs: Any, ) -> "MigrationUidTypedDict": with open(os.path.join(recipe_dir, "meta.yaml"), "r") as fp: cmeta = CondaMetaYAML(fp.read()) # cache round-tripped yaml for testing later s = io.StringIO() cmeta.dump(s) s.seek(0) old_meta_yaml = s.read() version = attrs["new_version"] assert isinstance(version, str) # if is a git url, then we error if _recipe_has_git_url(cmeta): logger.critical("Migrations do not work on `git_url`s!") return {} # mangle the version if it is R r_url = False for src_key in _gen_key_selector(cmeta.meta, "source"): r_url |= _has_r_url(cmeta.meta[src_key]) for key, val in cmeta.jinja2_vars.items(): if isinstance(val, str): r_url |= _is_r_url(val) if r_url: version = version.replace("_", "-") # replace the version if "version" in cmeta.jinja2_vars: # cache old version for testing later old_version = cmeta.jinja2_vars["version"] cmeta.jinja2_vars["version"] = version else: logger.critical( "Migrations do not work on versions " "not specified with jinja2!" ) return {} if len(list(_gen_key_selector(cmeta.meta, "source"))) > 0: did_update = True for src_key in _gen_key_selector(cmeta.meta, "source"): if isinstance(cmeta.meta[src_key], collections.abc.MutableSequence): for src in cmeta.meta[src_key]: did_update &= _try_to_update_version(cmeta, src, hash_type) else: did_update &= _try_to_update_version( cmeta, cmeta.meta[src_key], hash_type, ) else: did_update = False if did_update: # if the yaml did not change, then we did not migrate actually cmeta.jinja2_vars["version"] = old_version s = io.StringIO() cmeta.dump(s) s.seek(0) still_the_same = s.read() == old_meta_yaml cmeta.jinja2_vars["version"] = version # put back version if still_the_same and old_version != version: did_update = False logger.critical( "Recipe did not change in version migration " "but the code indicates an update was done!" ) if did_update: with indir(recipe_dir): with open("meta.yaml", "w") as fp: cmeta.dump(fp) self.set_build_number("meta.yaml") return super().migrate(recipe_dir, attrs) else: logger.critical("Recipe did not change in version migration!") return {}
def test_parsing(): meta_yaml = """\ {% set name = 'val1' %} # [py2k] {% set name = 'val2' %}#[py3k and win] {% set version = '4.5.6' %} {% set build = 0 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5#[py3k and win] build: number: 10 """ meta_yaml_canonical = """\ {% set name = "val1" %} # [py2k] {% set name = "val2" %} # [py3k and win] {% set version = "4.5.6" %} {% set build = 0 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5 # [py3k and win] build: number: 10 """ cm = CondaMetaYAML(meta_yaml) # check the jinja2 keys assert cm.jinja2_vars['name__###conda-selector###__py2k'] == 'val1' assert cm.jinja2_vars['name__###conda-selector###__py3k and win'] == 'val2' assert cm.jinja2_vars['version'] == '4.5.6' # check selectors assert cm.meta['source']['sha256__###conda-selector###__py2k'] == 1 assert cm.meta['source']['sha256__###conda-selector###__py3k and win'] == 5 # check other keys assert cm.meta['build']['number'] == 10 assert cm.meta['package']['name'] == '{{ name|lower }}' assert cm.meta['source']['url'] == 'foobar' s = io.StringIO() cm.dump(s) s.seek(0) assert meta_yaml_canonical == s.read() # now add stuff and test outputs cm.jinja2_vars['foo'] = 'bar' cm.jinja2_vars['xfoo__###conda-selector###__win or osx'] = 10 cm.jinja2_vars['build'] = 100 cm.meta['about'] = 10 cm.meta['requirements__###conda-selector###__win'] = 'blah' cm.meta['requirements__###conda-selector###__not win'] = 'not_win_blah' s = io.StringIO() cm.dump(s) s.seek(0) new_meta_yaml = s.read() assert new_meta_yaml == """\
def test_recipe_parses_islpy(): meta_yaml_ok = """\ {% set name = "islpy" %} {% set version = "2020.2.2" %} {% set sha256 = "7eb7dfa41d6a67d9ee4ea4bb9f08bdbcbee42b364502136b7882cfd80ff427e0" %} package: name: {{ name|lower }} version: {{ version }} source: url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz sha256: {{ sha256 }} build: number: 0 requirements: build: - python # [build_platform != target_platform] - cross-python_{{ target_platform }} # [build_platform != target_platform] - pybind11 # [build_platform != target_platform] - {{ compiler('cxx') }} host: - python - setuptools - six - pybind11 - isl run: - python - six # Need the same version of isl we had when the package was built - {{ pin_compatible("isl", max_pin="x.x.x") }} test: requires: - pytest imports: - islpy source_files: - test commands: - cd test && python -m pytest about: home: http://github.com/inducer/islpy license: MIT license_file: - doc/misc.rst license_family: MIT summary: Wrapper around isl, an integer set library description: | islpy is a Python wrapper around Sven Verdoolaege's [isl](http://www.kotnet.org/~skimo/isl/), a library for manipulating sets and relations of integer points bounded by linear constraints. Supported operations on sets include - intersection, union, set difference, - emptiness check, - convex hull, - (integer) affine hull, - integer projection, - computing the lexicographic minimum using parametric integer programming, - coalescing, and - parametric vertex enumeration. It also includes an ILP solver based on generalized basis reduction, transitive closures on maps (which may encode infinite graphs), dependence analysis and bounds on piecewise step-polynomials. extra: recipe-maintainers: - inducer """ # noqa meta_yaml_notok = """\ {% set name = "islpy" %} {% set version = "2020.2.2" %} {% set sha256 = "7eb7dfa41d6a67d9ee4ea4bb9f08bdbcbee42b364502136b7882cfd80ff427e0" %} package: name: {{ name|lower }} version: {{ version }} source: url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz sha256: {{ sha256 }} build: number: 0 requirements: build: - python # [build_platform != target_platform] - cross-python_{{ target_platform }} # [build_platform != target_platform] - pybind11 # [build_platform != target_platform] - {{ compiler('cxx') }} host: - python - setuptools - six - pybind11 - isl run: - python - six # Need the same version of isl we had when the package was built - {{ pin_compatible("isl", max_pin="x.x.x") }} test: requires: - pytest imports: - islpy source_files: - test commands: - cd test && python -m pytest about: home: http://github.com/inducer/islpy license: MIT license_file: - doc/misc.rst license_family: MIT summary: Wrapper around isl, an integer set library description: | islpy is a Python wrapper around Sven Verdoolaege's [isl](http://www.kotnet.org/~skimo/isl/), a library for manipulating sets and relations of integer points bounded by linear constraints. Supported operations on sets include - intersection, union, set difference, - emptiness check, - convex hull, - (integer) affine hull, - integer projection, - computing the lexicographic minimum using parametric integer programming, - coalescing, and - parametric vertex enumeration. It also includes an ILP solver based on generalized basis reduction, transitive closures on maps (which may encode infinite graphs), dependence analysis and bounds on piecewise step-polynomials. extra: recipe-maintainers: - inducer """ # noqa cm = CondaMetaYAML(meta_yaml_ok) s = io.StringIO() cm.dump(s) s.seek(0) assert meta_yaml_ok == s.read() cm = CondaMetaYAML(meta_yaml_notok) s = io.StringIO() cm.dump(s) s.seek(0) assert meta_yaml_notok != s.read()
def test_recipe_parses_fftw(): recipe = """\ {% set version = "3.3.9" %} {% set build = 1 %} # ensure mpi is defined (needed for conda-smithy recipe-lint) {% set mpi = mpi or 'nompi' %} package: name: fftw version: {{ version }} source: fn: fftw-{{ version }}.tar.gz url: http://www.fftw.org/fftw-{{ version }}.tar.gz sha256: bf2c7ce40b04ae811af714deb512510cc2c17b9ab9d6ddcf49fe4487eea7af3d build: # prioritize nompi variant via build number {% if mpi == 'nompi' %} {% set build = build + 100 %} {% endif %} number: {{ build }} # add build string so packages can depend on # mpi or nompi variants explicitly: # `pkg * mpi_mpich_*` for mpich # `pkg * mpi_*` for any mpi # `pkg * nompi_*` for no mpi {% if mpi != 'nompi' %} {% set mpi_prefix = "mpi_" + mpi %} {% else %} {% set mpi_prefix = "nompi" %} {% endif %} string: "{{ mpi_prefix }}_h{{ PKG_HASH }}_{{ build }}" run_exports: - {{ pin_compatible('fftw', max_pin='x') }} {% if mpi != 'nompi' %} - fftw * {{ mpi_prefix }}_* {% endif %} requirements: build: - perl 5.* # [not win] - cmake # [win] - {{ compiler('c') }} - {{ compiler('fortran') }} # [not win] - llvm-openmp >=4.0.1 # [osx] - make # [unix] - autoconf # [unix] - automake # [unix] - gettext # [unix] - m4 # [unix] - libtool # [unix] - {{ mpi }} # [build_platform != target_platform and mpi == 'openmpi'] host: - {{ mpi }} # [mpi != 'nompi'] - llvm-openmp >=4.0.1 # [osx] run: - llvm-openmp >=4.0.1 # [osx] test: requires: - python commands: # Verify library contains Fortran symbols - strings ${PREFIX}/lib/libfftw3.a | grep -q dfftw || exit 1 # [not win] # Verify existence of library files - test -f ${PREFIX}/lib/libfftw3f.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3l.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3f_threads.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3_threads.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3l_threads.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3f_omp.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3_omp.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3l_omp.a || exit 1 # [not win] # Verify headers are installed - test -f ${PREFIX}/include/fftw3.h || exit 1 # [not win] - if not exist %LIBRARY_INC%\\fftw3.h exit 1 # [win] # Verify shared libraries are installed {% set fftw_libs = [ "libfftw3", "libfftw3_threads", "libfftw3f", "libfftw3f_threads", "libfftw3l", "libfftw3l_threads", ] %} {% set fftw_omp_libs = [ "libfftw3_omp", "libfftw3f_omp", "libfftw3l_omp", ] %} {% set fftw_mpi_libs = [ "libfftw3_mpi", "libfftw3f_mpi", "libfftw3l_mpi", ] %} {% for lib in fftw_libs %} - python -c "import ctypes; ctypes.cdll[r'${PREFIX}/lib/{{ lib }}${SHLIB_EXT}']" # [unix] {% endfor %} {% for lib in fftw_omp_libs %} - python -c "import ctypes; ctypes.cdll[r'${PREFIX}/lib/{{ lib }}${SHLIB_EXT}']" # [unix] {% endfor %} {% if mpi != 'nompi' %} {% for lib in fftw_mpi_libs %} # you need to link to the mpi libs to load the dll, so we just test # if it exists - test -f ${PREFIX}/lib/{{ lib }}${SHLIB_EXT} || exit 1 # [unix] {% endfor %} {% endif %} {% set fftw_libs = ["fftw3f", "fftw3"] %} {% for base in fftw_libs %} - if not exist %LIBRARY_LIB%\\{{ base }}.lib exit 1 # [win] - if not exist %LIBRARY_BIN%\\{{ base }}.dll exit 1 # [win] {% endfor %} about: home: http://fftw.org license: GPL-2.0-or-later license_file: COPYING summary: "The fastest Fourier transform in the west." extra: recipe-maintainers: - alexbw - jakirkham - grlee77 - jschueller - egpbos """ # noqa recipe_parsed = """\ {% set version = "3.3.9" %} {% set build = 1 %} # ensure mpi is defined (needed for conda-smithy recipe-lint) {% set mpi = mpi or 'nompi' %} package: name: fftw version: {{ version }} source: fn: fftw-{{ version }}.tar.gz url: http://www.fftw.org/fftw-{{ version }}.tar.gz sha256: bf2c7ce40b04ae811af714deb512510cc2c17b9ab9d6ddcf49fe4487eea7af3d build: # prioritize nompi variant via build number {% if mpi == 'nompi' %} {% set build = build + 100 %} {% endif %} number: {{ build }} # add build string so packages can depend on # mpi or nompi variants explicitly: # `pkg * mpi_mpich_*` for mpich # `pkg * mpi_*` for any mpi # `pkg * nompi_*` for no mpi {% if mpi != 'nompi' %} {% set mpi_prefix = "mpi_" + mpi %} {% else %} {% set mpi_prefix = "nompi" %} {% endif %} string: {{ mpi_prefix }}_h{{ PKG_HASH }}_{{ build }} run_exports: - {{ pin_compatible('fftw', max_pin='x') }} {% if mpi != 'nompi' %} - fftw * {{ mpi_prefix }}_* {% endif %} requirements: build: - perl 5.* # [not win] - cmake # [win] - {{ compiler('c') }} - {{ compiler('fortran') }} # [not win] - llvm-openmp >=4.0.1 # [osx] - make # [unix] - autoconf # [unix] - automake # [unix] - gettext # [unix] - m4 # [unix] - libtool # [unix] - {{ mpi }} # [build_platform != target_platform and mpi == 'openmpi'] host: - {{ mpi }} # [mpi != 'nompi'] - llvm-openmp >=4.0.1 # [osx] run: - llvm-openmp >=4.0.1 # [osx] test: requires: - python commands: # Verify library contains Fortran symbols - strings ${PREFIX}/lib/libfftw3.a | grep -q dfftw || exit 1 # [not win] # Verify existence of library files - test -f ${PREFIX}/lib/libfftw3f.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3l.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3f_threads.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3_threads.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3l_threads.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3f_omp.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3_omp.a || exit 1 # [not win] - test -f ${PREFIX}/lib/libfftw3l_omp.a || exit 1 # [not win] # Verify headers are installed - test -f ${PREFIX}/include/fftw3.h || exit 1 # [not win] - if not exist %LIBRARY_INC%\\fftw3.h exit 1 # [win] # Verify shared libraries are installed {% set fftw_libs = [ "libfftw3", "libfftw3_threads", "libfftw3f", "libfftw3f_threads", "libfftw3l", "libfftw3l_threads", ] %} {% set fftw_omp_libs = [ "libfftw3_omp", "libfftw3f_omp", "libfftw3l_omp", ] %} {% set fftw_mpi_libs = [ "libfftw3_mpi", "libfftw3f_mpi", "libfftw3l_mpi", ] %} {% for lib in fftw_libs %} - python -c "import ctypes; ctypes.cdll[r'${PREFIX}/lib/{{ lib }}${SHLIB_EXT}']" # [unix] {% endfor %} {% for lib in fftw_omp_libs %} - python -c "import ctypes; ctypes.cdll[r'${PREFIX}/lib/{{ lib }}${SHLIB_EXT}']" # [unix] {% endfor %} {% if mpi != 'nompi' %} {% for lib in fftw_mpi_libs %} # you need to link to the mpi libs to load the dll, so we just test # if it exists - test -f ${PREFIX}/lib/{{ lib }}${SHLIB_EXT} || exit 1 # [unix] {% endfor %} {% endif %} {% set fftw_libs = ["fftw3f", "fftw3"] %} {% for base in fftw_libs %} - if not exist %LIBRARY_LIB%\\{{ base }}.lib exit 1 # [win] - if not exist %LIBRARY_BIN%\\{{ base }}.dll exit 1 # [win] {% endfor %} about: home: http://fftw.org license: GPL-2.0-or-later license_file: COPYING summary: The fastest Fourier transform in the west. extra: recipe-maintainers: - alexbw - jakirkham - grlee77 - jschueller - egpbos """ # noqa cm = CondaMetaYAML(recipe) s = io.StringIO() cm.dump(s) s.seek(0) assert s.read() == recipe_parsed
def test_recipe_parses_cupy(): recipe = r"""{% set name = "cupy" %} {% set version = "10.1.0" %} {% set sha256 = "ad28e7311b2023391f2278b7649828decdd9d9599848e18845eb4ab1b2d01936" %} {% if cuda_compiler_version in (None, "None", True, False) %} {% set cuda_major = 0 %} {% set cuda_minor = 0 %} {% set cuda_major_minor = (0, 0) %} {% else %} {% set cuda_major = environ.get("cuda_compiler_version", "11.2").split(".")[0]|int %} {% set cuda_minor = environ.get("cuda_compiler_version", "11.2").split(".")[1]|int %} {% set cuda_major_minor = (cuda_major, cuda_minor) %} {% endif %} package: name: {{ name|lower }} version: {{ version }} source: - url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz sha256: {{ sha256 }} build: number: 0 skip: true # [(not win64 and not linux64 and (ppc64le and cuda_compiler_version != "10.2") and not (aarch64 and arm_variant_type == "sbsa")) or cuda_compiler_version in (undefined, "None")] script_env: # for some reason /usr/local/cuda is not added to $PATH in the docker image - CUDA_HOME # [ppc64le or aarch64] script: # CuPy default detects CUDA from nvcc, but on Conda-Forge's dockers nvcc lives in a different place... # With conda-forge/nvcc-feedstock#58, CUDA_PATH is set correctly - export NVCC=$(which nvcc) # [linux] - echo "nvcc is $NVCC, CUDA path is $CUDA_PATH" # [linux] - for /f "tokens=* usebackq" %%f in (`where nvcc`) do (set "dummy=%%f" && call set "NVCC=%%dummy:\=\\%%") # [win] - echo "nvcc is %NVCC%, CUDA path is %CUDA_PATH%" # [win] {% if cuda_major_minor >= (11, 2) %} - export CUSPARSELT_PATH=$PREFIX # [linux64 or win] {% endif %} # Workaround __ieee128 error; see https://github.com/LLNL/blt/issues/341 - export NVCC="$NVCC -Xcompiler -mno-float128" # [ppc64le] - {{ PYTHON }} -m pip install . --no-deps -vv - if errorlevel 1 exit 1 # [win] # copy activate/deactivate scripts - mkdir -p "${PREFIX}/etc/conda/activate.d" # [linux] - cp "${RECIPE_DIR}/activate.sh" "${PREFIX}/etc/conda/activate.d/cupy_activate.sh" # [linux] - mkdir -p "${PREFIX}/etc/conda/deactivate.d" # [linux] - cp "${RECIPE_DIR}/deactivate.sh" "${PREFIX}/etc/conda/deactivate.d/cupy_deactivate.sh" # [linux] - if not exist %PREFIX%\etc\conda\activate.d mkdir %PREFIX%\etc\conda\activate.d # [win] - copy %RECIPE_DIR%\activate.bat %PREFIX%\etc\conda\activate.d\cupy_activate.bat # [win] - if not exist %PREFIX%\etc\conda\deactivate.d mkdir %PREFIX%\etc\conda\deactivate.d # [win] - copy %RECIPE_DIR%\deactivate.bat %PREFIX%\etc\conda\deactivate.d\cupy_deactivate.bat # [win] # enable CuPy's preload mechanism - mkdir -p "${SP_DIR}/cupy/.data/" # [linux] - if not exist %SP_DIR%\cupy\.data mkdir %SP_DIR%\cupy\.data # [win] {% if cuda_major_minor >= (11, 2) %} - cp ${RECIPE_DIR}/preload_config/linux64_cuda11_wheel.json ${SP_DIR}/cupy/.data/_wheel.json # [linux] - copy %RECIPE_DIR%\preload_config\win64_cuda11_wheel.json %SP_DIR%\cupy\.data\_wheel.json # [win] {% else %} - cp ${RECIPE_DIR}/preload_config/linux64_cuda{{ cuda_compiler_version }}_wheel.json ${SP_DIR}/cupy/.data/_wheel.json # [linux] - copy %RECIPE_DIR%\preload_config\win64_cuda{{ cuda_compiler_version }}_wheel.json %SP_DIR%\cupy\.data\_wheel.json # [win] {% endif %} missing_dso_whitelist: - '*/libcuda.*' # [linux] - '*/nvcuda.dll' # [win] ignore_run_exports_from: - cudnn # [linux64 or ppc64le or win] - nccl # [linux64 or ppc64le] - cutensor {% if cuda_major_minor >= (11, 2) %} - cusparselt # [linux64 or win] {% endif %} requirements: build: - {{ compiler("c") }} - {{ compiler("cxx") }} - {{ compiler("cuda") }} - sysroot_linux-64 2.17 # [linux] host: - python - pip - setuptools - cython >=0.29.22,<3 - fastrlock >=0.5 - cudnn # [linux64 or ppc64le or win] - nccl >=2.8 # [linux64 or ppc64le] - cutensor {% if cuda_major_minor >= (11, 2) %} - cusparselt !=0.2.0.* # [linux64 or win] {% endif %} run: - python - setuptools - fastrlock >=0.5 - numpy >=1.18 run_constrained: # Only GLIBC_2.17 or older symbols present - __glibc >=2.17 # [linux] - scipy >=1.4 - optuna >=2 - {{ pin_compatible('cudnn') }} # [linux64 or ppc64le or win] - {{ pin_compatible('nccl') }} # [linux64 or ppc64le] - {{ pin_compatible('cutensor', lower_bound='1.3') }} {% if cuda_major_minor >= (11, 2) %} - {{ pin_compatible('cusparselt', max_pin='x.x') }} # [linux64 or win] {% endif %} test: requires: - pytest - {{ compiler("c") }} - {{ compiler("cxx") }} - {{ compiler("cuda") }} # tests need nvcc source_files: - tests about: home: https://cupy.dev/ license: MIT license_family: MIT license_file: LICENSE summary: | CuPy: NumPy & SciPy for GPU dev_url: https://github.com/cupy/cupy/ doc_url: https://docs.cupy.dev/en/stable/ extra: recipe-maintainers: - jakirkham - leofang - kmaehashi - asi1024 - emcastillo - toslunar """ # noqa recipe_parsed = r"""{% set name = "cupy" %} {% set version = "10.1.0" %} {% set sha256 = "ad28e7311b2023391f2278b7649828decdd9d9599848e18845eb4ab1b2d01936" %} {% if cuda_compiler_version in (None, "None", True, False) %} {% set cuda_major = 0 %} {% set cuda_minor = 0 %} {% set cuda_major_minor = (0, 0) %} {% else %} {% set cuda_major = environ.get("cuda_compiler_version", "11.2").split(".")[0]|int %} {% set cuda_minor = environ.get("cuda_compiler_version", "11.2").split(".")[1]|int %} {% set cuda_major_minor = (cuda_major, cuda_minor) %} {% endif %} package: name: {{ name|lower }} version: {{ version }} source: - url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz sha256: {{ sha256 }} build: number: 0 skip: true # [(not win64 and not linux64 and (ppc64le and cuda_compiler_version != "10.2") and not (aarch64 and arm_variant_type == "sbsa")) or cuda_compiler_version in (undefined, "None")] script_env: # for some reason /usr/local/cuda is not added to $PATH in the docker image - CUDA_HOME # [ppc64le or aarch64] script: # CuPy default detects CUDA from nvcc, but on Conda-Forge's dockers nvcc lives in a different place... # With conda-forge/nvcc-feedstock#58, CUDA_PATH is set correctly - export NVCC=$(which nvcc) # [linux] - echo "nvcc is $NVCC, CUDA path is $CUDA_PATH" # [linux] - for /f "tokens=* usebackq" %%f in (`where nvcc`) do (set "dummy=%%f" && call set "NVCC=%%dummy:\=\\%%") # [win] - echo "nvcc is %NVCC%, CUDA path is %CUDA_PATH%" # [win] {% if cuda_major_minor >= (11, 2) %} - export CUSPARSELT_PATH=$PREFIX # [linux64 or win] {% endif %} # Workaround __ieee128 error; see https://github.com/LLNL/blt/issues/341 - export NVCC="$NVCC -Xcompiler -mno-float128" # [ppc64le] - {{ PYTHON }} -m pip install . --no-deps -vv - if errorlevel 1 exit 1 # [win] # copy activate/deactivate scripts - mkdir -p "${PREFIX}/etc/conda/activate.d" # [linux] - cp "${RECIPE_DIR}/activate.sh" "${PREFIX}/etc/conda/activate.d/cupy_activate.sh" # [linux] - mkdir -p "${PREFIX}/etc/conda/deactivate.d" # [linux] - cp "${RECIPE_DIR}/deactivate.sh" "${PREFIX}/etc/conda/deactivate.d/cupy_deactivate.sh" # [linux] - if not exist %PREFIX%\etc\conda\activate.d mkdir %PREFIX%\etc\conda\activate.d # [win] - copy %RECIPE_DIR%\activate.bat %PREFIX%\etc\conda\activate.d\cupy_activate.bat # [win] - if not exist %PREFIX%\etc\conda\deactivate.d mkdir %PREFIX%\etc\conda\deactivate.d # [win] - copy %RECIPE_DIR%\deactivate.bat %PREFIX%\etc\conda\deactivate.d\cupy_deactivate.bat # [win] # enable CuPy's preload mechanism - mkdir -p "${SP_DIR}/cupy/.data/" # [linux] - if not exist %SP_DIR%\cupy\.data mkdir %SP_DIR%\cupy\.data # [win] {% if cuda_major_minor >= (11, 2) %} - cp ${RECIPE_DIR}/preload_config/linux64_cuda11_wheel.json ${SP_DIR}/cupy/.data/_wheel.json # [linux] - copy %RECIPE_DIR%\preload_config\win64_cuda11_wheel.json %SP_DIR%\cupy\.data\_wheel.json # [win] {% else %} - cp ${RECIPE_DIR}/preload_config/linux64_cuda{{ cuda_compiler_version }}_wheel.json ${SP_DIR}/cupy/.data/_wheel.json # [linux] - copy %RECIPE_DIR%\preload_config\win64_cuda{{ cuda_compiler_version }}_wheel.json %SP_DIR%\cupy\.data\_wheel.json # [win] {% endif %} missing_dso_whitelist: - '*/libcuda.*' # [linux] - '*/nvcuda.dll' # [win] ignore_run_exports_from: - cudnn # [linux64 or ppc64le or win] - nccl # [linux64 or ppc64le] - cutensor {% if cuda_major_minor >= (11, 2) %} - cusparselt # [linux64 or win] {% endif %} requirements: build: - {{ compiler("c") }} - {{ compiler("cxx") }} - {{ compiler("cuda") }} - sysroot_linux-64 2.17 # [linux] host: - python - pip - setuptools - cython >=0.29.22,<3 - fastrlock >=0.5 - cudnn # [linux64 or ppc64le or win] - nccl >=2.8 # [linux64 or ppc64le] - cutensor {% if cuda_major_minor >= (11, 2) %} - cusparselt !=0.2.0.* # [linux64 or win] {% endif %} run: - python - setuptools - fastrlock >=0.5 - numpy >=1.18 run_constrained: # Only GLIBC_2.17 or older symbols present - __glibc >=2.17 # [linux] - scipy >=1.4 - optuna >=2 - {{ pin_compatible('cudnn') }} # [linux64 or ppc64le or win] - {{ pin_compatible('nccl') }} # [linux64 or ppc64le] - {{ pin_compatible('cutensor', lower_bound='1.3') }} {% if cuda_major_minor >= (11, 2) %} - {{ pin_compatible('cusparselt', max_pin='x.x') }} # [linux64 or win] {% endif %} test: requires: - pytest - {{ compiler("c") }} - {{ compiler("cxx") }} - {{ compiler("cuda") }} # tests need nvcc source_files: - tests about: home: https://cupy.dev/ license: MIT license_family: MIT license_file: LICENSE summary: | CuPy: NumPy & SciPy for GPU dev_url: https://github.com/cupy/cupy/ doc_url: https://docs.cupy.dev/en/stable/ extra: recipe-maintainers: - jakirkham - leofang - kmaehashi - asi1024 - emcastillo - toslunar """ # noqa cm = CondaMetaYAML(recipe) s = io.StringIO() cm.dump(s) s.seek(0) assert s.read() == recipe_parsed
def test_parsing(add_extra_req): meta_yaml = """\ {% set name = 'val1' %} # [py2k] {% set name = 'val2' %}#[py3k and win] {% set version = '4.5.6' %} {% set major_ver = version.split('.')[0] %} {% set bad_ver = bad_version.split('.')[0] %} {% set build = 0 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5#[py3k and win] build: number: 10 """ if add_extra_req: meta_yaml += """\ requirements: host: - blah <{{ blarg }} """ meta_yaml_canonical = """\ {% set name = "val1" %} # [py2k] {% set name = "val2" %} # [py3k and win] {% set version = "4.5.6" %} {% set major_ver = version.split('.')[0] %} {% set bad_ver = bad_version.split('.')[0] %} {% set build = 0 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5 # [py3k and win] build: number: 10 """ if add_extra_req: meta_yaml_canonical += """\ requirements: host: - blah <{{ blarg }} """ cm = CondaMetaYAML(meta_yaml) # check the jinja2 keys assert cm.jinja2_vars['name__###conda-selector###__py2k'] == 'val1' assert cm.jinja2_vars['name__###conda-selector###__py3k and win'] == 'val2' assert cm.jinja2_vars['version'] == '4.5.6' # check jinja2 expressions assert cm.jinja2_exprs[ "major_ver"] == "{% set major_ver = version.split('.')[0] %}" assert cm.jinja2_exprs[ "bad_ver"] == "{% set bad_ver = bad_version.split('.')[0] %}" # check it when we eval jinja2_exprs_evaled = cm.eval_jinja2_exprs(cm.jinja2_vars) assert jinja2_exprs_evaled["major_ver"] == 4 # check selectors assert cm.meta['source']['sha256__###conda-selector###__py2k'] == 1 assert cm.meta['source']['sha256__###conda-selector###__py3k and win'] == 5 # check other keys assert cm.meta['build']['number'] == 10 assert cm.meta['package']['name'] == '{{ name|lower }}' assert cm.meta['source']['url'] == 'foobar' if add_extra_req: assert cm.meta['requirements']['host'][0] == 'blah <{{ blarg }}' s = io.StringIO() cm.dump(s) s.seek(0) assert meta_yaml_canonical == s.read() # now add stuff and test outputs cm.jinja2_vars['foo'] = 'bar' cm.jinja2_vars['xfoo__###conda-selector###__win or osx'] = 10 cm.jinja2_vars['build'] = 100 cm.meta['about'] = 10 cm.meta['extra__###conda-selector###__win'] = 'blah' cm.meta['extra__###conda-selector###__not win'] = 'not_win_blah' s = io.StringIO() cm.dump(s) s.seek(0) new_meta_yaml = s.read() true_new_meta_yaml = """\ {% set foo = "bar" %} {% set xfoo = 10 %} # [win or osx] {% set name = "val1" %} # [py2k] {% set name = "val2" %} # [py3k and win] {% set version = "4.5.6" %} {% set major_ver = version.split('.')[0] %} {% set bad_ver = bad_version.split('.')[0] %} {% set build = 100 %} {% if False %} {% set build = build + 100 %} {% endif %} package: name: {{ name|lower }} source: url: foobar sha256: 1 # [py2k] sha256: 5 # [py3k and win] build: number: 10 """ if add_extra_req: true_new_meta_yaml += """\ requirements: host: - blah <{{ blarg }} """ true_new_meta_yaml += """\ about: 10 extra: blah # [win] extra: not_win_blah # [not win] """ assert new_meta_yaml == true_new_meta_yaml