| #!/bin/bash |
| set -e |
| |
| VERSION="1.00" |
| |
| PROGRAM=$0 |
| PROGNAME="$(basename "${PROGRAM}")" |
| |
| MODIFIED_FILES=() |
| CLEAN_DIR_LIST=(configs Documentation payloads spd src util) |
| KEEP_FILES=(util/kconfig/) |
| REQUIRED_MAKEFILES="util/testing\|util/crossgcc\|payloads/coreinfo\|payloads/nvramcui\|payloads/libpayload\|payloads/external/tint\|util/amdfwtool\|util/ectool\|util/futility\|util/intelmetool\|util/inteltool\|util/intelvbttool\|til/post\|util/superiotool" |
| VERBOSE= |
| |
| # Text STYLE variables |
| BOLD="\033[1m" |
| RED='\033[38;5;9m' |
| GREEN='\033[38;5;2m' |
| NO_COLOR='\033[0m' |
| |
| ################################################################################ |
| |
| usage() { |
| cat << EOF |
| The ${PROGNAME} script is used to create a git patch that removes all files |
| not used in a single build. It does this by creating a temporary directory |
| and configuring it to show the last time a file was accessed. It then sets |
| the time on all files back to midnight on 2021-01-01 and then does a full |
| build. Because all Kconfig and Makefiles are accessed during the built, |
| it then creates a new Kconfig file containing all of the old Kconfigs. |
| The next step is to delete all of the files that have an access time still |
| set to 2021. The final step of the cleaning process is to recursively remove |
| any Makefile that is alone in a directory by itself. The script then makes |
| a commit and creates a patch. |
| |
| Usage: ${PROGNAME} [options] |
| |
| Options: |
| -b | --blddir <dir> Set /tmp/<dir> as the build directory |
| -h | --help Print usage and exit |
| -D | --debug Print debug information. Use -DD to show all commands |
| -V | --version Print the version and exit |
| --nocolor Don't print color codes |
| EOF |
| } |
| |
| _echo_color() { |
| local color="$1" |
| local text="$2" |
| local newline="$3" |
| if [[ ${newline} == "0" ]]; then |
| printf "${color}%s${NO_COLOR}" "${text}" |
| else |
| printf "${color}%s${NO_COLOR}\n" "${text}" |
| fi |
| } |
| |
| _echo_error() { |
| _echo_color "${RED}" "$*" 1 >&2 |
| } |
| |
| show_version() { |
| echo |
| _echo_color "${BOLD}${GREEN}" "${PROGNAME} version ${VERSION}" |
| echo |
| } |
| |
| get_args() { |
| if ! args="$(getopt -l version,help,debug,nocolor,blddir: -o b:DhV -- "$@")"; then |
| usage |
| exit 1 |
| fi |
| |
| eval set -- "${args}" |
| |
| while true; do |
| case "$1" in |
| -b | --blddir) |
| shift |
| BLD_DIR="/tmp/$1" |
| ;; |
| -D | --debug) |
| # -d prints extra debug info |
| # -dd prints all script steps |
| if [ -n "${VERBOSE}" ]; then |
| set -x |
| else |
| VERBOSE="V=1" |
| fi |
| ;; |
| -h | --help) |
| usage |
| exit 0 |
| ;; |
| --nocolor) |
| BOLD="" |
| RED="" |
| GREEN="" |
| NO_COLOR="" |
| ;; |
| -V | --version) exit 0 ;; |
| --) |
| shift |
| break |
| ;; |
| *) |
| _echo_error "Unknown argument '$1'" |
| usage |
| exit 1 |
| ;; |
| esac |
| shift |
| done |
| |
| if [[ -n $1 ]]; then |
| _echo_error "Unknown command '$1'" |
| usage |
| exit 1 |
| fi |
| |
| BLD_DIR="${BLD_DIR:-$(mktemp -d)}" |
| } |
| |
| recursively_rm_dir_onlyfile() { |
| local dir=$1 |
| local beforecount |
| local aftercount |
| |
| while true; do |
| if [[ ! -d ${dir} ]]; then |
| break |
| fi |
| beforecount="$(find "${dir}" | wc -l)" |
| while read -r file; do |
| # Don't delete any of the makefiles required for building. |
| if echo "${file}" | grep -q "${REQUIRED_MAKEFILES}"; then |
| break |
| fi |
| # Remove the directory if a makefile is the only file present. |
| if [[ "$(cd "${file}" && find . -maxdepth 1 | grep -v "./Makefile")" == "." ]]; then |
| rm -rf "${file}" |
| fi |
| done < <(find "${dir}" -depth -type d) |
| if [[ ! -d ${dir} ]]; then |
| break |
| fi |
| find "${dir}" -type d -empty -delete |
| if [[ ! -d ${dir} ]]; then |
| break |
| fi |
| aftercount="$(find "${dir}" | wc -l)" |
| if [[ ${aftercount} -eq ${beforecount} ]]; then |
| break |
| fi |
| done |
| } |
| |
| verify_atime_enabled() { |
| local testfile |
| # Make sure the build directory is mounted correctly |
| if [ ! -d "${BLD_DIR}" ]; then |
| mkdir "${BLD_DIR}" |
| fi |
| if ! grep -q "${BLD_DIR}" /proc/mounts; then |
| echo "Mounting the ${BLD_DIR} directory with atime enabled" |
| sudo mount -t tmpfs -o rw,relatime tmpfs "${BLD_DIR}" |
| elif ! grep "${BLD_DIR}" /proc/mounts | grep -q relatime; then |
| echo "Remounting the ${BLD_DIR} directory with relatime enabled" |
| sudo mount -o remount,relatime "${BLD_DIR}" |
| fi |
| |
| testfile="$(mktemp -p "${BLD_DIR}")" |
| touch -a --date="2020-01-01 00:00:00" "${testfile}" |
| if ! stat "${testfile}" | grep -q "Access: 2020-01-01"; then |
| _echo_error "Error: could not set access time." |
| sudo umount "${BLD_DIR}" |
| rm -rf "${BLD_DIR}" |
| exit 1 |
| fi |
| rm -f "${testfile}" |
| } |
| |
| update_codebase() { |
| local tempconfig |
| tempconfig="$(mktemp)" |
| if [ ! -f "${BLD_DIR}/COPYING" ]; then |
| echo "Downloading coreboot tree" |
| git clone https://review.coreboot.org/coreboot.git "${BLD_DIR}" |
| make -C "${BLD_DIR}" build/xcompile |
| fi |
| |
| # Start from a completely clean tree or we'll miss anything that |
| # doesn't need to be rebuilt. Save the config if it exists. |
| if [[ -f .config ]]; then |
| mv .config "${tempconfig}" |
| fi |
| _echo_color "${GREEN}" "Cleaning coreboot tree" |
| make -s -C "${BLD_DIR}" distclean |
| if [[ -f ${tempconfig} ]]; then |
| mv "${tempconfig}" .config |
| fi |
| |
| # force a refresh of all submodules |
| _echo_color "${GREEN}" "Refreshing all submodules..." |
| git submodule update --recursive --remote --init --checkout |
| } |
| |
| save_kconfig() ( |
| cd "${BLD_DIR}" && util/lint/kconfig_lint -w -p -o kconfig.tmp |
| ) |
| |
| update_times() { |
| _echo_color "${GREEN}" "Updating access time of all files" |
| git ls-files | xargs touch -a -m -t 202001010000 |
| if ! stat "${BLD_DIR}/COPYING" | grep -q "Access: 2020-01-01"; then |
| _echo_error "Error: could not set access time." |
| _echo_error " One of the following processes may be accessing it." |
| fuser -uvm "${BLD_DIR}/COPYING" |
| exit 1 |
| fi |
| } |
| |
| mark_files_to_keep() { |
| for file in "${KEEP_FILES[@]}"; do |
| find "${BLD_DIR}/${file}" -depth -exec touch {} \; |
| done |
| } |
| |
| build_platform() { |
| local extra_text="$1" |
| _echo_color "${GREEN}" "Building platform ${extra_text}" |
| if [[ ! -f "${BLD_DIR}/.config" ]]; then |
| if [[ -n ${CONFIG_FILE} ]]; then |
| cp "${CONFIG_FILE}" "${BLD_DIR}/.config" |
| fi |
| echo "CONFIG_PAYLOAD_NONE=y" >>"${BLD_DIR}/.config" |
| fi |
| |
| make -C "${BLD_DIR}" -s clean UPDATED_SUBMODULES=1 BUILD_TIMELESS=1 |
| make -C "${BLD_DIR}" -s olddefconfig |
| make -C "${BLD_DIR}" -s UPDATED_SUBMODULES=1 BUILD_TIMELESS=1 ${VERBOSE} |
| HASH="$(sha256sum build/coreboot.rom)" |
| make -C "${BLD_DIR}" -s clean UPDATED_SUBMODULES=1 BUILD_TIMELESS=1 |
| } |
| |
| show_modified() { |
| readarray MODIFIED_FILES < <(find "${BLD_DIR}" -atime -1 -type f -path ./.git -prune) |
| echo "Files changed: ${#MODIFIED_FILES[@]}" |
| } |
| |
| remove_kconfigs() { |
| # Dump all Kconfigs into a single file so that directories |
| # can be removed, while maintaining the entire Kconfig |
| # structure. |
| find "${BLD_DIR}/src" -name 'Kconfig*' -delete |
| mv "${BLD_DIR}/kconfig.tmp" "${BLD_DIR}/src/Kconfig" |
| } |
| |
| remove_unused() { |
| local dir |
| # Most files can be removed simply by looking at the time, but |
| # all Kconfig and Makefiles in the entire tree are accessed |
| # whether they're used or not. |
| remove_kconfigs |
| |
| echo |
| _echo_color "${GREEN}" "Checking access time and removing unused files in:" |
| for dir in "${CLEAN_DIR_LIST[@]}"; do |
| printf "%s\n" "${BLD_DIR}/${dir}" |
| # find and remove all files without updated times. |
| find "${BLD_DIR}/${dir}" -atime +5 -type f -delete |
| |
| recursively_rm_dir_onlyfile "${BLD_DIR}/${dir}" |
| done |
| printf "\n\n" |
| } |
| |
| create_patch() { |
| _echo_color "${GREEN}" "Creating patch" |
| ( |
| cd "${BLD_DIR}" |
| git add -A |
| git commit -m "remove unused files" --no-verify |
| git format-patch HEAD^ |
| ) |
| } |
| |
| main() { |
| show_version |
| get_args "$@" |
| |
| verify_atime_enabled |
| update_codebase |
| save_kconfig |
| update_times |
| mark_files_to_keep |
| build_platform "to mark used files" |
| OLDHASH="${HASH}" |
| HASH="" |
| #show_modified |
| remove_unused |
| create_patch |
| build_platform "to verify the build still works" |
| NEWHASH="${HASH}" |
| |
| echo |
| _echo_color "${GREEN}" "Checksums:" |
| echo "Old: ${OLDHASH}" |
| echo "New: ${NEWHASH}" |
| } |
| |
| main "$@" |