1#!/bin/bash 2set -e 3 4VERSION="1.00" 5 6PROGRAM=$0 7PROGNAME="$(basename "${PROGRAM}")" 8 9MODIFIED_FILES=() 10CLEAN_DIR_LIST=(configs Documentation payloads spd src util) 11KEEP_FILES=(util/kconfig/) 12REQUIRED_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" 13VERBOSE= 14 15# Text STYLE variables 16BOLD="\033[1m" 17RED='\033[38;5;9m' 18GREEN='\033[38;5;2m' 19NO_COLOR='\033[0m' 20 21################################################################################ 22 23usage() { 24cat << EOF 25The ${PROGNAME} script is used to create a git patch that removes all files 26not used in a single build. It does this by creating a temporary directory 27and configuring it to show the last time a file was accessed. It then sets 28the time on all files back to midnight on 2021-01-01 and then does a full 29build. Because all Kconfig and Makefiles are accessed during the built, 30it then creates a new Kconfig file containing all of the old Kconfigs. 31The next step is to delete all of the files that have an access time still 32set to 2021. The final step of the cleaning process is to recursively remove 33any Makefile that is alone in a directory by itself. The script then makes 34a commit and creates a patch. 35 36 Usage: ${PROGNAME} [options] 37 38Options: 39 -b | --blddir <dir> Set /tmp/<dir> as the build directory 40 -h | --help Print usage and exit 41 -D | --debug Print debug information. Use -DD to show all commands 42 -V | --version Print the version and exit 43 --nocolor Don't print color codes 44EOF 45} 46 47_echo_color() { 48 local color="$1" 49 local text="$2" 50 local newline="$3" 51 if [[ ${newline} == "0" ]]; then 52 printf "${color}%s${NO_COLOR}" "${text}" 53 else 54 printf "${color}%s${NO_COLOR}\n" "${text}" 55 fi 56} 57 58_echo_error() { 59 _echo_color "${RED}" "$*" 1 >&2 60} 61 62show_version() { 63 echo 64 _echo_color "${BOLD}${GREEN}" "${PROGNAME} version ${VERSION}" 65 echo 66} 67 68get_args() { 69 if ! args="$(getopt -l version,help,debug,nocolor,blddir: -o b:DhV -- "$@")"; then 70 usage 71 exit 1 72 fi 73 74 eval set -- "${args}" 75 76 while true; do 77 case "$1" in 78 -b | --blddir) 79 shift 80 BLD_DIR="/tmp/$1" 81 ;; 82 -D | --debug) 83 # -d prints extra debug info 84 # -dd prints all script steps 85 if [ -n "${VERBOSE}" ]; then 86 set -x 87 else 88 VERBOSE="V=1" 89 fi 90 ;; 91 -h | --help) 92 usage 93 exit 0 94 ;; 95 --nocolor) 96 BOLD="" 97 RED="" 98 GREEN="" 99 NO_COLOR="" 100 ;; 101 -V | --version) exit 0 ;; 102 --) 103 shift 104 break 105 ;; 106 *) 107 _echo_error "Unknown argument '$1'" 108 usage 109 exit 1 110 ;; 111 esac 112 shift 113 done 114 115 if [[ -n $1 ]]; then 116 _echo_error "Unknown command '$1'" 117 usage 118 exit 1 119 fi 120 121 BLD_DIR="${BLD_DIR:-$(mktemp -d)}" 122} 123 124recursively_rm_dir_onlyfile() { 125 local dir=$1 126 local beforecount 127 local aftercount 128 129 while true; do 130 if [[ ! -d ${dir} ]]; then 131 break 132 fi 133 beforecount="$(find "${dir}" | wc -l)" 134 while read -r file; do 135 # Don't delete any of the makefiles required for building. 136 if echo "${file}" | grep -q "${REQUIRED_MAKEFILES}"; then 137 break 138 fi 139 # Remove the directory if a makefile is the only file present. 140 if [[ "$(cd "${file}" && find . -maxdepth 1 | grep -v "./Makefile")" == "." ]]; then 141 rm -rf "${file}" 142 fi 143 done < <(find "${dir}" -depth -type d) 144 if [[ ! -d ${dir} ]]; then 145 break 146 fi 147 find "${dir}" -type d -empty -delete 148 if [[ ! -d ${dir} ]]; then 149 break 150 fi 151 aftercount="$(find "${dir}" | wc -l)" 152 if [[ ${aftercount} -eq ${beforecount} ]]; then 153 break 154 fi 155 done 156} 157 158verify_atime_enabled() { 159 local testfile 160 # Make sure the build directory is mounted correctly 161 if [ ! -d "${BLD_DIR}" ]; then 162 mkdir "${BLD_DIR}" 163 fi 164 if ! grep -q "${BLD_DIR}" /proc/mounts; then 165 echo "Mounting the ${BLD_DIR} directory with atime enabled" 166 sudo mount -t tmpfs -o rw,relatime tmpfs "${BLD_DIR}" 167 elif ! grep "${BLD_DIR}" /proc/mounts | grep -q relatime; then 168 echo "Remounting the ${BLD_DIR} directory with relatime enabled" 169 sudo mount -o remount,relatime "${BLD_DIR}" 170 fi 171 172 testfile="$(mktemp -p "${BLD_DIR}")" 173 touch -a --date="2020-01-01 00:00:00" "${testfile}" 174 if ! stat "${testfile}" | grep -q "Access: 2020-01-01"; then 175 _echo_error "Error: could not set access time." 176 sudo umount "${BLD_DIR}" 177 rm -rf "${BLD_DIR}" 178 exit 1 179 fi 180 rm -f "${testfile}" 181} 182 183update_codebase() { 184 local tempconfig 185 tempconfig="$(mktemp)" 186 if [ ! -f "${BLD_DIR}/COPYING" ]; then 187 echo "Downloading coreboot tree" 188 git clone https://review.coreboot.org/coreboot.git "${BLD_DIR}" 189 make -C "${BLD_DIR}" build/xcompile 190 fi 191 192 # Start from a completely clean tree or we'll miss anything that 193 # doesn't need to be rebuilt. Save the config if it exists. 194 if [[ -f .config ]]; then 195 mv .config "${tempconfig}" 196 fi 197 _echo_color "${GREEN}" "Cleaning coreboot tree" 198 make -s -C "${BLD_DIR}" distclean 199 if [[ -f ${tempconfig} ]]; then 200 mv "${tempconfig}" .config 201 fi 202 203 # force a refresh of all submodules 204 _echo_color "${GREEN}" "Refreshing all submodules..." 205 git submodule update --recursive --remote --init --checkout 206} 207 208save_kconfig() ( 209 cd "${BLD_DIR}" && util/lint/kconfig_lint -w -p -o kconfig.tmp 210) 211 212update_times() { 213 _echo_color "${GREEN}" "Updating access time of all files" 214 git ls-files | xargs touch -a -m -t 202001010000 215 if ! stat "${BLD_DIR}/COPYING" | grep -q "Access: 2020-01-01"; then 216 _echo_error "Error: could not set access time." 217 _echo_error " One of the following processes may be accessing it." 218 fuser -uvm "${BLD_DIR}/COPYING" 219 exit 1 220 fi 221} 222 223mark_files_to_keep() { 224 for file in "${KEEP_FILES[@]}"; do 225 find "${BLD_DIR}/${file}" -depth -exec touch {} \; 226 done 227} 228 229build_platform() { 230 local extra_text="$1" 231 _echo_color "${GREEN}" "Building platform ${extra_text}" 232 if [[ ! -f "${BLD_DIR}/.config" ]]; then 233 if [[ -n ${CONFIG_FILE} ]]; then 234 cp "${CONFIG_FILE}" "${BLD_DIR}/.config" 235 fi 236 echo "CONFIG_PAYLOAD_NONE=y" >>"${BLD_DIR}/.config" 237 fi 238 239 make -C "${BLD_DIR}" -s clean UPDATED_SUBMODULES=1 BUILD_TIMELESS=1 240 make -C "${BLD_DIR}" -s olddefconfig 241 make -C "${BLD_DIR}" -s UPDATED_SUBMODULES=1 BUILD_TIMELESS=1 ${VERBOSE} 242 HASH="$(sha256sum build/coreboot.rom)" 243 make -C "${BLD_DIR}" -s clean UPDATED_SUBMODULES=1 BUILD_TIMELESS=1 244} 245 246show_modified() { 247 readarray MODIFIED_FILES < <(find "${BLD_DIR}" -atime -1 -type f -path ./.git -prune) 248 echo "Files changed: ${#MODIFIED_FILES[@]}" 249} 250 251remove_kconfigs() { 252 # Dump all Kconfigs into a single file so that directories 253 # can be removed, while maintaining the entire Kconfig 254 # structure. 255 find "${BLD_DIR}/src" -name 'Kconfig*' -delete 256 mv "${BLD_DIR}/kconfig.tmp" "${BLD_DIR}/src/Kconfig" 257} 258 259remove_unused() { 260 local dir 261 # Most files can be removed simply by looking at the time, but 262 # all Kconfig and Makefiles in the entire tree are accessed 263 # whether they're used or not. 264 remove_kconfigs 265 266 echo 267 _echo_color "${GREEN}" "Checking access time and removing unused files in:" 268 for dir in "${CLEAN_DIR_LIST[@]}"; do 269 printf "%s\n" "${BLD_DIR}/${dir}" 270 # find and remove all files without updated times. 271 find "${BLD_DIR}/${dir}" -atime +5 -type f -delete 272 273 recursively_rm_dir_onlyfile "${BLD_DIR}/${dir}" 274 done 275 printf "\n\n" 276} 277 278create_patch() { 279 _echo_color "${GREEN}" "Creating patch" 280 ( 281 cd "${BLD_DIR}" 282 git add -A 283 git commit -m "remove unused files" --no-verify 284 git format-patch HEAD^ 285 ) 286} 287 288main() { 289 show_version 290 get_args "$@" 291 292 verify_atime_enabled 293 update_codebase 294 save_kconfig 295 update_times 296 mark_files_to_keep 297 build_platform "to mark used files" 298 OLDHASH="${HASH}" 299 HASH="" 300 #show_modified 301 remove_unused 302 create_patch 303 build_platform "to verify the build still works" 304 NEWHASH="${HASH}" 305 306 echo 307 _echo_color "${GREEN}" "Checksums:" 308 echo "Old: ${OLDHASH}" 309 echo "New: ${NEWHASH}" 310} 311 312main "$@" 313