| #!/bin/sh |
| # 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 downloads and installs the basic packages that the user needs |
| # in developer mode. It also takes care of some configuration details |
| # that arise from not havin write access to the root filesystem. |
| |
| # Constant definitions. |
| P_CONFIG_CROS=/etc/portage |
| P_CONFIG_DEVELOPER=/usr/local/etc/portage |
| EMERGE_PACKAGES="/usr/share/dev-install/bootstrap.packages" |
| |
| # The base URL of the repository holding our portage prebuilt binpkgs. |
| PREFIX_BINHOST="https://commondatastorage.googleapis.com/chromeos-dev-installer/board" |
| |
| # Global variables. |
| BINHOST= |
| CHROMEOS_DEVSERVER= |
| CHROMEOS_RELEASE_BOARD= |
| |
| # Process flags. |
| . /usr/share/misc/shflags || exit 1 |
| DEFINE_string binhost "" \ |
| "URL of the binhost that emerge will use." b |
| DEFINE_string binhost_version "" \ |
| "Version number to use instead of the one in /etc/lsb-release." |
| DEFINE_boolean reinstall "${FLAGS_FALSE}" \ |
| "Remove all installed packages and re-bootstrap emerge." |
| DEFINE_boolean uninstall "${FLAGS_FALSE}" \ |
| "Remove all installed packages." |
| DEFINE_boolean yes "${FLAGS_FALSE}" \ |
| "Do not prompt for input. Assumes yes's to all responses." y |
| DEFINE_boolean only_bootstrap "${FLAGS_FALSE}" \ |
| "Only attempt to install the bootstrap packages." |
| |
| FLAGS "$@" || exit 1 |
| |
| set -e |
| |
| # Get the portage configuration variables. |
| . "${P_CONFIG_CROS}/make.profile/make.defaults" |
| |
| # Echo's args to stderr and prefixes with ERROR(dev_install). |
| error() { |
| echo "ERROR(dev_install): $*" >&2 |
| } |
| |
| # Reads the user's reply and returns 0 if the user responds with y. |
| # Accepts input like echo for the user prompt i.e. $@. |
| yes_or_no() { |
| [ ${FLAGS_yes} -eq ${FLAGS_TRUE} ] && return 0 |
| local reply |
| read -p "$*? (y/N) " reply |
| [ "${reply}" = "y" ] |
| } |
| |
| # Returns value of variable $2 from /etc/lsb-like file $1. |
| # $1 - Path to the /etc/lsb-like file. |
| # $2 - Variable name. |
| # Returns value or an empty string. |
| get_lsb_var() { |
| sed -n "s/^$2=//p" "$1" |
| } |
| |
| # Here we simply wipe /usr/local/* since that it's the only directory in which |
| # packages are installed to. |
| remove_installed_packages() { |
| echo "To clean up, we will run:" |
| echo " rm -rf /usr/local/" |
| echo "Any content you have stored in there will be deleted." |
| if yes_or_no "Remove all installed packages now"; then |
| rm -rf --one-file-system /usr/local/* |
| echo "Removed all installed packages." |
| else |
| echo "Operation cancelled." |
| exit 0 |
| fi |
| } |
| |
| # Parses flags to return the binhost or if none set, use the default binhost |
| # set up from installation. |
| get_binhost() { |
| if [ -n "${FLAGS_binhost}" ]; then |
| BINHOST="${FLAGS_binhost}" |
| elif [ -n "${CHROMEOS_DEVSERVER}" ]; then |
| echo "Devserver URL set to: ${CHROMEOS_DEVSERVER}" |
| if yes_or_no "Use it as the binhost"; then |
| local packages_path="static/pkgroot/${CHROMEOS_RELEASE_BOARD}/packages" |
| BINHOST="${CHROMEOS_DEVSERVER}/${packages_path}" |
| fi |
| fi |
| if [ -z "${BINHOST}" ]; then |
| local release_number |
| if [ -n "${FLAGS_binhost_version}" ]; then |
| release_number="${FLAGS_binhost_version}" |
| else |
| # Get the release number. |
| local cr_version="$(/opt/google/chrome/chrome --version)" |
| # Chrome version looks like (Google Chrome/Chromium) branch.0.a.b |
| local cr_branch="$(echo "${cr_version}" | |
| sed -r 's/.*[[:space:]]([0-9]+)\..*/\1/')" |
| local version="$(get_lsb_var /etc/lsb-release CHROMEOS_RELEASE_VERSION)" |
| release_number="R${cr_branch}-${version}" |
| fi |
| BINHOST="${PREFIX_BINHOST}/${CHROMEOS_RELEASE_BOARD}/canary-${release_number}/packages" |
| fi |
| } |
| |
| # Get the static Python binary and portage (emerge script and python modules). |
| download_bootstrap_packages() { |
| # Download packages that python/emerge require into /usr/local/bootsrap. |
| while read line; do |
| local package_url="${BINHOST}/${line}.tbz2" |
| local directory="$(echo "${line}" | cut -d "/" -f 1)" |
| local package_file="${PKGDIR}/${line}.tbz2" |
| mkdir -p -m 0755 "${PKGDIR}/${directory}" |
| echo "Downloading ${package_url}" |
| if ! curl --fail -o "${package_file}" "${package_url}"; then |
| error "Could not download package." |
| error "Command curl --fail -o ${package_file} ${package_url} failed." |
| return 1 |
| fi |
| echo "Extracting ${package_file}" |
| # TODO(sosa): pipeline our download/unpack cycles. |
| # Ignore std error output about trailing garbage after EOF. |
| if ! tar -C /usr/local/ -xjkf "${package_file}" 2>/dev/null; then |
| error "Could not extract package." |
| error "Command tar -C /usr/local -xjf ${package_file} failed." |
| return 1 |
| fi |
| done < "${EMERGE_PACKAGES}" |
| } |
| |
| # Configure emerge in /usr/local. |
| configure_emerge() { |
| # Copy emerge configuration to /usr/local. |
| mkdir -p -m 0755 "${P_CONFIG_DEVELOPER}/make.profile" |
| # Point our local profile to the rootfs one. This allows us to stack. |
| echo "${P_CONFIG_CROS}" > "${P_CONFIG_DEVELOPER}/make.profile/parent" |
| |
| # Install the package.provided entries for the rootfs. |
| mkdir -p -m 0755 "${P_CONFIG_DEVELOPER}/make.profile/package.provided" |
| local f |
| for f in /usr/share/dev-install/rootfs.provided/*; do |
| ln -sf "${f}" "${P_CONFIG_DEVELOPER}/make.profile/package.provided/" |
| done |
| |
| # Create the directories defined in the portage config files. Permissions are |
| # consistent with the other directories in /usr/local, which is a bind mount |
| # for /mnt/stateful_partition/dev_image. |
| mkdir -p -m 0755 "${PORTDIR}" |
| mkdir -p -m 0755 "${PKGDIR}" |
| mkdir -p -m 0755 "${DISTDIR}" |
| mkdir -p -m 0755 "${RPMDIR}" |
| mkdir -p -m 0755 "${PORTAGE_TMPDIR}" |
| mkdir -p -m 0755 "${BUILD_PREFIX}" |
| echo "PORTAGE_BINHOST=${BINHOST}" > "${P_CONFIG_DEVELOPER}/make.conf" |
| |
| # Add LD_LIBRARY_PATH within ebuild.sh. |
| # TODO(arkaitzr): find out a cleaner way to do this. |
| sed -i "3 a\export LD_LIBRARY_PATH=\"${LD_LIBRARY_PATH}\"" \ |
| /usr/local/lib/portage/*/ebuild.sh |
| } |
| |
| install_optional_packages() { |
| if yes_or_no "Install virtual/target-os-dev package now"; then |
| emerge virtual/target-os-dev |
| else |
| echo "You can install virtual/target-os-dev later by typing the command:" \ |
| "emerge virtual/target-os-dev" |
| fi |
| } |
| |
| import_lsb_release() { |
| local override |
| CHROMEOS_DEVSERVER=$(get_lsb_var /etc/lsb-release CHROMEOS_DEVSERVER) |
| CHROMEOS_RELEASE_BOARD=$(get_lsb_var /etc/lsb-release CHROMEOS_RELEASE_BOARD) |
| |
| # Check local overrides. |
| if [ -f /usr/local/etc/lsb-release ]; then |
| override=$(get_lsb_var /usr/local/etc/lsb-release CHROMEOS_DEVSERVER) |
| [ -n "${override}" ] && CHROMEOS_DEVSERVER="${override}" |
| override=$(get_lsb_var /usr/local/etc/lsb-release CHROMEOS_RELEASE_BOARD) |
| [ -n "${override}" ] && CHROMEOS_RELEASE_BOARD="${override}" |
| fi |
| |
| # The board might have signed information appended to it, so filter that out. |
| CHROMEOS_RELEASE_BOARD=${CHROMEOS_RELEASE_BOARD%-signed-*keys} |
| } |
| |
| main() { |
| # Check if we are root. |
| if [ $(id -u) -ne 0 ]; then |
| error "Can not run dev_install." |
| error "You are not root or you did not use sudo." |
| return 1 |
| fi |
| |
| # Sanity check. People often run `su` or `sudo su` and leave off that final |
| # - which means they don't get a proper environment. Catch them here rather |
| # than fail randomly midway through the process. |
| case :${PATH}: in |
| *:/usr/local/bin:*) ;; |
| *) |
| error "Your environment appears to be incomplete. When changing to root," |
| error "did you remember to run the full command (don't forget the dash):" |
| error " $ sudo su -" |
| return 1 |
| ;; |
| esac |
| |
| # This script should only run in developer mode or for developer images. |
| if crossystem "cros_debug?0"; then |
| error "Can not run dev_install." |
| error "Chrome OS is not in developer mode." |
| return 1 |
| fi |
| |
| if [ ${FLAGS_uninstall} -eq ${FLAGS_TRUE} \ |
| -o ${FLAGS_reinstall} -eq ${FLAGS_TRUE} ]; then |
| remove_installed_packages |
| if [ ${FLAGS_uninstall} -eq ${FLAGS_TRUE} ]; then |
| if [ ${FLAGS_reinstall} -eq ${FLAGS_TRUE} ]; then |
| error "Reinstall specified but ignored as uninstall was also specified." |
| fi |
| return 0 |
| else |
| echo "Reinstalling emerge." |
| fi |
| fi |
| |
| # Check if packages are already installed. |
| if [ -d "${P_CONFIG_DEVELOPER}" ]; then |
| error "Directory ${P_CONFIG_DEVELOPER} exists." |
| error "Did you mean dev_install --reinstall?" |
| return 1 |
| fi |
| |
| # Create this loop so uncompressed files in /usr/local/usr/* will be reachable |
| # through /usr/local*. |
| # Note: /usr/local is mount-binded onto the /mnt/stateful_partition/dev_mode |
| # during chromeos_startup during boot for machines in dev_mode. |
| if [ ! -d /usr/local/usr ]; then |
| ln -s . /usr/local/usr |
| fi |
| |
| if [ ! -d /usr/local/local ]; then |
| ln -s . /usr/local/local |
| fi |
| |
| # Set up symlinks for etc/{group,passwd}, so that packages can look up users |
| # and groups correctly. |
| if [ ! -d /usr/local/etc ]; then |
| mkdir -p /usr/local/etc |
| fi |
| if [ ! -f /usr/local/etc/group ]; then |
| ln -s ../../../etc/group /usr/local/etc/group |
| fi |
| if [ ! -f /usr/local/etc/passwd ]; then |
| ln -s ../../../etc/passwd /usr/local/etc/passwd |
| fi |
| |
| echo "Starting installation of developer packages." |
| echo "First, we download the necessary files." |
| import_lsb_release |
| get_binhost |
| download_bootstrap_packages |
| if [ ${FLAGS_only_bootstrap} -eq ${FLAGS_TRUE} ]; then |
| echo "dev_install done installing bootstrap packages. Enjoy!" |
| return 0 |
| fi |
| |
| echo "Files downloaded, configuring emerge." |
| configure_emerge |
| |
| echo "Emerge installation complete. Installing additional optional packages." |
| install_optional_packages |
| |
| echo "dev_install done. Enjoy!" |
| } |
| |
| main |