Add ipkg option to specify installation directory
[hvlinux.git] / functions / main
index d677cc7..2801230 100644 (file)
@@ -2,6 +2,10 @@
 set -o errtrace # Let shell functions inherit ERR trap.
 set -o errexit
 
+# Don't locate and remember (hash) commands as they are looked up for
+# execution (don't remember path):
+set +h
+
 hvtrap_setup()
 {
     # Setting ERR trap does implicit `set -o errexit'.
@@ -23,17 +27,21 @@ hvtrap_int()
     exit 1
 }
 
+# Load list of packages and versions.
+# They may be overridden in sysinfos.
+source ../config/packages-list
+
 # Load default values
 source ../config/sysinfos.default
 
+# Load user-defined configuration values
 if [ ! -f ../config/sysinfos ]; then
     echo "Missing config/sysinfos configuration file."
     return ${EXIT_FAILURE}
+else
+    source ../config/sysinfos
 fi
 
-# Load user-defined configuration values
-source ../config/sysinfos
-
 # We want hvtrap_setup called only with the first sourcing of this script.
 if [ -z "${HVTRAP_SETUP_DONE1}" ]; then
     hvtrap_setup
@@ -43,11 +51,24 @@ fi
 # Load stage number definition
 source ./stage.def
 
+if [ -z "${LFS_STAGE}" ]; then
+    echo "LFS_STAGE is undefined (see stage.def)"
+    return ${EXIT_FAILURE}
+fi
+
+if [ "x${LFS_STAGE}" != "xstage0" -a "x${LFS_STAGE}" != "xstage1" ]; then
+    LFS=""
+fi
+
+CLFS=${LFS}
+
+CROSS_TOOLS_DIR=${CLFS}/cross-tools
+TOOLS_DIR=${CLFS}/tools
+
 # Load functions
 source ../functions/fpkg
-
-# Load list of packages and versions
-source ../config/packages-list
+source ../functions/lpkg
+source ../functions/version
 
 # Constants for return codes
 EXIT_SUCCESS=0
@@ -70,69 +91,60 @@ BRACKET="\033[1;34m"
 TAR_OPTS="-b8"
 
 # List of default archive extensions to try
-DEFAULT_ARCH_EXT_LIST="tar.bz2 tar.gz tgz tar.Z zip"
-
-HV_FONTS_PATH="/usr/share/fonts"
+DEFAULT_ARCH_EXT_LIST="tar.bz2 tar.gz tgz tar.Z tar.xz zip"
 
 DEFAULT_IPKG_SCRIPT="ipkg.def"
 
 HVMAKE="make -j ${MAKEJOBS}"
 
-case "${HVL_TARGET}" in
-    arm*)
-        CLFS_BUILDFLAGS="-mabi=aapcs-linux -mfloat-abi=soft"
-        CLFS_TARGET="arm-linux-gnueabi"
-        CLFS_ARCH=$(echo ${CLFS_TARGET} | sed -e 's/-.*//' -e 's/arm.*/arm/g')
-        CLFS_ENDIAN=$(echo ${CLFS_ARCH} | sed -e 's/armeb/BIG/' -e 's/arm/LITTLE/')
-        if [ "${CLFS_ENDIAN}" = "LITTLE" ]; then
-            CLFS_NOT_ENDIAN="BIG"
-        else
-            CLFS_NOT_ENDIAN="LITTLE"
-        fi
-       ;;
-    "x86_64")
-       CLFS_BUILDFLAGS="-m64"
-        CLFS_TARGET="${HVL_TARGET}-unknown-linux-gnu"
-        CLFS_ARCH=${HVL_TARGET}
-       ;;
-    "x86")
-        # No special flags
-       CLFS_BUILDFLAGS=""
-        CLFS_TARGET="i686-unknown-linux-gnu"
-        CLFS_ARCH=${HVL_TARGET}
-       ;;
-    *)
-       echo "Unsupported target architecture: ${HVL_TARGET}"
-       return ${EXIT_FAILURE}
-       ;;
-esac
+# Load default values. They can be overriden
+# by a target-specific file
+source ../targets/default
 
-CLFS_HOST="$(echo $MACHTYPE | \
-    sed "s/$(echo $MACHTYPE | cut -d- -f2)/cross/")"
-
-export CLFS_BUILDFLAGS CLFS_TARGET CLFS_ARCH CLFS_HOST CLFS_ENDIAN CLFS_NOT_ENDIAN
-
-if [ -z "${LFS_STAGE}" ]; then
-    echo "LFS_STAGE is undefined (see stage.def)"
-    return ${EXIT_FAILURE}
+if [ -f ../targets/${HVL_TARGET} ]; then
+    source ../targets/${HVL_TARGET}
 fi
 
-if [ "x${LFS_STAGE}" != "xstage0" -a "x${LFS_STAGE}" != "xstage1" ] ;then
-    LFS=""
+# When CLFS_OS is defined, define target as the standard triplet:
+CLFS_TARGET="${CLFS_ARCH}"
+if [ x"${CLFS_OS}" != x ]; then
+    CLFS_TARGET+="-${CLFS_OS}-${CLFS_ABI}"
 fi
 
