#!/bin/bash # ----------------------------------------------------------------------------- # # Copyright (c) 2011..2018 bwLehrpool-Projektteam # # This program/file is free software distributed under the GPL version 2. # See https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html # # If you have any feedback please consult https://bwlehrpool.de and # send your feedback to support@bwlehrpool.de. # # General information about bwLehrpool can be found at https://bwlehrpool.de # # ----------------------------------------------------------------------------- # # This file contains the functions needed to setup a target. # The main function is 'generate_target' which will go over all # activated modules (symlinks found under core/targets/ or # overlay/targets/) and run the module-specific 'fetch_source', # 'build' and 'post_copy' functions of the modules. # # The functions defined here are module-independent, meaning they # can process any module, as long as following requirements are met: # # - Modules need to have a build-script and a config file, # i.e. core/module/kernel/module.build # and core/module/kernel/module.conf # as these will be read by 'process_module'. # # - Modules do not change anything inside the module dirs anymore, # everything (triggerfiles, source download, build) is done in # tmp/work/ # # - Several information have to be set in the config file of # a module: # * REQUIRED_BINARIES - list of binaries # * REQUIRED_FILES - list of regular files # * REQUIRED_DIRECTORIES - list of directories # * REQUIRED_SYSTEM_FILES - files to be copied directly from the system # * REQUIRED_INSTALLED_PACKAGES - list of packages needed at build-time # * REQUIRED_CONTENT_PACKAGES - list of packages where the contents are to be copied # * (optional variables needed by the module-build-script) # # NOTE: REQUIRED_BINARIES, REQUIRED_FILES, REQUIRED_FILES will be ONLY looked # for in the module's own build directory! # # Optionally, a module can have a static data directory under # core/modules//data which will be copied as is to the target build directory. # # Moreover modules do not need to specify the dynamic dependencies of given binaries # as these will be determined automaticly by the helper function 'get_dynamic_dependencies' # ----------------------------------------------------------------------------- # # Map modules from their names to path - contains all modules of current target declare -Ag MODULE_PATHS=() declare -g PACKAGES_INSTALLED_FLAG= declare -rg CORE_DIR="${ROOT_DIR}/core" declare -rg TARGETS_BASEDIR="$( readlink -f "${CORE_DIR}/targets" )" declare -rg EXPORT_DIR="/export/build" [ -z "$REMOTE_EXPORT_DIR" ] || EXPORT_DIR=$REMOTE_EXPORT_DIR initial_checks () { get_kernel_version detect_distribution } unset_required_vars () { for VARNAME in ${!REQUIRED_*}; do unset "$VARNAME" done } # Install all the packages from all selected modules in current target install_all_packages () { [ -n "$PACKAGES_INSTALLED_FLAG" ] && return 0 PACKAGES_INSTALLED_FLAG=1 local module file config declare -a missing declare -A packages dupcheck packages=() for module in $MODULES; do unset_required_vars config="${MODULE_PATHS["$module"]}/module.conf" for file in $SYS_VERSIONS; do if [ -e "${config}.${file}" ]; then validate_config "${config}.${file}" . "${config}.${file}" break fi done dupcheck=() for file in $REQUIRED_INSTALLED_PACKAGES; do [ -n "${dupcheck["$file"]}" ] && continue packages["$file"]+=" $module" dupcheck["$file"]=1 done for file in $REQUIRED_CONTENT_PACKAGES; do [ "${file:0:1}" = "@" ] && continue [ -n "${dupcheck["$file"]}" ] && continue packages["$file"]+=" $module" dupcheck["$file"]=1 done done missing=( $( filter_installed_packages "${!packages[@]}" ) ) if [ "${#missing[@]}" -gt 0 ]; then pinfo "Making sure required packages are installed..." pinfo "Potentially missing: ${missing[*]}" install_packages "${missing[@]}" fi unset_required_vars } # # generic function to read the config file of the current $MODULE # read_config () { # unset previous variables from other config files unset_required_vars local MODULE_CONFIG="${MODULE_DIR}/module.conf" # sanity checks [ ! -e "${MODULE_CONFIG}" ] && perror "Config for '$MODULE' not found." validate_config "$MODULE_CONFIG" # source the config file . "${MODULE_CONFIG}" || perror "Sourcing '${MODULE_CONFIG}' failed." # sanity checks - no distribution specific things in the global config local CHECK for CHECK in REQUIRED_CONTENT_PACKAGES REQUIRED_INSTALLED_PACKAGES; do [ -n "${!CHECK}" ] && perror "$CHECK is set in ${MODULE}.conf, but distribution specific config has to go to one of these files:\n$PRINT_SYS_VERSIONS" done local FILE for FILE in $SYS_VERSIONS; do if [ -e "${MODULE_CONFIG}.${FILE}" ]; then # a specific tool.conf seems to exist, use it to override/extend certain vars validate_config "${MODULE_CONFIG}.${FILE}" . "${MODULE_CONFIG}.${FILE}" || perror "Sourcing '${MODULE_CONFIG}.${FILE}' failed." pinfo "Sourced distribution specific config (${MODULE_CONFIG##*/}.${FILE})" return # end function fi done # reaching here means no specific config pinfo "Sourced general module config only (${MODULE_CONFIG##*/})" } validate_config () { local INVALID=$(grep -h -E -o '^\s*\w+=' "$1" | grep -v -E '^\s*REQUIRED_') [ -z "$INVALID" ] && return 0 local i for i in $INVALID; do pwarning "Invalid variable $i" done perror "$1 contains invalid variables. All variables must start with REQUIRED_" } # # generic function to read the build script of the current $MODULE # read_build () { local BUILD_SCRIPT="${MODULE_DIR}/module.build" [ ! -e "${BUILD_SCRIPT}" ] && perror "Build script for specified tool not found." . "${BUILD_SCRIPT}" || perror "Sourcing '${BUILD_SCRIPT}' failed." } export_builds() { [ ! -d "${ROOT_DIR}/var/builds" ] && perror "No ${ROOT_DIR}/var/builds, nothing to export." if mount | grep -q "${ROOT_DIR}/var/builds"; then pwarning "${ROOT_DIR}/var/builds already exported! Ignoring..." return 1; fi pinfo "Mounting ${ROOT_DIR}/var/builds to ${EXPORT_DIR}." pinfo "This will make the local var/builds syncable from another machine." [ ! -d "${EXPORT_DIR}" ] && mkdir -p "${EXPORT_DIR}" mount --bind "${ROOT_DIR}/var/builds" "${EXPORT_DIR}" \ || perror "Failed to bind mount ${ROOT_DIR}/var/builds to ${EXPORT_DIR}" } # # main function which copies all files, directories, binaries and external libraries to TARGET_BUILD_DIR # called after building the module # copy_files_with_deps () { [ ! -d "$MODULE_BUILD_DIR" ] && pinfo "No build directory found, skipping dependency copying" && return 0 cde "$MODULE_BUILD_DIR" local COPYFILES_LIST="${TARGET_BUILD_DIR}/opt/openslx/.mltk/${MODULE}.copy_files_with_deps" local TESTFILE BIN ENTRY OPTIONAL FILENAME LIB LOCATION FILE OLD CLISTCOUNT declare -a FILE_CANDIDATES=() declare -a FINAL_LIST=() rm -f -- "${COPYFILES_LIST}" # from REQUIRED_BINARIES - follow symlinks and ldd the file [ -n "${REQUIRED_BINARIES}" ] && pinfo "Gathering required binaries from config file..." for FILENAME in ${REQUIRED_BINARIES}; do if [[ "${FILENAME:0:1}" == "@" ]]; then OPTIONAL="yes" FILENAME="${FILENAME:1}" else OPTIONAL="no" fi mapfile -d '' -t FILE_CANDIDATES < <( find . -name "${FILENAME}" -a \( -type f -o -type l \) -print0 ) # Check result of find if (( ${#FILE_CANDIDATES[@]} > 1 )); then # More than one match for binary pdebug "Candidates for $FILENAME are: ${FILE_CANDIDATES[*]}" for FILE in "${FILE_CANDIDATES[@]}"; do TESTFILE="$(readlink -f "$FILE")" pdebug " $FILE leads to $TESTFILE" [ -f "$TESTFILE" ] && [ -x "$TESTFILE" ] && FINAL_LIST+=( "$FILE" ) done if (( ${#FINAL_LIST[@]} == 0 )); then # Not found in build dir; if install mode, check if it's in the PKG_CONTENT_FILE if [ -n "$MLTK_INSTALL" ]; then # TODO: Might want to iterate over all the matches and check if at least one of them # is +x grep -q "/${FILENAME}\$" "$PKG_CONTENT_FILE" && continue fi perror "\tNo binary found for ${FILENAME}. None of the candidates (${FILE_CANDIDATES[*]}) is executable." fi if (( ${#FINAL_LIST[@]} > 1 )); then pdebug "Found more than one match for required file '$FILENAME': ${FINAL_LIST[*]}" else pdebug "\tFound ${FILENAME} at ${FILE}" fi elif [ "$OPTIONAL" == "no" ] && (( ${#FILE_CANDIDATES[@]} == 0 )); then # No candidate # Same as above: accept existing file in / if [ -n "$MLTK_INSTALL" ]; then grep -q "/${FILENAME}\$" "$PKG_CONTENT_FILE" && continue fi perror "Could not find required binary $FILENAME" else # One candidate TESTFILE="$( readlink -f "${FILE_CANDIDATES[0]}" )" if [ -f "$TESTFILE" ] && [ -x "$TESTFILE" ]; then FINAL_LIST=( "${FILE_CANDIDATES[0]}" ) elif [ "$OPTIONAL" = "no" ]; then perror "No executable regular file found for '$FILENAME' (potential match was ${FILE_CANDIDATES[0]})" fi fi for FILE in "${FINAL_LIST[@]}"; do pdebug "* $FILE" get_link_chain "${MODULE_BUILD_DIR}/${FILE}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" get_dynamic_dependencies -l "${MODULE_BUILD_DIR}" "${FILE}" >> "${COPYFILES_LIST}" done done # from REQUIRED_LIBRARIES - like binaries, follow symlinks and ldd, but also expand the lib-name # by looking for files named .so* [ -n "$REQUIRED_LIBRARIES" ] && pinfo "Gathering required libraries from config file..." for LIB in $REQUIRED_LIBRARIES; do mapfile -d '' -t FILE_CANDIDATES < <(find . -name "${LIB}.so*" -print0) if (( ${#FILE_CANDIDATES[@]} == 0 )); then if [ -n "$MLTK_INSTALL" ]; then # TODO: Escape LIB and use a proper regex with -P grep -qF "/${LIB}.so" "$PKG_CONTENT_FILE" && continue fi perror "Cannot find required library $LIB" fi for LOCATION in "${FILE_CANDIDATES[@]}"; do pdebug "* $LOCATION" get_link_chain "${MODULE_BUILD_DIR}/${LOCATION}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" get_dynamic_dependencies -l "${MODULE_BUILD_DIR}" "${LOCATION}" >> "${COPYFILES_LIST}" done done # from REQUIRED_DIRECTORIES - recursively copy given dirs, look for files that seem to be an elf # binary and do the symlink+ldd trick on them [ -n "${REQUIRED_DIRECTORIES}" ] && pinfo "Gathering required directories from config file..." local ENTRY="" for ENTRY in ${REQUIRED_DIRECTORIES}; do [[ "$ENTRY" == /* ]] || perror "All entries in REQUIRED_DIRECTORIES have to start with a slash '/', but $ENTRY does not!" [ -e "$ENTRY" ] && [ ! -d "$ENTRY" ] && perror "$ENTRY is not a directory in base system" pdebug "* $ENTRY" if [ -d ".${ENTRY}" ]; then ENTRY=".${ENTRY}" echo "${ENTRY}" >> "${COPYFILES_LIST}" mapfile -d '' -t FILE_CANDIDATES < <(find "${ENTRY}" -type f -a \( -executable -o -name '*.so*' \) -a -not -name '*.a' -print0) for BIN in "${FILE_CANDIDATES[@]}"; do [ -f "$BIN" ] || continue #pdebug "\tSearching libs for ${BIN}..." get_link_chain "${MODULE_BUILD_DIR}/${BIN}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" get_dynamic_dependencies -l "${MODULE_BUILD_DIR}" "${BIN}" >> "${COPYFILES_LIST}" done elif [ -z "$MLTK_INSTALL" ] || ! [ -d "$ENTRY" ]; then perror "Required directory $ENTRY not found" fi done # from REQUIRED_FILES - these are assumed to be simple files, so only follow symlinks [ -n "${REQUIRED_FILES}" ] && pinfo "Gathering required files from config file..." for ENTRY in ${REQUIRED_FILES}; do if [ "${ENTRY:0:1}" = "@" ]; then ENTRY="${ENTRY:1}" if [ ! -e "${MODULE_BUILD_DIR}/${ENTRY}" ]; then pinfo "Skipping missing optional file: ${MODULE_BUILD_DIR}/${ENTRY}" continue fi fi if [ -n "$MLTK_INSTALL" ] && ! [ -e "${MODULE_BUILD_DIR}/${ENTRY}" ]; then [ -e "/${ENTRY}" ] && continue # OK for install mode... I think fi get_link_chain "${MODULE_BUILD_DIR}/${ENTRY}" "${MODULE_BUILD_DIR}" >> "${COPYFILES_LIST}" done #copy to initramfsdir pdebug "File list generated at ${COPYFILES_LIST}." # empty? if [ ! -s "$COPYFILES_LIST" ]; then return fi # unchanged? OLD="${COPYFILES_LIST}.old" if [ -s "$OLD" ] && cmp -s "$OLD" "$COPYFILES_LIST"; then return fi CLISTCOUNT=$( < "$COPYFILES_LIST" wc -l ) pinfo "Copying $CLISTCOUNT files to '${TARGET_BUILD_DIR}'." tarcopy "$(sort -u "$COPYFILES_LIST")" "${TARGET_BUILD_DIR}" mv -f "$COPYFILES_LIST" "$OLD" } # # # If the system has no usr split, recreate the structure in the given # directory, otherwise, do nothing # # prepare_usr_split () { local DIR DEST local BASE=$1 [ -z "$BASE" ] && perror "prepare_usr_split called with empty base dir" if [ "$USR_SPLIT" == "no" ]; then # no usr split, /bin /lib etc are symlinks for DIR in lib lib32 lib64 bin sbin; do [ -L "/${DIR}" ] || continue DEST=$(readlink "/${DIR}") if [ ! -L "/${DEST}" ]; then mkdir -p "${BASE}/${DEST}" || perror "Could not create '${BASE}/${DEST}'" fi [ -L "${BASE}/${DIR}" ] || ln -s "${DEST}" "${BASE}/${DIR}" || perror "Could not symlink '${BASE}/${DIR}' to '${DEST}'" done fi } # Recursively get all modules in the current target scan_target() { local dir item base real dir="$1" [ -d "$1" ] || perror "scan_target: $dir is not a directory" for item in "$dir/"*; do [ -L "$item" ] || continue base="$( basename "$item" )" real="$( readlink -f "$item" )" if [ -f "$item/module.conf" ]; then if [ -n "${MODULE_PATHS["$base"]}" ] \ && [ "${MODULE_PATHS["$base"]}" != "$real" ]; then perror "$base exists in multiple sub-targets and links to different modules\n1: $real\n2: ${MODULE_PATHS["$base"]}" fi MODULE_PATHS["$base"]="$real" elif [ "${real#"$TARGETS_BASEDIR"}" != "$real" ]; then scan_target "$real" else perror "Unknown link in target '$dir': '$base' is '$real', neither target nor module" fi done } # # # main public function. Requires the TARGET to be given as argument. # this will simply go over all the modules as found in the core/target/ # and run following functions: # # generate_target() { initial_checks TARGET="$1" shift TARGET_DIR="$( readlink -f "${TARGETS_BASEDIR}/${TARGET}" )" scan_target "$TARGET_DIR" if [[ "$TARGET" == *@* ]]; then VARNAME="${TARGET#*@}" TARGET="${TARGET%@*}" declare -r TARGET_VERSIONS=${!VARNAME} if [ -z "$TARGET_VERSIONS" ]; then perror "Asked to build versioned target $TARGET with version list in $VARNAME, but $VARNAME is empty." fi for TARGET_VERSION in ${TARGET_VERSIONS}; do TARGET_BUILD_DIR="${ROOT_DIR}/var/builds/${TARGET}@${TARGET_VERSION}" generate_target_real "$@" done else declare -r TARGET_VERSIONS= declare -r TARGET_VERSION= TARGET_BUILD_DIR="${ROOT_DIR}/var/builds/${TARGET}" generate_target_real "$@" fi } generate_target_real() { # Keep track of processed modules so we don't # build a module multiple times when following dependencies local item local PROCESSED_MODULES= [ -d "$TARGET_DIR" ] || perror "Given target directory does not exist: $TARGET_DIR" [[ "$TARGET" == "builds" || "$TARGET" == "modules" ]] && \ perror "Target directory cannot be named 'builds' or 'modules'." pdebug "Generating '$TARGET_BUILD_DIR' for '$TARGET' $TARGET_VERSION" mkdir -p "$TARGET_BUILD_DIR" || perror "Failed to create $TARGET_BUILD_DIR" prepare_usr_split "${TARGET_BUILD_DIR}" # if no arguments assume all. if [ -z "$1" ] || [ "$1" = "all" ]; then MODULES="${!MODULE_PATHS[*]}" else # tools = arguments given MODULES="$*" fi pinfo "Activated modules in '${TARGET}':" pinfo "\t$(echo ${MODULES})" # Make sure all selected modules seem valid for item in $MODULES; do [ -n "${MODULE_PATHS["$item"]}" ] || perror "Module $item not found in target" done # Run load hook # This will run before installing any packages, which might be required in # some cases declare -A mod_init_done=() for item in $MODULES; do process_module_init "$item" done install_all_packages # copy basic libs pinfo "Copying libc and ld-linux used by ${SHELL}" tarcopy "$(list_basic_libs)" "${TARGET_BUILD_DIR}" # now iterate over given tools and copy them for item in $MODULES; do process_module "$item" done post_process_target pinfo "Target completed. Total size: $(du -bsh "${TARGET_BUILD_DIR}" | awk 'END {print $1}')" TOOL_STR="" } process_module_init() { local item="$1" local dep mod_init_done["$item"]=1 unset_required_vars . "${MODULE_PATHS["$item"]}/module.conf" for dep in $REQUIRED_MODULES; do [ -n "${MODULE_PATHS["$dep"]}" ] || perror "Module $dep, dependency of $item, not found in target" [ -z "${mod_init_done["$dep"]}" ] && process_module_init "$dep" done . "${CORE_DIR}/includes/clean_module_funcs.inc" # Clean all hooks, in case the module doesn't define them all . "${MODULE_PATHS["$item"]}/module.build" # TODO? We only source the general .conf, not the .conf.distro one... needed for module_init? module_init } process_module() { # Parse arguments [ "$#" -lt "1" ] && perror "process_module: want >= 1 param." if [ "$1" = "--dep-of" ]; then shift local DEPOF=" $1" shift else local DEPOF="" fi local MODULE="$1" [ -z "$MODULE" ] && perror "No module given when calling process_module" [[ "$PROCESSED_MODULES" == *"!${MODULE}!"* ]] && return # Already processed this module # Set up dirs ans variables PROCESSED_MODULES="${PROCESSED_MODULES}!${MODULE}!" local MODULE_DIR="${MODULE_PATHS["${MODULE}"]}" local MODULE_WORK_DIR="${ROOT_DIR}/tmp/work/${MODULE}" if [[ "$MODULE" == *@ ]]; then [ -z "$TARGET_VERSION" ] && perror "Asked to build $MODULE in non-versioned target $TARGET" MODULE_WORK_DIR+="-${TARGET_VERSION}" fi local MODULE_BUILD_DIR="${MODULE_WORK_DIR}/build" local MODULE_META_DIR="${MODULE_WORK_DIR}/meta" local TOOL_STR="[${MODULE}${TARGET_VERSION}]" local SOURCE_FLAG="${MODULE_META_DIR}/fetched_source.flag" local BUILD_FLAG="${MODULE_META_DIR}/build_complete.flag" local COPY_FLAG="${MODULE_META_DIR}/copy_complete.flag" local AUTOCLONE_FILE="${MODULE_META_DIR}/repos" local MD5FILE="${TARGET_BUILD_DIR}/opt/openslx/.mltk/${MODULE}.md5" local PKG_CONTENT_FILE="${MODULE_META_DIR}/list_package_contents" if [ ! -d "${MODULE_DIR}" ]; then if [ -z "$DEPOF" ]; then perror "Module directory for '$MODULE' not found in ${TARGET_DIR}" fi perror "Module directory for '$MODULE' not found in ${TARGET_DIR} (is a dependency of${DEPOF})" return fi # Simple check for modified .build / .config, in which case we'll run a module clean first if [ -f "$MD5FILE" ] && ! md5sum --check "$MD5FILE"; then # Mismatch if [ "$MODULE" = "kernel" ]; then pwarning " ** Kernel .build/.config changed, but won't autoclean." pwarning " ** Check if cleaning should be done and use '-c kernel'" else pwarning "Module's .build/.config has changed, rebuilding!" clean_module "${MODULE}" fi fi # Write new md5 file if not existent yet mkdir -p "${TARGET_BUILD_DIR}/opt/openslx/.mltk" "${MODULE_META_DIR}" if ! [ -f "$MD5FILE" ]; then md5sum "$MODULE_DIR/module".* > "$MD5FILE" || perror "Could not create $MD5FILE" fi cde "${MODULE_DIR}" # Process module pdebug "## Reading config of $MODULE" read_config # Prepare build directory mkdir -p "${MODULE_BUILD_DIR}" || perror "Could not create build dir" prepare_usr_split "${MODULE_BUILD_DIR}" # Check if this module has a dependency that wasn't built yet: if [ -n "$REQUIRED_MODULES" ]; then pdebug "$MODULE depends on ${REQUIRED_MODULES}...." for DEP in $REQUIRED_MODULES; do process_module --dep-of "${MODULE}${DEPOF}" "$DEP" done # Read old config again, as it got overwritten by the deps cde "${MODULE_WORK_DIR}" read_config fi [ -n "$DEPOF" ] && local DEPOF_STR="(dependency of${DEPOF})" pinfo ">>>>>>>>>>>>>>>>> Processing module [ $MODULE ] $DEPOF_STR" # Source .build script of module pdebug "## Reading .build of $MODULE" . "${CORE_DIR}/includes/clean_module_funcs.inc" # Clean all hooks, in case the module doesn't define them all read_build # Read all the hooks from the module cde "${MODULE_DIR}" # update kernel version variables before running a module, as the last one might have been the kernel... get_kernel_version # Execute pre_exec before anything else, might change some of the _DIR vars pre_exec cde "${MODULE_WORK_DIR}" # Fetch source code if [ ! -e "$SOURCE_FLAG" ]; then pinfo "## Fetching source" rm -f -- "$AUTOCLONE_FILE" fetch_source touch "$SOURCE_FLAG" || pwarning "Error setting source-flag" fi # Build if ! [ -e "$BUILD_FLAG" ]; then list_packet_files > "$PKG_CONTENT_FILE" || perror "Could not write packet content list to $PKG_CONTENT_FILE" # Copy all required packages' contents to build dir, but not in install mode if [ -z "$MLTK_INSTALL" ] && [ -s "$PKG_CONTENT_FILE" ]; then pinfo "## Copying required packages to build dir" tarcopy "$( sort -u "$PKG_CONTENT_FILE" )" "${MODULE_BUILD_DIR}" fi cde "${MODULE_WORK_DIR}" pinfo "## Building" build # calls perror if something fails, no need to do that here strip_recursive "$MODULE_BUILD_DIR" touch "$BUILD_FLAG" || pwarning "Error setting built-flag" fi # Remove *.la files as they might confuse libtool/linker of other tool packages [ -d "${MODULE_BUILD_DIR}" ] && find "${MODULE_BUILD_DIR}" -name '*.la' -exec rm -f {} \; # Only copy stuff from system or build dir if md5s didn't change if ! [ -e "$COPY_FLAG" ]; then pinfo "## Copying files with dependencies" cde "${MODULE_DIR}" copy_files_with_deps if [ -n "$REQUIRED_SYSTEM_FILES" ]; then pinfo "## Copying required system files" # REQUIRED_SYSTEM_FILES cde "${MODULE_DIR}" copy_system_files fi touch "$COPY_FLAG" || pwarning "Error setting copy-flag" fi # Always copy static data if [ -d "${MODULE_DIR}/data" ]; then pinfo "## Copying static module files" copy_static_data fi # TODO: automatic copy of REQUIRED_CONTENT_PACKAGES pinfo "## Post copy" cde "${MODULE_DIR}" post_copy [ -s "$AUTOCLONE_FILE" ] && cp "$AUTOCLONE_FILE" "${TARGET_BUILD_DIR}/opt/openslx/.mltk/${MODULE}.git" # Sanity checks [ -e "$TARGET_BUILD_DIR/var/run" ] && ! [ -L "$TARGET_BUILD_DIR/var/run" ] \ && perror "Messup datected: $TARGET_BUILD_DIR/var/run exists and is not a symlink!" [ -e "$TARGET_BUILD_DIR/var/lock" ] && ! [ -L "$TARGET_BUILD_DIR/var/lock" ] \ && perror "Messup datected: $TARGET_BUILD_DIR/var/lock exists and is not a symlink!" [ -n "$(ls -A "$TARGET_BUILD_DIR/run" 2>> /dev/null)" ] && perror "Messup detected: $TARGET_BUILD_DIR/run is not empty. You cannot place static files there, use /etc/tmpfiles.d instead!" } post_process_target() { local TOOL_STR="$TOOL_STR post_process_target:" local i if [ -z "$MLTK_INSTALL" ]; then # traditional minilinux approach # figure out all relevant ld-paths pinfo "Running ldconfig" cp -r -L /etc/ld.so.conf* "${TARGET_BUILD_DIR}/etc/" ldconfig -v -r "${TARGET_BUILD_DIR}" # run depmod to generate the final modules.* files pinfo "Running depmod for '${TARGET_KERNEL_LONG}'" depmod -b "${TARGET_BUILD_DIR}" -a "${TARGET_KERNEL_LONG}" pinfo "Running glib-compile-schemas" for i in /usr/local/share/glib-2.0/schemas/ /usr/share/glib-2.0/schemas/; do glib-compile-schemas "${TARGET_BUILD_DIR}/$i" done return 0 fi # Installer mode local RSYNC_DEST RSYNC_EXCLUDE_LIST entry filelist target_file system_file # default dest and exclude list for non-addons RSYNC_DEST='/' RSYNC_EXCLUDE_LIST="$( mktemp "${ROOT_DIR}/tmp/${TARGET}${TARGET_VERSION}-rsync-install-XXXXX" )" # always exclude ld.so.cache, we handle that separately echo '/etc/ld.so*' > "$RSYNC_EXCLUDE_LIST" echo '/autoexec.bat' >> "$RSYNC_EXCLUDE_LIST" if [ -e "${TARGET_DIR}/.addon" ]; then # This is an addon, don't sync to / but to dedicated directory RSYNC_DEST="/opt/openslx/addons/${TARGET_BUILD_DIR##*/}" pinfo "Target is an addon, preparing install to '${RSYNC_DEST}'." mkdir -p "${RSYNC_DEST}" || perror "Failed to mkdir '${RSYNC_DEST}'." RSYNC_OPTS+=( "--delete" "--delete-excluded" ) cde "$TARGET_BUILD_DIR" readarray -t -d '' filelist < <( find . -not -type d -print0 ) for entry in "${filelist[@]}"; do # diff them to be sure they are the same? system_file="${entry:1}" target_file="${entry:2}" if [ -e "$system_file" ]; then if [ -L "$system_file" ] && [ -L "$target_file" ]; then # Two links with same destination? Skip [ "$( readlink "$system_file" )" == "$( readlink "$target_file" )" ] \ && echo "$system_file" >> "$RSYNC_EXCLUDE_LIST" elif [ -L "$system_file" ] || [ -L "$target_file" ]; then : # One is link, the other isn't, do not skip elif cmp -s "$system_file" "$target_file"; then # Two files with same contents? Skip echo "$system_file" >> "$RSYNC_EXCLUDE_LIST" fi fi done pinfo "Calling ldconfig on overlay'ed ${TARGET_BUILD_DIR##*/}..." chroot_run "${TARGET_BUILD_DIR}" <<< "ldconfig" if [ -f "${TARGET_BUILD_DIR}/etc/ld.so.cache" ]; then mkdir -p "${TARGET_BUILD_DIR}/opt/openslx/etc" mv -f -- "${TARGET_BUILD_DIR}/etc/ld.so.cache" \ "${TARGET_BUILD_DIR}/opt/openslx/etc/${TARGET_BUILD_DIR##*/}.ld.so.cache" pinfo "... generated cache at '${TARGET_BUILD_DIR}/opt/openslx/etc/${TARGET_BUILD_DIR##*/}.ld.so.cache'." fi fi pinfo "Rsyncing local build of '${TARGET_BUILD_DIR}' to '${RSYNC_DEST}'..." rsync -aAXv "${RSYNC_OPTS[@]}" \ --exclude-from="${RSYNC_EXCLUDE_LIST}" \ "${TARGET_BUILD_DIR}/" "${RSYNC_DEST}" || \ perror "Failed to rsync, your system is probably trashed ;-(" [ -z "$MLTK_DEBUG" ] && rm -f -- "$RSYNC_EXCLUDE_LIST" # compile Gsettings schema files "${RSYNC_DEST}/usr/bin/glib-compile-schemas" "${RSYNC_DEST}/usr/share/glib-2.0/schemas/" # finish by running ldconfig for the running system (when processing non-addons). if [ ! -e "${TARGET_DIR}/.addon" ]; then pinfo "Running ldconfig" ldconfig -v pinfo "Running depmod for '${TARGET_KERNEL_LONG}'" depmod -a "${TARGET_KERNEL_LONG}" pinfo "Running glib-compile-schemas" for i in /usr/local/share/glib-2.0/schemas/ /usr/share/glib-2.0/schemas/; do glib-compile-schemas "$i" done fi } clean_modules() { local module TARGET=$1 shift TARGET_DIR="$( readlink -f "${TARGETS_BASEDIR}/${TARGET}" )" scan_target "$TARGET_DIR" [ -d "$TARGET_DIR" ] || perror "Given target directory does not exist: $TARGET_DIR" if [[ "$TARGET" == *@* ]]; then VARNAME="${TARGET#*@}" TARGET="${TARGET%@*}" declare -r TARGET_VERSIONS=${!VARNAME} if [ -z "$TARGET_VERSIONS" ]; then perror "Asked to build versioned target $TARGET with version list in $VARNAME, but $VARNAME is empty." fi else VARNAME= declare -r TARGET_VERSIONS= fi TARGET_BUILD_BASE="${ROOT_DIR}/var/builds/${TARGET}" if [ -z "$1" ] || [ "$1" = "all" ]; then if [ -n "$TARGET_VERSIONS" ]; then for TARGET_VERSION in ${TARGET_VERSIONS}; do rm -rf -- "${TARGET_BUILD_BASE}@${TARGET_VERSION}" || perror "Could not delete target build dirs for $TARGET" done else rm -rf -- "${TARGET_BUILD_BASE}" || perror "Could not delete target build dir for $TARGET" fi # exclude kernel on "all" unset 'MODULE_PATHS[kernel]' set -- "${!MODULE_PATHS[@]}" fi for module in "$@"; do [ -n "${MODULE_PATHS["$module"]}" ] || perror "Target does not contain module '$module'" done cde "$TARGET_DIR" for module in "$@"; do if [[ "$module" == *@ ]]; then [ -z "$TARGET_VERSIONS" ] && perror "Found versioned module $module in non-versioned target $TARGET" for TARGET_VERSION in ${TARGET_VERSIONS}; do TARGET_BUILD_DIR="${TARGET_BUILD_BASE}@${TARGET_VERSION}" clean_module "${module}" done else TARGET_BUILD_DIR="${TARGET_BUILD_BASE}" clean_module "${module}" fi done } clean_module() { [ -z "$1" ] && perror "No module given on clean_module()" [ -z "$TARGET" ] && perror "clean_module called with no active target" pinfo "## clean_module $TARGET $1$TARGET_VERSION" local MODULE=$1 local MODULE_DIR="${MODULE_PATHS["${MODULE}"]}" local MODULE_WORK_DIR="${ROOT_DIR}/tmp/work/${MODULE}" if [[ "$MODULE" == *@ ]]; then [ -z "$TARGET_VERSION" ] && perror "Asked to clean $MODULE in non-versioned target $TARGET" MODULE_WORK_DIR+="-${TARGET_VERSION}" fi if ! [ -d "$MODULE_DIR" ]; then pwarning "No such module '$MODULE' in current target '$TARGET'" pwarning "Check your spelling" if [ -d "$MODULE_WORK_DIR" ]; then pwarning "Continuing anyways since the according work dir exists..." else return 1 fi fi pinfo "Cleaning '$1'..." rm -rf -- "${MODULE_WORK_DIR}" || perror "Could not delete work dir" rm -f -- "${TARGET_BUILD_DIR}/opt/openslx/.mltk/${MODULE}."* # no space here! return 1 } # Recursively strip binaries and libraries in the given directory strip_recursive() { local DIR="$1" if [ -z "$DIR" ] || ! [ -d "$DIR" ]; then perror "strip_recursive(): No such directory: '$DIR'" fi # Will try to strip shell scripts too but shouldn't do any harm # Ignore anything we compile ourselves so we have usable core dumps find "$DIR" -type f \! -path "*openslx*bin*" -a \( -executable -o -name "*.so*" \) -exec strip {} \; 2> /dev/null } # copies static data files from /data/ to copy_static_data() { [ ! -d "${MODULE_DIR}/data" ] && pinfo "${MODULE} has no static 'data' directory." && return cde "${MODULE_DIR}/data/" pinfo "tarcopy ${MODULE_DIR}/data/" tarcopy "$(find . -type f -o -type l)" "${TARGET_BUILD_DIR}" } # Copies files with their absolute paths in $REQUIRED_SYSTEM_FILES to $TARGET_BUILD_DIR copy_system_files() { [ -z "$REQUIRED_SYSTEM_FILES" ] && return local file list list= for file in $REQUIRED_SYSTEM_FILES; do list+=" $(get_link_chain "$file")" done tarcopy "$list" "$TARGET_BUILD_DIR" }