Repository: conda-forge/conda-smithy

Issue:
BUG: MACOSX_SDK_VERSION not participating in merge logic
I just noticed that is a recipe uses 
```
c_stdlib_version:          # [osx and x86_64]
  - "10.12"                # [osx and x86_64]
MACOSX_SDK_VERSION:        # [osx and x86_64]
  - "10.12"                # [osx and x86_64]
```
the merge logic of smithy will not be applied to `MACOSX_SDK_VERSION`, which will end up being incorrectly populated as 10.12 (while `MACOSX_DEPLOYMENT_TARGET` ends up being 10.13 after the merge with the global pinning).

I guess we should also add a linter warning for c_stdlib_version / MACOSX_SDK_VERSION <10.13?

CC @beckermr




Discussion:
There's about ~80 [feedstocks](https://github.com/search?q=org%3Aconda-forge+%2F%28%3Fm%29MACOSX_SDK_VERSION%3A.*%5Cs%2B-%5B%5Cs%5C%27%5C%22%5D*10%5C.%289%7C1%5B012%5D%29%2F+path%3Arecipe%2Fconda_build_config.yaml&type=code) that set a too-low SDK currently.
I don't follow if this is a longer thing versus something we should change in smithy itself to fix the issue. 
IMO we should warn on obsolete `c_stdlib_version` and either fix the merge logic for `MACOSX_SDK_VERSION` (so that 10.12 still ends up as 10.13 in the variant configs), and/or error on it in smithy. That way feedstocks can remove these things at their own pace without being broken.
But shouldn't people still be able to build against an older sdk if they want to?
> But shouldn't people still be able to build against an older sdk if they want to?

Using a newer SDK doesn't break anything, only the deployment target is really relevant, so pulling in SDK 10.13 should be harmless (if we go with `sdk=max(sdk,dep_target)`). However, anything using C++ is going to be broken by libcxx 17 requiring 10.13 (and failing on stdlib-header inclusion on older SDKs).

If we want to let people opt into stuff <10.13, we would need to disable all merge logic between `c_stdlib` & `MACOSX_DEPLOYMENT_TARGET`, which I think is going to do more harm than good (by breaking recipes pointlessly). It's also not necessary from my POV to support this - we've dropped support for <10.13, so I don't see the problem if feedstocks don't get to build against older versions anymore.
I still don't follow completely but am happy to review a pr if needed. 

Resolving patch:
diff --git a/conda_smithy/configure_feedstock.py b/conda_smithy/configure_feedstock.py
--- a/conda_smithy/configure_feedstock.py
+++ b/conda_smithy/configure_feedstock.py
@@ -439,6 +439,8 @@ def _merge_deployment_target(container_of_dicts, has_macdt):
       For now, take the maximum to populate both.
     - In any case, populate MACOSX_DEPLOYMENT_TARGET, as that is the key picked
       up by https://github.com/conda-forge/conda-forge-ci-setup-feedstock
+    - If MACOSX_SDK_VERSION is lower than the merged value from the previous step,
+      update it to match the merged value.
     """
     result = []
     for var_dict in container_of_dicts:
@@ -452,17 +454,21 @@ def _merge_deployment_target(container_of_dicts, has_macdt):
         # case where we need to do processing
         v_stdlib = var_dict["c_stdlib_version"]
         macdt = var_dict.get("MACOSX_DEPLOYMENT_TARGET", v_stdlib)
+        sdk = var_dict.get("MACOSX_SDK_VERSION", v_stdlib)
         # error out if someone puts in a range of versions; we need a single version
         try:
-            cond_update = VersionOrder(v_stdlib) < VersionOrder(macdt)
+            stdlib_lt_macdt = VersionOrder(v_stdlib) < VersionOrder(macdt)
+            sdk_lt_stdlib = VersionOrder(sdk) < VersionOrder(v_stdlib)
+            sdk_lt_macdt = VersionOrder(sdk) < VersionOrder(macdt)
         except InvalidVersionSpec:
             raise ValueError(
-                "both and c_stdlib_version/MACOSX_DEPLOYMENT_TARGET need to be a "
-                "single version, not a version range!"
+                "all of c_stdlib_version/MACOSX_DEPLOYMENT_TARGET/"
+                "MACOSX_SDK_VERSION need to be a single version, "
+                "not a version range!"
             )
         if v_stdlib != macdt:
             # determine maximum version and use it to populate both
-            v_stdlib = macdt if cond_update else v_stdlib
+            v_stdlib = macdt if stdlib_lt_macdt else v_stdlib
             msg = (
                 "Conflicting specification for minimum macOS deployment target!\n"
                 "If your conda_build_config.yaml sets `MACOSX_DEPLOYMENT_TARGET`, "
@@ -474,6 +480,18 @@ def _merge_deployment_target(container_of_dicts, has_macdt):
             if has_macdt:
                 warn_once(msg)
 
+        if sdk_lt_stdlib or sdk_lt_macdt:
+            sdk_lt_merged = VersionOrder(sdk) < VersionOrder(v_stdlib)
+            sdk = v_stdlib if sdk_lt_merged else sdk
+            msg = (
+                "Conflicting specification for minimum macOS SDK version!\n"
+                "If your conda_build_config.yaml sets `MACOSX_SDK_VERSION`, "
+                "it must be larger or equal than `c_stdlib_version` "
+                "(which is also influenced by the global pinning)!\n"
+                f"Using {sdk}=max(c_stdlib_version, MACOSX_SDK_VERSION)."
+            )
+            warn_once(msg)
+
         # we set MACOSX_DEPLOYMENT_TARGET to match c_stdlib_version,
         # for ease of use in conda-forge-ci-setup;
         # use new dictionary to avoid mutating existing var_dict in place
@@ -482,6 +500,7 @@ def _merge_deployment_target(container_of_dicts, has_macdt):
                 **var_dict,
                 "c_stdlib_version": v_stdlib,
                 "MACOSX_DEPLOYMENT_TARGET": v_stdlib,
+                "MACOSX_SDK_VERSION": sdk,
             }
         )
         result.append(new_dict)


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -253,6 +253,9 @@ def stdlib_deployment_target_recipe(config_yaml, stdlib_recipe):
 MACOSX_DEPLOYMENT_TARGET:       # [osx]
   - 10.14                       # [osx and x86_64]
   - 12.0                        # [osx and arm64]
+MACOSX_SDK_VERSION:             # [osx]
+  - 10.12                       # [osx and x86_64]
+  - 12.0                        # [osx and arm64]
 """
         )
     return RecipeConfigPair(
diff --git a/tests/test_configure_feedstock.py b/tests/test_configure_feedstock.py
--- a/tests/test_configure_feedstock.py
+++ b/tests/test_configure_feedstock.py
@@ -271,6 +271,8 @@ def test_stdlib_deployment_target(
     assert re.match(
         r"(?s).*MACOSX_DEPLOYMENT_TARGET:\s*- ['\"]?10\.14", content
     )
+    # MACOSX_SDK_VERSION gets updated as well if it's below the other two
+    assert re.match(r"(?s).*MACOSX_SDK_VERSION:\s*- ['\"]?10\.14", content)
 
 
 def test_upload_on_branch_azure(upload_on_branch_recipe, jinja_env):