-CLFS=${LFS}
+CLFS_HOST="$(echo $MACHTYPE | \
+    sed "s/$(echo $MACHTYPE | cut -d- -f2)/cross/")"
+
+export CLFS_BUILDFLAGS CLFS_TARGET CLFS_ARCH CLFS_HOST
+
+test_presence_of_packages_directory()
+{
+    if [ ! -d "$(dirname $(pwd))/packages" ]; then
+        echo "Error, could not find packages directory \"$(dirname $(pwd))/packages\", aborting."
+        exit 1
+    fi
+}
 
 # Create log directory and log file for current stage if necessary
-# This should be done automatically...
-init_log_file()
+# Arg #1: log file name
+static_init_log_file()
 {
+    # Checking for correct number of arguments
+    if [ $# -ne 1 ]; then
+       echo "${FUNCNAME}(), wrong number of arguments: ${*}"
+       return 1
+    fi
+
+    local LOG_FILE_NAME=${1}
+
     # Scripts directory
     export SCRDIR=$(pwd)
 
-    export LFS_PKG_DIR="$(dirname $(pwd))/packages/${LFS_STAGE}"
+    test_presence_of_packages_directory
+
+    export LFS_PKG_BASE="$(dirname $(pwd))/packages"
+    export LFS_PKG_DIR="${LFS_PKG_BASE}/${LFS_STAGE}"
     export LFS_LOG_DIR=${LFS}/var/log/hvlinux-install/${LFS_STAGE}
-    export LFS_LOG_FILE=${LFS_LOG_DIR}/install.log
+    export LFS_LOG_FILE=${LFS_LOG_DIR}/${LOG_FILE_NAME}
     export LFS_TMP="${LFS}/tmp"
 
     mkdir -p ${LFS_LOG_DIR} &&
@@ -141,6 +153,29 @@ init_log_file()
     fi
 }
 
+# Create log directory and install log file for current stage if necessary
+init_log_file()
+{
+    static_init_log_file "install.log"
+    export LFS_PATCHES_DIR="${SCRDIR}/patches"
+}
+
+# Create log directory and update log file for current stage if necessary
+init_log_file_update()
+{
+    static_init_log_file "pkg-update.log"
+    export LFS_PATCHES_DIR="${LFS_PKG_DIR}/patches"
+}
+
+# Check that we are logged in as the root user, and exit if this is not the case.
+check_for_root_user()
+{
+    if [ "x${USER}" != "xroot" ]; then
+        echo "You must be the superuser to install hvlinux."
+        exit 1
+    fi
+}
+
 # Extracting the version number from a complete package name.
 # Arg. #1: Complete package name with version (ex: firefox-3.5.5.source will output 3.5.5)
 get_pkg_ver()
@@ -154,9 +189,9 @@ get_pkg_ver()
     echo ${1} | sed "s!.*-\([0-9].*\)!\1!g"
 }
 
