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-$name from the makefile system.
151  (\cd "$(gettop)";
152  CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
153    command make --no-print-directory -f build/core/config.mk \
154    $extras \
155    dumpvar-$varname)
156}
157
158# Defaults from command-line.
159
160mode=""  # blank or 'golem' if --golem was specified.
161golem_target="" # --golem=$golem_target
162config="" # --machine-type=$config
163j_arg="-j8"
164showcommands=""
165simulate=""
166make_tarball=""
167tarball=""
168
169# Parse command line arguments
170
171while [[ "$1" != "" ]]; do
172  case "$1" in
173    --help)
174      usage
175      exit 1
176      ;;
177    --golem=*)
178      mode="golem"
179      golem_target="${1##--golem=}"
180
181      if [[ "x$golem_target" == x ]]; then
182        log_usage_error "Missing --golem target type."
183      fi
184
185      shift
186      ;;
187    --machine-type=*)
188      config="${1##--machine-type=}"
189      if ! contains_element "$config" "${ALL_CONFIGS[@]}"; then
190        log_usage_error "Invalid --machine-type value '$config'"
191      fi
192      shift
193      ;;
194    --tarball)
195      tarball="" # reuse the machine type name.
196      make_tarball="make_tarball"
197      shift
198      ;;
199    --tarball=*)
200      tarball="${1##--tarball=}"
201      make_tarball="make_tarball"
202      shift
203      ;;
204    -j*)
205      j_arg="$1"
206      shift
207      ;;
208    --showcommands)
209      showcommands="showcommands"
210      shift
211      ;;
212    --simulate)
213      simulate="simulate"
214      shift
215      ;;
216    *)
217      log_usage_error "Unknown options $1"
218      ;;
219  esac
220done
221
222###################################
223###################################
224###################################
225
226if [[ -z $config ]]; then
227  log_usage_error "--machine-type option is required."
228fi
229
230# --tarball defaults to the --machine-type value with .tar.gz.
231tarball="${tarball:-$config.tar.gz}"
232
233target_product="$TARGET_PRODUCT"
234target_build_variant="$TARGET_BUILD_VARIANT"
235
236# If not using --golem, use whatever the user had lunch'd prior to this script.
237if [[ $mode == "golem" ]]; then
238  # This section is intended solely to be executed by a golem build server.
239
240  target_build_variant=eng
241  case "$config" in
242    *-armv7)
243      target_product="arm_krait"
244      ;;
245    *-armv8)
246      target_product="armv8"
247      ;;
248    *)
249      target_product="sdk"
250      ;;
251  esac
252
253  if [[ $target_product = arm* ]]; then
254    # If using the regular manifest, e.g. 'master'
255    # The lunch command for arm will assuredly fail because we don't have device/generic/art.
256    #
257    # Print a human-readable error message instead of trying to lunch and failing there.
258    if ! [[ -d "$(gettop)/device/generic/art" ]]; then
259      log_fatal "Missing device/generic/art directory. Perhaps try master-art repo manifest?\n" \
260                "       Cannot build ARM targets (arm_krait, armv8) for Golem." >&2
261    fi
262    # We could try to keep on simulating but it seems brittle because we won't have the proper
263    # build variables to output the right strings.
264  fi
265
266  # Get this particular target's environment variables (e.g. ART read barrier on/off).
267  source "$(thisdir)"/env "$golem_target" || exit 1
268
269  lunch_target="$target_product-$target_build_variant"
270
271  execute 'source' build/envsetup.sh
272  # Build generic targets (as opposed to something specific like aosp_angler-eng).
273  execute lunch "$lunch_target"
274  setenv JACK_SERVER false
275  setenv_escape JACK_REPOSITORY "$PWD/prebuilts/sdk/tools/jacks" '$PWD/prebuilts/sdk/tools/jacks'
276  # Golem uses master-art repository which is missing a lot of other libraries.
277  setenv SOONG_ALLOW_MISSING_DEPENDENCIES true
278  # Golem may be missing tools such as javac from its path.
279  setenv_escape PATH "/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH" '/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH'
280else
281  # Look up the default variables from the build system if they weren't set already.
282  [[ -z $target_product ]] && target_product="$(get_build_var TARGET_PRODUCT)"
283  [[ -z $target_build_variant ]] && target_build_variant="$(get_build_var TARGET_BUILD_VARIANT)"
284fi
285
286# Defaults for all machine types.
287make_target="build-art-target-golem"
288out_dir="out/x86_64"
289root_dir_var="PRODUCT_OUT"
290strip_symbols=false
291bit64_suffix=""
292tar_directories=(system data/art-test)
293
294# Per-machine type overrides
295if [[ $config == linux-arm* ]]; then
296    setenv ART_TARGET_LINUX true
297fi
298
299case "$config" in
300  linux-ia32|linux-x64)
301    root_dir_var="HOST_OUT"
302    # Android strips target builds automatically, but not host builds.
303    strip_symbols=true
304    make_target="build-art-host-golem"
305
306    if [[ $config == linux-ia32 ]]; then
307      out_dir="out/x86"
308      setenv HOST_PREFER_32_BIT true
309    else
310      bit64_suffix="64"
311    fi
312
313    tar_directories=(bin framework usr lib${bit64_suffix})
314    ;;
315  *-armv8)
316    bit64_suffix="64"
317    ;;
318  *-armv7)
319    ;;
320  *)
321    log_fatal "Unsupported machine-type '$config'"
322esac
323
324# Golem benchmark run commands expect a certain $OUT_DIR to be set,
325# so specify it here.
326#
327# Note: It is questionable if we want to customize this since users
328# could alternatively probably use their own build directly (and forgo this script).
329setenv OUT_DIR "$out_dir"
330root_dir="$(get_build_var "$root_dir_var")"
331
332if [[ $mode == "golem" ]]; then
333  # For golem-style running only.
334  # Sets the DT_INTERP to this path in every .so we can run the
335  # non-system version of dalvikvm with our own copies of the dependencies (e.g. our own libc++).
336  if [[ $config == android-* ]]; then
337    # TODO: the linker can be relative to the binaries
338    # (which is what we do for linux-armv8 and linux-armv7)
339    golem_run_path="/data/local/tmp/runner/"
340  else
341    golem_run_path=""
342  fi
343
344  # Only do this for target builds. Host doesn't need this.
345  if [[ $config == *-arm* ]]; then
346    setenv CUSTOM_TARGET_LINKER "${golem_run_path}${root_dir}/system/bin/linker${bit64_suffix}"
347  fi
348fi
349
350#
351# Main command execution below here.
352# (everything prior to this just sets up environment variables,
353#  and maybe calls lunch).
354#
355
356execute make "${j_arg}" "${make_target}"
357
358if $strip_symbols; then
359  # Further reduce size by stripping symbols.
360  execute_noshow strip $root_dir/bin/* || true
361  show_command strip $root_dir/bin/'*'  '|| true'
362  execute_noshow strip $root_dir/lib${bit64_suffix}/'*'
363  show_command strip $root_dir/lib${bit64_suffix}/'*'
364fi
365
366if [[ "$make_tarball" == "make_tarball" ]]; then
367  # Create a tarball which is required for the golem build resource.
368  # (In particular, each golem benchmark's run commands depend on a list of resource files
369  #  in order to have all the files it needs to actually execute,
370  #  and this tarball would satisfy that particular target+machine-type's requirements).
371  dirs_rooted=()
372  for tar_dir in "${tar_directories[@]}"; do
373    dirs_rooted+=("$root_dir/$tar_dir")
374  done
375
376  execute tar -czf "${tarball}" "${dirs_rooted[@]}" --exclude .git --exclude .gitignore
377  tar_result=$?
378  if [[ $tar_result -ne 0 ]]; then
379    [[ -f $tarball ]] && rm $tarball
380  fi
381
382  show_command '[[ $? -ne 0 ]] && rm' "$tarball"
383fi
384
385