blob: 1fbf57403d60568b05168746fe6159becc295729 [file] [log] [blame]
#!/bin/bash
# Copyright 2020 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.
#### CATEGORY=Software delivery
### unarchive a Fuchsia package archive in FAR format
## Usage: fx unarchive-package <.far file> [ --out-dir <output directory> ]
##
## The script unarchives a Fuchsia package archive in FAR format and puts the
## contents in the output directory. Files in the package will be renamed and
## arranged according to its meta/contents file.
##
## The command will by default create a directory with the same name as the .far
## file under the current directory, which can be overridden by --output-dir
## option. If --output-dir is an existing directory, then a sub-directory with
## the same name as the .far file will be created.
##
## Note: although a Fuchsia package archive is in FAR format, not all .far files
## are valid Fuchsia packages! To interact with any .far files, use `fx far`.
## Also, This tool only works with fuchsia package archives, although it could be
## extended in the future to work with plain fuchsia packages.
##
## Example:
## $ fx unarchive-package foo.far
## Unarchived foo.far under foo
##
## # directory "foo" will be created as
## $ tree foo
## foo
## ├── bin
## │   └── foo
## ├── lib
## │   ├── ld.so.1
## │   ├── libc++abi.so.1
## │   ├── libc++.so.2
## │   ├── libfdio.so
## │   └── libunwind.so.1
## └── meta
## ├── contents
## ├── foo_component.cmx
## └── package
##
## $ fx unarchive-package foo.far --out-dir foo2 # assuming foo2 doesn't exist
## Unarchived foo.far under foo2
##
## $ fx unarchive-package foo.far --out-dir /tmp
## Unarchived foo.far under /tmp/foo
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/../lib/vars.sh || exit $?
fx-config-read
# Cleanup at exit
function cleanup {
[[ ${TMP_META_FAR} && -f ${TMP_META_FAR} ]] && rm "${TMP_META_FAR}"
}
trap cleanup EXIT
# Useful function
function die {
fx-error "$@"
exit 1
}
# `fx far` wrapper
function far {
local far="${FUCHSIA_BUILD_DIR}/host-tools/far"
if [[ ! -f ${far} ]]; then
fx-error "\"far\" host tool not found."
fx-error "Run \"fx build\" to build host tools."
exit 1
fi
"${far}" "$@" || exit $?
}
# Parse arguments
while [[ $# -ge 1 ]]; do
case "$1" in
-h|--help)
fx-command-help
exit 0
;;
--out-dir)
shift
[[ ! $1 ]] && die "--out-dir requires an argument after it"
OUTPUT="$1"
;;
-*)
die "Cannot understand option $1"
;;
*)
[[ ${FAR_FILE} ]] && die "Only one .far file can be provided"
FAR_FILE="$1"
esac
shift
done
[[ ! ${FAR_FILE} ]] && fx-command-help && die "No input provided"
[[ ! -e ${FAR_FILE} ]] && die "${FAR_FILE} does not exist"
PKG_NAME="$(basename "${FAR_FILE%.far}")"
if [[ ${OUTPUT} ]]; then
# Append $PKG_NAME if $OUTPUT is existing, e.g. /tmp
[[ -e ${OUTPUT} ]] && OUTPUT="${OUTPUT}/${PKG_NAME}"
else
# By default output to the current directory
OUTPUT="${PKG_NAME}"
fi
[[ -e ${OUTPUT} ]] && die "${OUTPUT} already exists"
TMP_META_FAR="$(mktemp)"
# It first extracts meta.far from $FAR_FILE and then from its contents
# file we lookup the blob ID to extract that particular file indexed by
# blob ID under its readable name.
far extract-file "--archive=${FAR_FILE}" --file=meta.far "--output=${TMP_META_FAR}"
far extract "--archive=${TMP_META_FAR}" "--output=${OUTPUT}"
[[ ! -f ${OUTPUT}/meta/contents ]] && die "Cannot find ${OUTPUT}/meta/contents"
while IFS= read -r line; do
# Each line is of the format "$dst=$src" where $dst is a readable name like "lib/libfoo.so"
# and $src is a blob sha1.
dst="${line%=*}"
src="${line##*=}"
dir="$(dirname "${OUTPUT}/${dst}")"
if [[ ! -d ${dir} ]]; then
mkdir -p "${dir}"
fi
far extract-file "--archive=${FAR_FILE}" "--file=${src}" "--output=${OUTPUT}/${dst}"
done < "${OUTPUT}/meta/contents"
echo "Unarchived ${FAR_FILE} under ${OUTPUT}"