| #!/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 |