diff --git a/conan/api/subapi/install.py b/conan/api/subapi/install.py index 657f9a419af..c12862ab2e0 100644 --- a/conan/api/subapi/install.py +++ b/conan/api/subapi/install.py @@ -50,7 +50,8 @@ def install_consumer(self, deps_graph, generators=None, source_folder=None, outp _do_deploys(self.conan_api, deps_graph, deploy, base_folder) conanfile.generators = list(set(conanfile.generators).union(generators or [])) - write_generators(conanfile) + app = ConanApp(self.conan_api.cache_folder) + write_generators(conanfile, app.hook_manager) call_system_requirements(conanfile) diff --git a/conans/client/conanfile/build.py b/conans/client/conanfile/build.py index bd7b45f5d47..b0be6955c90 100644 --- a/conans/client/conanfile/build.py +++ b/conans/client/conanfile/build.py @@ -8,5 +8,9 @@ def run_build_method(conanfile, hook_manager): hook_manager.execute("pre_build", conanfile=conanfile) conanfile.output.highlight("Calling build()") with conanfile_exception_formatter(conanfile, "build"): - conanfile.build() + try: + conanfile.build() + except Exception: + hook_manager.execute("post_build_fail", conanfile=conanfile) + raise hook_manager.execute("post_build", conanfile=conanfile) diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index 4bf6651da48..385d3d6e015 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -75,10 +75,12 @@ def _get_generator_class(generator_name): "not complete".format(generator_name)) -def write_generators(conanfile): +def write_generators(conanfile, hook_manager): new_gen_folder = conanfile.generators_folder _receive_conf(conanfile) + hook_manager.execute("pre_generate", conanfile=conanfile) + for generator_name in set(conanfile.generators): generator_class = _get_generator_class(generator_name) if generator_class: @@ -117,6 +119,8 @@ def write_generators(conanfile): conanfile.output.highlight("Aggregating env generators") _generate_aggregated_env(conanfile) + hook_manager.execute("post_generate", conanfile=conanfile) + def _receive_conf(conanfile): """ collect conf_info from the immediate build_requires, aggregate it and injects/update diff --git a/conans/client/hook_manager.py b/conans/client/hook_manager.py index 4208317054c..2df571f4204 100644 --- a/conans/client/hook_manager.py +++ b/conans/client/hook_manager.py @@ -5,7 +5,8 @@ valid_hook_methods = ["pre_export", "post_export", "pre_source", "post_source", - "pre_build", "post_build", + "pre_generate", "post_generate", + "pre_build", "post_build", "post_build_fail", "pre_package", "post_package", "pre_package_info", "post_package_info"] diff --git a/conans/client/installer.py b/conans/client/installer.py index 822b2ad915e..c2df019dd5c 100644 --- a/conans/client/installer.py +++ b/conans/client/installer.py @@ -106,7 +106,7 @@ def _copy_sources(conanfile, source_folder, build_folder): raise ConanException("%s\nError copying sources to build folder" % msg) def _build(self, conanfile, pref): - write_generators(conanfile) + write_generators(conanfile, self._hook_manager) try: run_build_method(conanfile, self._hook_manager) @@ -304,7 +304,7 @@ def _handle_node_editable(self, install_node): output = conanfile.output output.info("Rewriting files of editable package " "'{}' at '{}'".format(conanfile.name, conanfile.generators_folder)) - write_generators(conanfile) + write_generators(conanfile, self._hook_manager) call_system_requirements(conanfile) if node.binary == BINARY_EDITABLE_BUILD: diff --git a/conans/test/integration/hooks/hook_test.py b/conans/test/integration/hooks/hook_test.py index 7a91c0cb6f4..0678ef81e2f 100644 --- a/conans/test/integration/hooks/hook_test.py +++ b/conans/test/integration/hooks/hook_test.py @@ -21,6 +21,12 @@ def pre_source(conanfile): def post_source(conanfile): conanfile.output.info("Hello") +def pre_generate(conanfile): + conanfile.output.info("Hello") + +def post_generate(conanfile): + conanfile.output.info("Hello") + def pre_build(conanfile): conanfile.output.info("Hello") @@ -55,10 +61,14 @@ def test_complete_hook(self): assert f"conanfile.py (pkg/0.1): {hook_msg} post_source(): Hello" in c.out c.run("install .") - assert "HOOK" not in c.out + assert f"conanfile.py (pkg/0.1): {hook_msg} pre_generate(): Hello" in c.out + assert f"conanfile.py (pkg/0.1): {hook_msg} post_generate(): Hello" in c.out + c.run("build .") assert f"conanfile.py (pkg/0.1): {hook_msg} pre_build(): Hello" in c.out assert f"conanfile.py (pkg/0.1): {hook_msg} post_build(): Hello" in c.out + assert f"conanfile.py (pkg/0.1): {hook_msg} pre_generate(): Hello" in c.out + assert f"conanfile.py (pkg/0.1): {hook_msg} post_generate(): Hello" in c.out c.run("export . ") assert f"pkg/0.1: {hook_msg} pre_export(): Hello" in c.out @@ -123,3 +133,26 @@ def pre_export(conanfile): c.run("export . ", assert_error=True) assert "ERROR: [HOOK - my_hook/hook_my_hook.py] pre_export(): Boom" in c.out + + def test_post_build_fail(self): + """ Test the post_build_fail hook + """ + c = TestClient() + my_hook = textwrap.dedent(""" + def post_build_fail(conanfile): + conanfile.output.info("Hello") + """) + hook_path = os.path.join(c.cache.hooks_path, "my_hook", "hook_my_hook.py") + save(hook_path, my_hook) + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + def build(self): + raise Exception("Boom!") + """) + c.save({"conanfile.py": conanfile}) + + c.run("build . ", assert_error=True) + assert "conanfile.py: [HOOK - my_hook/hook_my_hook.py] post_build_fail(): Hello" in c.out + assert "ERROR: conanfile.py: Error in build() method, line 5" in c.out +