-# Extracting the base version number from a complete package name.
-# Arg. #1: Complete package name with version (ex: gcc-3.4.4 will output 3.4)
-get_pkg_ver_base()
+# Extracting the version number from a complete package name using underscore as separator.
+# Arg. #1: Complete package name with version (ex: ecryptfs-utils_103 will output 103)
+get_pkg_ver_underscore()
 {
     # Checking for correct number of arguments
     if [ $# -ne 1 ]; then
@@ -164,12 +199,46 @@ get_pkg_ver_base()
        return 1
     fi
 
-    echo ${1} | sed "s!^.*-\([0-9]*\.[0-9]*\).*!\1!g"
+    echo ${1} | sed "s!.*_\([0-9].*\)!\1!g"
+}
+
+# Extracting the version number from a complete package name
+# when the package name uses the underscore as the separator.
+# Arg. #1: Complete package name with version (ex: icu4c-54_1 will output 54.1)
+get_pkg_ver_underscore2()
+{
+    # Checking for correct number of arguments
+    if [ $# -ne 1 ]; then
+       echo "${FUNCNAME}(), wrong number of arguments: ${*}"
+       return 1
+    fi
+
+    local v=$(get_pkg_ver ${1})
+
+    # Replace underscore by dot
+    echo ${v} | sed "s!_!\.!g"
+}
+
+# Convert package version dots to uderscore.
+# Ex: gcc-3.4.6 will output 3_4_6
+# Arg. #1: Complete package name with version
+ver_dots_to_underscore()
+{
+    # Checking for correct number of arguments
+    if [ $# -ne 1 ]; then
+       echo "${FUNCNAME}(), wrong number of arguments: ${*}"
+       return 1
+    fi
+
+    local v=$(get_pkg_ver ${1})
+
+    # Replace dots by underscores
+    echo ${v} | sed "s!\.!_!g"
 }
 
-# Extracting the base version number from a complete package name.
+# Extracting the first digit version number from a complete package name.
 # Arg. #1: Complete package name with version (ex: gcc-3.4.4 will output 3)
-get_pkg_ver_major()
+get_pkg_ver1()
 {
     # Checking for correct number of arguments
     if [ $# -ne 1 ]; then
@@ -180,9 +249,10 @@ get_pkg_ver_major()
     echo ${1} | sed "s!^.*-\([0-9]*\)\..*!\1!g"
 }
 
-# Extracting the name from a complete package name.
-# Arg. #1: Complete package name with version (ex: gcc-3.4.4 will output gcc)
-get_pkg_name()
+# Extracting the first digit version number from a complete package name,
+# when the package name uses the underscore as the separator.
+# Arg. #1: Complete package name with version (ex: icu4c-54_1 will output 54)
+get_pkg_ver1_underscore()
 {
     # Checking for correct number of arguments
     if [ $# -ne 1 ]; then
@@ -190,41 +260,50 @@ get_pkg_name()
        return 1
     fi
 
-    # SED do not support non-greedy regexp:
-    # We want to match any characters preceeding a dash followed by a number
-    # (shortest match -> non-greedy)
-    echo ${1} | sed "s!\([^-][^0-9]*\)-[0-9].*!\1!"
+    echo ${1} | sed "s!^.*-\([0-9]*\)_.*!\1!g"
 }
 
-# Saves the content of CFLAGS and CXXFLAGS environment variables.
-save_flags()
+# Extracting the first 2 digits version number from a complete package name.
+# Arg. #1: Complete package name with version (ex: gcc-3.4.4 will output 3.4)
+get_pkg_ver2()
 {
-    export OLDCFLAGS=${CFLAGS}
-    export OLDCXXFLAGS=${CXXFLAGS}
+    # Checking for correct number of arguments
+    if [ $# -ne 1 ]; then
+       echo "${FUNCNAME}(), wrong number of arguments: ${*}"
+       return 1
+    fi
 
-    return ${EXIT_SUCCESS}
+    echo ${1} | sed "s!^.*-\([0-9]*\.[0-9]*\).*!\1!g"
 }
 
-# Saves the content of CFLAGS and CXXFLAGS environment variables, and then
-# set them to empty strings.
-save_flags_no_optimizations()
+# Extracting the first 3 digit version number from a complete package name.
+# Arg. #1: Complete package name with version
+# (ex: linux-2.6.37.1 will output 2.6.37)
+get_pkg_ver3()
 {
-    export OLDCFLAGS=${CFLAGS}
-    export OLDCXXFLAGS=${CXXFLAGS}
-    export CFLAGS=""
-    export CXXFLAGS=""
+    # Checking for correct number of arguments
+    if [ $# -ne 1 ]; then
+       echo "${FUNCNAME}(), wrong number of arguments: ${*}"
+       return 1
+    fi
 
-    return ${EXIT_SUCCESS}
+    echo ${1} | sed "s!^.*-\([0-9]*\.[0-9]*\.[0-9]*\).*!\1!g"
 }
 
-# Restores the previous values of CFLAGS and CXXFLAGS environment variables. These
-# must have been saved first using save_flags().
-restore_flags()
+# Extracting the name from a complete package name.
+# Arg. #1: Complete package name with version (ex: gcc-3.4.4 will output gcc)
+get_pkg_name()
 {
-    export CFLAGS=${OLDCFLAGS}
-    export CXXFLAGS=${OLDCXXFLAGS}
+    # Checking for correct number of arguments
+    if [ $# -ne 1 ]; then
+       echo "${FUNCNAME}(), wrong number of arguments: ${*}"
+       return 1
+    fi
 
-    return ${EXIT_SUCCESS}
+    # SED do not support non-greedy regexp:
+    # We want to match any characters preceeding a dash followed by a number
+    # (shortest match -> non-greedy)
+    echo ${1} | sed "s!\([^-][^0-9]*\)-[0-9].*!\1!"
 }
 
 # Create symbolic links for start/stop scripts in /etc/rc.d
@@ -355,20 +434,20 @@ var_export()
     fi
 
     # Checking if variable exists
-    if ! grep "${VARIABLE}=" ${FILE} 1> /dev/null 2>&1; then
+    if ! grep -q "${VARIABLE}=" ${FILE}; then
        echo "${FUNCNAME}(), variable not found: ${VARIABLE}"
        return 1
     fi
 
     # Checking if variable is already exported when it is defined
-    if grep "${VARIABLE}=" ${FILE} | grep "export " 1> /dev/null 2>&1; then
+    if grep "${VARIABLE}=" ${FILE} | grep -q "export "; then
        echo "${FUNCNAME}(), variable already exported in definition: ${VARIABLE}"
        return 0
     fi
 
     # Checking if variable is already exported, in
     # a "export VARIABLE1 VARIABLE2..." statement
-    if grep "export " ${FILE} | grep " ${VARIABLE}" 1> /dev/null 2>&1; then
+    if grep "export " ${FILE} | grep -q " ${VARIABLE}"; then
        echo "${FUNCNAME}(), variable already exported in export list: ${VARIABLE}"
        return 0
     fi
@@ -402,13 +481,13 @@ var_add()
     fi
 
     # Checking if variable exists
-    if ! grep "${VARIABLE}=" ${FILE} 1> /dev/null 2>&1; then
+    if ! grep -q "${VARIABLE}=" ${FILE}; then
        echo "${VARIABLE}=\"${VALUE}\"" >> ${FILE}
        return $?
     fi
 
     # Checking if variable contains the new value
-    if grep "${VARIABLE}=" ${FILE} | grep "${VALUE}" 1> /dev/null 2>&1; then
+    if grep "${VARIABLE}=" ${FILE} | grep -q "${VALUE}"; then
        echo "${FUNCNAME}(), variable ${VARIABLE} already contains value: ${VALUE}"
        return 0
     fi
@@ -417,7 +496,7 @@ var_add()
     # We search for the variable name starting at the beginning of the line
     # For example, this ensures that if the variable name is PATH, then
     # PATH=... matches but not MANPATH=...
-    if grep "^${VARIABLE}=\"" ${FILE} 1> /dev/null 2>&1; then
+    if grep -q "^${VARIABLE}=\"" ${FILE}; then
        # Variable value is enclosed by double-quotes
        sed -i "s!\(^${VARIABLE}=\".*\)\(\"\)!\1${SEP}${VALUE}\"!" ${FILE}
     else
@@ -430,7 +509,7 @@ var_add()
     # at the beginning of a line.
     # For example, this ensures that if the variable name is PATH, then
     # PATH=... matches but not MANPATH=...
-    if grep "^export ${VARIABLE}=\"" ${FILE} 1> /dev/null 2>&1; then
+    if grep -q "^export ${VARIABLE}=\"" ${FILE}; then
        # Variable value is enclosed by double-quotes
        sed -i "s!\(^export ${VARIABLE}=\".*\)\(\"\)!\1${SEP}${VALUE}\"!" ${FILE}
     else
@@ -483,7 +562,7 @@ string_add()
     fi
 
     # Checking if string exists
-    if grep "${STRING}" ${FILE} 1> /dev/null 2>&1; then
+    if grep -q "${STRING}" ${FILE}; then
        echo "${FUNCNAME}(), string already defined: ${STRING}"
        return 0
     fi
@@ -515,7 +594,7 @@ var_add_shadow()
     fi
 
     # Checking if variable contains the new value
-    if egrep "^${VARIABLE}" ${FILE} | grep "${VALUE}" 1> /dev/null 2>&1; then
+    if egrep "^${VARIABLE}" ${FILE} | grep -q "${VALUE}"; then
        echo "${FUNCNAME}(), variable ${VARIABLE} already contains value: ${VALUE}"
        return 0
     fi
@@ -527,23 +606,6 @@ var_add_shadow()
     sed -i "s!\(^${VARIABLE}.*\)!\1:${VALUE}!" ${FILE}
 }
 
-# Adding a new group, checking if it already exist before.
-# Arguments: same arguments as for standard groupadd command.
-hv_groupadd()
-{
-    arguments=${*}
-
-    # The last argument is the group name
-    while [ $# -ne 0 ]; do
-       groupname=${*}
-       shift
-    done
-
-    if ! cat /etc/group | egrep "^${groupname}:" 1> /dev/null 2>&1; then
-        groupadd ${arguments}    
-    fi
-}
-
 # Adding a new user, checking if it already exist before
 # Arguments: same arguments as for standard useradd command.
 hv_useradd()
@@ -556,18 +618,28 @@ hv_useradd()
        shift
     done
 
-    if ! cat /etc/passwd | egrep "^${username}:" 1> /dev/null 2>&1; then
+    if ! cat /etc/passwd | egrep -q "^${username}:"; then
         useradd ${arguments}
     fi
 }
 
 # Applying patch
 # First argument is the name of the patch
-# Second argument is the package name (target dir in $LFS_TMP)
+# Second argument is the package name
+# Third argument is the subdirectory inside the patches directory
+# Fourth argument: optional target directory (default is $LFS_TMP)
 apply_patch()
 {
     local PATCH_FILE=${1}
-    local TARGET_DIR=${2}
+    local PACKAGE=${2}
+    local PDIR=${3}
+    local TARGET_DIR=""
+
+    if [ $# -eq 4 ]; then
+       TARGET_DIR=${4}/${PACKAGE}
+    else
+       TARGET_DIR=${LFS_TMP}/${PACKAGE}
+    fi
 
     if [ -z "${PATCH_FILE}" ]; then
         echo
@@ -575,14 +647,14 @@ apply_patch()
        return ${EXIT_FAILURE}
     fi
 
-    if [ ! -f ${LFS_PKG_DIR}/${PATCH_FILE} ]; then
+    if [ ! -f ${PDIR}/${PATCH_FILE} ]; then
         echo
        echo "${FUNCNAME}(): patch file '${PATCH_FILE}' not found."
        return ${EXIT_FAILURE}
     fi
 
     echo "Applying patch: ${PATCH_FILE}"
-    patch -Np1 -d ${LFS_TMP}/${TARGET_DIR} -i ${LFS_PKG_DIR}/${PATCH_FILE}
+    patch -Np1 -d ${TARGET_DIR} -i ${PDIR}/${PATCH_FILE}
 }
 
 # Applying any patch(es) found for the current package.
@@ -595,7 +667,7 @@ apply_patch()
 apply_patches()
 {
     PACKAGE=${1}
-    local TARGET_DIR=${PACKAGE}
+    local TARGET_DIR=""
 
     # Checking for correct number of arguments
     if [ $# -gt 2 ]; then
@@ -606,17 +678,19 @@ apply_patches()
 
     if [ $# -eq 2 ]; then
        TARGET_DIR=${2}
-    else
-       TARGET_DIR=${PACKAGE}
     fi
 
-    # Checking if we can find at least one patch.
-    if ls ${LFS_PKG_DIR}/${1}-*.patch 1> /dev/null 2>&1; then
-       cd ${LFS_PKG_DIR}
-       for patch in ${1}-*.patch; do
-            apply_patch ${patch} ${TARGET_DIR}
-       done
-    fi
+    for s in common ${HVL_TARGET}; do
+        pdir=${LFS_PATCHES_DIR}/${s}
+        if [ -d ${pdir} ]; then
+            if ls ${pdir}/${1}-*.patch 1> /dev/null 2>&1; then
+               cd ${pdir}
+               for patch in ${1}-*.patch; do
+                    apply_patch ${patch} ${PACKAGE} ${pdir} ${TARGET_DIR}
+               done
+            fi
+        fi
+    done
 
     return $?
 }
@@ -633,23 +707,28 @@ static_decompressed_dirname()
             continue;
         fi
 
+       local DIRNAME=""
+
         case ${arch_ext} in
-           tar.bz2|tar.gz|tgz|tar.Z)
+           tar.bz2|tar.gz|tgz|tar.Z|tar.xz)
                 # Remove optional "./" leading component with sed
                 # and extract base directory name with awk.
                 # tar 1.23 reports an error when using pipes, so
                 # remove error message with "2> /dev/null"
-                local DIRNAME=$(tar ${TAR_OPTS} -tf \
+                # (we extract the last line from tar output)
+                DIRNAME=$(tar ${TAR_OPTS} -tf \
                     ${LFS_PKG_DIR}/${PACKAGE}.${arch_ext} 2> /dev/null | \
-                    head -n1 | sed 's!^\./!!' | awk -F \/ '{print $1}')
-                echo ${DIRNAME}
+                    tail -n 1 | sed 's!^\./!!' | awk -F \/ '{print $1}')
                 ;;
             zip)
-                # TODO
-                echo ${PACKAGE}
+                DIRNAME=$(unzip -l \
+                    ${LFS_PKG_DIR}/${PACKAGE}.${arch_ext} | head -n4 | \
+                    sed '$!d' | awk -F " " '{print $4}' | sed 's!/$!!')
                 ;;
         esac
 
+        echo ${DIRNAME}
+
         return $?
     done
 
@@ -661,6 +740,7 @@ static_decompressed_dirname()
 # Decompression of a package
 # First argument: package name
 # Second argument: directory where decompressing (optional, defaults to LFS_TMP)
+# Third argument: directory levels to strip (optional)
 decompress_package()
 {
     # Checking for correct number of arguments
@@ -668,8 +748,11 @@ decompress_package()
        local TOPDIR=${LFS_TMP}
     elif [ $# -eq 2 ]; then
        local TOPDIR=${2}
+    elif [ $# -eq 3 ]; then
+       local TOPDIR=${2}
+       local STRIP_LEVEL="--strip-components=${3}"
     else
-       echo "${FUNCNAME}(): Incorrect number of arguments (must be 1 or 2)" > /dev/stderr
+       echo "${FUNCNAME}(): Incorrect number of arguments (must be 1, 2 or 3)" > /dev/stderr
        return ${EXIT_FAILURE}
     fi
 
@@ -686,17 +769,15 @@ decompress_package()
            rm -rf ${TOPDIR}/${PACKAGE}
         fi
 
+
         # Decompressing package
         case ${arch_ext} in
-           tar.bz2)
-                tar -C ${TOPDIR} ${TAR_OPTS} -jxf \
-                    ${LFS_PKG_DIR}/${PACKAGE}.${arch_ext}
-                ;;
-           tar.gz|tgz|tar.Z)
-                tar -C ${TOPDIR} ${TAR_OPTS} -zxf \
+           tar.bz2|tar.gz|tgz|tar.Z|tar.xz)
+                tar -C ${TOPDIR} ${TAR_OPTS} ${STRIP_LEVEL} -xf \
                     ${LFS_PKG_DIR}/${PACKAGE}.${arch_ext}
                 ;;
             zip)
+                # Support STRIP_LEVEL???
                 unzip -qq -d ${TOPDIR} ${LFS_PKG_DIR}/${PACKAGE}.${arch_ext}
                 ;;
         esac
@@ -744,7 +825,7 @@ ipkg_display_build_infos()
     echo "  BUILD64:  ${BUILD64}"
     echo "  CFLAGS:   ${CFLAGS}"
     echo "  CPPFLAGS: ${CPPFLAGS}"
-    echo "  CXXFLAGS: ${CFLAGS}"
+    echo "  CXXFLAGS: ${CXXFLAGS}"
     echo "  LDFLAGS:  ${LDFLAGS}"
     echo "  RANLIB:   ${RANLIB}"
     echo "  CC:       ${CC}"
@@ -752,37 +833,87 @@ ipkg_display_build_infos()
     echo "  AR:       ${AR}"
     echo "  AS:       ${AS}"
     echo "  PATH:     ${PATH}"
+    echo "  HVMAKE:   ${HVMAKE}"
 
     GCCCPUOPT=$(gcc ${MARCH_FLAGS} -E -v - </dev/null 2>&1 | \
         sed -n 's/.* -v - //p')
     echo "  GCC CPU OPTIONS: ${GCCCPUOPT}"
 }
 
+# Arg. #1: Package label
+indicate_pkg_build_complete()
+{
+    # Checking for correct number of arguments
+    if [ $# -ne 1 ]; then
+       echo "${FUNCNAME}(): Missing argument(s)"
+       exit ${EXIT_FAILURE}
+    fi
+
+    local HVLABEL=${1}
+
+    # The following will be renamed to PACKAGE_DONE in one atomic operation.
+    local PACKAGE_STATUS=${LFS_LOG_DIR}/${HVLABEL}.status
+    local PACKAGE_DONE=${LFS_LOG_DIR}/${HVLABEL}.done
+
+    ipkg_display_build_infos > ${PACKAGE_STATUS}
+    echo "Source size: " ${SOURCE_SIZE} >> ${PACKAGE_STATUS}
+    echo "Build  size: " ${BUILD_SIZE}  >> ${PACKAGE_STATUS}
+    echo -n "Build time: " >> ${PACKAGE_STATUS}
+    display_build_time >> ${PACKAGE_STATUS}
+    echo -e "\n" >> ${PACKAGE_STATUS}
+    mv ${PACKAGE_STATUS} ${PACKAGE_DONE}
+}
+
 # Installation of a package
 # Arg. #1: Package name and version (ex: gcc-4.5.1)
 # Remaining arguments: Additional configure options
 # Options:
-#   -h     Display this help and returns
+#   -c     Do not decompress package
+#   -d DIR Change to directory DIR before configuring and building.
+#   -i DIR Installation directory (for cross-compilation, stage1)
+#   -j     Disable parallel make for this package
 #   -l     Unique identification label in 'install.log'
 #          (default is package name and version)
 #   -m     Installation mode:
 #            ac   Standard autoconf package, build in separate dir (default)
 #            acnb Standard autoconf package, build in source dir
 #            noac No autoconf (configure)
-#            gnome
-#            xorg
 #            pm
+#   -t     Enable dependency tracking
 #   -s     Name of script to source (default is from name of package)
 ipkg()
 {
     START_TIME=$(echo `date +%s`)
 
     unset ALT_SCRIPT_NAME
+
+    export DECOMPRESS="1"
     export IPKG_MODE="ac" # Default mode if not specified
     export HVLABEL="" # Global variable
+    export SRC_DIR="" # Global variable, source directory
+    export BUILD_DIR="" # Global variable, build directory
+    export SRC_SUBDIR="" # Global variable
+    export INSTALL_DIR="" # Global variable, installation directory
+    export ENABLE_DEPENDENCY_TRACKING="0"
+
+    local HVMAKE_ORIG=${HVMAKE}
 
-    while getopts "l:m:s:" flag ;do
+    while getopts "cd:i:jl:m:s:t" flag ;do
         case ${flag} in
+            c)
+                # Do not decompress package
+                DECOMPRESS="0"
+                ;;
+            d)
+                SRC_SUBDIR=${OPTARG}
+               ;;
+            i)
+                INSTALL_DIR=${OPTARG}
+               ;;
+            j)
+                # Disable parallel make only for this package
+                HVMAKE="make"
+                ;;
            l)
                 # Alternate label
                 HVLABEL=${OPTARG}
@@ -790,7 +921,7 @@ ipkg()
            m)
                 # Installation mode
                 case ${OPTARG} in
-                   ac|acnb|noac|gnome|xorg|pm)
+                   ac|acnb|noac|pm)
                         IPKG_MODE=${OPTARG}
                        ;;
                     *)
