From b3dcb4cf6e1119f3d16dee31882611ba6373d332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Wed, 29 Jul 2026 13:29:57 +0200 Subject: [PATCH 1/3] runtime_artifacts attribute for recipes --- conan/internal/model/conan_file.py | 3 + conan/internal/model/requires.py | 4 + .../cmake/test_runtime_artifacts.py | 237 ++++++++++++++++++ .../graph/core/graph_manager_test.py | 26 ++ 4 files changed, 270 insertions(+) create mode 100644 test/functional/toolchains/cmake/test_runtime_artifacts.py diff --git a/conan/internal/model/conan_file.py b/conan/internal/model/conan_file.py index c678865e2b3..29a2d9eb8a9 100644 --- a/conan/internal/model/conan_file.py +++ b/conan/internal/model/conan_file.py @@ -48,6 +48,9 @@ class ConanFile: default_options = None default_build_options = None package_type = None + # For packages that ship artifacts needed at runtime (shared libs, plugins, data) even if + # their ``package_type`` doesn't imply it, like a static-library also containing a shared one + runtime_artifacts = False vendor = False languages = [] implements = [] diff --git a/conan/internal/model/requires.py b/conan/internal/model/requires.py index 4f30269a52d..163d9e5f2ff 100644 --- a/conan/internal/model/requires.py +++ b/conan/internal/model/requires.py @@ -185,6 +185,10 @@ def set_if_none(field, value): if getattr(self, field) is None: setattr(self, field, value) + if node.conanfile.runtime_artifacts: + # The dependency ships artifacts needed at runtime, no matter its package_type + set_if_none("_run", True) + if pkg_type is PackageType.APP: # Change the default requires headers&libs to False for APPS set_if_none("_headers", False) diff --git a/test/functional/toolchains/cmake/test_runtime_artifacts.py b/test/functional/toolchains/cmake/test_runtime_artifacts.py new file mode 100644 index 00000000000..87d25a0611b --- /dev/null +++ b/test/functional/toolchains/cmake/test_runtime_artifacts.py @@ -0,0 +1,237 @@ +import json +import textwrap + +import pytest + +from conan.test.utils.tools import TestClient + + +liba_cmake = textwrap.dedent("""\ + cmake_minimum_required(VERSION 3.15) + project(liba CXX) + + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + + # This package is a static-library, but it also ships a shared library that consumers + # have to link, and that has to be available at runtime + add_library(myshared SHARED src/myshared.cpp) + target_include_directories(myshared PUBLIC include) + + add_library(mystatic STATIC src/mystatic.cpp) + target_include_directories(mystatic PUBLIC include) + target_link_libraries(mystatic PRIVATE myshared) + + install(TARGETS mystatic myshared) + install(DIRECTORY include/ DESTINATION include) + """) + +liba_h = textwrap.dedent("""\ + #pragma once + void myshared_func(); + void mystatic_func(); + """) + +liba_shared_cpp = textwrap.dedent("""\ + #include + #include "liba.h" + void myshared_func() { std::cout << "liba: myshared_func!" << std::endl; } + """) + +liba_static_cpp = textwrap.dedent("""\ + #include + #include "liba.h" + void mystatic_func() { + std::cout << "liba: mystatic_func!" << std::endl; + myshared_func(); + } + """) + + +def _liba_conanfile(runtime_artifacts): + return textwrap.dedent(f"""\ + from conan import ConanFile + from conan.tools.cmake import CMake, cmake_layout + + class LibA(ConanFile): + name = "liba" + version = "0.1" + package_type = "static-library" + runtime_artifacts = {runtime_artifacts} + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeToolchain" + exports_sources = "CMakeLists.txt", "include/*", "src/*" + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + def package_info(self): + # "mystatic" first, it is the one requiring symbols from "myshared" + self.cpp_info.libs = ["mystatic", "myshared"] + """) + + +def _liba_files(runtime_artifacts): + return {"liba/conanfile.py": _liba_conanfile(runtime_artifacts), + "liba/CMakeLists.txt": liba_cmake, + "liba/include/liba.h": liba_h, + "liba/src/myshared.cpp": liba_shared_cpp, + "liba/src/mystatic.cpp": liba_static_cpp} + + +app_conanfile = textwrap.dedent("""\ + import os + from conan import ConanFile + from conan.tools.cmake import CMake, cmake_layout + + class App(ConanFile): + settings = "os", "compiler", "build_type", "arch" + package_type = "application" + generators = "CMakeToolchain", "CMakeDeps" + requires = "{requires}" + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + self.run(os.path.join(self.cpp.build.bindir, "app"), env="conanrun") + """) + + +@pytest.mark.tool("cmake") +def test_static_lib_shipping_shared_lib(): + """ a package_type=static-library package that also contains a shared library, which the + consumer links too, and which has to be available at runtime => runtime_artifacts=True + """ + c = TestClient() + app_cmake = textwrap.dedent("""\ + cmake_minimum_required(VERSION 3.15) + project(app CXX) + + find_package(liba CONFIG REQUIRED) + + add_executable(app main.cpp) + target_link_libraries(app PRIVATE liba::liba) + """) + main_cpp = textwrap.dedent("""\ + #include "liba.h" + int main() { + mystatic_func(); + } + """) + c.save(_liba_files(runtime_artifacts=True)) + c.save({"app/conanfile.py": app_conanfile.format(requires="liba/0.1"), + "app/CMakeLists.txt": app_cmake, + "app/main.cpp": main_cpp}) + c.run("create liba") + + c.run("graph info app --format=json", redirect_stdout="graph.json") + liba_require = json.loads(c.load("graph.json"))["graph"]["nodes"]["0"]["dependencies"]["1"] + # The "run" trait is True, even if liba is a static-library + assert liba_require["ref"] == "liba/0.1" + assert liba_require["run"] is True + assert liba_require["libs"] is True + + c.run("build app") + # If "runtime_artifacts" was not defined, run=False, so the shared library location would + # not be in the "conanrun" environment, and this would fail to run in Windows + assert "liba: mystatic_func!" in c.out + assert "liba: myshared_func!" in c.out + + +@pytest.mark.tool("cmake") +@pytest.mark.parametrize("runtime_artifacts", [True, False]) +def test_static_lib_shipping_shared_lib_transitive(runtime_artifacts): + """ app -> libb (shared-library) -> liba (static-library shipping a shared library) + The libs of liba are linked into libb.so, so app doesn't need them, but the shared library + inside liba still has to be there at runtime, so its binary cannot be skipped + """ + c = TestClient() + libb_conanfile = textwrap.dedent("""\ + from conan import ConanFile + from conan.tools.cmake import CMake, cmake_layout + + class LibB(ConanFile): + name = "libb" + version = "0.1" + package_type = "shared-library" + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeToolchain", "CMakeDeps" + requires = "liba/0.1" + exports_sources = "CMakeLists.txt", "include/*", "src/*" + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = ["libb"] + """) + libb_cmake = textwrap.dedent("""\ + cmake_minimum_required(VERSION 3.15) + project(libb CXX) + + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + find_package(liba CONFIG REQUIRED) + + add_library(libb SHARED src/libb.cpp) + target_include_directories(libb PUBLIC include) + target_link_libraries(libb PRIVATE liba::liba) + + install(TARGETS libb) + install(DIRECTORY include/ DESTINATION include) + """) + app_cmake = textwrap.dedent("""\ + cmake_minimum_required(VERSION 3.15) + project(app CXX) + + find_package(libb CONFIG REQUIRED) + + add_executable(app main.cpp) + target_link_libraries(app PRIVATE libb::libb) + """) + c.save(_liba_files(runtime_artifacts)) + c.save({"libb/conanfile.py": libb_conanfile, + "libb/CMakeLists.txt": libb_cmake, + "libb/include/libb.h": "#pragma once\nvoid libb_func();\n", + "libb/src/libb.cpp": '#include "libb.h"\n#include "liba.h"\n' + "void libb_func() { mystatic_func(); }\n", + "app/conanfile.py": app_conanfile.format(requires="libb/0.1"), + "app/CMakeLists.txt": app_cmake, + "app/main.cpp": '#include "libb.h"\nint main() { libb_func(); }\n'}) + c.run("create liba") + c.run("create libb") + + if runtime_artifacts: + c.run("build app") + # The liba binary is necessary at runtime, so it cannot be skipped + assert "Skipped binaries" not in c.out + assert "liba: mystatic_func!" in c.out + assert "liba: myshared_func!" in c.out + else: + # Without "runtime_artifacts" the run trait is False, and as liba is a static-library + # linked inside libb, Conan understands its binary is not necessary anymore and skips it, + # but the shared library it ships is still necessary, and the app cannot run + c.run("build app", assert_error=True) + assert "Skipped binaries" in c.out + assert "liba/0.1" in c.out + assert "Error in build() method" in c.out diff --git a/test/integration/graph/core/graph_manager_test.py b/test/integration/graph/core/graph_manager_test.py index 60741c0cb98..ea41f80926a 100644 --- a/test/integration/graph/core/graph_manager_test.py +++ b/test/integration/graph/core/graph_manager_test.py @@ -714,6 +714,32 @@ def test_multiple_levels_transitive_headers(self): (libb, True, True, False, False), (liba, True, True, False, False)]) + @pytest.mark.parametrize("runtime_artifacts", [True, False]) + def test_runtime_artifacts_from_static_lib(self, runtime_artifacts): + # app -> libb0.1 (static) -> liba0.1 (static, runtime_artifacts) + liba = GenConanfile("liba", "0.1").with_shared_option(False) + if runtime_artifacts: + liba = liba.with_class_attribute("runtime_artifacts=True") + self.recipe_conanfile("liba/0.1", liba) + self.recipe_cache("libb/0.1", ["liba/0.1"], option_shared=False) + consumer = self.recipe_consumer("app/0.1", ["libb/0.1"]) + + deps_graph = self.build_consumer(consumer) + + assert 3 == len(deps_graph.nodes) + app = deps_graph.root + libb = app.edges[0].dst + liba = libb.edges[0].dst + + self._check_node(app, "app/0.1", deps=[libb]) + self._check_node(libb, "libb/0.1#123", deps=[liba], dependents=[app]) + self._check_node(liba, "liba/0.1#123", dependents=[libb]) + + # node, headers, lib, build, run + _check_transitive(app, [(libb, True, True, False, False), + (liba, False, True, False, runtime_artifacts)]) + _check_transitive(libb, [(liba, True, True, False, runtime_artifacts)]) + class TestLinearFourLevels(GraphManagerTest): def test_default(self): From 7d304bb6d29263c60f27ffc0c7d82c95009cb6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Wed, 29 Jul 2026 13:52:28 +0200 Subject: [PATCH 2/3] Move to integration --- .../cmake/test_runtime_artifacts.py | 237 ------------------ .../graph/test_runtime_artifacts.py | 89 +++++++ 2 files changed, 89 insertions(+), 237 deletions(-) delete mode 100644 test/functional/toolchains/cmake/test_runtime_artifacts.py create mode 100644 test/integration/graph/test_runtime_artifacts.py diff --git a/test/functional/toolchains/cmake/test_runtime_artifacts.py b/test/functional/toolchains/cmake/test_runtime_artifacts.py deleted file mode 100644 index 87d25a0611b..00000000000 --- a/test/functional/toolchains/cmake/test_runtime_artifacts.py +++ /dev/null @@ -1,237 +0,0 @@ -import json -import textwrap - -import pytest - -from conan.test.utils.tools import TestClient - - -liba_cmake = textwrap.dedent("""\ - cmake_minimum_required(VERSION 3.15) - project(liba CXX) - - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - - # This package is a static-library, but it also ships a shared library that consumers - # have to link, and that has to be available at runtime - add_library(myshared SHARED src/myshared.cpp) - target_include_directories(myshared PUBLIC include) - - add_library(mystatic STATIC src/mystatic.cpp) - target_include_directories(mystatic PUBLIC include) - target_link_libraries(mystatic PRIVATE myshared) - - install(TARGETS mystatic myshared) - install(DIRECTORY include/ DESTINATION include) - """) - -liba_h = textwrap.dedent("""\ - #pragma once - void myshared_func(); - void mystatic_func(); - """) - -liba_shared_cpp = textwrap.dedent("""\ - #include - #include "liba.h" - void myshared_func() { std::cout << "liba: myshared_func!" << std::endl; } - """) - -liba_static_cpp = textwrap.dedent("""\ - #include - #include "liba.h" - void mystatic_func() { - std::cout << "liba: mystatic_func!" << std::endl; - myshared_func(); - } - """) - - -def _liba_conanfile(runtime_artifacts): - return textwrap.dedent(f"""\ - from conan import ConanFile - from conan.tools.cmake import CMake, cmake_layout - - class LibA(ConanFile): - name = "liba" - version = "0.1" - package_type = "static-library" - runtime_artifacts = {runtime_artifacts} - settings = "os", "compiler", "build_type", "arch" - generators = "CMakeToolchain" - exports_sources = "CMakeLists.txt", "include/*", "src/*" - - def layout(self): - cmake_layout(self) - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - - def package(self): - cmake = CMake(self) - cmake.install() - - def package_info(self): - # "mystatic" first, it is the one requiring symbols from "myshared" - self.cpp_info.libs = ["mystatic", "myshared"] - """) - - -def _liba_files(runtime_artifacts): - return {"liba/conanfile.py": _liba_conanfile(runtime_artifacts), - "liba/CMakeLists.txt": liba_cmake, - "liba/include/liba.h": liba_h, - "liba/src/myshared.cpp": liba_shared_cpp, - "liba/src/mystatic.cpp": liba_static_cpp} - - -app_conanfile = textwrap.dedent("""\ - import os - from conan import ConanFile - from conan.tools.cmake import CMake, cmake_layout - - class App(ConanFile): - settings = "os", "compiler", "build_type", "arch" - package_type = "application" - generators = "CMakeToolchain", "CMakeDeps" - requires = "{requires}" - - def layout(self): - cmake_layout(self) - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - self.run(os.path.join(self.cpp.build.bindir, "app"), env="conanrun") - """) - - -@pytest.mark.tool("cmake") -def test_static_lib_shipping_shared_lib(): - """ a package_type=static-library package that also contains a shared library, which the - consumer links too, and which has to be available at runtime => runtime_artifacts=True - """ - c = TestClient() - app_cmake = textwrap.dedent("""\ - cmake_minimum_required(VERSION 3.15) - project(app CXX) - - find_package(liba CONFIG REQUIRED) - - add_executable(app main.cpp) - target_link_libraries(app PRIVATE liba::liba) - """) - main_cpp = textwrap.dedent("""\ - #include "liba.h" - int main() { - mystatic_func(); - } - """) - c.save(_liba_files(runtime_artifacts=True)) - c.save({"app/conanfile.py": app_conanfile.format(requires="liba/0.1"), - "app/CMakeLists.txt": app_cmake, - "app/main.cpp": main_cpp}) - c.run("create liba") - - c.run("graph info app --format=json", redirect_stdout="graph.json") - liba_require = json.loads(c.load("graph.json"))["graph"]["nodes"]["0"]["dependencies"]["1"] - # The "run" trait is True, even if liba is a static-library - assert liba_require["ref"] == "liba/0.1" - assert liba_require["run"] is True - assert liba_require["libs"] is True - - c.run("build app") - # If "runtime_artifacts" was not defined, run=False, so the shared library location would - # not be in the "conanrun" environment, and this would fail to run in Windows - assert "liba: mystatic_func!" in c.out - assert "liba: myshared_func!" in c.out - - -@pytest.mark.tool("cmake") -@pytest.mark.parametrize("runtime_artifacts", [True, False]) -def test_static_lib_shipping_shared_lib_transitive(runtime_artifacts): - """ app -> libb (shared-library) -> liba (static-library shipping a shared library) - The libs of liba are linked into libb.so, so app doesn't need them, but the shared library - inside liba still has to be there at runtime, so its binary cannot be skipped - """ - c = TestClient() - libb_conanfile = textwrap.dedent("""\ - from conan import ConanFile - from conan.tools.cmake import CMake, cmake_layout - - class LibB(ConanFile): - name = "libb" - version = "0.1" - package_type = "shared-library" - settings = "os", "compiler", "build_type", "arch" - generators = "CMakeToolchain", "CMakeDeps" - requires = "liba/0.1" - exports_sources = "CMakeLists.txt", "include/*", "src/*" - - def layout(self): - cmake_layout(self) - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - - def package(self): - cmake = CMake(self) - cmake.install() - - def package_info(self): - self.cpp_info.libs = ["libb"] - """) - libb_cmake = textwrap.dedent("""\ - cmake_minimum_required(VERSION 3.15) - project(libb CXX) - - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - find_package(liba CONFIG REQUIRED) - - add_library(libb SHARED src/libb.cpp) - target_include_directories(libb PUBLIC include) - target_link_libraries(libb PRIVATE liba::liba) - - install(TARGETS libb) - install(DIRECTORY include/ DESTINATION include) - """) - app_cmake = textwrap.dedent("""\ - cmake_minimum_required(VERSION 3.15) - project(app CXX) - - find_package(libb CONFIG REQUIRED) - - add_executable(app main.cpp) - target_link_libraries(app PRIVATE libb::libb) - """) - c.save(_liba_files(runtime_artifacts)) - c.save({"libb/conanfile.py": libb_conanfile, - "libb/CMakeLists.txt": libb_cmake, - "libb/include/libb.h": "#pragma once\nvoid libb_func();\n", - "libb/src/libb.cpp": '#include "libb.h"\n#include "liba.h"\n' - "void libb_func() { mystatic_func(); }\n", - "app/conanfile.py": app_conanfile.format(requires="libb/0.1"), - "app/CMakeLists.txt": app_cmake, - "app/main.cpp": '#include "libb.h"\nint main() { libb_func(); }\n'}) - c.run("create liba") - c.run("create libb") - - if runtime_artifacts: - c.run("build app") - # The liba binary is necessary at runtime, so it cannot be skipped - assert "Skipped binaries" not in c.out - assert "liba: mystatic_func!" in c.out - assert "liba: myshared_func!" in c.out - else: - # Without "runtime_artifacts" the run trait is False, and as liba is a static-library - # linked inside libb, Conan understands its binary is not necessary anymore and skips it, - # but the shared library it ships is still necessary, and the app cannot run - c.run("build app", assert_error=True) - assert "Skipped binaries" in c.out - assert "liba/0.1" in c.out - assert "Error in build() method" in c.out diff --git a/test/integration/graph/test_runtime_artifacts.py b/test/integration/graph/test_runtime_artifacts.py new file mode 100644 index 00000000000..f9f89d80395 --- /dev/null +++ b/test/integration/graph/test_runtime_artifacts.py @@ -0,0 +1,89 @@ +import re +import textwrap + +import pytest + +from conan.test.assets.genconanfile import GenConanfile +from conan.test.utils.tools import TestClient + + +liba = textwrap.dedent("""\ + import os + from conan import ConanFile + from conan.tools.files import save + + class LibA(ConanFile): + name = "liba" + version = "0.1" + package_type = "static-library" + runtime_artifacts = {runtime_artifacts} + + def package(self): + # This static-library also ships a shared library, faked here with a script, that + # consumers link, and that has to be found at runtime + save(self, os.path.join(self.package_folder, "lib", "liba.a"), "fake static lib") + save(self, os.path.join(self.package_folder, "bin", "myshared.bat"), + "@echo off\\necho MYSHARED RUNTIME!!") + sh = os.path.join(self.package_folder, "bin", "myshared.sh") + save(self, sh, "echo MYSHARED RUNTIME!!") + os.chmod(sh, 0o777) + """) + +app = textwrap.dedent("""\ + import platform + from conan import ConanFile + + class App(ConanFile): + requires = "{requires}" + + def build(self): + cmd = "myshared.bat" if platform.system() == "Windows" else "myshared.sh" + self.run(cmd, env="conanrun") + """) + + +@pytest.mark.parametrize("runtime_artifacts", [True, False]) +def test_runtime_artifacts(runtime_artifacts): + """ a package_type=static-library that also contains a shared library, which the consumer + has to find at runtime, so its location must be in the "conanrun" environment + """ + c = TestClient(light=True) + c.save({"liba/conanfile.py": liba.format(runtime_artifacts=runtime_artifacts), + "app/conanfile.py": app.format(requires="liba/0.1")}) + c.run("create liba") + + c.run("build app", assert_error=not runtime_artifacts) + if runtime_artifacts: + assert "MYSHARED RUNTIME!!" in c.out + else: + # Without "runtime_artifacts" the run trait is False, as it is a static-library, so the + # "bindirs" of liba are not added to the "conanrun" environment + assert "MYSHARED RUNTIME!!" not in c.out + assert "Error in build() method" in c.out + + +@pytest.mark.parametrize("runtime_artifacts", [True, False]) +def test_runtime_artifacts_transitive(runtime_artifacts): + """ app -> libb (shared-library) -> liba (static-library shipping a shared library) + The libs of liba are linked inside libb, so app doesn't need them, but the shared library + that liba ships still has to be there at runtime, so its binary cannot be skipped + """ + c = TestClient(light=True) + c.save({"liba/conanfile.py": liba.format(runtime_artifacts=runtime_artifacts), + "libb/conanfile.py": GenConanfile("libb", "0.1").with_package_type("shared-library") + .with_requires("liba/0.1"), + "app/conanfile.py": app.format(requires="libb/0.1")}) + c.run("create liba") + c.run("create libb") + + c.run("build app", assert_error=not runtime_artifacts) + if runtime_artifacts: + # The liba binary is necessary at runtime, so it cannot be skipped + assert "Skipped binaries" not in c.out + assert "MYSHARED RUNTIME!!" in c.out + else: + # Without "runtime_artifacts" the run trait is False, and as liba is a static-library + # linked inside libb, Conan understands its binary is not necessary anymore and skips it + assert re.search(r"Skipped binaries(\s*)liba/0.1", c.out) + assert "MYSHARED RUNTIME!!" not in c.out + assert "Error in build() method" in c.out From b3fdb992e7a23d3aa52a5f14dd6d542073beb047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Wed, 29 Jul 2026 15:27:29 +0200 Subject: [PATCH 3/3] Better tests --- test/integration/graph/test_runtime_artifacts.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/integration/graph/test_runtime_artifacts.py b/test/integration/graph/test_runtime_artifacts.py index f9f89d80395..917339b4a7e 100644 --- a/test/integration/graph/test_runtime_artifacts.py +++ b/test/integration/graph/test_runtime_artifacts.py @@ -34,6 +34,7 @@ def package(self): from conan import ConanFile class App(ConanFile): + settings = "os" # Necessary, or the "conanrun" env would be a .sh script in Windows requires = "{requires}" def build(self): @@ -42,6 +43,13 @@ def build(self): """) +def _not_found(out): + """ the shell couldn't find our script, that is, it was not in the PATH of the run + environment: 'not recognized' in cmd, 'not found' in sh (with or without 'command') + """ + return re.search(r"myshared\.(bat|sh).*(not recognized|not found)", out) + + @pytest.mark.parametrize("runtime_artifacts", [True, False]) def test_runtime_artifacts(runtime_artifacts): """ a package_type=static-library that also contains a shared library, which the consumer @@ -59,6 +67,7 @@ def test_runtime_artifacts(runtime_artifacts): # Without "runtime_artifacts" the run trait is False, as it is a static-library, so the # "bindirs" of liba are not added to the "conanrun" environment assert "MYSHARED RUNTIME!!" not in c.out + assert _not_found(c.out) assert "Error in build() method" in c.out @@ -86,4 +95,5 @@ def test_runtime_artifacts_transitive(runtime_artifacts): # linked inside libb, Conan understands its binary is not necessary anymore and skips it assert re.search(r"Skipped binaries(\s*)liba/0.1", c.out) assert "MYSHARED RUNTIME!!" not in c.out + assert _not_found(c.out) assert "Error in build() method" in c.out