summaryrefslogtreecommitdiff
path: root/util/getversion.sh
blob: 3146b29c88dc04e135675e21c9326a8cad2dd78a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/bin/bash
#
# Copyright 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Generate version information for the EC binary

: "${BOARD:=}"
: "${CR50_DEV:=}"
: "${CR50_SQA:=}"
: "${CRYPTO_TEST:=}"
: "${REPRODUCIBLE_BUILD:=}"
: "${STATIC_VERSION:=}"
: "${VCSID:=}"

# Use this symbol as a separator to be able to reliably concatenate strings of
# text.
dc=$'\001'

# Default marker to indicate 'dirty' repositories
dirty_marker='+'

# Derive path to chromeos_version.sh script
CHROOT_SOURCE_ROOT="/mnt/host/source"
CHROMIUMOS_OVERLAY="${CHROOT_SOURCE_ROOT}/src/third_party/chromiumos-overlay"
CROS_VERSION_SCRIPT="${CHROMIUMOS_OVERLAY}/chromeos/config/chromeos_version.sh"

# This function examines the state of the current directory and attempts to
# extract its version information: the latest tag, if any, how many patches
# are there since the latest tag, the top sha1, and if there are local
# modifications.
#
# Local modifications are reported by concatenating the revision string and
# the string '-dirty' using the $dc symbol as the separator.
#
# If there is no tags defined in this git repository, the base version is
# considered to be 0.0.
#
# If current directory is not a git depository, this function prints out
# "no_version"

get_tree_version() {
  local marker
  local ghash
  local numcommits
  local tag
  local vbase
  local ver_branch
  local ver_major

  if ghash="$(git rev-parse --short --verify HEAD 2>/dev/null)"; then
    if gdesc="$(git describe --dirty --match='v*' 2>/dev/null)"; then
      IFS="-" read -r -a fields <<< "${gdesc}"
      tag="${fields[0]}"
      IFS="." read -r -a vernum <<< "${tag}"
      numcommits=$((vernum[2]+${fields[1]:-0}))
      ver_major="${vernum[0]}"
      ver_branch="${vernum[1]}"
    else
      numcommits=$(git rev-list HEAD | wc -l)
      ver_major="v0"
      ver_branch="0"
    fi
    # avoid putting the -dirty attribute if only the timestamp
    # changed
    git status > /dev/null 2>&1

    if [ -n "$(git diff-index --name-only HEAD 2>/dev/null)" ]; then
      marker="${dirty_marker}"
    else
      marker="-"
    fi
    vbase="${ver_major}.${ver_branch}.${numcommits}${marker}${ghash}"
  else
    # Fall back to the VCSID provided by the packaging system if available.
    # Ex VCSID: 0.0.1-r1519-9b368af6a4943b90941471d0bdf7e7208788f898
    if [[ -n "${VCSID}" ]]; then
      ghash="${VCSID##*-}"
      vbase="1.1.9999-${ghash:0:7}"
    else
      # then ultimately fails to "no_version"
      vbase="no_version"
    fi
  fi
  if [[ "${marker}" == "${dirty_marker}" ]]; then
      echo "${vbase}${dc}${marker}"
  else
      echo "${vbase}${dc}"
  fi
}