@@ -803,6 +934,10 @@ ipkg()
                 # Alternate script name
                 ALT_SCRIPT_NAME=${OPTARG}
                ;;
+            t)
+                # Do not disable dependency tracking
+                ENABLE_DEPENDENCY_TRACKING="1"
+               ;;
            ?)
                echo "${FUNCNAME}(): Invalid option: ${OPTARG}."
                return 1
@@ -834,13 +969,32 @@ ipkg()
     fi
 
     PACKAGE_LOG=${LFS_LOG_DIR}/${HVLABEL}.log
+    PACKAGE_DONE=${LFS_LOG_DIR}/${HVLABEL}.done
+    PACKAGE_STATUS=${LFS_LOG_DIR}/${HVLABEL}.status
+    SRC_DIR="${LFS_TMP}/${PACKAGE}"
+    BUILD_DIR="${LFS_TMP}/${PACKAGE}"
+
+    if [ -n "${SRC_SUBDIR}" ]; then
+        SRC_DIR+="/${SRC_SUBDIR}"
+    fi
+
+    case "${IPKG_MODE}" in
+        ac)
+            BUILD_DIR="${LFS_TMP}/${PACKAGE}-build"
+           ;;
+        acnb|noac|xpm)
+            BUILD_DIR="${SRC_DIR}"
+           ;;
+    esac
 
     # Checking if package was previously successfully installed
