1#!/bin/bash
2set -e
3
4# Copyright 2019 Google Inc. All rights reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18# Mounts the components of soong into a directory structure that Go tools
19# and editors expect.
20
21
22#####################################################################
23# Print the message to stderr with the prefix ERROR and abort this
24# script.
25#####################################################################
26function log_FATAL() {
27  echo "ERROR:" "$*" >&2
28  exit 1
29}
30
31#####################################################################
32# Print the message to stderr with the prefix WARN
33#####################################################################
34function log_WARN() {
35  echo "WARN:" "$*" >&2
36}
37
38
39#####################################################################
40# Print the message with the prefix INFO.
41#####################################################################
42function log_INFO() {
43  echo "INFO:" "$*"
44}
45
46
47#####################################################################
48# Find the root project directory of this repo. This is done by
49# finding the directory of where this script lives and then go up one
50# directory to check the ".repo" directory exist. If not, keep going
51# up until we find the ".repo" file or we reached to the filesystem
52# root. Project root directory is printed to stdout.
53#####################################################################
54function root_dir() (
55  local dir
56  if ! dir="$("${readlink}" -e $(dirname "$0"))"; then
57    log_FATAL "failed to read the script's current directory."
58  fi
59
60  dir=${dir}/../../..
61  if ! dir="$("${readlink}" -e "${dir}")"; then
62    log_FATAL "Cannot find the root project directory"
63  fi
64
65  echo "${dir}"
66)
67
68
69#####################################################################
70# executes a shell command by printing out to the screen first and
71# then evaluating the command.
72#####################################################################
73function execute() {
74  echo "$@"
75  eval "$@"
76}
77
78
79#####################################################################
80# Returns the source directory of a passed in path from BIND_PATHS
81# array.
82#####################################################################
83function bind_path_src_dir() (
84  local -r bind_path="$1"
85  echo "${bind_path/%|*/}"
86)
87
88
89#####################################################################
90# Returns the destination directory of a passed in path from
91# BIND_PATHS array.
92#####################################################################
93function bind_path_dst_dir() (
94  local -r bind_path="$1"
95  echo  "${bind_path/#*|}"
96)
97
98
99#####################################################################
100# Executes the bindfs command in linux. Expects $1 to be src
101# directory and $2 to be destination directory.
102#####################################################################
103function linux_bind_dir() (
104  execute bindfs "$1" "$2"
105)
106
107#####################################################################
108# Executes the fusermount -u command in linux. Expects $1 to be the
109# destination directory.
110#####################################################################
111function linux_unbind_dir() (
112  execute fusermount -u "$1"
113)
114
115#####################################################################
116# Executes the bindfs command in darwin. Expects $1 to be src
117# directory and $2 to be destination directory.
118#####################################################################
119function darwin_bind_dir() (
120  execute bindfs -o allow_recursion -n "$1" "$2"
121)
122
123
124#####################################################################
125# Execute the umount command in darwin to unbind a directory. Expects
126# $1 to be the destination directory
127#####################################################################
128function darwin_unbind_dir() (
129  execute umount -f "$1"
130)
131
132
133#####################################################################
134# Bind all the paths that are specified in the BIND_PATHS array.
135#####################################################################
136function bind_all() (
137  local src_dir
138  local dst_dir
139
140  for path in ${BIND_PATHS[@]}; do
141    src_dir=$(bind_path_src_dir "${path}")
142
143    dst_dir=$(bind_path_dst_dir "${path}")
144    mkdir -p "${dst_dir}"
145
146    "${bind_dir}" ${src_dir} "${dst_dir}"
147  done
148
149  echo
150  log_INFO "Created GOPATH-compatible directory structure at ${OUTPUT_PATH}."
151)
152
153
154#####################################################################
155# Unbind all the paths that are specified in the BIND_PATHS array.
156#####################################################################
157function unbind_all() (
158  local dst_dir
159  local exit_code=0
160
161  # need to go into reverse since several parent directory may have been
162  # first before the child one.
163  for (( i=${#BIND_PATHS[@]}-1; i>=0; i-- )); do
164    dst_dir=$(bind_path_dst_dir "${BIND_PATHS[$i]}")
165
166    # continue to unmount even one of them fails
167    if ! "${unbind_dir}" "${dst_dir}"; then
168      log_WARN "Failed to umount ${dst_dir}."
169      exit_code=1
170    fi
171  done
172
173  if [[ ${exit_code} -ne 0 ]]; then
174    exit ${exit_code}
175  fi
176
177  echo
178  log_INFO "Unmounted the GOPATH-compatible directory structure at ${OUTPUT_PATH}."
179)
180
181
182#####################################################################
183# Asks the user to create the GOPATH-compatible directory structure.
184#####################################################################
185function confirm() (
186  while true; do
187    echo "Will create GOPATH-compatible directory structure at ${OUTPUT_PATH}"
188    echo -n "Ok [Y/n]?"
189    read decision
190    if [ "${decision}" == "y" -o "${decision}" == "Y" -o "${decision}" == "" ]; then
191      return 0
192    else
193      if [ "${decision}" == "n" ]; then
194        return 1
195      else
196        log_WARN "Invalid choice ${decision}; choose either 'y' or 'n'"
197      fi
198    fi
199  done
200)
201
202
203#####################################################################
204# Help function.
205#####################################################################
206function help() (
207  cat <<EOF
208Mounts the components of soong into a directory structure that Go tools
209and editors expect.
210
211  --help
212    This help
213
214  --bind
215    Create the directory structure that Go tools and editors expect by
216    binding the one to aosp build directory.
217
218  --unbind
219    Reverse operation of bind.
220
221If no flags were specified, the --bind one is selected by default.
222EOF
223)
224
225
226#####################################################################
227# Parse the arguments passed in to this script.
228#####################################################################
229function parse_arguments() {
230  while [[ -n "$1" ]]; do
231    case "$1" in
232          --bind)
233            ACTION="bind"
234            shift
235            ;;
236          --unbind)
237            ACTION="unbind"
238            shift
239            ;;
240          --help )
241            help
242            shift
243            exit 0
244            ;;
245          *)
246            log_WARN "Unknown option: $1"
247            help
248            exit 1
249            ;;
250    esac
251  done
252
253  if [[ -z "${ACTION}" ]]; then
254    ACTION=bind
255  fi
256}
257
258
259#####################################################################
260# Verifies that a list of required binaries are installed in the
261# host in order to run this script.
262#####################################################################
263function check_exec_existence() (
264  function check() {
265    if ! hash "$1" &>/dev/null; then
266      log_FATAL "missing $1"
267    fi
268  }
269
270  local bins
271  case "${os_type}" in
272    Darwin)
273      bins=("bindfs" "greadlink")
274      ;;
275    Linux)
276      bins=("bindfs" "fusermount")
277      ;;
278    *)
279      log_FATAL "${os_type} is not a recognized system."
280  esac
281
282  for bin in "${bins[@]}"; do
283    check "${bin}"
284  done
285)
286
287
288function main() {
289  parse_arguments "$@"
290
291  check_exec_existence
292
293  if [[ "${ACTION}" == "bind" ]]; then
294    if confirm; then
295      echo
296      bind_all
297    else
298      echo "skipping due to user request"
299      exit 1
300    fi
301  else
302    echo
303    unbind_all
304  fi
305}
306
307readonly os_type="$(uname -s)"
308case "${os_type}" in
309  Darwin)
310    bind_dir=darwin_bind_dir
311    unbind_dir=darwin_unbind_dir
312    readlink=greadlink
313    ;;
314  Linux)
315    bind_dir=linux_bind_dir
316    unbind_dir=linux_unbind_dir
317    readlink=readlink
318    ;;
319    *)
320    log_FATAL "${os_type} is not a recognized system."
321esac
322readonly bind_dir
323readonly unbind_dir
324readonly readlink
325
326
327if ! ANDROID_PATH="$(root_dir)"; then
328  log_FATAL "failed to find the root of the repo checkout"
329fi
330readonly ANDROID_PATH
331
332#if GOPATH contains multiple paths, use the first one
333if ! OUTPUT_PATH="$(echo ${GOPATH} | sed 's/\:.*//')"; then
334  log_FATAL "failed to extract the first GOPATH environment variable"
335fi
336readonly OUTPUT_PATH
337if [ -z "${OUTPUT_PATH}" ]; then
338  log_FATAL "Could not determine the desired location at which to create a" \
339            "Go-compatible workspace. Please update GOPATH to specify the" \
340            "desired destination directory."
341fi
342
343# Below are the paths to bind from src to dst. The paths are separated by |
344# where the left side is the source and the right side is destination.
345readonly BIND_PATHS=(
346  "${ANDROID_PATH}/build/blueprint|${OUTPUT_PATH}/src/github.com/google/blueprint"
347  "${ANDROID_PATH}/build/soong|${OUTPUT_PATH}/src/android/soong"
348  "${ANDROID_PATH}/art/build|${OUTPUT_PATH}/src/android/soong/art"
349  "${ANDROID_PATH}/external/golang-protobuf|${OUTPUT_PATH}/src/github.com/golang/protobuf"
350  "${ANDROID_PATH}/external/llvm/soong|${OUTPUT_PATH}/src/android/soong/llvm"
351  "${ANDROID_PATH}/external/clang/soong|${OUTPUT_PATH}/src/android/soong/clang"
352  "${ANDROID_PATH}/external/robolectric-shadows/soong|${OUTPUT_PATH}/src/android/soong/robolectric"
353)
354
355main "$@"
356