blob: 937416200190d7bb485dacb62bd64edd4b7cd31c [file] [log] [blame]
#!/bin/bash
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This script moves ebuilds between 'stable' and 'live' states.
# By default 'stable' ebuilds point at and build from source at the
# last known good commit. Moving an ebuild to 'live' (via cros_workon start)
# is intended to support development. The current source tip is fetched,
# source modified and built using the unstable 'live' (9999) ebuild.
. /usr/lib/crosutils/common.sh || exit 1
# Script must be run inside the chroot
get_default_board
DEFINE_string board "${DEFAULT_BOARD}" \
"The board to set package keywords for."
DEFINE_boolean host "${FLAGS_FALSE}" \
"Uses the host instead of board"
DEFINE_string command "git status" \
"The command to be run by forall."
DEFINE_boolean all "${FLAGS_FALSE}" \
"Apply to all possible packages for the given command"
FLAGS_HELP="usage: $0 <command> [flags] [<list of packages>|--all]
commands:
start: Moves an ebuild to live (intended to support development)
stop: Moves an ebuild to stable (use last known good)
info: Print package name, repo name, and source directory.
list: List of live ebuilds (workon ebuilds if --all)
list-all: List all of the live ebuilds for all setup boards
iterate: For each ebuild, cd to the source dir and run a command"
FLAGS "$@" || { [ "${FLAGS_help}" = "${FLAGS_TRUE}" ] && exit 0; } || exit 1
eval set -- "${FLAGS_ARGV}"
# eat the workon command keywords: start, stop or list.
WORKON_CMD=$1
shift
# Board dir config
# If both are specified, just use host, because board does not
# have to be specified and may come from default, in which case
# there's no way to override.
[ -n "${FLAGS_board}" ] && [ "${FLAGS_host}" = ${FLAGS_TRUE} ] && \
FLAGS_board="" # kill board
[ -z "${FLAGS_board}" ] && [ "${FLAGS_host}" = ${FLAGS_FALSE} ] && \
[ "${WORKON_CMD}" != "list-all" ] && \
die "You must specify either --host or --board="
if [ -n "${FLAGS_board}" ]; then
BOARD_DIR=/build/"${FLAGS_board}" # --board specified
EQUERYCMD=equery-"${FLAGS_board}"
EBUILDCMD=ebuild-"${FLAGS_board}"
PORTAGEQCMD=portageq-"${FLAGS_board}"
BOARD_STR="${FLAGS_board}"
BOARD_KEYWORD="$(portageq-${FLAGS_board} envvar ARCH)"
else
BOARD_DIR="" # --host specified
EQUERYCMD=equery
EBUILDCMD=ebuild
PORTAGEQCMD=portageq
BOARD_STR="host"
BOARD_KEYWORD="$(portageq envvar ARCH)"
fi
WORKON_DIR=${CHROOT_TRUNK_DIR}/.config/cros_workon
KEYWORDS_DIR=${BOARD_DIR}/etc/portage/package.keywords
UNMASK_DIR=${BOARD_DIR}/etc/portage/package.unmask
WORKON_FILE=${WORKON_DIR}/${FLAGS_board:-host}
KEYWORDS_FILE=${KEYWORDS_DIR}/cros-workon
UNMASK_FILE=${UNMASK_DIR}/cros-workon
# TODO(msb): remove the backward compatibility after 10/01/2010
if [ -d "${WORKON_DIR}" ]; then
sudo chown -R "${USER}" "${WORKON_DIR}"
fi
mkdir -p "${WORKON_DIR}" || die "mkdir -p ${WORKON_DIR}"
touch "${WORKON_FILE}" || die "touch ${WORKON_FILE}"
sudo mkdir -p "${KEYWORDS_DIR}" "${UNMASK_DIR}" || \
die "mkdir -p ${KEYWORDS_DIR} ${UNMASK_DIR}"
if [ ! -L "${KEYWORDS_FILE}" ]; then
sudo rm -f "${KEYWORDS_FILE}"
sudo ln -s "${WORKON_FILE}" "${KEYWORDS_FILE}" || \
die "ln -s ${WORKON_FILE} ${KEYWORDS_FILE}"
fi
if [ ! -L "${UNMASK_FILE}" ]; then
[ -f "${UNMASK_FILE}" ] && sudo mv "${UNMASK_FILE}" "${WORKON_FILE}"
sudo ln -s "${WORKON_FILE}" "${UNMASK_FILE}" || \
die "ln -s ${WORKON_FILE} ${UNMASK_FILE}"
fi
find_keyword_workon_ebuilds() {
local keyword="${1}"
local overlay
local cros_overlays=$("${PORTAGEQCMD}" envvar PORTDIR_OVERLAY)
# NOTE: overlay may be a symlink, and we have to use ${overlay}/
for overlay in ${cros_overlays}; do
# only look up ebuilds named 9999 to eliminate duplicates
find ${overlay}/ -name '*9999.ebuild' | \
xargs grep -l "inherit.*cros-workon" | \
xargs grep -l "KEYWORDS=.*${keyword}.*"
done
}
ebuild_to_package() {
# This changes the absolute path to ebuilds into category/package.
sed -e 's/.*\/\([^/]*\)\/\([^/]*\)\/.*\.ebuild/\1\/\2/'
}
show_project_ebuild_map() {
local keyword="$1"
# Column 1: Repo name
# Column 2: Package name
for EBUILD in $(find_keyword_workon_ebuilds ${keyword}); do
(
eval $(grep -E '^CROS_WORKON' "${EBUILD}")
CP=$(echo "$EBUILD" | ebuild_to_package)
echo "${CROS_WORKON_PROJECT}" "${CP}"
)
done
}
show_project_path_map() {
# Column 1: Repo name
# Column 2: Source directory
repo list | awk -F ' : ' '{ print $2, $1 }'
}
show_workon_ebuilds() {
local keyword="$1"
find_keyword_workon_ebuilds "${keyword}" | ebuild_to_package | sort -u
}
show_workon_info() {
local atoms="$1"
local keyword="$2"
# Column 1: Package name
# Column 2: Repo name
# Column 3: Source directory (if present locally)
join \
<(echo "$atoms" | sed -e 's/ /\n/g' | sort -u) \
<(join -a 1 -e - -o 1.2,1.1,2.2 \
<(show_project_ebuild_map "${keyword}" | sort -u) \
<(show_project_path_map | sort -u) \
| sort -u)
}
# Canonicalize package name to category/package.
canonicalize_name () {
local pkgfile
local pkgname
if grep -qx "=$1-9999" "${WORKON_FILE}" ; then
echo $1
return 0
fi
if ! pkgfile=$(ACCEPT_KEYWORDS="~${BOARD_KEYWORD}" ${EQUERYCMD} which $1); then
warn "error looking up package $1" 1>&2
return 1
fi
pkgname=$(\
echo "${pkgfile}" |awk -F '/' '{ print $(NF-2) "/" $(NF-1) }')
if ! grep -q "cros-workon" ${pkgfile}; then
warn "${pkgname} is not a cros-workon package" 1>&2
return 1
fi
echo "${pkgname}"
return 0
}
# Canonicalize a list of names.
canonicalize_names () {
local atoms=$1
local names=""
local atom
for atom in ${atoms}; do
local name=$(canonicalize_name "${atom}")
[ -n "${name}" ] || return 1
names+=" ${name}"
done
echo "${names}"
}
# Display ebuilds currently part of the live branch and open for development.
show_live_ebuilds () {
sed -n 's/^=\(.*\)-9999$/\1/p' "${WORKON_FILE}"
}
# Display ebuilds currently part of the live branch and open for development
# for any board that currently has live ebuilds.
show_all_live_ebuilds () {
local workon_file
for workon_file in ${WORKON_DIR}/*; do
if [ -s "${workon_file}" ]; then
echo -e "${V_BOLD_GREEN}$(basename ${workon_file}):${V_VIDOFF}"
sed -n 's/^=\(.*\)-9999$/ \1/p' "${workon_file}"
echo ""
fi
done
}
# This is called only for "cros-workon start". We dont handle the
# "stop" case since the local changes are ignored anyway since the
# 9999.ebuild is masked and we dont want to deal with what to do with
# the user's local changes.
regen_manifest_and_sync() {
# Nothing to do unless you are working on the minilayout
local manifest=${CHROOT_TRUNK_DIR}/.repo/manifest.xml
if [ $(basename $(readlink -f ${manifest})) != "minilayout.xml" ]; then
return
fi
local pkgname
for pkgname in $(show_live_ebuilds); do
eval $(${EBUILDCMD} $(${EQUERYCMD} which ${pkgname}) info)
local srcdir=$(readlink -m ${CROS_WORKON_SRCDIR})
local trunkdir=$(readlink -m ${CHROOT_TRUNK_DIR})
local project_path=${srcdir#${trunkdir}/}
loman add --workon "${CROS_WORKON_PROJECT}" "${project_path}"
done
echo "Please run \"repo sync\" now."
}
# Move a stable ebuild to the live development catgeory. The ebuild
# src_unpack step fetches the package source for local development.
ebuild_to_live () {
local atoms=$1
local atoms_success=""
local atom
for atom in ${atoms}; do
if ! grep -qx "=${atom}-9999" "${WORKON_FILE}" ; then
if sudo bash -c "echo \"=${atom}-9999\" >> \"${WORKON_FILE}\""; then
atoms_success="${atoms_success} ${atom}"
fi
else
warn "Already working on ${atom}"
fi
done
[ -n "${atoms_success}" ] && regen_manifest_and_sync && \
info "Started working on '${atoms_success/ /}' for '${BOARD_STR}'"
}
# Move a live development ebuild back to stable.
ebuild_to_stable () {
local atoms=$1
local atoms_success=""
local atom
for atom in ${atoms}; do
if grep -qx "=${atom}-9999" "${WORKON_FILE}" ; then
if sudo sed -e "/^=${atom/\//\\/}-9999\$/d" -i "${WORKON_FILE}"; then
atoms_success="${atoms_success} ${atom}"
fi
else
warn "Not working on ${atom}"
fi
done
[ -n "${atoms_success}" ] && \
info "Stopped working on '${atoms_success/ /}' for '${BOARD_STR}'"
}
# Run a command on all or a set of repos.
ebuild_iterate() {
local atoms=$1
local atom
for atom in ${atoms}; do
info "Running \"${FLAGS_command}\" on ${atom}"
eval $(${EBUILDCMD} $(${EQUERYCMD} which ${atom}) info)
(cd "${CROS_WORKON_SRCDIR}" && bash -c "${FLAGS_command}")
done
}
# --all makes commands operate on different lists
if [ ${FLAGS_all} = "${FLAGS_TRUE}" ]; then
case ${WORKON_CMD} in
start|info) ATOM_LIST=$(show_workon_ebuilds ${BOARD_KEYWORD});;
stop|iterate) ATOM_LIST=$(show_live_ebuilds);;
list) ;;
*) die "--all is invalid for the given command";;
esac
else # not selected --all
case ${WORKON_CMD} in
start|stop|info|iterate)
ATOM_LIST=$@
if [ -z "${ATOM_LIST}" ]; then
die "${WORKON_CMD}: No packages specified"
elif ! ATOM_LIST=$(canonicalize_names "${ATOM_LIST}"); then
die "Error parsing package list"
fi;;
*) ;;
esac
fi
case ${WORKON_CMD} in
start) ebuild_to_live "${ATOM_LIST}" ;;
stop) ebuild_to_stable "${ATOM_LIST}" ;;
info) show_workon_info "${ATOM_LIST}" "${BOARD_KEYWORD}" ;;
list) [ ${FLAGS_all} = "${FLAGS_FALSE}" ] && show_live_ebuilds || \
show_workon_ebuilds ${BOARD_KEYWORD} ;;
list-all) show_all_live_ebuilds ;;
iterate) ebuild_iterate "${ATOM_LIST}" ;;
*) die "$(basename $0): command '${WORKON_CMD}' not recognized" ;;
esac