changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > demo / tools/dep.sh

changeset 28: 242002f9f098
child: 7e640cebeada
author: ellis <ellis@rwest.io>
date: Tue, 06 Jun 2023 20:21:08 -0400
permissions: -rwxr-xr-x
description: deps
1 #!/usr/bin/sh
2 # install demo build dependencies
3 set -u
4 PKG_URL_ROOT="${PKG_URL_ROOT:-https://rwest.io/otom8/packy/bundle}"
5 PKG_NAME="demo_build_deps"
6 check_proc() {
7  # Check for /proc by looking for the /proc/self/exe link
8  # This only run on Linux
9  if ! test -L /proc/self/exe ; then
10  err "fatal: Unable to find /proc/self/exe. Is /proc mounted?"
11  fi
12 }
13 
14 get_bitness() {
15  need_cmd head
16  # Architecture detection without dependencies beyond coreutils.
17  # ELF files start out "\x7fELF", and the following byte is
18  # 0x01 for 32-bit and
19  # 0x02 for 64-bit.
20  # The printf builtin on some shells like dash only supports octal
21  # escape sequences, so we use those.
22  local _current_exe_head
23  _current_exe_head=$(head -c 5 /proc/self/exe )
24  if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then
25  echo 32
26  elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then
27  echo 64
28  else
29  err "unknown platform bitness"
30  fi
31 }
32 
33 is_host_amd64_elf() {
34  need_cmd head
35  need_cmd tail
36  # ELF e_machine detection without dependencies beyond coreutils.
37  # Two-byte field at offset 0x12 indicates the CPU,
38  # but we're interested in it being 0x3E to indicate amd64, or not that.
39  local _current_exe_machine
40  _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1)
41  [ "$_current_exe_machine" = "$(printf '\076')" ]
42 }
43 
44 get_endianness() {
45  local cputype=$1
46  local suffix_eb=$2
47  local suffix_el=$3
48 
49  # detect endianness without od/hexdump, like get_bitness() does.
50  need_cmd head
51  need_cmd tail
52 
53  local _current_exe_endianness
54  _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)"
55  if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then
56  echo "${cputype}${suffix_el}"
57  elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then
58  echo "${cputype}${suffix_eb}"
59  else
60  err "unknown platform endianness"
61  fi
62 }
63 
64 get_architecture() {
65  local _ostype _cputype _bitness _arch _clibtype
66  _ostype="$(uname -s)"
67  _cputype="$(uname -m)"
68  _clibtype="gnu"
69 
70  if [ "$_ostype" = Linux ]; then
71  if [ "$(uname -o)" = Android ]; then
72  _ostype=Android
73  fi
74  if ldd --version 2>&1 | grep -q 'musl'; then
75  _clibtype="musl"
76  fi
77  fi
78 
79  if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then
80  # Darwin `uname -m` lies
81  if sysctl hw.optional.x86_64 | grep -q ': 1'; then
82  _cputype=x86_64
83  fi
84  fi
85 
86  if [ "$_ostype" = SunOS ]; then
87  # Both Solaris and illumos presently announce as "SunOS" in "uname -s"
88  # so use "uname -o" to disambiguate. We use the full path to the
89  # system uname in case the user has coreutils uname first in PATH,
90  # which has historically sometimes printed the wrong value here.
91  if [ "$(/usr/bin/uname -o)" = illumos ]; then
92  _ostype=illumos
93  fi
94 
95  # illumos systems have multi-arch userlands, and "uname -m" reports the
96  # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86
97  # systems. Check for the native (widest) instruction set on the
98  # running kernel:
99  if [ "$_cputype" = i86pc ]; then
100  _cputype="$(isainfo -n)"
101  fi
102  fi
103 
104  case "$_ostype" in
105 
106  Android)
107  _ostype=linux-android
108  ;;
109 
110  Linux)
111  check_proc
112  _ostype=unknown-linux-$_clibtype
113  _bitness=$(get_bitness)
114  ;;
115 
116  FreeBSD)
117  _ostype=unknown-freebsd
118  ;;
119 
120  NetBSD)
121  _ostype=unknown-netbsd
122  ;;
123 
124  DragonFly)
125  _ostype=unknown-dragonfly
126  ;;
127 
128  Darwin)
129  _ostype=apple-darwin
130  ;;
131 
132  illumos)
133  _ostype=unknown-illumos
134  ;;
135 
136  MINGW* | MSYS* | CYGWIN*)
137  _ostype=pc-windows-gnu
138  ;;
139 
140  *)
141  err "unrecognized OS type: $_ostype"
142  ;;
143 
144  esac
145 
146  case "$_cputype" in
147 
148  i386 | i486 | i686 | i786 | x86)
149  _cputype=i686
150  ;;
151 
152  xscale | arm)
153  _cputype=arm
154  if [ "$_ostype" = "linux-android" ]; then
155  _ostype=linux-androideabi
156  fi
157  ;;
158 
159  armv6l)
160  _cputype=arm
161  if [ "$_ostype" = "linux-android" ]; then
162  _ostype=linux-androideabi
163  else
164  _ostype="${_ostype}eabihf"
165  fi
166  ;;
167 
168  armv7l | armv8l)
169  _cputype=armv7
170  if [ "$_ostype" = "linux-android" ]; then
171  _ostype=linux-androideabi
172  else
173  _ostype="${_ostype}eabihf"
174  fi
175  ;;
176 
177  aarch64 | arm64)
178  _cputype=aarch64
179  ;;
180 
181  x86_64 | x86-64 | x64 | amd64)
182  _cputype=x86_64
183  ;;
184 
185  mips)
186  _cputype=$(get_endianness mips '' el)
187  ;;
188 
189  mips64)
190  if [ "$_bitness" -eq 64 ]; then
191  # only n64 ABI is supported for now
192  _ostype="${_ostype}abi64"
193  _cputype=$(get_endianness mips64 '' el)
194  fi
195  ;;
196 
197  ppc)
198  _cputype=powerpc
199  ;;
200 
201  ppc64)
202  _cputype=powerpc64
203  ;;
204 
205  ppc64le)
206  _cputype=powerpc64le
207  ;;
208 
209  s390x)
210  _cputype=s390x
211  ;;
212  riscv64)
213  _cputype=riscv64gc
214  ;;
215  *)
216  err "unknown CPU type: $_cputype"
217 
218  esac
219 
220  # Detect 64-bit linux with 32-bit userland
221  if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then
222  case $_cputype in
223  x86_64)
224  if [ -n "${BABEL_CPUTYPE:-}" ]; then
225  _cputype="$BABEL_CPUTYPE"
226  else {
227  # 32-bit executable for amd64 = x32
228  if is_host_amd64_elf; then {
229  echo "This host is running an x32 userland; as it stands, x32 support is poor," 1>&2
230  echo "and there isn't a native toolchain -- you will have to install" 1>&2
231  echo "multiarch compatibility with i686 and/or amd64, then select one" 1>&2
232  echo "by re-running this script with the BABEL_CPUTYPE environment variable" 1>&2
233  echo "set to i686 or x86_64, respectively." 1>&2
234  echo 1>&2
235  exit 1
236  }; else
237  _cputype=i686
238  fi
239  }; fi
240  ;;
241  mips64)
242  _cputype=$(get_endianness mips '' el)
243  ;;
244  powerpc64)
245  _cputype=powerpc
246  ;;
247  aarch64)
248  _cputype=armv7
249  if [ "$_ostype" = "linux-android" ]; then
250  _ostype=linux-androideabi
251  else
252  _ostype="${_ostype}eabihf"
253  fi
254  ;;
255  riscv64gc)
256  err "riscv64 with 32-bit userland unsupported"
257  ;;
258  esac
259  fi
260 
261  # Detect armv7 but without the CPU features Rust needs in that build,
262  # and fall back to arm.
263  # See https://github.com/rust-lang/rustup.rs/issues/587.
264  if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then
265  if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then
266  # At least one processor does not have NEON.
267  _cputype=arm
268  fi
269  fi
270 
271  _arch="${_cputype}-${_ostype}"
272 
273  RETVAL="$_arch"
274 }
275 say() {
276  printf 'dep.sh: %s\n' "$1"
277 }
278 err() {
279  say "$1" >&2; exit 1
280 }
281 need_cmd() {
282  if ! check_cmd "$1"; then
283  err "need '$1' (command not found)"
284  fi
285 }
286 check_cmd() {
287  command -v "$1" > /dev/null 2>&1
288 }
289 assert_nz() {
290  if [ -z "$1" ]; then err "assert_nz $2"; fi
291 }
292 ensure() {
293  if ! "$@"; then err "command failed: $*"; fi
294 }
295 ignore() {
296  "$@"
297 }
298 main () {
299  need_cmd chmod
300  need_cmd mkdir
301  need_cmd rm
302 
303  get_architecture || return 1
304  local _arch="$RETVAL"
305  assert_nz "$_arch" "arch"
306 
307  # no extension unless on windows
308  local _ext=""
309  case "$_arch" in
310  *windows*)
311  _ext=".exe"
312  ;;
313  esac
314 
315  local _url="${PKG_URL_ROOT}/bin/dist/${_arch}/${PKG_NAME}${_ext}"
316 
317  local _dir
318  _dir="$(ensure mktemp -d)"
319  local _file="${_dir}/${PKG_NAME}${_ext}"
320 
321  local _ansi_escapes_are_valid=false
322  if [ -t 2 ]; then
323  if [ "${TERM+set}" = 'set' ]; then
324  case "$TERM" in
325  xterm*|rxvt*|urxvt*|linux*|vt*)
326  _ansi_escapes_are_valid=true
327  ;;
328  esac
329  fi
330  fi
331 
332  # check if we have to use /dev/tty to prompt the user
333  local need_tty=yes
334  for arg in "$@"; do
335  case "$arg" in
336  q)
337  # user wants to skip the prompt --
338  # we don't need /dev/tty
339  need_tty=no
340  ;;
341  *)
342  ;;
343  esac
344  done
345 
346  if $_ansi_escapes_are_valid; then
347  printf "\33[1minfo:\33[0m downloading $PKG_NAME\n" 1>&2
348  else
349  printf '%s\n' 'info: downloading $PKG_NAME' 1>&2
350  fi
351 
352  ensure mkdir -p "$_dir"
353  ensure dl "$_url" "$_file" "$_arch"
354  ensure chmod u+x "$_file"
355  if [ ! -x "$_file" ]; then
356  printf '%s\n' "Cannot execute $_file (likely because of mounting /tmp as noexec)." 1>&2
357  printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./${PKG_NAME}${_ext}." 1>&2
358  exit 1
359  fi
360 
361  if [ "$need_tty" = "yes" ]; then
362  # The installer is going to want to ask for confirmation by
363  # reading stdin. This script was piped into `sh` though and
364  # doesn't have stdin to pass to its children. Instead we're going
365  # to explicitly connect /dev/tty to the installer's stdin.
366  if [ ! -t 1 ]; then
367  err "Unable to run interactively. Run with -y to accept defaults"
368  fi
369 
370  ignore "$_file" "$@" < /dev/tty
371  else
372  ignore "$_file" "$@"
373  fi
374 
375  local _retval=$?
376 
377  ignore rm "$_file"
378  ignore rmdir "$_dir"
379 
380  return "$_retval"
381 }
382 
383 dl() { # curl || wget
384  local _dld
385  local _ciphersuites
386  local _err
387  local _status
388  if check_cmd curl; then
389  _dld=curl
390  elif check_cmd wget; then
391  _dld=wget
392  else
393  _dld='curl or wget' # to be used in error message of need_cmd
394  fi
395 
396  if [ "$1" = --check ]; then
397  need_cmd "$_dld"
398  elif [ "$_dld" = curl ]; then
399  get_ciphersuites_for_curl
400  _ciphersuites="$RETVAL"
401  if [ -n "$_ciphersuites" ]; then
402  _err=$(curl --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1)
403  _status=$?
404  else
405  echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
406  if ! check_help_for "$3" curl --proto --tlsv1.2; then
407  echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
408  _err=$(curl --silent --show-error --fail --location "$1" --output "$2" 2>&1)
409  _status=$?
410  else
411  _err=$(curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1)
412  _status=$?
413  fi
414  fi
415  if [ -n "$_err" ]; then
416  echo "$_err" >&2
417  if echo "$_err" | grep -q 404$; then
418  err "installer for platform '$3' not found 8^C - ask ellis to support your platform"
419  fi
420  fi
421  return $_status
422  elif [ "$_dld" = wget ]; then
423  get_ciphersuites_for_wget
424  _ciphersuites="$RETVAL"
425  if [ -n "$_ciphersuites" ]; then
426  _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1)
427  _status=$?
428  else
429  echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
430  if ! check_help_for "$3" wget --https-only --secure-protocol; then
431  echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
432  _err=$(wget "$1" -O "$2" 2>&1)
433  _status=$?
434  else
435  _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1)
436  _status=$?
437  fi
438  fi
439  if [ -n "$_err" ]; then
440  echo "$_err" >&2
441  if echo "$_err" | grep -q ' 404 Not Found$'; then
442  err "installer for platform '$3' not found!"
443  fi
444  fi
445  return $_status
446  else
447  err "Unknown dl program" # should not reach here
448  fi
449 }
450 
451 check_help_for() {
452  local _arch
453  local _cmd
454  local _arg
455  _arch="$1"
456  shift
457  _cmd="$1"
458  shift
459 
460  local _category
461  if "$_cmd" --help | grep -q 'For all options use the manual or "--help all".'; then
462  _category="all"
463  else
464  _category=""
465  fi
466 
467  case "$_arch" in
468 
469  *darwin*)
470  if check_cmd sw_vers; then
471  case $(sw_vers -productVersion) in
472  10.*)
473  # If we're running on macOS, older than 10.13, then we always
474  # fail to find these options to force fallback
475  if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then
476  # Older than 10.13
477  echo "Warning: Detected macOS platform older than 10.13"
478  return 1
479  fi
480  ;;
481  11.*)
482  # We assume Big Sur will be OK for now
483  ;;
484  *)
485  # Unknown product version, warn and continue
486  echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)"
487  echo "Warning TLS capabilities detection may fail"
488  ;;
489  esac
490  fi
491  ;;
492 
493  esac
494 
495  for _arg in "$@"; do
496  if ! "$_cmd" --help $_category | grep -q -- "$_arg"; then
497  return 1
498  fi
499  done
500 
501  true # not strictly needed
502 }
503 
504 # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
505 # if support by local tools is detected. Detection currently supports these curl backends:
506 # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
507 get_ciphersuites_for_curl() {
508  if [ -n "${BABEL_TLS_CIPHERSUITES-}" ]; then
509  # user specified custom cipher suites, assume they know what they're doing
510  RETVAL="$BABEL_TLS_CIPHERSUITES"
511  return
512  fi
513 
514  local _openssl_syntax="no"
515  local _gnutls_syntax="no"
516  local _backend_supported="yes"
517  if curl -V | grep -q ' OpenSSL/'; then
518  _openssl_syntax="yes"
519  elif curl -V | grep -iq ' LibreSSL/'; then
520  _openssl_syntax="yes"
521  elif curl -V | grep -iq ' BoringSSL/'; then
522  _openssl_syntax="yes"
523  elif curl -V | grep -iq ' GnuTLS/'; then
524  _gnutls_syntax="yes"
525  else
526  _backend_supported="no"
527  fi
528 
529  local _args_supported="no"
530  if [ "$_backend_supported" = "yes" ]; then
531  # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
532  if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then
533  _args_supported="yes"
534  fi
535  fi
536 
537  local _cs=""
538  if [ "$_args_supported" = "yes" ]; then
539  if [ "$_openssl_syntax" = "yes" ]; then
540  _cs=$(get_strong_ciphersuites_for "openssl")
541  elif [ "$_gnutls_syntax" = "yes" ]; then
542  _cs=$(get_strong_ciphersuites_for "gnutls")
543  fi
544  fi
545 
546  RETVAL="$_cs"
547 }
548 
549 # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
550 # if support by local tools is detected. Detection currently supports these wget backends:
551 # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
552 get_ciphersuites_for_wget() {
553  if [ -n "${BABEL_TLS_CIPHERSUITES-}" ]; then
554  # user specified custom cipher suites, assume they know what they're doing
555  RETVAL="$BABEL_TLS_CIPHERSUITES"
556  return
557  fi
558 
559  local _cs=""
560  if wget -V | grep -q '\-DHAVE_LIBSSL'; then
561  # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
562  if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
563  _cs=$(get_strong_ciphersuites_for "openssl")
564  fi
565  elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then
566  # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
567  if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
568  _cs=$(get_strong_ciphersuites_for "gnutls")
569  fi
570  fi
571 
572  RETVAL="$_cs"
573 }
574 
575 # Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2
576 # excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad
577 # DH params often found on servers (see RFC 7919). Sequence matches or is
578 # similar to Firefox 68 ESR with weak cipher suites disabled via about:config.
579 # $1 must be openssl or gnutls.
580 get_strong_ciphersuites_for() {
581  if [ "$1" = "openssl" ]; then
582  # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet.
583  echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
584  elif [ "$1" = "gnutls" ]; then
585  # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't.
586  # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order.
587  echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM"
588  fi
589 }
590 
591 main "$@" || exit 1
592