-    if grep "^${HVLABEL} successfully installed" ${LFS_LOG_FILE} \
-        1> /dev/null 2>&1; then
+    if [ -f ${PACKAGE_DONE} ]; then
        return $EXIT_SUCCESS
     fi
 
+    # Create or empty status file
+    cp /dev/null ${PACKAGE_STATUS}
+
     # Displaying label
     MSGSTRING="Installing ${HVLABEL}"
     display_checkbox_msg ${MSGSTRING}
@@ -850,12 +1004,6 @@ ipkg()
     echo "------------------------" 1>> ${LFS_LOG_FILE}
     echo "${MSGSTRING}" 1>> ${LFS_LOG_FILE}
 
-    exec 7>&1 # Save current "value" of stdout.
-    # All output from commands in this block sent to file $LFS_LOG_FILE.
-    exec >> ${LFS_LOG_FILE} 2>&1
-    ipkg_display_build_infos
-    exec 1>&7 7>&- # Restore stdout and close file descriptor #7.
-
     # All output from commands in this block sent to file $PACKAGE_LOG.
     exec > ${PACKAGE_LOG} 2>&1
 
@@ -864,17 +1012,29 @@ ipkg()
     source ${SCRDIR}/${DEFAULT_IPKG_SCRIPT}
     ipkg_script ${ALT_SCRIPT_NAME}
 
