blob: 11144c2da15299a95aa518e5cf556d25b1777151 [file] [log] [blame]
# Copyright 2018 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.
load(":package_info.bzl", "PackageAggregateInfo", "PackageComponentInfo",
"PackageGeneratedInfo", "PackageInfo", "PackageLocalInfo",
"get_aggregate_info")
"""
Defines a Fuchsia package
The package template is used to define a unit of related code and data.
Parameters
name(string, required)
The name of the package
deps(list, required)
The list of targets to be built into this package
"""
# The attributes along which the aspect propagates.
_ASPECT_ATTRIBUTES = [
"data",
"deps",
"srcs",
]
def _info_impl(target, context):
components = []
mappings = []
if PackageComponentInfo in target:
info = target[PackageComponentInfo]
components += [(info.name, info.manifest)]
if PackageLocalInfo in target:
mappings += target[PackageLocalInfo].mappings
if PackageGeneratedInfo in target:
mappings += target[PackageGeneratedInfo].mappings
deps = []
for attribute in _ASPECT_ATTRIBUTES:
if hasattr(context.rule.attr, attribute):
value = getattr(context.rule.attr, attribute)
deps += value
return [
get_aggregate_info(components, mappings, deps),
]
# An aspect which turns PackageLocalInfo providers into a PackageAggregateInfo
# provider to identify all elements which need to be included in the package.
_info_aspect = aspect(
implementation = _info_impl,
attr_aspects = _ASPECT_ATTRIBUTES,
provides = [
PackageAggregateInfo,
],
# If any other aspect is applied to produce package mappings, let the result
# of that process be visible to the present aspect.
required_aspect_providers = [
PackageGeneratedInfo,
],
)
def _fuchsia_package_impl(context):
# List all the files that need to be included in the package.
info = get_aggregate_info([], [], context.attr.deps)
manifest_file_contents = ""
package_contents = []
# Generate the manifest file with a script: this helps ignore empty files.
base = context.attr.name + "_pkg/"
manifest_file = context.actions.declare_file(base + "package_manifest")
content = "#!/bin/bash\n"
for dest, source in info.mappings.to_list():
# Only add file to the manifest if not empty.
content += "if [[ -s %s ]]; then\n" % source.path
content += " echo '%s=%s' >> %s\n" % (dest, source.path,
manifest_file.path)
content += "fi\n"
package_contents.append(source)
# Add cmx file for each component.
for name, cmx in info.components.to_list():
content += "echo 'meta/%s.cmx=%s' >> %s\n" % (name, cmx.path,
manifest_file.path)
package_contents.append(cmx)
# Add the meta/package file to the manifest.
meta_package = context.actions.declare_file(base + "meta/package")
content += "echo 'meta/package=%s' >> %s\n" % (meta_package.path,
manifest_file.path)
# Write the manifest file.
manifest_script = context.actions.declare_file(base + "package_manifest.sh")
context.actions.write(
output = manifest_script,
content = content,
is_executable = True,
)
context.actions.run(
executable = manifest_script,
inputs = package_contents,
outputs = [
manifest_file,
],
mnemonic = "FuchsiaManifest",
)
# Initialize the package's meta directory.
package_dir = manifest_file.dirname
context.actions.run(
executable = context.executable._pm,
arguments = [
"-o",
package_dir,
"-n",
context.attr.name,
"init",
],
outputs = [
meta_package,
],
mnemonic = "PmInit",
)
# TODO(pylaligand): figure out how to specify this key.
# Generate a signing key.
signing_key = context.actions.declare_file(base + "development.key")
context.actions.run(
executable = context.executable._pm,
arguments = [
"-o",
package_dir,
"-k",
signing_key.path,
"genkey",
],
inputs = [
meta_package,
],
outputs = [
signing_key,
],
mnemonic = "PmGenkey",
)
# Build the package metadata.
meta_far = context.actions.declare_file(base + "meta.far")
context.actions.run(
executable = context.executable._pm,
arguments = [
"-o",
package_dir,
"-k",
signing_key.path,
"-m",
manifest_file.path,
"build",
],
inputs = package_contents + [
manifest_file,
meta_package,
signing_key,
],
outputs = [
meta_far,
],
mnemonic = "PmBuild",
)
# Create the package archive.
package_archive = context.actions.declare_file(base + context.attr.name + "-0.far")
context.actions.run(
executable = context.executable._pm,
arguments = [
"-o",
package_dir,
"-k",
signing_key.path,
"-m",
manifest_file.path,
"archive",
],
inputs = [
manifest_file,
signing_key,
meta_far,
] + package_contents,
outputs = [
package_archive,
],
mnemonic = "PmArchive",
)
components_file = context.actions.declare_file(context.attr.name + "_components.txt")
components_contents = "\n".join([n for n, _ in info.components.to_list()])
context.actions.write(
output = components_file,
content = components_contents,
)
executable_file = context.actions.declare_file(context.attr.name + "_run.sh")
executable_contents = """#!/bin/sh\n
%s \\
--config %s \\
--package-name %s \\
--package %s \\
--dev-finder %s \\
--pm %s \\
run \\
\"$@\"
""" % (
context.executable._runner.short_path,
components_file.short_path,
context.attr.name,
package_archive.short_path,
context.executable._dev_finder.short_path,
context.executable._pm.short_path,
)
context.actions.write(
output = executable_file,
content = executable_contents,
is_executable = True,
)
runfiles = context.runfiles(files = [
components_file,
context.executable._dev_finder,
context.executable._pm,
context.executable._runner,
executable_file,
package_archive,
])
return [
DefaultInfo(
files = depset([package_archive]),
executable = executable_file,
runfiles = runfiles,
),
PackageInfo(
name = context.attr.name,
archive = package_archive,
),
]
fuchsia_package = rule(
implementation = _fuchsia_package_impl,
attrs = {
"deps": attr.label_list(
doc = "The objects to include in the package",
aspects = [
_info_aspect,
],
mandatory = True,
),
"_pm": attr.label(
default = Label("//tools:pm"),
allow_single_file = True,
executable = True,
cfg = "host",
),
"_dev_finder": attr.label(
default = Label("//tools:dev_finder"),
allow_single_file = True,
executable = True,
cfg = "host",
),
"_runner": attr.label(
default = Label("//build_defs/internal/component_runner:component_runner.par"),
allow_single_file = True,
executable = True,
cfg = "host",
),
},
provides = [PackageInfo],
executable = True,
)