main() {
  local component
  local dir_list
  local gitdate
  local most_recent_file
  local most_recents
  local timestamp
  local tool_ver
  local values
  local vbase
  local ver

  if [[ -z "${STATIC_VERSION}" ]]; then
    ver="${CR50_SQA:+SQA/}${CR50_DEV:+DBG/}${CRYPTO_TEST:+CT/}${BOARD}_"
    tool_ver=""
  else
    ver="STATIC_VERSION"
    tool_ver="STATIC_VERSION_TOOL"
  fi
  most_recents=()    # Non empty if any of the component repos is 'dirty'.
  dir_list=( . )   # list of component directories, always includes the EC tree

  case "${BOARD}" in
    (cr50)
      dir_list+=( ../../third_party/tpm2 ../../third_party/cryptoc )
      ;;
    (*_fp|*dartmonkey|*bloonchipper)
      if [[ -d ../../third_party/cryptoc ]]; then
        dir_list+=( ../../third_party/cryptoc )
      fi
      if [[ -d ./private ]]; then
        dir_list+=( ./private )
      fi
      if [[ -d ./private/fingerprint/fpc ]]; then
        dir_list+=( ./private/fingerprint/fpc )
      fi
      ;;
  esac

  # Create a combined version string for all component directories.
  if [[ -z "${STATIC_VERSION}" ]]; then
    for git_dir in "${dir_list[@]}"; do
      pushd "${git_dir}" > /dev/null || exit 1
      component="$(basename "${git_dir}")"
      IFS="${dc}" read -r -a values <<< "$(get_tree_version)"
      vbase="${values[0]}"             # Retrieved version information.
      if [[ -n "${values[1]}" ]]; then
        # From each modified repo get the most recently modified file.
        most_recent_file="$(git status --porcelain | \
                                 awk '$1 ~ /[M|A|?]/ {print $2}' |  \
                                 xargs ls -t | head -1)"
        most_recents+=("$(realpath "${most_recent_file}")")
      fi
      if [ "${component}" != "." ]; then
      ver+=" ${component}:"
      fi
      ver+="${vbase}"
      tool_ver+="${vbase}"

      if [[ "${git_dir}" == "." ]]; then
        # Truncate to 31 chars to leave room for terminating NUL that is
        # automatically added to constant C strings.
        ver_32="${ver:0:31}"
        ver="${ver_32}"
      fi

      popd > /dev/null || exit 1
    done
  fi

  echo "/* This file is generated by util/getversion.sh */"

  # Truncated version string that is exactly 32-bytes (NUL terminated).
  echo "#define CROS_EC_VERSION32 \"${ver_32}\""

  echo "/* Version string for ectool. */"
  echo "#define CROS_ECTOOL_VERSION \"${tool_ver}\""

  echo "/* Version string for stm32mon. */"
  echo "#define CROS_STM32MON_VERSION \"${tool_ver}\""

  echo "/* Sub-fields for use in Makefile.rules and to form build info string"
  echo " * in common/version.c. */"
  echo "#define VERSION \"${ver}\""
  if [[ -n "${STATIC_VERSION}" ]] || [[ "${REPRODUCIBLE_BUILD}" = 1 ]]; then
    echo '#define BUILDER "reproducible@build"'
  else
    echo "#define BUILDER \"${USER}@$(hostname)\""
  fi

  if [[ -n "${STATIC_VERSION}" ]]; then
    echo "#define DATE \"STATIC_VERSION_DATE\""
  elif [[ ${#most_recents[@]} != 0  ]]; then
    # There are modified files, use the timestamp of the most recent one as
    # the build version timestamp.
    # shellcheck disable=SC2012
    most_recent_file="$(ls -t "${most_recents[@]}"| head -1)"
    timestamp="$(stat -c '%y' "${most_recent_file}" | sed 's/\..*//')"
    echo "/* Repo is dirty, using time of most recent file modification. */"
    echo "#define DATE \"${timestamp}\""
  else
    echo "/* Repo is clean, use the commit date of the last commit. */"
    # If called from an ebuild we won't have a git repo, so redirect stderr
    # to avoid annoying 'Not a git repository' errors.
    gitdate="$(
      for git_dir in "${dir_list[@]}"; do
        git -C "${git_dir}" log -1 --format='%ct %ci' HEAD 2>/dev/null
      done | sort | tail -1 | cut -d ' ' -f '2 3')"
    echo "#define DATE \"${gitdate}\""
  fi

  # Use the chromeos_version_string when available.
  # This will not work if run from a standalone CrOS EC checkout.
  echo "#define CROS_FWID_MISSING_STR \"CROS_FWID_MISSING\""
  if [[ -f "${CROS_VERSION_SCRIPT}" ]]; then
    cros_version_output=$("${CROS_VERSION_SCRIPT}")
    CHROMEOS_BUILD=$(echo "${cros_version_output}" | \
                     grep "^ *CHROMEOS_BUILD=" | cut -d= -f2)
    CHROMEOS_BRANCH=$(echo "${cros_version_output}" | \
                      grep "^ *CHROMEOS_BRANCH=" | cut -d= -f2)
    CHROMEOS_PATCH=$(echo "${cros_version_output}" | \
                     grep "^ *CHROMEOS_PATCH=" | cut -d= -f2)
    # Official builds must set CHROMEOS_OFFICIAL=1.
    if [ "${CHROMEOS_OFFICIAL:-0}" -ne 1 ]; then
      # For developer builds, overwrite CHROMEOS_PATCH with the date.
      # This date is abbreviated compared to chromeos_version.sh so
      # fwid_version will more likely fit in 32 bytes.
      CHROMEOS_PATCH=$(date +%y_%m_%d)
    fi
    fwid="${BOARD}_${CHROMEOS_BUILD}.${CHROMEOS_BRANCH}.${CHROMEOS_PATCH}"
    echo "/* CrOS FWID of this build */"
    echo "#define CROS_FWID32 \"${fwid:0:31}\""
  else
    echo "/* CrOS FWID is not available for this build */"
    echo "#define CROS_FWID32 CROS_FWID_MISSING_STR"
  fi
}

main