| # Copyright 2022 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Implement fuchsia_prebuilt_package() rule.""" |
| |
| load("//fuchsia/private:providers.bzl", "FuchsiaPackageInfo") |
| load("//fuchsia/private:package_publishing.bzl", "package_repo_path_from_label", "publish_package") |
| load("//fuchsia/private/workflows:fuchsia_task_publish.bzl", "fuchsia_task_publish") |
| |
| _COMPONENT_VALIDATION_SCRIPT = """ |
| components=$($FAR list --archive=$OUTPUT_DIR/meta.far | grep -F meta/$COMPONENT.cm | wc -l) |
| |
| if [ $components -eq 0 ]; then |
| echo |
| echo "Component '$COMPONENT' is not included in the package!" |
| echo |
| exit 1 |
| fi |
| touch $STAMP |
| """ |
| |
| def _relative_file_name(ctx, filename): |
| return ctx.label.name + "_expanded/" + filename |
| |
| def _validate_components_and_drivers(ctx, outdir): |
| far = ctx.toolchains["@rules_fuchsia//fuchsia:toolchain"].far |
| deps = [] |
| for component in ctx.attr.components + ctx.attr.drivers: |
| stamp_file = ctx.actions.declare_file(_relative_file_name(ctx, component + "_stamp")) |
| |
| # NOTE: outdir is a ctx.actions.declare_directory() path, so declare |
| # it as an input, even though only the `meta.far` inside it is used. |
| # (There is no way to create a File() instance from it). |
| # |
| # This ensures the action that populates the directory is always run |
| # properly before the run_shell() one below. |
| ctx.actions.run_shell( |
| inputs = [outdir, far], |
| outputs = [stamp_file], |
| command = _COMPONENT_VALIDATION_SCRIPT, |
| env = { |
| "FAR": far.path, |
| "COMPONENT": component, |
| "OUTPUT_DIR": outdir.path, |
| "STAMP": stamp_file.path, |
| }, |
| progress_message = "Validating the component %s" % component, |
| ) |
| deps.append(stamp_file) |
| return deps |
| |
| def _fuchsia_prebuilt_package_impl(ctx): |
| sdk = ctx.toolchains["@rules_fuchsia//fuchsia:toolchain"] |
| far_archive = ctx.files.archive[0] |
| |
| # Technical note: `pm expand` below will populate its output directory |
| # with multiple files whose name are content hashes and cannot be known in |
| # advance, so use ctx.actions.declare_directory() to declare an output |
| # directory for the expansion. This tells Bazel that all files present |
| # in the directory after the command execution are outputs, and should |
| # be copied from the sandbox to the corresponding final output location |
| # in the output_base directory (otherwise, they would disappear once the |
| # sandbox is destroyed). |
| # |
| # The top-level directory for this target will be computed from |
| # `${label}_expanded/`, which, for a label like `//package/foo:bar` |
| # will expand to something like this (relative to the sandbox execroot): |
| # |
| # `bazel-out/aarch64-opt/bin/package/foo/bar/bar_expanded` |
| # |
| # Inside this TARGET_OUT_DIR, the following is generated: |
| # |
| # $TARGET_OUT_DIR/ |
| # content/ |
| # A directory that contains the expanded content from the |
| # prebuilt package, as well as a `package_manifest.json` file |
| # that lists all entries, where source paths appear relative |
| # to the execroot too, e.g.: |
| # |
| # "blobs": [ |
| # { |
| # "source_path": "bazel-out/aarch64-opt/bin/package/foo/bar/bar_expanded/_content/meta.far", |
| # "path": "meta/", |
| # "merkle": "d0d73e04d89e393b71f2280831421ebe279e247265e25714c71fdc8961928822", |
| # "size": 94288, |
| # }, |
| # ... |
| # |
| # rebased_package_manifest.json |
| # A version of _content/package_manifest.json that contains |
| # source paths that are relative to an alternative `artifacts_base_path` |
| # value. However, since the default value for this argument is just '.', |
| # it will have the same content as `_content/package_manifest.json` in |
| # most cases. |
| # |
| # Note that this file _cannot_ be inside `_content`, as Bazel |
| # would complain otherwise, as it is generated by a different action |
| # than the one that generated `_content/`. |
| # |
| # <component>_stamp |
| # For each component listed in ctx.attr.components or |
| # ctx.attr.drivers, a stamp file generated by the action that |
| # verifies it belongs to the package. |
| # |
| output_dir = ctx.actions.declare_directory(_relative_file_name(ctx, "content")) |
| output_files = [output_dir] |
| output_files += _validate_components_and_drivers(ctx, output_dir) |
| |
| # extract the package |
| ctx.actions.run( |
| executable = sdk.pm, |
| arguments = [ |
| "-o", |
| output_dir.path, |
| "-r", |
| "fuchsia.com", |
| "expand", |
| far_archive.path, |
| ], |
| inputs = [far_archive], |
| outputs = [ |
| output_dir, |
| ], |
| mnemonic = "FuchsiaPmExpand", |
| progress_message = "expanding package for %{label}", |
| ) |
| |
| # rebase paths in package manifest |
| rebased_package_manifest_json = ctx.actions.declare_file(_relative_file_name(ctx, "rebased_package_manifest.json")) |
| ctx.actions.run( |
| outputs = [rebased_package_manifest_json], |
| inputs = [output_dir], |
| executable = ctx.executable._rebase_package_manifest, |
| arguments = [ |
| "--package-manifest", |
| output_dir.path + "/package_manifest.json", |
| "--updated-package-manifest", |
| rebased_package_manifest_json.path, |
| "--relative-base", |
| ctx.attr.artifacts_base_path, |
| ], |
| ) |
| output_files.append(rebased_package_manifest_json) |
| |
| # Attempt to publish if told to do so |
| repo_path = package_repo_path_from_label(ctx.attr._package_repo_path) |
| if repo_path: |
| stamp_file = publish_package(ctx, sdk.pm, repo_path, [rebased_package_manifest_json]) |
| output_files.append(stamp_file) |
| |
| return [ |
| DefaultInfo(files = depset(output_files)), |
| FuchsiaPackageInfo( |
| package_manifest = rebased_package_manifest_json, |
| far_file = ctx.files.archive, |
| components = ctx.attr.components, |
| drivers = ctx.attr.drivers, |
| ), |
| ] |
| |
| _fuchsia_prebuilt_package = rule( |
| doc = """Provides access to a fuchsia package from a prebuilt package archive (.far). |
| """, |
| implementation = _fuchsia_prebuilt_package_impl, |
| toolchains = ["@rules_fuchsia//fuchsia:toolchain"], |
| attrs = { |
| "archive": attr.label( |
| doc = "The fuchsia archive", |
| allow_single_file = True, |
| mandatory = True, |
| ), |
| "components": attr.string_list( |
| doc = "components of this driver", |
| default = [], |
| ), |
| "drivers": attr.string_list( |
| doc = "drivers of this driver", |
| default = [], |
| ), |
| "_package_repo_path": attr.label( |
| doc = "The command line flag used to publish packages.", |
| default = "//fuchsia:package_repo", |
| ), |
| "artifacts_base_path": attr.string( |
| doc = "Artifacts base directories that items in config files are relative to.", |
| default = ".", |
| ), |
| "_rebase_package_manifest": attr.label( |
| default = "//fuchsia/tools:rebase_package_manifest", |
| executable = True, |
| cfg = "exec", |
| ), |
| }, |
| ) |
| |
| def fuchsia_prebuilt_package(*, name, archive, components = [], drivers = [], artifacts_base_path = ".", **kwargs): |
| _fuchsia_prebuilt_package( |
| name = name, |
| archive = archive, |
| components = components, |
| drivers = drivers, |
| artifacts_base_path = artifacts_base_path, |
| **kwargs |
| ) |
| |
| fuchsia_task_publish( |
| name = "%s.publish" % name, |
| packages = [name], |
| **kwargs |
| ) |