prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Support project hooks for core24 snaps
  ### What needs to get done
  
  The `PackageService` for core24 snaps should support project hooks.  The behavior should be the same as core22.
  
  The failures can be found by running `spread google:ubuntu-24.04-64:tests/spread/general/hooks/`.  See failing logs [here](https://paste.ubuntu.com/p/CjBwVKcwyR/).
  
  ### Why it needs to get done
  
  To support building core24 snaps with craft-application
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `snapcraft/services/package.py`
  Content:
  ```
  1 # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
  2 #
  3 # Copyright 2023 Canonical Ltd.
  4 #
  5 # This program is free software: you can redistribute it and/or modify
  6 # it under the terms of the GNU General Public License version 3 as
  7 # published by the Free Software Foundation.
  8 #
  9 # This program is distributed in the hope that it will be useful,
  10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 # GNU General Public License for more details.
  13 #
  14 # You should have received a copy of the GNU General Public License
  15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16 
  17 """Snapcraft Package service."""
  18 
  19 from __future__ import annotations
  20 
  21 import os
  22 import pathlib
  23 import shutil
  24 from typing import TYPE_CHECKING, cast
  25 
  26 from craft_application import AppMetadata, PackageService
  27 from overrides import override
  28 
  29 from snapcraft import errors, linters, models, pack, utils
  30 from snapcraft.linters import LinterStatus
  31 from snapcraft.meta import snap_yaml
  32 from snapcraft.services import Lifecycle
  33 from snapcraft.utils import process_version
  34 
  35 if TYPE_CHECKING:
  36     from snapcraft.services import SnapcraftServiceFactory
  37 
  38 
  39 class Package(PackageService):
  40     """Package service subclass for Snapcraft."""
  41 
  42     _project: models.Project
  43 
  44     def __init__(  # noqa: PLR0913 (Too many arguments)
  45         self,
  46         app: AppMetadata,
  47         services: SnapcraftServiceFactory,
  48         *,
  49         project: models.Project,
  50         snapcraft_yaml_path: pathlib.Path,
  51         platform: str | None,
  52         build_for: str,
  53     ) -> None:
  54         super().__init__(app, services, project=project)
  55         self._platform = platform
  56         self._build_for = build_for
  57         self._snapcraft_yaml_path = snapcraft_yaml_path
  58 
  59     @override
  60     def pack(self, prime_dir: pathlib.Path, dest: pathlib.Path) -> list[pathlib.Path]:
  61         """Create one or more packages as appropriate.
  62 
  63         :param prime_dir: Path to the directory to pack.
  64         :param dest: Directory into which to write the package(s).
  65         :returns: A list of paths to created packages.
  66         """
  67         issues = linters.run_linters(prime_dir, lint=self._project.lint)
  68         status = linters.report(issues, intermediate=True)
  69 
  70         # In case of linter errors, stop execution and return the error code.
  71         if status in (LinterStatus.ERRORS, LinterStatus.FATAL):
  72             raise errors.LinterError("Linter errors found", exit_code=status)
  73 
  74         return [
  75             pathlib.Path(
  76                 pack.pack_snap(
  77                     prime_dir,
  78                     output=str(dest),
  79                     compression=self._project.compression,
  80                     name=self._project.name,
  81                     version=process_version(self._project.version),
  82                     target_arch=self._build_for,
  83                 )
  84             )
  85         ]
  86 
  87     @override
  88     def write_metadata(self, path: pathlib.Path) -> None:
  89         """Write the project metadata to metadata.yaml in the given directory.
  90 
  91         :param path: The path to the prime directory.
  92         """
  93         meta_dir = path / "meta"
  94         meta_dir.mkdir(parents=True, exist_ok=True)
  95         self.metadata.to_yaml_file(meta_dir / "snap.yaml")
  96 
  97         enable_manifest = utils.strtobool(os.getenv("SNAPCRAFT_BUILD_INFO", "n"))
  98 
  99         if enable_manifest:
  100             snap_dir = path / "snap"
  101             snap_dir.mkdir(parents=True, exist_ok=True)
  102             lifecycle = cast(Lifecycle, self._services.lifecycle)
  103             manifest = lifecycle.generate_manifest()
  104             manifest.to_yaml_file(snap_dir / "manifest.yaml")
  105 
  106             shutil.copy(self._snapcraft_yaml_path, snap_dir)
  107 
  108     @property
  109     def metadata(self) -> snap_yaml.SnapMetadata:
  110         """Get the metadata model for this project."""
  111         return snap_yaml.get_metadata_from_project(
  112             self._project, self._services.lifecycle.prime_dir, arch=self._build_for
  113         )
  114
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/snapcraft/services/package.py b/snapcraft/services/package.py
  --- a/snapcraft/services/package.py
  +++ b/snapcraft/services/package.py
  @@ -29,6 +29,7 @@
   from snapcraft import errors, linters, models, pack, utils
   from snapcraft.linters import LinterStatus
   from snapcraft.meta import snap_yaml
  +from snapcraft.parts.setup_assets import setup_assets
   from snapcraft.services import Lifecycle
   from snapcraft.utils import process_version
   
  @@ -84,6 +85,23 @@
               )
           ]
   
  +    def _get_assets_dir(self) -> pathlib.Path:
  +        """Return a snapcraft assets directory.
  +
  +        Asset directories can exist in:
  +
  +        - <PROJECT_ROOT>/snap
  +        - <PROJECT_ROOT>/build-aux/snap
  +        """
  +        project_dir = self._services.lifecycle.project_info.project_dir
  +        for asset_reldir in ("snap", "build-aux/snap"):
  +            asset_dir = project_dir / asset_reldir
  +            if asset_dir.exists():
  +                return asset_dir
  +
  +        # This is for backwards compatibility with setup_assets(...)
  +        return project_dir / "snap"
  +
       @override
       def write_metadata(self, path: pathlib.Path) -> None:
           """Write the project metadata to metadata.yaml in the given directory.
  @@ -105,9 +123,79 @@
   
               shutil.copy(self._snapcraft_yaml_path, snap_dir)
   
  +        assets_dir = self._get_assets_dir()
  +        setup_assets(
  +            self._project,
  +            assets_dir=assets_dir,
  +            project_dir=self._services.lifecycle.project_info.project_dir,
  +            prime_dir=path,
  +            meta_directory_handler=meta_directory_handler,
  +        )
  +
       @property
       def metadata(self) -> snap_yaml.SnapMetadata:
           """Get the metadata model for this project."""
           return snap_yaml.get_metadata_from_project(
               self._project, self._services.lifecycle.prime_dir, arch=self._build_for
           )
  +
  +
  +def _hardlink_or_copy(source: pathlib.Path, destination: pathlib.Path) -> bool:
  +    """Try to hardlink and fallback to copy if it fails.
  +
  +    :param source: the source path.
  +    :param destination: the destination path.
  +    :returns: True if a hardlink was done or False for copy.
  +    """
  +    # Unlink the destination to avoid link failures
  +    destination.unlink(missing_ok=True)
  +
  +    try:
  +        destination.hardlink_to(source)
  +    except OSError as os_error:
  +        # Cross device link
  +        if os_error.errno != 18:
  +            raise
  +        shutil.copy(source, destination)
  +        return False
  +
  +    return True
  +
  +
  +def meta_directory_handler(assets_dir: pathlib.Path, path: pathlib.Path):
  +    """Handle hooks and gui assets from Snapcraft.
  +
  +    :param assets_dir: directory with project assets.
  +    :param path: directory to write assets to.
  +    """
  +    meta_dir = path / "meta"
  +    built_snap_hooks = path / "snap" / "hooks"
  +    hooks_project_dir = assets_dir / "hooks"
  +
  +    hooks_meta_dir = meta_dir / "hooks"
  +
  +    if built_snap_hooks.is_dir():
  +        hooks_meta_dir.mkdir(parents=True, exist_ok=True)
  +        for hook in built_snap_hooks.iterdir():
  +            meta_dir_hook = hooks_meta_dir / hook.name
  +            # Remove to always refresh to the latest
  +            meta_dir_hook.unlink(missing_ok=True)
  +            meta_dir_hook.hardlink_to(hook)
  +
  +    # Overwrite any built hooks with project level ones
  +    if hooks_project_dir.is_dir():
  +        hooks_meta_dir.mkdir(parents=True, exist_ok=True)
  +        for hook in hooks_project_dir.iterdir():
  +            meta_dir_hook = hooks_meta_dir / hook.name
  +
  +            _hardlink_or_copy(hook, meta_dir_hook)
  +
  +    # Write any gui assets
  +    gui_project_dir = assets_dir / "gui"
  +    gui_meta_dir = meta_dir / "gui"
  +    if gui_project_dir.is_dir():
  +        gui_meta_dir.mkdir(parents=True, exist_ok=True)
  +        for gui in gui_project_dir.iterdir():
  +            meta_dir_gui = gui_meta_dir / gui.name
  +
  +            _hardlink_or_copy(gui, meta_dir_gui)
  
verification_info: {"golden_diff": "diff --git a/snapcraft/services/package.py b/snapcraft/services/package.py\n--- a/snapcraft/services/package.py\n+++ b/snapcraft/services/package.py\n@@ -29,6 +29,7 @@\n from snapcraft import errors, linters, models, pack, utils\n from snapcraft.linters import LinterStatus\n from snapcraft.meta import snap_yaml\n+from snapcraft.parts.setup_assets import setup_assets\n from snapcraft.services import Lifecycle\n from snapcraft.utils import process_version\n \n@@ -84,6 +85,23 @@\n             )\n         ]\n \n+    def _get_assets_dir(self) -> pathlib.Path:\n+        \"\"\"Return a snapcraft assets directory.\n+\n+        Asset directories can exist in:\n+\n+        - <PROJECT_ROOT>/snap\n+        - <PROJECT_ROOT>/build-aux/snap\n+        \"\"\"\n+        project_dir = self._services.lifecycle.project_info.project_dir\n+        for asset_reldir in (\"snap\", \"build-aux/snap\"):\n+            asset_dir = project_dir / asset_reldir\n+            if asset_dir.exists():\n+                return asset_dir\n+\n+        # This is for backwards compatibility with setup_assets(...)\n+        return project_dir / \"snap\"\n+\n     @override\n     def write_metadata(self, path: pathlib.Path) -> None:\n         \"\"\"Write the project metadata to metadata.yaml in the given directory.\n@@ -105,9 +123,79 @@\n \n             shutil.copy(self._snapcraft_yaml_path, snap_dir)\n \n+        assets_dir = self._get_assets_dir()\n+        setup_assets(\n+            self._project,\n+            assets_dir=assets_dir,\n+            project_dir=self._services.lifecycle.project_info.project_dir,\n+            prime_dir=path,\n+            meta_directory_handler=meta_directory_handler,\n+        )\n+\n     @property\n     def metadata(self) -> snap_yaml.SnapMetadata:\n         \"\"\"Get the metadata model for this project.\"\"\"\n         return snap_yaml.get_metadata_from_project(\n             self._project, self._services.lifecycle.prime_dir, arch=self._build_for\n         )\n+\n+\n+def _hardlink_or_copy(source: pathlib.Path, destination: pathlib.Path) -> bool:\n+    \"\"\"Try to hardlink and fallback to copy if it fails.\n+\n+    :param source: the source path.\n+    :param destination: the destination path.\n+    :returns: True if a hardlink was done or False for copy.\n+    \"\"\"\n+    # Unlink the destination to avoid link failures\n+    destination.unlink(missing_ok=True)\n+\n+    try:\n+        destination.hardlink_to(source)\n+    except OSError as os_error:\n+        # Cross device link\n+        if os_error.errno != 18:\n+            raise\n+        shutil.copy(source, destination)\n+        return False\n+\n+    return True\n+\n+\n+def meta_directory_handler(assets_dir: pathlib.Path, path: pathlib.Path):\n+    \"\"\"Handle hooks and gui assets from Snapcraft.\n+\n+    :param assets_dir: directory with project assets.\n+    :param path: directory to write assets to.\n+    \"\"\"\n+    meta_dir = path / \"meta\"\n+    built_snap_hooks = path / \"snap\" / \"hooks\"\n+    hooks_project_dir = assets_dir / \"hooks\"\n+\n+    hooks_meta_dir = meta_dir / \"hooks\"\n+\n+    if built_snap_hooks.is_dir():\n+        hooks_meta_dir.mkdir(parents=True, exist_ok=True)\n+        for hook in built_snap_hooks.iterdir():\n+            meta_dir_hook = hooks_meta_dir / hook.name\n+            # Remove to always refresh to the latest\n+            meta_dir_hook.unlink(missing_ok=True)\n+            meta_dir_hook.hardlink_to(hook)\n+\n+    # Overwrite any built hooks with project level ones\n+    if hooks_project_dir.is_dir():\n+        hooks_meta_dir.mkdir(parents=True, exist_ok=True)\n+        for hook in hooks_project_dir.iterdir():\n+            meta_dir_hook = hooks_meta_dir / hook.name\n+\n+            _hardlink_or_copy(hook, meta_dir_hook)\n+\n+    # Write any gui assets\n+    gui_project_dir = assets_dir / \"gui\"\n+    gui_meta_dir = meta_dir / \"gui\"\n+    if gui_project_dir.is_dir():\n+        gui_meta_dir.mkdir(parents=True, exist_ok=True)\n+        for gui in gui_project_dir.iterdir():\n+            meta_dir_gui = gui_meta_dir / gui.name\n+\n+            _hardlink_or_copy(gui, meta_dir_gui)\n", "issue": "Support project hooks for core24 snaps\n### What needs to get done\n\nThe `PackageService` for core24 snaps should support project hooks.  The behavior should be the same as core22.\r\n\r\nThe failures can be found by running `spread google:ubuntu-24.04-64:tests/spread/general/hooks/`.  See failing logs [here](https://paste.ubuntu.com/p/CjBwVKcwyR/).\n\n### Why it needs to get done\n\nTo support building core24 snaps with craft-application\n", "before_files": [{"content": "# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-\n#\n# Copyright 2023 Canonical Ltd.\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License version 3 as\n# published by the Free Software Foundation.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Snapcraft Package service.\"\"\"\n\nfrom __future__ import annotations\n\nimport os\nimport pathlib\nimport shutil\nfrom typing import TYPE_CHECKING, cast\n\nfrom craft_application import AppMetadata, PackageService\nfrom overrides import override\n\nfrom snapcraft import errors, linters, models, pack, utils\nfrom snapcraft.linters import LinterStatus\nfrom snapcraft.meta import snap_yaml\nfrom snapcraft.services import Lifecycle\nfrom snapcraft.utils import process_version\n\nif TYPE_CHECKING:\n    from snapcraft.services import SnapcraftServiceFactory\n\n\nclass Package(PackageService):\n    \"\"\"Package service subclass for Snapcraft.\"\"\"\n\n    _project: models.Project\n\n    def __init__(  # noqa: PLR0913 (Too many arguments)\n        self,\n        app: AppMetadata,\n        services: SnapcraftServiceFactory,\n        *,\n        project: models.Project,\n        snapcraft_yaml_path: pathlib.Path,\n        platform: str | None,\n        build_for: str,\n    ) -> None:\n        super().__init__(app, services, project=project)\n        self._platform = platform\n        self._build_for = build_for\n        self._snapcraft_yaml_path = snapcraft_yaml_path\n\n    @override\n    def pack(self, prime_dir: pathlib.Path, dest: pathlib.Path) -> list[pathlib.Path]:\n        \"\"\"Create one or more packages as appropriate.\n\n        :param prime_dir: Path to the directory to pack.\n        :param dest: Directory into which to write the package(s).\n        :returns: A list of paths to created packages.\n        \"\"\"\n        issues = linters.run_linters(prime_dir, lint=self._project.lint)\n        status = linters.report(issues, intermediate=True)\n\n        # In case of linter errors, stop execution and return the error code.\n        if status in (LinterStatus.ERRORS, LinterStatus.FATAL):\n            raise errors.LinterError(\"Linter errors found\", exit_code=status)\n\n        return [\n            pathlib.Path(\n                pack.pack_snap(\n                    prime_dir,\n                    output=str(dest),\n                    compression=self._project.compression,\n                    name=self._project.name,\n                    version=process_version(self._project.version),\n                    target_arch=self._build_for,\n                )\n            )\n        ]\n\n    @override\n    def write_metadata(self, path: pathlib.Path) -> None:\n        \"\"\"Write the project metadata to metadata.yaml in the given directory.\n\n        :param path: The path to the prime directory.\n        \"\"\"\n        meta_dir = path / \"meta\"\n        meta_dir.mkdir(parents=True, exist_ok=True)\n        self.metadata.to_yaml_file(meta_dir / \"snap.yaml\")\n\n        enable_manifest = utils.strtobool(os.getenv(\"SNAPCRAFT_BUILD_INFO\", \"n\"))\n\n        if enable_manifest:\n            snap_dir = path / \"snap\"\n            snap_dir.mkdir(parents=True, exist_ok=True)\n            lifecycle = cast(Lifecycle, self._services.lifecycle)\n            manifest = lifecycle.generate_manifest()\n            manifest.to_yaml_file(snap_dir / \"manifest.yaml\")\n\n            shutil.copy(self._snapcraft_yaml_path, snap_dir)\n\n    @property\n    def metadata(self) -> snap_yaml.SnapMetadata:\n        \"\"\"Get the metadata model for this project.\"\"\"\n        return snap_yaml.get_metadata_from_project(\n            self._project, self._services.lifecycle.prime_dir, arch=self._build_for\n        )\n", "path": "snapcraft/services/package.py"}], "after_files": [{"content": "# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-\n#\n# Copyright 2023 Canonical Ltd.\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License version 3 as\n# published by the Free Software Foundation.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Snapcraft Package service.\"\"\"\n\nfrom __future__ import annotations\n\nimport os\nimport pathlib\nimport shutil\nfrom typing import TYPE_CHECKING, cast\n\nfrom craft_application import AppMetadata, PackageService\nfrom overrides import override\n\nfrom snapcraft import errors, linters, models, pack, utils\nfrom snapcraft.linters import LinterStatus\nfrom snapcraft.meta import snap_yaml\nfrom snapcraft.parts.setup_assets import setup_assets\nfrom snapcraft.services import Lifecycle\nfrom snapcraft.utils import process_version\n\nif TYPE_CHECKING:\n    from snapcraft.services import SnapcraftServiceFactory\n\n\nclass Package(PackageService):\n    \"\"\"Package service subclass for Snapcraft.\"\"\"\n\n    _project: models.Project\n\n    def __init__(  # noqa: PLR0913 (Too many arguments)\n        self,\n        app: AppMetadata,\n        services: SnapcraftServiceFactory,\n        *,\n        project: models.Project,\n        snapcraft_yaml_path: pathlib.Path,\n        platform: str | None,\n        build_for: str,\n    ) -> None:\n        super().__init__(app, services, project=project)\n        self._platform = platform\n        self._build_for = build_for\n        self._snapcraft_yaml_path = snapcraft_yaml_path\n\n    @override\n    def pack(self, prime_dir: pathlib.Path, dest: pathlib.Path) -> list[pathlib.Path]:\n        \"\"\"Create one or more packages as appropriate.\n\n        :param prime_dir: Path to the directory to pack.\n        :param dest: Directory into which to write the package(s).\n        :returns: A list of paths to created packages.\n        \"\"\"\n        issues = linters.run_linters(prime_dir, lint=self._project.lint)\n        status = linters.report(issues, intermediate=True)\n\n        # In case of linter errors, stop execution and return the error code.\n        if status in (LinterStatus.ERRORS, LinterStatus.FATAL):\n            raise errors.LinterError(\"Linter errors found\", exit_code=status)\n\n        return [\n            pathlib.Path(\n                pack.pack_snap(\n                    prime_dir,\n                    output=str(dest),\n                    compression=self._project.compression,\n                    name=self._project.name,\n                    version=process_version(self._project.version),\n                    target_arch=self._build_for,\n                )\n            )\n        ]\n\n    def _get_assets_dir(self) -> pathlib.Path:\n        \"\"\"Return a snapcraft assets directory.\n\n        Asset directories can exist in:\n\n        - <PROJECT_ROOT>/snap\n        - <PROJECT_ROOT>/build-aux/snap\n        \"\"\"\n        project_dir = self._services.lifecycle.project_info.project_dir\n        for asset_reldir in (\"snap\", \"build-aux/snap\"):\n            asset_dir = project_dir / asset_reldir\n            if asset_dir.exists():\n                return asset_dir\n\n        # This is for backwards compatibility with setup_assets(...)\n        return project_dir / \"snap\"\n\n    @override\n    def write_metadata(self, path: pathlib.Path) -> None:\n        \"\"\"Write the project metadata to metadata.yaml in the given directory.\n\n        :param path: The path to the prime directory.\n        \"\"\"\n        meta_dir = path / \"meta\"\n        meta_dir.mkdir(parents=True, exist_ok=True)\n        self.metadata.to_yaml_file(meta_dir / \"snap.yaml\")\n\n        enable_manifest = utils.strtobool(os.getenv(\"SNAPCRAFT_BUILD_INFO\", \"n\"))\n\n        if enable_manifest:\n            snap_dir = path / \"snap\"\n            snap_dir.mkdir(parents=True, exist_ok=True)\n            lifecycle = cast(Lifecycle, self._services.lifecycle)\n            manifest = lifecycle.generate_manifest()\n            manifest.to_yaml_file(snap_dir / \"manifest.yaml\")\n\n            shutil.copy(self._snapcraft_yaml_path, snap_dir)\n\n        assets_dir = self._get_assets_dir()\n        setup_assets(\n            self._project,\n            assets_dir=assets_dir,\n            project_dir=self._services.lifecycle.project_info.project_dir,\n            prime_dir=path,\n            meta_directory_handler=meta_directory_handler,\n        )\n\n    @property\n    def metadata(self) -> snap_yaml.SnapMetadata:\n        \"\"\"Get the metadata model for this project.\"\"\"\n        return snap_yaml.get_metadata_from_project(\n            self._project, self._services.lifecycle.prime_dir, arch=self._build_for\n        )\n\n\ndef _hardlink_or_copy(source: pathlib.Path, destination: pathlib.Path) -> bool:\n    \"\"\"Try to hardlink and fallback to copy if it fails.\n\n    :param source: the source path.\n    :param destination: the destination path.\n    :returns: True if a hardlink was done or False for copy.\n    \"\"\"\n    # Unlink the destination to avoid link failures\n    destination.unlink(missing_ok=True)\n\n    try:\n        destination.hardlink_to(source)\n    except OSError as os_error:\n        # Cross device link\n        if os_error.errno != 18:\n            raise\n        shutil.copy(source, destination)\n        return False\n\n    return True\n\n\ndef meta_directory_handler(assets_dir: pathlib.Path, path: pathlib.Path):\n    \"\"\"Handle hooks and gui assets from Snapcraft.\n\n    :param assets_dir: directory with project assets.\n    :param path: directory to write assets to.\n    \"\"\"\n    meta_dir = path / \"meta\"\n    built_snap_hooks = path / \"snap\" / \"hooks\"\n    hooks_project_dir = assets_dir / \"hooks\"\n\n    hooks_meta_dir = meta_dir / \"hooks\"\n\n    if built_snap_hooks.is_dir():\n        hooks_meta_dir.mkdir(parents=True, exist_ok=True)\n        for hook in built_snap_hooks.iterdir():\n            meta_dir_hook = hooks_meta_dir / hook.name\n            # Remove to always refresh to the latest\n            meta_dir_hook.unlink(missing_ok=True)\n            meta_dir_hook.hardlink_to(hook)\n\n    # Overwrite any built hooks with project level ones\n    if hooks_project_dir.is_dir():\n        hooks_meta_dir.mkdir(parents=True, exist_ok=True)\n        for hook in hooks_project_dir.iterdir():\n            meta_dir_hook = hooks_meta_dir / hook.name\n\n            _hardlink_or_copy(hook, meta_dir_hook)\n\n    # Write any gui assets\n    gui_project_dir = assets_dir / \"gui\"\n    gui_meta_dir = meta_dir / \"gui\"\n    if gui_project_dir.is_dir():\n        gui_meta_dir.mkdir(parents=True, exist_ok=True)\n        for gui in gui_project_dir.iterdir():\n            meta_dir_gui = gui_meta_dir / gui.name\n\n            _hardlink_or_copy(gui, meta_dir_gui)\n", "path": "snapcraft/services/package.py"}]}