Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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()
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
    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 {}
Ejemplo n.º 7
0
    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 {}
Ejemplo n.º 8
0
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 == """\
Ejemplo n.º 9
0
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()
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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