+    # Restore original value in case it was modified
+    HVMAKE=${HVMAKE_ORIG}
+
     # Make sure we are at a known location
     cd ${SCRDIR}
 
-    exec 1>&6 # Restore stdout.
+    if [ "x${LFS_STAGE}" != "xstage0" -a "x${LFS_STAGE}" != "xstage1" ]; then
+       # Make sure to update dynamic linker run-time bindings, except for
+       # stages 0 and 1 where we run as the LFS user.
+       ldconfig
+    fi
 
-    # Writing success string to the end of the log file
-    echo "${HVLABEL} successfully installed" 1>> ${LFS_LOG_FILE}
+    exec 1>&6 # Restore stdout.
 
-    # Displaying build time after the package name
+    # Displaying build time after the package name.
     print_status success
 
+    # Indicate success in global log file.
+    echo "Completed" 1>> ${LFS_LOG_FILE}
+
+    # Report successfull installation of the package.
+    indicate_pkg_build_complete ${HVLABEL}
+
     ipkg_trap_end
 
     return $EXIT_SUCCESS
@@ -915,19 +1075,29 @@ rscr()
        exit ${EXIT_FAILURE}
     fi
 
-    # Checking if script is valid and executable
-    if [ ! -x ${SCRDIR}/${SCRIPT} ]; then
+    # Checking if script file is found
+    if [ ! -f ${SCRDIR}/${SCRIPT} ]; then
        echo "${FUNCNAME}(): script not found: ${SCRIPT}"
        exit ${EXIT_FAILURE}
     fi
