Repository: conda/conda-build

Issue:
Provide method to override run_exports
Ideally, people never need to do this.  However, it's important to have the ability.


Discussion:

Resolving patch:
diff --git a/conda_build/metadata.py b/conda_build/metadata.py
--- a/conda_build/metadata.py
+++ b/conda_build/metadata.py
@@ -306,6 +306,17 @@ def parse(data, config, path=None):
     'build/script_env': list,
     'build/run_exports': list,
     'build/track_features': list,
+    'build/osx_is_app': bool,
+    'build/preserve_egg_dir': bool,
+    'build/binary_relocation': bool,
+    'build/noarch': text_type,
+    'build/noarch_python': bool,
+    'build/detect_binary_files_with_prefix': bool,
+    'build/skip': bool,
+    'build/skip_compile_pyc': list,
+    'build/preferred_env': text_type,
+    'build/preferred_env_executable_paths': list,
+    'build/ignore_run_exports': list,
     'requirements/build': list,
     'requirements/host': list,
     'requirements/run': list,
@@ -326,16 +337,6 @@ def parse(data, config, path=None):
     'source/git_rev': text_type,
     'source/path': text_type,
     'source/git_url': text_type,
-    'build/osx_is_app': bool,
-    'build/preserve_egg_dir': bool,
-    'build/binary_relocation': bool,
-    'build/noarch': text_type,
-    'build/noarch_python': bool,
-    'build/detect_binary_files_with_prefix': bool,
-    'build/skip': bool,
-    'build/skip_compile_pyc': list,
-    'build/preferred_env': text_type,
-    'build/preferred_env_executable_paths': list,
     'app/own_environment': bool
 }
 
@@ -416,6 +417,7 @@ def _git_clean(source_meta):
               'script_env', 'always_include_files', 'skip', 'msvc_compiler',
               'pin_depends', 'include_recipe',  # pin_depends is experimental still
               'preferred_env', 'preferred_env_executable_paths', 'run_exports',
+              'ignore_run_exports',
               ],
     'requirements': ['build', 'host', 'run', 'conflicts', 'run_constrained'],
     'app': ['entry', 'icon', 'summary', 'type', 'cli_opts',
diff --git a/conda_build/render.py b/conda_build/render.py
--- a/conda_build/render.py
+++ b/conda_build/render.py
@@ -125,6 +125,17 @@ def get_pin_from_build(m, dep, build_dep_versions):
     return dep
 
 
+def _filter_run_exports(specs, ignore_list):
+    filtered_specs = []
+    for spec in specs:
+        if hasattr(spec, 'decode'):
+            spec = spec.decode()
+        if not any((spec == ignore_spec or spec.startswith(ignore_spec + ' '))
+                   for ignore_spec in ignore_list):
+            filtered_specs.append(spec)
+    return filtered_specs
+
+
 def get_upstream_pins(m, actions, index):
     """Download packages from specs, then inspect each downloaded package for additional
     downstream dependency specs.  Return these additional specs."""
@@ -138,6 +149,7 @@ def get_upstream_pins(m, actions, index):
     #    we read contents directly
     if actions:
         execute_actions(actions, index, verbose=m.config.debug)
+        ignore_list = utils.ensure_list(m.get_value('build/ignore_run_exports'))
 
         _pkgs_dirs = pkgs_dirs + list(m.config.bldpkgs_dirs)
         for pkg in linked_packages:
@@ -153,15 +165,16 @@ def get_upstream_pins(m, actions, index):
                 if os.path.isdir(pkg_dir):
                     downstream_file = os.path.join(pkg_dir, 'info/run_exports')
                     if os.path.isfile(downstream_file):
-                        additional_specs.extend(open(downstream_file).read().splitlines())
+                        specs = open(downstream_file).read().splitlines()
+                        additional_specs.extend(_filter_run_exports(specs, ignore_list))
                     break
                 elif os.path.isfile(pkg_file):
                     extra_specs = utils.package_has_file(pkg_file, 'info/run_exports')
                     if extra_specs:
                         # exclude packages pinning themselves (makes no sense)
-                        extra_specs = [spec for spec in extra_specs
-                                       if not spec.startswith(pkg_dist.rsplit('-', 2)[0])]
-                        additional_specs.extend(extra_specs.splitlines())
+                        extra_specs = [spec for spec in extra_specs.splitlines()
+                            if not spec.startswith(pkg_dist.rsplit('-', 2)[0])]
+                        additional_specs.extend(_filter_run_exports(extra_specs, ignore_list))
                     break
                 elif utils.conda_43():
                     # TODO: this is a vile hack reaching into conda's internals. Replace with
@@ -176,7 +189,8 @@ def get_upstream_pins(m, actions, index):
                                 extra_specs = utils.package_has_file(pkg_file,
                                                                     'info/run_exports')
                                 if extra_specs:
-                                    additional_specs.extend(extra_specs.splitlines())
+                                    specs = extra_specs.splitlines()
+                                    additional_specs.extend(_filter_run_exports(specs, ignore_list))
                                 break
                         break
                     except KeyError:


Test patch:
diff --git a/tests/test-recipes/variants/10_runtimes/ignore_exports/meta.yaml b/tests/test-recipes/variants/10_runtimes/ignore_exports/meta.yaml
new file mode 100644
--- /dev/null
+++ b/tests/test-recipes/variants/10_runtimes/ignore_exports/meta.yaml
@@ -0,0 +1,11 @@
+package:
+  name: package_consuming_run_exports
+  version: 1.0
+
+build:
+  ignore_run_exports:
+    - bzip2
+
+requirements:
+  build:
+    - package_has_run_exports
diff --git a/tests/test_api_build.py b/tests/test_api_build.py
--- a/tests/test_api_build.py
+++ b/tests/test_api_build.py
@@ -889,6 +889,15 @@ def test_run_exports(testing_metadata, testing_config):
     assert 'downstream_pinned_package 1.0' in m.meta['requirements']['run']
 
 
+def test_ignore_run_exports(testing_metadata, testing_config):
+    api.build(os.path.join(metadata_dir, '_run_exports'), config=testing_config)
+    testing_metadata.meta['requirements']['build'] = ['test_has_run_exports']
+    testing_metadata.meta['build']['ignore_run_exports'] = ['downstream_pinned_package']
+    testing_metadata.config.index = None
+    m = finalize_metadata(testing_metadata)
+    assert 'downstream_pinned_package 1.0' not in m.meta['requirements']['run']
+
+
 def test_pin_subpackage_exact(testing_config):
     recipe = os.path.join(metadata_dir, '_pin_subpackage_exact')
     ms = api.render(recipe, config=testing_config)