1#!/bin/bash
2#
3# Copyright (C) 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17if [[ ! -d art ]]; then
18  echo "Script needs to be run at the root of the android tree"
19  exit 1
20fi
21
22ALL_CONFIGS=(linux-ia32 linux-x64 linux-armv8 linux-armv7 android-armv8 android-armv7)
23
24usage() {
25  local config
26  local golem_target
27
28  (cat << EOF
29  Usage: $(basename "${BASH_SOURCE[0]}") [--golem=<target>] --machine-type=MACHINE_TYPE
30                 [--tarball[=<target>.tar.gz]]
31
32  Build minimal art binaries required to run golem benchmarks either
33  locally or on the golem servers.
34
35  Creates the \$MACHINE_TYPE binaries in your \$OUT_DIR, and if --tarball was specified,
36  it also tars the results of the build together into your <target.tar.gz> file.
37  --------------------------------------------------------
38  Required Flags:
39    --machine-type=MT   Specify the machine type that will be built.
40
41  Optional Flags":
42    --golem=<target>    Builds with identical commands that Golem servers use.
43    --tarball[=o.tgz]   Tar/gz the results. File name defaults to <machine_type>.tar.gz
44    -j<num>             Specify how many jobs to use for parallelism.
45    --help              Print this help listing.
46    --showcommands      Show commands as they are being executed.
47    --simulate          Print commands only, don't execute commands.
48EOF
49  ) | sed -e 's/^[[:space:]][[:space:]]//g' >&2 # Strip leading whitespace from heredoc.
50
51  echo >&2 "Available machine types:"
52  for config in "${ALL_CONFIGS[@]}"; do
53    echo >&2 "  $config"
54  done
55
56  echo >&2
57  echo >&2 "Available Golem targets:"
58  while IFS='' read -r golem_target; do
59    echo >&2 "  $golem_target"
60  done < <("$(thisdir)/env" --list-targets)
61}
62
63# Check if $1 element is in array $2
64contains_element() {
65  local e
66  for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
67  return 1
68}
69
70# Display a command, but don't execute it, if --showcommands was set.
71show_command() {
72  if [[ $showcommands == "showcommands" ]]; then
73    echo "$@"
74  fi
75}
76
77# Execute a command, displaying it if --showcommands was set.
78# If --simulate is used, command is not executed.
79execute() {
80  show_command "$@"
81  execute_noshow "$@"
82}
83
84# Execute a command unless --simulate was used.
85execute_noshow() {
86  if [[ $simulate == "simulate" ]]; then
87    return 0
88  fi
89
90  local prog="$1"
91  shift
92  "$prog" "$@"
93}
94
95# Export environment variable, echoing it to screen.
96setenv() {
97  local name="$1"
98  local value="$2"
99
100  export $name="$value"
101  echo export $name="$value"
102}
103
104# Export environment variable, echoing $3 to screen ($3 is meant to be unevaluated).
105setenv_escape() {
106  local name="$1"
107  local value="$2"
108  local escaped_value="$3"
109
110  export $name="$value"
111  echo export $name="$escaped_value"
112}
113
114log_usage_error() {
115  echo >&2 "ERROR: " "$@"
116  echo >&2 "       See --help for the correct usage information."
117  exit 1
118}
119
120log_fatal() {
121  echo >&2 "FATAL: " "$@"
122  exit 2
123}
124
125# Get the directory of this script.
126thisdir() {
127  (\cd "$(dirname "${BASH_SOURCE[0]}")" && pwd )
128}
129
130# Get the path to the top of the Android source tree.
131gettop() {
132  if [[ "x$ANDROID_BUILD_TOP" != "x" ]]; then
133    echo "$ANDROID_BUILD_TOP";
134  else
135    echo "$(thisdir)/../../.."
136  fi
137}
138
139# Get a build variable from the Android build system.
140get_build_var() {
141  local varname="$1"
142
143  # include the desired target product/build-variant
144  # which won't be set in our env if neither we nor the user first executed
145  # source build/envsetup.sh (e.g. if simulating from a fresh shell).
146  local extras
147  [[ -n $target_product ]] && extras+=" TARGET_PRODUCT=$target_product"
148  [[ -n $target_build_variant ]] && extras+=" TARGET_BUILD_VARIANT=$target_build_variant"
149
150  # call dumpvar from the build system.
151  (\cd "$(gettop)"; env $extras build/soong/soong_ui.bash --dumpvar-mode $varname)
152}
153
154# Defaults from command-line.
155
156mode=""  # blank or 'golem' if --golem was specified.
157golem_target="" # --golem=$golem_target
158config="" # --machine-type=$config
159j_arg=""
160showcommands=""
161simulate=""
162make_tarball=""
163tarball=""
164
165# Parse command line arguments
166
167while [[ "$1" != "" ]]; do
168  case "$1" in
169    --help)
170      usage
171      exit 1
172      ;;
173    --golem=*)
174      mode="golem"
175      golem_target="${1##--golem=}"
176
177      if [[ "x$golem_target" == x ]]; then
178        log_usage_error "Missing --golem target type."
179      fi
180
181      shift
182      ;;
183    --machine-type=*)
184      config="${1##--machine-type=}"
185      if ! contains_element "$config" "${ALL_CONFIGS[@]}"; then
186        log_usage_error "Invalid --machine-type value '$config'"
187      fi
188      shift
189      ;;
190    --tarball)
191      tarball="" # reuse the machine type name.
192      make_tarball="make_tarball"
193      shift
194      ;;
195    --tarball=*)
196      tarball="${1##--tarball=}"
197      make_tarball="make_tarball"
198      shift
199      ;;
200    -j*)
201      j_arg="$1"
202      shift
203      ;;
204    --showcommands)
205      showcommands="showcommands"
206      shift
207      ;;
208    --simulate)
209      simulate="simulate"
210      shift
211      ;;
212    *)
213      log_usage_error "Unknown options $1"
214      ;;
215  esac
216done
217
218###################################
219###################################
220###################################
221
222if [[ -z $config ]]; then
223  log_usage_error "--machine-type option is required."
224fi
225
226# --tarball defaults to the --machine-type value with .tar.gz.
227tarball="${tarball:-$config.tar.gz}"
228
229target_product="$TARGET_PRODUCT"
230target_build_variant="$TARGET_BUILD_VARIANT"
231
232# If not using --golem, use whatever the user had lunch'd prior to this script.
233if [[ $mode == "golem" ]]; then
234  # This section is intended solely to be executed by a golem build server.
235
236  target_build_variant=eng
237  case "$config" in
238    *-armv7)
239      target_product="arm_krait"
240      ;;
241    *-armv8)
242      target_product="armv8"
243      ;;
244    *)
245      target_product="sdk"
246      ;;
247  esac
248
249  if [[ $target_product = arm* ]]; then
250    # If using the regular manifest, e.g. 'master'
251    # The lunch command for arm will assuredly fail because we don't have device/generic/art.
252    #
253    # Print a human-readable error message instead of trying to lunch and failing there.
254    if ! [[ -d "$(gettop)/device/generic/art" ]]; then
255      log_fatal "Missing device/generic/art directory. Perhaps try master-art repo manifest?\n" \
256                "       Cannot build ARM targets (arm_krait, armv8) for Golem." >&2
257    fi
258    # We could try to keep on simulating but it seems brittle because we won't have the proper
259    # build variables to output the right strings.
260  fi
261
262  # Get this particular target's environment variables (e.g. ART read barrier on/off).
263  source "$(thisdir)"/env "$golem_target" || exit 1
264
265  lunch_target="$target_product-$target_build_variant"
266
267  execute 'source' build/envsetup.sh
268  # Build generic targets (as opposed to something specific like aosp_angler-eng).
269  execute lunch "$lunch_target"
270  # Golem uses master-art repository which is missing a lot of other libraries.
271  setenv SOONG_ALLOW_MISSING_DEPENDENCIES true
272  # Let the build system know we're not aiming to do a full platform build.
273  # TODO(b/159109002): Clean this up.
274  if [ ! -d frameworks/base ]; then
275    setenv TARGET_BUILD_UNBUNDLED true
276  fi
277  # Golem may be missing tools such as javac from its path.
278  setenv_escape PATH "/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH" '/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH'
279else
280  # Look up the default variables from the build system if they weren't set already.
281  [[ -z $target_product ]] && target_product="$(get_build_var TARGET_PRODUCT)"
282  [[ -z $target_build_variant ]] && target_build_variant="$(get_build_var TARGET_BUILD_VARIANT)"
283fi
284
285# Defaults for all machine types.
286make_target="build-art-target-golem"
287out_dir="out/x86_64"
288root_dir_var="PRODUCT_OUT"
289strip_symbols=false
290bit64_suffix=""
291tar_directories=(system data/art-test)
292
293# Per-machine type overrides
294if [[ $config == linux-arm* ]]; then
295    setenv ART_TARGET_LINUX true
296fi
297
298case "$config" in
299  linux-ia32|linux-x64)
300    root_dir_var="HOST_OUT"
301    # Android strips target builds automatically, but not host builds.
302    strip_symbols=true
303    make_target="build-art-host-golem"
304
305    if [[ $config == linux-ia32 ]]; then
306      out_dir="out/x86"
307      setenv HOST_PREFER_32_BIT true
308    else
309      bit64_suffix="64"
310    fi
311
312    tar_directories=(bin framework usr lib${bit64_suffix})
313    ;;
314  *-armv8)
315    bit64_suffix="64"
316    ;;
317  *-armv7)
318    ;;
319  *)
320    log_fatal "Unsupported machine-type '$config'"
321esac
322
323# Golem benchmark run commands expect a certain $OUT_DIR to be set,
324# so specify it here.
325#
326# Note: It is questionable if we want to customize this since users
327# could alternatively probably use their own build directly (and forgo this script).
328setenv OUT_DIR "$out_dir"
329root_dir="$(get_build_var "$root_dir_var")"
330
331if [[ $mode == "golem" ]]; then
332  # For golem-style running only.
333  # Sets the DT_INTERP to this path in every .so we can run the
334  # non-system version of dalvikvm with our own copies of the dependencies (e.g. our own libc++).
335  if [[ $config == android-* ]]; then
336    # TODO: the linker can be relative to the binaries
337    # (which is what we do for linux-armv8 and linux-armv7)
338    golem_run_path="/data/local/tmp/runner/"
339  else
340    golem_run_path=""
341  fi
342
343  # Only do this for target builds. Host doesn't need this.
344  if [[ $config == *-arm* ]]; then
345    setenv CUSTOM_TARGET_LINKER "${golem_run_path}${root_dir}/system/bin/linker${bit64_suffix}"
346  fi
347fi
348
349#
350# Main command execution below here.
351# (everything prior to this just sets up environment variables,
352#  and maybe calls lunch).
353#
354
355execute build/soong/soong_ui.bash --make-mode "${j_arg}" "${make_target}"
356
357if $strip_symbols; then
358  # Further reduce size by stripping symbols.
359  execute_noshow strip $root_dir/bin/* || true
360  show_command strip $root_dir/bin/'*'  '|| true'
361  execute_noshow strip $root_dir/lib${bit64_suffix}/'*'
362  show_command strip $root_dir/lib${bit64_suffix}/'*'
363fi
364
365if [[ "$make_tarball" == "make_tarball" ]]; then
366  # Create a tarball which is required for the golem build resource.
367  # (In particular, each golem benchmark's run commands depend on a list of resource files
368  #  in order to have all the files it needs to actually execute,
369  #  and this tarball would satisfy that particular target+machine-type's requirements).
370  dirs_rooted=()
371  for tar_dir in "${tar_directories[@]}"; do
372    dirs_rooted+=("$root_dir/$tar_dir")
373  done
374
375  execute tar -czf "${tarball}" --exclude ".git" --exclude ".gitignore" "${dirs_rooted[@]}"
376  tar_result=$?
377  if [[ $tar_result -ne 0 ]]; then
378    [[ -f $tarball ]] && rm $tarball
379  fi
380
381  show_command '[[ $? -ne 0 ]] && rm' "$tarball"
382fi
383
384