+    # ... and make sure it is executable
+    if [ ! -x ${SCRDIR}/${SCRIPT} ]; then
+       echo "${FUNCNAME}(): execute bit not set: ${SCRIPT}"
+       exit ${EXIT_FAILURE}
+    fi
 
     PACKAGE_LOG=${LFS_LOG_DIR}/${HVLABEL}.log
+    PACKAGE_DONE=${LFS_LOG_DIR}/${HVLABEL}.done
+    PACKAGE_STATUS=${LFS_LOG_DIR}/${HVLABEL}.status
 
     if [ "x${SCRMODE}" = "xonce" ]; then
         # Checking if package was previously successfully installed
-        if grep "^${HVLABEL} successfully installed" ${LFS_LOG_FILE} 1> /dev/null 2>&1; then
+        if [ -f ${PACKAGE_DONE} ]; then
            return $EXIT_SUCCESS
         fi
+
+        # Create or empty status file
+        cp /dev/null ${PACKAGE_STATUS}
     fi
 
     display_checkbox_msg ${MSGSTRING}
@@ -935,19 +1105,22 @@ rscr()
     ipkg_trap_setup
 
     echo "------------------------" 1>> ${LFS_LOG_FILE}
-    echo ${HVLABEL} 1>> ${LFS_LOG_FILE}
+    echo "Running script ${HVLABEL}" 1>> ${LFS_LOG_FILE}
 
     # Executing script
     ${SCRDIR}/${SCRIPT} ${SCRIPT_ARGS} 1>> ${PACKAGE_LOG} 2>&1
 
