diff --git a/conan/api/subapi/local.py b/conan/api/subapi/local.py index a3925fddebf..ed45ce646a3 100644 --- a/conan/api/subapi/local.py +++ b/conan/api/subapi/local.py @@ -81,6 +81,7 @@ def source(self, path, name=None, version=None, user=None, channel=None): folder = conanfile.recipe_folder conanfile.folders.set_base_source(folder) conanfile.folders.set_base_export_sources(folder) + conanfile.folders.set_base_recipe_metadata(os.path.join(folder, "metadata")) conanfile.folders.set_base_build(None) conanfile.folders.set_base_package(None) @@ -92,6 +93,7 @@ def build(self, conanfile): """ app = ConanApp(self._conan_api.cache_folder) conanfile.folders.set_base_package(conanfile.folders.base_build) + conanfile.folders.set_base_pkg_metadata(os.path.join(conanfile.build_folder, "metadata")) run_build_method(conanfile, app.hook_manager) @staticmethod diff --git a/conan/internal/cache/conan_reference_layout.py b/conan/internal/cache/conan_reference_layout.py index 1412368b21c..a4c29ac39b4 100644 --- a/conan/internal/cache/conan_reference_layout.py +++ b/conan/internal/cache/conan_reference_layout.py @@ -50,6 +50,9 @@ def export(self): def export_sources(self): return os.path.join(self.base_folder, EXPORT_SRC_FOLDER) + def metadata(self): + return os.path.join(self.download_export(), "metadata") + def download_export(self): return os.path.join(self.base_folder, DOWNLOAD_EXPORT_FOLDER) @@ -112,6 +115,9 @@ def package(self): def download_package(self): return os.path.join(self.base_folder, DOWNLOAD_EXPORT_FOLDER) + def metadata(self): + return os.path.join(self.download_package(), "metadata") + def package_manifests(self): package_folder = self.package() readed_manifest = FileTreeManifest.load(package_folder) diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py index 35daa5cd0cf..d7f54384988 100644 --- a/conans/client/cmd/export.py +++ b/conans/client/cmd/export.py @@ -39,6 +39,7 @@ def cmd_export(app, conanfile_path, name, version, user, channel, graph_lock=Non # TODO: cache2.0 move this creation to other place mkdir(export_folder) mkdir(export_src_folder) + conanfile.folders.set_base_recipe_metadata(recipe_layout.metadata()) export_recipe(conanfile, export_folder) export_source(conanfile, export_src_folder) shutil.copy2(conanfile_path, recipe_layout.conanfile()) diff --git a/conans/client/installer.py b/conans/client/installer.py index d0f9bda379f..2f0de5f4063 100644 --- a/conans/client/installer.py +++ b/conans/client/installer.py @@ -145,6 +145,7 @@ def build_package(self, node, package_layout): conanfile.folders.set_base_package(base_package) # In local cache, generators folder always in build_folder conanfile.folders.set_base_generators(base_build) + conanfile.folders.set_base_pkg_metadata(package_layout.metadata()) if not skip_build: # In local cache, install folder always is build_folder @@ -187,6 +188,7 @@ def _install_source(self, node, remotes): conanfile.folders.set_base_source(source_folder) conanfile.folders.set_base_export_sources(source_folder) + conanfile.folders.set_base_recipe_metadata(recipe_layout.metadata()) config_source(export_source_folder, conanfile, self._hook_manager) @staticmethod diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py index 173c406d1fa..592938a48cf 100644 --- a/conans/model/conan_file.py +++ b/conans/model/conan_file.py @@ -252,6 +252,14 @@ def build_folder(self): """ return self.folders.build_folder + @property + def recipe_metadata_folder(self): + return self.folders.recipe_metadata_folder + + @property + def pkg_metadata_folder(self): + return self.folders.pkg_metadata_folder + @property def build_path(self) -> Path: assert self.build_folder is not None, "`build_folder` is `None`" diff --git a/conans/model/layout.py b/conans/model/layout.py index e9149b001e3..5a6650e7e9f 100644 --- a/conans/model/layout.py +++ b/conans/model/layout.py @@ -43,6 +43,9 @@ def __init__(self): self._base_export = None self._base_export_sources = None + self._base_recipe_metadata = None + self._base_pkg_metadata = None + self.source = "" self.build = "" self.package = "" @@ -78,6 +81,8 @@ def set_base_folders(self, conanfile_folder, output_folder): self._base_build = output_folder or base_folder self._base_generators = output_folder or base_folder self._base_export_sources = output_folder or base_folder + self._base_recipe_metadata = base_folder + self._base_pkg_metadata = output_folder or base_folder @property def source_folder(self): @@ -103,6 +108,20 @@ def build_folder(self): return self._base_build return os.path.join(self._base_build, self.build) + @property + def recipe_metadata_folder(self): + return self._base_recipe_metadata + + def set_base_recipe_metadata(self, folder): + self._base_recipe_metadata = folder + + @property + def pkg_metadata_folder(self): + return self._base_pkg_metadata + + def set_base_pkg_metadata(self, folder): + self._base_pkg_metadata = folder + @property def base_build(self): return self._base_build diff --git a/conans/test/integration/metadata/__init__.py b/conans/test/integration/metadata/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/conans/test/integration/metadata/test_metadata_logs.py b/conans/test/integration/metadata/test_metadata_logs.py new file mode 100644 index 00000000000..391ff6e6a3e --- /dev/null +++ b/conans/test/integration/metadata/test_metadata_logs.py @@ -0,0 +1,140 @@ +import os +import textwrap + +import pytest + +from conans.model.recipe_ref import RecipeReference +from conans.test.utils.tools import TestClient +from conans.util.files import load, save + + +class TestRecipeMetadataLogs: + + conanfile = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.files import save, copy + + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + + def export(self): + copy(self, "*.log", src=self.recipe_folder, + dst=os.path.join(self.recipe_metadata_folder, "logs")) + + def layout(self): + self.folders.build = "mybuild" + self.folders.generators = "mybuild/generators" + + def source(self): + save(self, os.path.join(self.recipe_metadata_folder, "logs", "src.log"), "srclog!!") + + def build(self): + save(self, "mylogs.txt", "some logs!!!") + copy(self, "mylogs.txt", src=self.build_folder, + dst=os.path.join(self.pkg_metadata_folder, "logs")) + """) + + def test_metadata_logs(self): + c = TestClient(default_server_user=True) + c.save({"conanfile.py": self.conanfile, + "file.log": "log contents!"}) + c.run("create .") + # Test local cache looks good + ref = RecipeReference.loads("pkg/0.1") + ref_layout = c.get_latest_ref_layout(ref) + assert os.listdir(ref_layout.metadata()) == ["logs"] + assert os.listdir(os.path.join(ref_layout.metadata(), "logs")) == ["file.log", "src.log"] + assert load(os.path.join(ref_layout.metadata(), "logs", "file.log")) == "log contents!" + assert load(os.path.join(ref_layout.metadata(), "logs", "src.log")) == "srclog!!" + + pref = c.get_latest_package_reference(ref) + pref_layout = c.get_latest_pkg_layout(pref) + assert os.listdir(pref_layout.metadata()) == ["logs"] + assert os.listdir(os.path.join(pref_layout.metadata(), "logs")) == ["mylogs.txt"] + assert load(os.path.join(pref_layout.metadata(), "logs", "mylogs.txt")) == "some logs!!!" + + def test_metadata_logs_local(self): + c = TestClient(default_server_user=True) + c.save({"conanfile.py": self.conanfile, + "file.log": "log contents!"}) + c.run("source .") + assert c.load("metadata/logs/src.log") == "srclog!!" + c.run("build .") + assert c.load("mybuild/metadata/logs/mylogs.txt") == "some logs!!!" + + +class TestHooksMetadataLogs: + + @pytest.fixture() + def _client(self): + c = TestClient(default_server_user=True) + my_hook = textwrap.dedent("""\ + import os + from conan.tools.files import copy + + def post_export(conanfile): + conanfile.output.info("post_export") + copy(conanfile, "*.log", src=conanfile.recipe_folder, + dst=os.path.join(conanfile.recipe_metadata_folder, "logs")) + + def post_source(conanfile): + conanfile.output.info("post_source") + copy(conanfile, "*", src=os.path.join(conanfile.source_folder, "logs"), + dst=os.path.join(conanfile.recipe_metadata_folder, "logs")) + + def post_build(conanfile): + conanfile.output.info("post_build") + copy(conanfile, "*", src=os.path.join(conanfile.build_folder, "logs"), + dst=os.path.join(conanfile.pkg_metadata_folder, "logs")) + """) + hook_path = os.path.join(c.cache.hooks_path, "my_hook", "hook_my_hook.py") + save(hook_path, my_hook) + conanfile = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.files import save, copy + + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + no_copy_source = True + + def layout(self): + self.folders.build = "mybuild" + self.folders.generators = "mybuild/generators" + + def source(self): + save(self, "logs/src.log", "srclog!!") + + def build(self): + save(self, "logs/mylogs.txt", "some logs!!!") + """) + c.save({"conanfile.py": conanfile, + "file.log": "log contents!"}) + return c + + def test_metadata_logs_hook(self, _client): + c = _client + c.run("create .") + # Test local cache looks good + ref = RecipeReference.loads("pkg/0.1") + ref_layout = c.get_latest_ref_layout(ref) + assert os.listdir(ref_layout.metadata()) == ["logs"] + assert os.listdir(os.path.join(ref_layout.metadata(), "logs")) == ["file.log", "src.log"] + assert load(os.path.join(ref_layout.metadata(), "logs", "file.log")) == "log contents!" + assert load(os.path.join(ref_layout.metadata(), "logs", "src.log")) == "srclog!!" + + pref = c.get_latest_package_reference(ref) + pref_layout = c.get_latest_pkg_layout(pref) + assert os.listdir(pref_layout.metadata()) == ["logs"] + assert os.listdir(os.path.join(pref_layout.metadata(), "logs")) == ["mylogs.txt"] + assert load(os.path.join(pref_layout.metadata(), "logs", "mylogs.txt")) == "some logs!!!" + + def test_metadata_logs_local(self, _client): + c = _client + c.run("source .") + assert c.load("metadata/logs/src.log") == "srclog!!" + c.run("build .") + assert c.load("mybuild/metadata/logs/mylogs.txt") == "some logs!!!"