• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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