+    # Displaying build time after the package name.
+    print_status success
+
+    # Indicate success in global log file.
+    echo "Completed" 1>> ${LFS_LOG_FILE}
+
     if [ "x${SCRMODE}" = "xonce" ]; then
-        # Writing success string to the end of the log file
-        echo "${HVLABEL} successfully installed" 1>> ${LFS_LOG_FILE}
+        # Report successfull installation of the script.
+        indicate_pkg_build_complete ${HVLABEL}
     fi
 
-    # Displaying build time after the package name
-    print_status success
-
     ipkg_trap_end
 
     return $EXIT_SUCCESS
@@ -1034,19 +1207,19 @@ get_total_build_time()
        exit ${EXIT_FAILURE}
     fi
 
-    TIMES=$(cat ${INFILE} | grep "Build time:" | sed "s!Build time: \(.*\)h!\1!g")
+    TIMES=$(cat ${LFS_LOG_DIR}/*.done | grep "Build time:" | sed "s!Build time: \(.*\)h!\1!g")
 
     for time in ${TIMES}; do
        HOURS=$(( $HOURS + $(echo ${time} | sed "s!^\([0-9]*\)\..*!\1!g") ))
-       
+
        # The first SED command extracts the minutes (fractions of an hour).
        # The second SED command removed the leading zero, if applicable.
        MIN=$(( $MIN + $(echo ${time} | sed "s!.*\.\([0-9][0-9]\)!\1!g" | sed "s!^0\([0-9]\)!\1!g" ) ))
     done
-    
+
     HOURS=$(( ${HOURS} + ( ${MIN} / 100 ) ))
     MIN=$(( ${MIN} % 100 ))
-    
+
     echo "${HOURS}.${MIN}"
 }
 
@@ -1056,27 +1229,22 @@ display_build_time()
     END_TIME=$(echo `date +%s`)
 
     HOURS=$(( ( ${END_TIME} - ${START_TIME} ) / 3600 ))
-    echo -n " ("${HOURS}"."
-    echo -n "  Build time: ${HOURS}." 1>> ${LFS_LOG_FILE}
+    echo -n ${HOURS}"."
 
     # Time is inferior to 1 hour...
     MINUTES=$(( ( ( ${END_TIME} - ${START_TIME} ) % 3600 ) / 36 ))
 
     if [ ${MINUTES} -lt 10 ]; then
        echo -n "0"
-       echo -n "0" 1>> ${LFS_LOG_FILE}
     fi
 
     if [ ${MINUTES} -eq 0 ]; then
        echo -n "1"
-       echo -n "1" 1>> ${LFS_LOG_FILE}
     else
        echo -n ${MINUTES}
-       echo -n ${MINUTES} 1>> ${LFS_LOG_FILE}
     fi
 
-    echo -n "h)"
-    echo "h" 1>> ${LFS_LOG_FILE}
+    echo -n "h"
 }
 
 # Global variable: MSGSTRING
@@ -1095,7 +1263,8 @@ print_status()
     if [ $# -ne 1 ]; then
         # If no parameters are given to the print_status function, print usage
         # information.
-       echo "Usage: ${FUNCNAME}() {success|warning|failure} [STRING] [TIME]"
+       echo "Usage: ${FUNCNAME}() {success|warning|failure}"
+       echo "       got: \"${*}\""
        return ${EXIT_FAILURE}
     fi
 
@@ -1123,8 +1292,31 @@ print_status()
     echo -en "${BRACKET}[${COLOR}X${BRACKET}]${NORMAL} ${MSGSTRING}"
 
     if [ "x${MSGSTRING}" != "x" ]; then
+        echo -n " ("
         display_build_time
+        echo -n ")"
     fi
 
     echo
 }
+
+# This function will exit if the stage is already completed
+check_completed_stage()
+{
+    if [ -f ${LFS_LOG_DIR}/stage-completed ]; then
+        echo "${LFS_STAGE} completed"
+        exit 0
+    else
+        return 0
+    fi
+}
+
+write_completed_stage()
+{
+    touch ${LFS_LOG_DIR}/stage-completed
+}
+
+display_stage_build_stats()
+{
+    echo "Total build time: $(get_total_build_time ${LFS_LOG_FILE})h"
+}