Repository: conda-incubator/grayskull
Pull request #424 (2023-01-22 22:01:17)

Add compilers to R recipes

codecov-commenter: # [Codecov](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator) Report
> Merging [#424](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator) (95e3bbe) into [main](https://codecov.io/gh/conda-incubator/grayskull/commit/9c3242e1667135f34875a6d9e44d53b83ab3d9ba?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator) (9c3242e) will **increase** coverage by `0.48%`.
> The diff coverage is `92.48%`.

```diff
@@            Coverage Diff             @@
##             main     #424      +/-   ##
==========================================
+ Coverage   91.30%   91.79%   +0.48%     
==========================================
  Files          17       17              
  Lines        1576     1938     +362     
==========================================
+ Hits         1439     1779     +340     
- Misses        137      159      +22     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `91.79% <92.48%> (+0.48%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator) | Coverage Δ | |
|---|---|---|
| [setup.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-c2V0dXAucHk=) | `0.00% <0.00%> (ø)` | |
| [grayskull/base/github.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL2Jhc2UvZ2l0aHViLnB5) | `72.83% <72.83%> (ø)` | |
| [grayskull/license/discovery.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL2xpY2Vuc2UvZGlzY292ZXJ5LnB5) | `93.81% <72.97%> (-3.33%)` | :arrow_down: |
| [grayskull/strategy/abstract\_strategy.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL3N0cmF0ZWd5L2Fic3RyYWN0X3N0cmF0ZWd5LnB5) | `83.33% <83.33%> (ø)` | |
| [grayskull/strategy/cran.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL3N0cmF0ZWd5L2NyYW4ucHk=) | `90.20% <90.20%> (ø)` | |
| [grayskull/base/factory.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL2Jhc2UvZmFjdG9yeS5weQ==) | `90.69% <92.30%> (+0.69%)` | :arrow_up: |
| [grayskull/\_\_main\_\_.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL19fbWFpbl9fLnB5) | `93.49% <92.94%> (-4.66%)` | :arrow_down: |
| [grayskull/strategy/py\_base.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL3N0cmF0ZWd5L3B5X2Jhc2UucHk=) | `95.43% <95.43%> (ø)` | |
| [grayskull/utils.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL3V0aWxzLnB5) | `98.12% <97.58%> (-1.88%)` | :arrow_down: |
| [grayskull/strategy/pypi.py](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator#diff-Z3JheXNrdWxsL3N0cmF0ZWd5L3B5cGkucHk=) | `97.68% <97.68%> (ø)` | |
| ... and [9 more](https://codecov.io/gh/conda-incubator/grayskull/pull/424?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator) | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=conda-incubator)


Patch:
diff --git a/grayskull/strategy/cran.py b/grayskull/strategy/cran.py
index 6920f54..0fcc5a9 100644
--- a/grayskull/strategy/cran.py
+++ b/grayskull/strategy/cran.py
@@ -12,6 +12,7 @@ from urllib.request import Request, urlopen
 
 import requests
 from bs4 import BeautifulSoup
+from souschef.jinja_expression import set_global_jinja_var
 
 from grayskull.cli.stdout import print_msg
 from grayskull.config import Configuration
@@ -51,6 +52,9 @@ class CranStrategy(AbstractStrategy):
             metadata_section = metadata.get(sec)
             if metadata_section:
                 recipe[sec] = metadata_section
+        if metadata.get("need_compiler", False):
+            set_global_jinja_var(recipe, "posix", 'm2-" if win else "')
+            set_global_jinja_var(recipe, "native", 'm2w64-" if win else "')
         return recipe
 
 
@@ -259,15 +263,7 @@ def get_cran_metadata(config: Configuration, cran_url: str):
     _, pkg_version, pkg_url = get_cran_index(cran_url, pkg_name, pkg_version)
     print_msg(pkg_name)
     print_msg(pkg_version)
-    tarball_name = pkg_url.rsplit("/", 1)[-1]
-    print_msg(pkg_url)
-    response = requests.get(pkg_url)
-    response.raise_for_status()
-    download_file = os.path.join(
-        str(mkdtemp(f"grayskull-cran-metadata-{config.name}-")), tarball_name
-    )
-    with open(download_file, "wb") as f:
-        f.write(response.content)
+    download_file = download_cran_pkg(config, pkg_url)
     metadata = get_archive_metadata(download_file)
     r_recipe_end_comment = "\n".join(
         [f"# {line}" for line in metadata["orig_lines"] if line]
@@ -295,7 +291,7 @@ def get_cran_metadata(config: Configuration, cran_url: str):
     imports.append("r-base")
     imports.sort()  # this is not a requirement in conda but good for readability
 
-    return {
+    dict_metadata = {
         "package": {
             "name": "r-{{ name }}",
             "version": "{{ version }}",
@@ -306,11 +302,13 @@ def get_cran_metadata(config: Configuration, cran_url: str):
         },
         "build": {
             "number": 0,
+            "merge_build_host": True,
             "script": "R CMD INSTALL --build .",
             "entry_points": metadata.get("entry_points"),
             "rpaths": ["lib/R/lib/", "lib/"],
         },
         "requirements": {
+            "build": [],
             "host": deepcopy(imports),
             "run": deepcopy(imports),
         },
@@ -329,4 +327,33 @@ def get_cran_metadata(config: Configuration, cran_url: str):
             "license": match_license(metadata.get("License", "")).get("licenseId")
             or metadata.get("License", ""),
         },
-    }, r_recipe_end_comment
+    }
+    if metadata.get("NeedsCompilation", "no").lower() == "yes":
+        dict_metadata["need_compiler"] = True
+        dict_metadata["requirements"]["build"].extend(
+            [
+                "cross-r-base {{ r_base }}  # [build_platform != target_platform]",
+                "autoconf  # [unix]",
+                "{{ compiler('c') }}  # [unix]",
+                "{{ compiler('m2w64_c') }}  # [win]",
+                "{{ compiler('cxx') }}  # [unix]",
+                "{{ compiler('m2w64_cxx') }}  # [win]",
+                "posix  # [win]",
+            ]
+        )
+    if not dict_metadata["requirements"]["build"]:
+        del dict_metadata["requirements"]["build"]
+    return dict_metadata, r_recipe_end_comment
+
+
+def download_cran_pkg(config, pkg_url):
+    tarball_name = pkg_url.rsplit("/", 1)[-1]
+    print_msg(pkg_url)
+    response = requests.get(pkg_url)
+    response.raise_for_status()
+    download_file = os.path.join(
+        str(mkdtemp(f"grayskull-cran-metadata-{config.name}-")), tarball_name
+    )
+    with open(download_file, "wb") as f:
+        f.write(response.content)
+    return download_file


Test patch:
diff --git a/tests/cli/test_cli_cmds.py b/tests/cli/test_cli_cmds.py
index 791531b..16bdbbd 100644
--- a/tests/cli/test_cli_cmds.py
+++ b/tests/cli/test_cli_cmds.py
@@ -112,10 +112,7 @@ def test_recursive_option(mocker, option, tmpdir):
 # The CRAN test is not passing. I don't understand why!
 @pytest.mark.parametrize(
     "index, name, version",
-    [
-        # ("pypi", "pytest", "5.3.2"),
-        ("cran", "future", "1.26.1")
-    ],
+    [("pypi", "pytest", "5.3.2"), ("cran", "future", "1.26.1")],
 )
 def test_part_reload_recipe(tmpdir, index, name, version):
     recipe = GrayskullFactory.create_recipe(index, Configuration(name, version))
diff --git a/tests/test_cran.py b/tests/test_cran.py
index e884eda..1060a15 100644
--- a/tests/test_cran.py
+++ b/tests/test_cran.py
@@ -2,7 +2,9 @@ from unittest.mock import MagicMock, patch
 
 import pytest
 
+from grayskull.config import Configuration
 from grayskull.strategy.cran import (
+    get_cran_metadata,
     scrap_cran_archive_page_for_package_folder_url,
     scrap_cran_pkg_folder_page_for_full_url,
     scrap_main_page_cran_find_latest_package,
@@ -65,3 +67,39 @@ def test_scrap_cran_pkg_folder_page_for_full_url(mock_get_webpage, webpage_magic
         scrap_cran_pkg_folder_page_for_full_url("CRAN_URL", "PKG_NAME", "1.0.0")
         == "CRAN_URL/PKG_NAME_URL"
     )
+
+
+@patch("grayskull.strategy.cran.get_archive_metadata")
+@patch("grayskull.strategy.cran.sha256_checksum")
+@patch("grayskull.strategy.cran.download_cran_pkg")
+@patch("grayskull.strategy.cran.get_cran_index")
+def test_get_cran_metadata_need_compilation(
+    mock_get_cran_index,
+    mock_download_cran_pkg,
+    mock_sha256,
+    mock_get_archive_metadata,
+    tmp_path,
+):
+    mock_get_cran_index.return_value = ("rpkg", "1.0.0", "http://foobar")
+    mock_sha256.return_value = 123456
+    mock_download_cran_pkg.return_value = str(tmp_path / "rpkg-1.0.0.tar.gz")
+    mock_get_archive_metadata.return_value = {
+        "orig_lines": ["foo", "bar"],
+        "License": "MIT",
+        "NeedsCompilation": "yes",
+        "URL": "PKG-URL",
+    }
+    cfg = Configuration(name="rpkg", version="1.0.0")
+    result_metadata, r_recipe_comment = get_cran_metadata(
+        cfg, "https://cran.r-project.org"
+    )
+    assert r_recipe_comment == "# foo\n# bar"
+    assert result_metadata["requirements"]["build"] == [
+        "cross-r-base {{ r_base }}  # [build_platform != target_platform]",
+        "autoconf  # [unix]",
+        "{{ compiler('c') }}  # [unix]",
+        "{{ compiler('m2w64_c') }}  # [win]",
+        "{{ compiler('cxx') }}  # [unix]",
+        "{{ compiler('m2w64_cxx') }}  # [win]",
+        "posix  # [win]",
+    ]