You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

2360 lines
70 KiB

  1. #!/bin/sh
  2. #
  3. # Copyright (c) 2018, Julian Ospald <hasufell@posteo.de>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are met:
  8. #
  9. # 1. Redistributions of source code must retain the above copyright notice,
  10. # this list of conditions and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. # 3. Neither the name of the <ORGANIZATION> nor the names of its
  15. # contributors may be used to endorse or promote products derived from
  16. # this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. ##########################
  30. #--[ Global Variables ]--#
  31. ##########################
  32. # @VARIABLE: VERSION
  33. # @DESCRIPTION:
  34. # Version of this script.
  35. VERSION=0.0.8
  36. # @VARIABLE: SCRIPT
  37. # @DESCRIPTION:
  38. # Name of this script. This will be the
  39. # shell name if this script is sourced, so
  40. # only rely on this for echos and trivial things.
  41. SCRIPT="$(basename "$0")"
  42. # @VARIABLE: VERBOSE
  43. # @DESCRIPTION:
  44. # Whether to print verbose messages in this script.
  45. VERBOSE=false
  46. # @VARIABLE: FORCE
  47. # @DESCRIPTION:
  48. # Whether to force installation and overwrite files.
  49. FORCE=false
  50. # @VARIABLE: GHCUP_INSTALL_BASE_PREFIX
  51. # @DESCRIPTION:
  52. # The main install directory prefix, under which .ghcup
  53. # directory will be created. This directory is user
  54. # configurable via the environment variable of the
  55. # same name. It must be non-empty and the path
  56. # it points to must exist.
  57. : "${GHCUP_INSTALL_BASE_PREFIX:=$HOME}"
  58. # @VARIABLE: INSTALL_BASE
  59. # @DESCRIPTION:
  60. # The main install directory where all ghcup stuff happens.
  61. INSTALL_BASE="$GHCUP_INSTALL_BASE_PREFIX/.ghcup"
  62. # @VARIABLE: GHC_LOCATION
  63. # @DESCRIPTION:
  64. # The location where ghcup will install different ghc versions.
  65. # This is expected to be a subdirectory of INSTALL_BASE.
  66. GHC_LOCATION="$INSTALL_BASE/ghc"
  67. # @VARIABLE: BIN_LOCATION
  68. # @DESCRIPTION:
  69. # The location where ghcup will create symlinks for GHC binaries.
  70. # This is expected to be a subdirectory of INSTALL_BASE.
  71. BIN_LOCATION="$INSTALL_BASE/bin"
  72. # @VARIABLE: CACHE_LOCATION
  73. # @DESCRIPTION:
  74. # The location where ghcup will put tarballs for caching.
  75. # This is expected to be a subdirectory of INSTALL_BASE.
  76. CACHE_LOCATION="$INSTALL_BASE/cache"
  77. # @VARIABLE: DOWNLOADER
  78. # @DESCRIPTION:
  79. # What program to use for downloading files.
  80. DOWNLOADER="curl"
  81. # @VARIABLE: DOWNLOADER_OPTS
  82. # @DESCRIPTION:
  83. # Options passed to the download program.
  84. DOWNLOADER_OPTS="-L --fail -O"
  85. # @VARIABLE: DOWNLOADER_STDOUT_OPTS
  86. # @DESCRIPTION:
  87. # Options passed to the download program when printing the content to stdout.
  88. DOWNLOADER_STDOUT_OPTS="-L --fail"
  89. # @VARIABLE: GHC_DOWNLOAD_BASEURL
  90. # @DESCRIPTION:
  91. # Base URL for all GHC tarballs.
  92. GHC_DOWNLOAD_BASEURL="https://downloads.haskell.org/~ghc"
  93. # @VARIABLE: JOBS
  94. # @DESCRIPTION:
  95. # How many jobs to use for compiling GHC.
  96. JOBS="1"
  97. # @VARIABLE: SOURCE
  98. # @DESCRIPTION:
  99. # The $0 argument, which contains
  100. # the script name.
  101. SOURCE="$0"
  102. # @VARIABLE: BASE_DOWNLOAD_URL
  103. # DESCRIPTION:
  104. # The base url for downloading stuff like meta files, requirements files etc.
  105. BASE_DOWNLOAD_URL="https://gitlab.haskell.org/haskell/ghcup/raw/master/"
  106. # @VARIABLE: SCRIPT_UPDATE_URL
  107. # @DESCRIPTION:
  108. # Location to update this script from.
  109. SCRIPT_UPDATE_URL="${BASE_DOWNLOAD_URL}/ghcup"
  110. # @VARIABLE: META_DOWNLOAD_URL
  111. # DESCRIPTION:
  112. # The url of the meta file for getting
  113. # download information for ghc/cabal-install etc.
  114. META_DOWNLOAD_URL="${GHCUP_META_DOWNLOAD_URL:=${BASE_DOWNLOAD_URL}/.download-urls}"
  115. # @VARIABLE: META_DOWNLOAD_FORMAT
  116. # DESCRIPTION:
  117. # The version of the meta file format.
  118. # This determines whether this script can read the
  119. # file from "${META_DOWNLOAD_URL}".
  120. META_DOWNLOAD_FORMAT="1"
  121. # @VARIABLE: META_VERSION_URL
  122. # DESCRIPTION:
  123. # The url of the meta file for getting
  124. # available versions for ghc/cabal-install etc.
  125. META_VERSION_URL="${GHCUP_META_VERSION_URL:=${BASE_DOWNLOAD_URL}/.available-versions}"
  126. # @VARIABLE: META_VERSION_FORMAT
  127. # DESCRIPTION:
  128. # The version of the meta file format.
  129. # This determines whether this script can read the
  130. # file from "${META_VERSION_URL}".
  131. META_VERSION_FORMAT="1"
  132. # @VARIABLE: BUG_URL
  133. # DESCRIPTION:
  134. # The url to report bugs to.
  135. BUG_URL="https://gitlab.haskell.org/haskell/ghcup/issues"
  136. # @VARIABLE: CACHING
  137. # @DESCRIPTION:
  138. # Whether to cache tarballs in $CACHE_LOCATION.
  139. CACHING=false
  140. ####################
  141. #--[ Print Help ]--#
  142. ####################
  143. # @FUNCTION: usage
  144. # @DESCRIPTION:
  145. # Print the help message for 'ghcup' to STDERR
  146. # and exit the script with status code 1.
  147. usage() {
  148. (>&2 echo "ghcup ${VERSION}
  149. GHC up toolchain installer
  150. USAGE:
  151. ${SCRIPT} [FLAGS] <SUBCOMMAND>
  152. FLAGS:
  153. -v, --verbose Enable verbose output
  154. -h, --help Prints help information
  155. -V, --version Prints version information
  156. -w, --wget Use wget instead of curl
  157. -c, --cache Use \"${CACHE_LOCATION}\" for caching tarballs
  158. (these will not be removed by ghcup)$(${VERBOSE} && printf "\n -o, --os Overwrite OS detection with the given string (must be a correct OS alias, e.g. 'alpine')")
  159. SUBCOMMANDS:
  160. install Install GHC$(${VERBOSE} && printf "\n compile Compile and install GHC from source (UNSTABLE!!!)")
  161. set Set currently active GHC version
  162. list Show available GHCs and other tools
  163. upgrade Upgrade this script in-place
  164. rm Remove an already installed GHC
  165. install-cabal Install cabal-install
  166. debug-info Print debug info (e.g. detected system/distro)
  167. changelog Show the changelog of a GHC release (online)
  168. print-system-reqs Print an approximation of system requirements
  169. DISCUSSION:
  170. ghcup installs the Glasgow Haskell Compiler from the official
  171. release channels, enabling you to easily switch between different
  172. versions.
  173. ")
  174. exit 1
  175. }
  176. # @FUNCTION: install_usage
  177. # @DESCRIPTION:
  178. # Print the help message for 'ghcup install' to STDERR
  179. # and exit the script with status code 1.
  180. install_usage() {
  181. (>&2 echo "ghcup-install
  182. Install GHC from binary tarball
  183. USAGE:
  184. ${SCRIPT} install [FLAGS] [VERSION|TAG]
  185. FLAGS:
  186. -h, --help Prints help information
  187. -f, --force Overwrite already existing installation
  188. ARGS:
  189. [VERSION|TAG] E.g. \"8.4.3\" or \"8.6.1\" or
  190. a tag like \"recommended\" or \"latest\"
  191. (default: discovers recommended version)
  192. DISCUSSION:
  193. Installs the specified GHC version (or a recommended default one) into
  194. a self-contained \"~/.ghcup/ghc/<ghcver>\" directory
  195. and symlinks the ghc binaries to \"~/.ghcup/bin/<binary>-<ghcver>\".
  196. ")
  197. exit 1
  198. }
  199. # @FUNCTION: set_usage
  200. # @DESCRIPTION:
  201. # Print the help message for 'ghcup set' to STDERR
  202. # and exit the script with status code 1.
  203. set_usage() {
  204. (>&2 echo "ghcup-set
  205. Set the currently active GHC to the specified version
  206. USAGE:
  207. ${SCRIPT} set [FLAGS] [VERSION|TAG]
  208. FLAGS:
  209. -h, --help Prints help information
  210. ARGS:
  211. [VERSION|TAG] E.g. \"8.4.3\" or \"8.6.3\" or
  212. a tag like \"recommended\" or \"latest\"
  213. (default: discovers recommended version)
  214. DISCUSSION:
  215. Sets the the current GHC version by creating non-versioned
  216. symlinks for all ghc binaries of the specified version in
  217. \"~/.ghcup/bin/<binary>\".
  218. ")
  219. exit 1
  220. }
  221. # @FUNCTION: upgrade_usage
  222. # @DESCRIPTION:
  223. # Print the help message for 'ghcup upgrade' to STDERR
  224. # and exit the script with status code 1.
  225. upgrade_usage() {
  226. (>&2 echo "ghcup-upgrade
  227. Update the ghcup script in-place
  228. USAGE:
  229. ${SCRIPT} upgrade [FLAGS] [TARGET-LOCATION]
  230. FLAGS:
  231. -i, --inplace Update this script in-place (wherever it's at)
  232. -h, --help Prints help information
  233. ARGS:
  234. [TARGET-LOCATION] Where to place the updated script (defaults to ${BIN_LOCATION}).
  235. This is ignored if --inplace is issued as well.
  236. ")
  237. exit 1
  238. }
  239. # @FUNCTION: rm_usage
  240. # @DESCRIPTION:
  241. # Print the help message for 'ghcup rm' to STDERR
  242. # and exit the script with status code 1.
  243. rm_usage() {
  244. (>&2 echo "ghcup-rm
  245. Remove the given GHC version installed by ghcup
  246. USAGE:
  247. ${SCRIPT} rm [FLAGS] <VERSION>
  248. FLAGS:
  249. -h, --help Prints help information
  250. -f, --force Don't prompt user
  251. ARGS:
  252. <VERSION> E.g. \"8.4.3\" or \"8.6.1\"
  253. ")
  254. exit 1
  255. }
  256. # @FUNCTION: install_cabal_usage
  257. # @DESCRIPTION:
  258. # Print the help message for 'ghcup install-cabal' to STDERR
  259. # and exit the script with status code 1.
  260. install_cabal_usage() {
  261. (>&2 echo "ghcup-install-cabal
  262. Install the specified or a default cabal version
  263. USAGE:
  264. ${SCRIPT} install-cabal [FLAGS] [VERSION|TAG]
  265. FLAGS:
  266. -h, --help Prints help information
  267. ARGS:
  268. [VERSION|TAG] E.g. \"2.4.0.0\" or a tag
  269. like \"recommended\" or \"latest\"
  270. DISCUSSION:
  271. Installs the specified cabal-install version (or the default recommended)
  272. into \"${BIN_LOCATION}\", so it can be overwritten
  273. by later \"cabal new-install cabal-install\", which installs into
  274. \"~/.cabal/bin\". Make sure to set up your PATH appropriately, so
  275. the cabal installation takes precedence.
  276. ")
  277. exit 1
  278. }
  279. # @FUNCTION: compile_usage
  280. # @DESCRIPTION:
  281. # Print the help message for 'ghcup compile' to STDERR
  282. # and exit the script with status code 1.
  283. compile_usage() {
  284. (>&2 echo "ghcup-compile
  285. Compile and install the specified GHC version
  286. USAGE:
  287. ${SCRIPT} compile [FLAGS] <VERSION> <BOOTSTRAP-GHC>
  288. FLAGS:
  289. -h, --help Prints help information
  290. -f, --force Overwrite already existing installation
  291. -j, --jobs <n> How many jobs for compilation
  292. -c, --build-config <filepath> Use the given config file as build config
  293. ARGS:
  294. <VERSION> E.g. \"8.4.3\" or \"8.6.1\"
  295. <BOOTSTRAP-GHC> E.g. \"ghc-8.2.2\" or a full path
  296. DISCUSSION:
  297. Compiles and installs the specified GHC version into
  298. a self-contained \"~/.ghcup/ghc/<ghcver>\" directory
  299. and symlinks the ghc binaries to \"~/.ghcup/bin/<binary>-<ghcver>\".
  300. EXAMPLE:
  301. ghcup -v compile -f -j 4 8.4.2 ghc-8.2.2
  302. ")
  303. exit 1
  304. }
  305. # @FUNCTION: debug_info_usage
  306. # @DESCRIPTION:
  307. # Print the help message for 'ghcup debug-info' to STDERR
  308. # and exit the script with status code 1.
  309. debug_info_usage() {
  310. (>&2 echo "ghcup-debug-info
  311. Print debug info (e.g. detected system/distro)
  312. USAGE:
  313. ${SCRIPT} debug-info
  314. FLAGS:
  315. -h, --help Prints help information
  316. DISCUSSION:
  317. Prints debug information, e.g. detected system architecture,
  318. distribution, version, as well as script variables. This
  319. is mainly useful for debugging purposes.
  320. ")
  321. exit 1
  322. }
  323. # @FUNCTION: list_usage
  324. # @DESCRIPTION:
  325. # Print the help message for 'ghcup list' to STDERR
  326. # and exit the script with status code 1.
  327. list_usage() {
  328. (>&2 echo "ghcup-list
  329. Show available GHCs and other tools
  330. USAGE:
  331. ${SCRIPT} list
  332. FLAGS:
  333. -h, --help Prints help information
  334. -t, --tool <all|ghc|cabal-install> Tool to list versions for. Default is ghc only.
  335. -c, --show-criteria <installed|set> Show only installed or set tool versions
  336. -r, --raw-format Raw format, for machine parsing
  337. DISCUSSION:
  338. Prints tools (e.g. GHC and cabal-install) and their
  339. available/installed/set versions.
  340. ")
  341. exit 1
  342. }
  343. # @FUNCTION: changelog_usage
  344. # @DESCRIPTION:
  345. # Print the help message for 'ghcup changelog' to STDERR
  346. # and exit the script with status code 1.
  347. changelog_usage() {
  348. (>&2 echo "ghcup-changelog
  349. View the online changelog for the given GHC version
  350. USAGE:
  351. ${SCRIPT} changelog [FLAGS] [VERSION|TAG]
  352. FLAGS:
  353. -h, --help Prints help information
  354. ARGS:
  355. [VERSION|TAG] E.g. \"8.4.3\" or \"8.6.3\" or
  356. a tag like \"recommended\" or \"latest\"
  357. (default: discovers latest version)
  358. DISCUSSION:
  359. Opens the online changelog for the given GHC version via
  360. xdg-open.
  361. ")
  362. exit 1
  363. }
  364. # @FUNCTION: print_system_reqs_usage
  365. # @DESCRIPTION:
  366. # Print the help message for 'ghcup print-system-reqs' to STDERR
  367. # and exit the script with status code 1.
  368. print_system_reqs_usage() {
  369. (>&2 echo "ghcup-print-system-reqs
  370. Print an approximation of system requirements
  371. USAGE:
  372. ${SCRIPT} print-system-reqs
  373. FLAGS:
  374. -h, --help Prints help information
  375. DISCUSSION:
  376. Just prints an approximation of the system requirements
  377. for the 'recommended' GHC version and the 'latest' distro version
  378. you are on.
  379. Review this output carefully!
  380. ")
  381. exit 1
  382. }
  383. ###########################
  384. #--[ Utility functions ]--#
  385. ###########################
  386. # @FUNCTION: die
  387. # @USAGE: [msg]
  388. # @DESCRIPTION:
  389. # Exits the shell script with status code 2
  390. # and prints the given message in red to STDERR, if any.
  391. die() {
  392. (>&2 red_message "$1")
  393. exit 2
  394. }
  395. # @FUNCTION: edo
  396. # @USAGE: <command>
  397. # @DESCRIPTION:
  398. # Executes the given command. Also prints what
  399. # command that is (in blue) if verbosity is enabled.
  400. # Exits with status code 2 if the command failed.
  401. edo()
  402. {
  403. if ${VERBOSE} ; then
  404. printf "\\033[0;34m%s\\033[0m\\n" "$*" 1>&2
  405. fi
  406. "$@" || exit 2
  407. }
  408. # @FUNCTION: emake
  409. # @USAGE: [arguments]
  410. # @DESCRIPTION:
  411. # Wrapper around 'make', may call 'gmake' if it exists.
  412. emake() { # avoid re-checking for gmake
  413. if [ -n "${MAKE}" ] ; then
  414. # shellcheck disable=SC2086
  415. edo ${MAKE} "$@"
  416. else
  417. if command_exists gmake ; then
  418. MAKE="gmake"
  419. # shellcheck disable=SC2086
  420. edo ${MAKE} "$@"
  421. else
  422. MAKE="make"
  423. # shellcheck disable=SC2086
  424. edo ${MAKE} "$@"
  425. fi
  426. fi
  427. }
  428. # @FUNCTION: debug_message
  429. # @USAGE: <msg>
  430. # @DESCRIPTION:
  431. # Print a blue debug message if verbosity is enabled.
  432. debug_message() {
  433. if ${VERBOSE} ; then
  434. (>&2 printf "\\033[0;34m%s\\033[0m\\n" "$1")
  435. fi
  436. }
  437. # @FUNCTION: optionv
  438. # @USAGE: <arg1> [arg2]
  439. # @DESCRIPTION:
  440. # If verbosity is enabled, echo the first argument, otherwise
  441. # the second (if any).
  442. # @STDOUT: first or second argument
  443. optionv() {
  444. if ${VERBOSE} ; then
  445. echo "$1"
  446. else
  447. if [ -n "$2" ] ; then
  448. echo "$2"
  449. fi
  450. fi
  451. }
  452. # @FUNCTION: status_message
  453. # @USAGE: <msg>
  454. # @DESCRIPTION:
  455. # Print a green status message.
  456. status_message() {
  457. printf "\\033[0;32m%s\\033[0m\\n" "$1"
  458. }
  459. # @FUNCTION: warning_message
  460. # @USAGE: <msg>
  461. # @DESCRIPTION:
  462. # Print a yellow warning message.
  463. warning_message() {
  464. printf "\\033[1;33m%s\\033[0m\\n" "$1"
  465. }
  466. # @FUNCTION: red_message
  467. # @USAGE: <msg>
  468. # @DESCRIPTION:
  469. # Print a red message.
  470. red_message() {
  471. printf "\\033[0;31m%s\\033[0m\\n" "$1"
  472. }
  473. # @FUNCTION: command_exists
  474. # @USAGE: <command>
  475. # @DESCRIPTION:
  476. # Check if a command exists (no arguments).
  477. # @RETURNS: 0 if the command exists, non-zero otherwise
  478. command_exists() {
  479. [ -z "$1" ] && die "Internal error: no argument given to command_exists"
  480. command -V "$1" >/dev/null 2>&1
  481. return $?
  482. }
  483. # @FUNCTION: check_required_commands
  484. # @USAGE: [additional-commands]
  485. # @DESCRIPTION:
  486. # Check that all required commands for this script exist.
  487. # @STDOUT: The commands that do not exist
  488. # @RETURNS: 0 if all command exists, non-zero otherwise
  489. check_required_commands() {
  490. _missing_commands=
  491. mydistro=$(get_distro_alias "$(get_distro_name)")
  492. for com in "$@" awk uname basename tar gzip mktemp dirname ; do
  493. command_exists "${com}" || {
  494. _missing_commands="${_missing_commands} ${com}"
  495. }
  496. done
  497. unset com
  498. # darwin uses tar's built-in xz decompression
  499. if test "${mydistro}" != "darwin"; then
  500. command_exists xz || {
  501. _missing_commands="${_missing_commands} xz"
  502. }
  503. fi
  504. if [ -n "${_missing_commands}" ] ; then
  505. printf "%s" "${_missing_commands}"
  506. unset _missing_commands mydistro
  507. return 1
  508. else
  509. unset _missing_commands mydistro
  510. return 0
  511. fi
  512. }
  513. # @FUNCTION: get_distro_name
  514. # @DESCRIPTION:
  515. # Gets the current distro identifier following
  516. # https://unix.stackexchange.com/a/6348
  517. # (see also http://linuxmafia.com/faq/Admin/release-files.html)
  518. # @STDOUT: current distro identifier
  519. get_distro_name() {
  520. if [ -f /etc/os-release ]; then
  521. # freedesktop.org and systemd
  522. # shellcheck disable=SC1091
  523. . /etc/os-release
  524. printf "%s" "$NAME"
  525. elif command_exists lsb_release ; then
  526. # linuxbase.org
  527. printf "%s" "$(lsb_release -si)"
  528. elif [ -f /etc/lsb-release ]; then
  529. # For some versions of Debian/Ubuntu without lsb_release command
  530. # shellcheck disable=SC1091
  531. . /etc/lsb-release
  532. printf "%s" "$DISTRIB_ID"
  533. elif [ -f /etc/redhat-release ]; then
  534. case "$(cat /etc/redhat-release)" in
  535. # Older CentOS releases didn't have a /etc/centos-release file
  536. "CentOS release "*)
  537. printf "CentOS"
  538. ;;
  539. "CentOS Linux release "*)
  540. printf "CentOS Linux"
  541. ;;
  542. "Fedora release "*)
  543. printf "Fedora"
  544. ;;
  545. # Fallback to uname
  546. *)
  547. printf "%s" "$(uname -s)"
  548. ;;
  549. esac
  550. elif [ -f /etc/debian_version ]; then
  551. # Older Debian/Ubuntu/etc.
  552. printf "Debian"
  553. else
  554. # Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
  555. printf "%s" "$(uname -s)"
  556. fi
  557. }
  558. # @FUNCTION: get_distro_ver
  559. # @DESCRIPTION:
  560. # Gets the current distro version (if any) following
  561. # https://unix.stackexchange.com/a/6348
  562. # @STDOUT: current distro version, if any
  563. get_distro_ver() {
  564. if [ -f /etc/os-release ]; then
  565. # freedesktop.org and systemd
  566. # shellcheck disable=SC1091
  567. . /etc/os-release
  568. printf "%s" "$VERSION_ID"
  569. elif command_exists lsb_release ; then
  570. # linuxbase.org
  571. printf "%s" "$(lsb_release -sr)"
  572. elif [ -f /etc/lsb-release ]; then
  573. # For some versions of Debian/Ubuntu without lsb_release command
  574. # shellcheck disable=SC1091
  575. . /etc/lsb-release
  576. printf "%s" "$DISTRIB_RELEASE"
  577. elif [ -f /etc/redhat-release ]; then
  578. case "$(cat /etc/redhat-release)" in
  579. # NB: Older CentOS releases didn't have a /etc/centos-release file
  580. "CentOS release "*|"Fedora release "*)
  581. printf "%s" "$(awk 'NR==1 { split($3, a, "."); print a[1] }' /etc/redhat-release)"
  582. ;;
  583. "CentOS Linux release "*)
  584. printf "%s" "$(awk 'NR==1 { split($4, a, "."); print a[1] }' /etc/redhat-release)"
  585. ;;
  586. # Fallback to uname
  587. *)
  588. printf "%s" "$(uname -r)"
  589. ;;
  590. esac
  591. elif [ -f /etc/debian_version ]; then
  592. # Older Debian/Ubuntu/etc.
  593. printf "%s" "$(cat /etc/debian_version)"
  594. else
  595. case "$(uname -s)" in
  596. AIX)
  597. printf "%s" "$(uname -v)"
  598. ;;
  599. FreeBSD)
  600. # we only care about the major numeric version part left of
  601. # the '.' in "11.2-RELEASE".
  602. printf "%s" "$(uname -r | cut -d . -f 1)"
  603. ;;
  604. *)
  605. # Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
  606. printf "%s" "$(uname -r)"
  607. esac
  608. fi
  609. }
  610. # @FUNCTION: get_arch
  611. # @DESCRIPTION:
  612. # Gets the architecture following
  613. # https://unix.stackexchange.com/a/6348
  614. # Fails for any architecture that we don't know a GHC version for.
  615. # @STDOUT: current architecture
  616. get_arch() {
  617. myarch=$(uname -m)
  618. case "${myarch}" in
  619. x86_64|amd64)
  620. printf "x86_64" # or AMD64 or Intel64 or whatever
  621. ;;
  622. i*86)
  623. printf "i386" # or IA32 or Intel32 or whatever
  624. ;;
  625. *)
  626. case "$(uname -s)" in
  627. AIX)
  628. case "$(uname -p)" in
  629. powerpc)
  630. printf "powerpc"
  631. ;;
  632. *)
  633. die "Cannot figure out architecture on AIX (was: ${myarch})"
  634. ;;
  635. esac
  636. ;;
  637. *)
  638. die "Cannot figure out architecture (was: ${myarch})"
  639. ;;
  640. esac
  641. esac
  642. unset myarch
  643. }
  644. # @FUNCTION: try_download_url
  645. # @USAGE: <tool> <ver> <arch> <distro-ident> <file>
  646. # @DESCRIPTION:
  647. # Tries to get the download url of a tool with our
  648. # specified format for download urls (see ${META_DOWNLOAD_URL}").
  649. # STDOUT: the download url, if an appropriate was found
  650. try_download_url() {
  651. [ "$#" -lt 5 ] && die "Internal error: not enough arguments to try_download_url"
  652. tool=$1
  653. ver=$2
  654. arch=$3
  655. distro_ident=$4
  656. filename=$5
  657. awk "
  658. NF {
  659. split(\$4,a,\",\")
  660. if (\$1 == \"${tool}\" && \$2 == \"${ver}\" && \$3 == \"${arch}\") {
  661. for (i in a) if (a[i] == \"${distro_ident}\") {
  662. print \$5
  663. exit
  664. }
  665. }
  666. }" "${filename}" || die "awk failed!"
  667. unset tool ver arch distro_ident filename
  668. }
  669. # @FUNCTION: check_meta_file_version
  670. # @USAGE: <file> <metaver>
  671. # @DESCRIPTION:
  672. # Check that the given meta file has the same format version
  673. # as specified, otherwise die.
  674. check_meta_file_version() {
  675. { [ -z "$1" ] || [ -z "$2" ] ;} && die "Internal error: not enough arguments given to check_meta_file_version"
  676. mymetavar=$(awk "
  677. NR==1 {
  678. if (\$2 ~ \"fmt-version\") {
  679. {
  680. split(\$2,a,\"=\")
  681. print a[2]
  682. exit
  683. }
  684. }
  685. }" "$1")
  686. if [ "${mymetavar}" != "$2" ] ; then
  687. die "Unsupported meta file format, run: ${SCRIPT} upgrade"
  688. fi
  689. unset mymetavar
  690. }
  691. # @FUNCTION: get_download_url
  692. # @USAGE: <tool> <version> [os-overwrite]
  693. # @DESCRIPTION:
  694. # Gets the download url for the given tool and version
  695. # and the current distro and architecture (which it tries to discover).
  696. # This uses "${META_DOWNLOAD_URL}" for url discovery.
  697. # @STDOUT: download url or nothing if no appropriate was found
  698. get_download_url() {
  699. { [ -z "$1" ] || [ -z "$2" ] ;} && die "Internal error: not enough arguments given to get_download_url"
  700. mytool=$1
  701. myver=$2
  702. myarch=$(get_arch)
  703. [ -z "${myarch}" ] && die "failed to get architecture"
  704. if [ -n "$3" ] ; then
  705. mydistro=$(get_distro_alias "$3")
  706. else
  707. mydistro=$(get_distro_alias "$(get_distro_name)")
  708. fi
  709. mydistrover=$(get_distro_ver)
  710. meta_file="$(get_meta_download_file)"
  711. [ -z "${meta_file}" ] && die "failed to get meta file"
  712. # 1st try with full distro=ver
  713. url=$(try_download_url "${mytool}" "${myver}" "${myarch}" "${mydistro}=${mydistrover}" "${meta_file}")
  714. if [ -n "${url}" ] ; then
  715. printf "%s" "${url}"
  716. exit 0
  717. fi
  718. # 2nd try with just distro
  719. url=$(try_download_url "${mytool}" "${myver}" "${myarch}" "${mydistro}" "${meta_file}")
  720. if [ -n "${url}" ] ; then
  721. printf "%s" "${url}"
  722. exit 0
  723. fi
  724. # 3rd try with unknown
  725. url=$(try_download_url "${mytool}" "${myver}" "${myarch}" "unknown" "${meta_file}")
  726. if [ -n "${url}" ] ; then
  727. printf "%s" "${url}"
  728. exit 0
  729. fi
  730. unset mytool myver myarch mydistro mydistrover meta_file
  731. }
  732. # @FUNCTION: get_tool_ver_from_tag
  733. # @USAGE: <tool> <tag>
  734. # @DESCRIPTION:
  735. # Gets the tool version with the given tag (first match) from
  736. # "${META_VERSION_URL}".
  737. # STDOUT: the version, if any, or nothing
  738. get_tool_ver_from_tag() {
  739. { [ -z "$1" ] || [ -z "$2" ] ;} && die "Internal error: not enough arguments given to get_tool_ver_from_tag"
  740. mytool=$1
  741. mytag=$2
  742. meta_file="$(get_meta_version_file)"
  743. [ -z "${meta_file}" ] && die "failed to get meta file"
  744. awk "
  745. NF {
  746. if (\$1 == \"${mytool}\") {
  747. split(\$3,a,\",\");
  748. for (i in a) if (a[i] == \"${mytag}\") {
  749. print \$2
  750. exit
  751. }
  752. }
  753. }" "${meta_file}" || die "awk failed!"
  754. unset mytool mytag meta_file
  755. }
  756. # @FUNCTION: ghc_already_installed
  757. # @USAGE: <ghcversion>
  758. # @DESCRIPTION:
  759. # Checks whether the specified GHC version
  760. # has been installed by ghcup already.
  761. # @RETURN: 0 if GHC is already installed, 1 otherwise
  762. ghc_already_installed() {
  763. [ -z "$1" ] && die "Internal error: no argument given to ghc_already_installed"
  764. if [ -e "$(get_ghc_location "$1")" ] ; then
  765. return 0
  766. else
  767. return 1
  768. fi
  769. }
  770. # @FUNCTION: cabal_already_installed
  771. # @USAGE: <cabalversion>
  772. # @DESCRIPTION:
  773. # Checks whether the specified cabal version
  774. # has been installed by ghcup already.
  775. # @RETURN: 0 if cabal is already installed, 1 otherwise
  776. cabal_already_installed() {
  777. [ -z "$1" ] && die "Internal error: no argument given to cabal_already_installed"
  778. if [ -x "${BIN_LOCATION}/cabal" ] ; then
  779. if [ "$("${BIN_LOCATION}/cabal" --numeric-version)" = "$1" ] ; then
  780. return 0
  781. else
  782. return 1
  783. fi
  784. else
  785. return 1
  786. fi
  787. }
  788. # @FUNCTION: tool_already_installed
  789. # @USAGE: <tool> <cabalversion>
  790. # @DESCRIPTION:
  791. # Checks whether the specified tool and version
  792. # has been installed by ghcup already.
  793. # @RETURN: 0 if tool is already installed, 1 otherwise
  794. tool_already_installed() {
  795. if [ "$1" = "ghc" ] ; then
  796. ghc_already_installed "$2"
  797. return $?
  798. elif [ "$1" = "cabal-install" ] ; then
  799. cabal_already_installed "$2"
  800. return $?
  801. else
  802. return 1
  803. fi
  804. }
  805. # @FUNCTION: get_ghc_location
  806. # @USAGE: <ghcversion>
  807. # @DESCRIPTION:
  808. # Gets/prints the location where the specified GHC is or would be installed.
  809. # Doesn't check whether that directory actually exist. Use
  810. # 'ghc_already_installed' for that.
  811. # @STDOUT: ghc location
  812. get_ghc_location() {
  813. [ -z "$1" ] && die "Internal error: no argument given to get_ghc_location"
  814. myghcver=$1
  815. inst_location=${GHC_LOCATION}/${myghcver}
  816. printf "%s" "${inst_location}"
  817. unset myghcver inst_location
  818. }
  819. # @FUNCTION: download
  820. # @USAGE: <url>
  821. # @DESCRIPTION:
  822. # Downloads the given url as a file into the current directory.
  823. download() {
  824. [ -z "$1" ] && die "Internal error: no argument given to download"
  825. # shellcheck disable=SC2086
  826. edo ${DOWNLOADER} ${DOWNLOADER_OPTS} "$1"
  827. }
  828. # @FUNCTION: download_to_cache
  829. # @USAGE: <url>
  830. # @DESCRIPTION:
  831. # Downloads the given url as a file into the cache directory
  832. # and makes sure the file is deleted on failed/interrupted download.
  833. download_to_cache() {
  834. [ -z "$1" ] && die "Internal error: no argument given to download_to_cache"
  835. _dtc_download_url="$1"
  836. _dtc_download_tarball_name=$(basename "${_dtc_download_url}")
  837. rm_tarball() {
  838. if [ -f "${CACHE_LOCATION}/${_dtc_download_tarball_name}" ] ; then
  839. rm "${CACHE_LOCATION}/${_dtc_download_tarball_name}"
  840. fi
  841. }
  842. (
  843. trap 'rm_tarball' 2
  844. edo cd "${CACHE_LOCATION}"
  845. # shellcheck disable=SC2086
  846. edo ${DOWNLOADER} ${DOWNLOADER_OPTS} "${_dtc_download_url}"
  847. trap - 2
  848. ) || {
  849. rm_tarball
  850. die "Failed to download"
  851. }
  852. unset _dtc_download_tarball_name _dtc_download_url
  853. }
  854. # @FUNCTION: download_silent
  855. # @USAGE: <url>
  856. # @DESCRIPTION:
  857. # Downloads the given url as a file into the current directory, silent, unless
  858. # verbosity is on.
  859. download_silent() {
  860. [ -z "$1" ] && die "Internal error: no argument given to download"
  861. if ${VERBOSE} ; then
  862. # shellcheck disable=SC2086
  863. edo ${DOWNLOADER} ${DOWNLOADER_OPTS} "$1"
  864. else
  865. # shellcheck disable=SC2086
  866. edo ${DOWNLOADER} ${DOWNLOADER_OPTS} "$1" 1> /dev/null 2> /dev/null
  867. fi
  868. }
  869. # @FUNCTION: download_to_stdout
  870. # @USAGE: <url>
  871. # @DESCRIPTION:
  872. # Downloads the given url to stdout.
  873. download_to_stdout() {
  874. [ -z "$1" ] && die "Internal error: no argument given to download"
  875. # shellcheck disable=SC2086
  876. edo ${DOWNLOADER} ${DOWNLOADER_STDOUT_OPTS} "$1" 2> /dev/null
  877. }
  878. # @FUNCTION: mktempdir
  879. # @DESCRIPTION:
  880. # Makes a temporary directory.
  881. mktempdir() {
  882. mktemp_mydistro=$(get_distro_alias "$(get_distro_name)")
  883. if test "${mktemp_mydistro}" = "darwin"; then
  884. debug_message "mktemp -d -t ghcup.XXXXXXX"
  885. mktemp -d -t ghcup.XXXXXXX
  886. else
  887. debug_message "mktemp -d"
  888. mktemp -d
  889. fi
  890. unset mktemp_mydistro
  891. }
  892. # @FUNCTION: unpack
  893. # @USAGE: <tarball>
  894. # @DESCRIPTION:
  895. # Uncompresses and unpacks the given tarball if needed by discovering the
  896. # file extension.
  897. unpack() {
  898. [ -z "$1" ] && die "Internal error: no argument given to unpack"
  899. mydistro=$(get_distro_alias "$(get_distro_name)")
  900. filename=$1
  901. file_ext=${filename##*.}
  902. # this is for portability, since not all
  903. # distros have tar with compression detection
  904. # capability
  905. case "${file_ext}" in
  906. xz)
  907. if test "${mydistro}" = "darwin"; then
  908. debug_message "tar xf \"${filename}\""
  909. ( tar xf "${filename}" ) || die "unpacking failed!"
  910. else
  911. debug_message "xz -cd \"${filename}\" | tar -xf -"
  912. ( xz -cd "${filename}" | tar xf - ; ) || die "unpacking failed!"
  913. fi
  914. ;;
  915. gz)
  916. debug_message "gzip -cd \"${filename}\" | tar -xf -"
  917. ( gzip -cd "${filename}" | tar xf - ; ) || die "unpacking failed!"
  918. ;;
  919. bz2)
  920. debug_message "bzip2 -cd \"${filename}\" | tar -xf -"
  921. ( bzip2 -cd "${filename}" | tar xf - ; ) || die "unpacking failed!"
  922. ;;
  923. tar)
  924. edo tar xf "${filename}"
  925. ;;
  926. *)
  927. die "Unknown file extension: \"${file_ext}\""
  928. esac
  929. unset mydistro filename file_ext
  930. }
  931. # @FUNCTION: ask_for_confirmation
  932. # @USAGE: [confirmation-msg]
  933. # @DESCRIPTION:
  934. # Asks the user for confirmation and returns 0 for yes, 1 for no.
  935. # @RETURN: 0 if user confirmed, 1 otherwise
  936. ask_for_confirmation() {
  937. confirmation_msg=$1
  938. if [ -n "${confirmation_msg}" ] ; then
  939. printf "%s\\n(y/n and press Enter)\\n" "${confirmation_msg}"
  940. else
  941. printf "Confirm action: (y/n and press Enter)\\n"
  942. fi
  943. read -r answer
  944. if [ "${answer}" != "${answer#[Yy]}" ] ;then
  945. return 0
  946. else
  947. return 1
  948. fi
  949. unset confirmation_msg answer
  950. }
  951. # @FUNCTION: get_distro_alias
  952. # @USAGE: <distro name>
  953. # @DESCRIPTION:
  954. # For a given known distro name, return our internal
  955. # unique distro alias. E.g.:
  956. # Debian GNU/Linux -> debian
  957. # STDOUT: our internal distro alias
  958. get_distro_alias() {
  959. distro_name=$1
  960. distro_alias=unknown
  961. case "${distro_name}" in
  962. "Debian"|"Debian GNU/Linux"|"debian")
  963. distro_alias=debian
  964. ;;
  965. "Ubuntu"|"ubuntu")
  966. distro_alias=ubuntu
  967. ;;
  968. "Exherbo"|"exherbo")
  969. distro_alias=exherbo
  970. ;;
  971. "Fedora"|"fedora")
  972. distro_alias=fedora
  973. ;;
  974. "CentOS Linux"|"CentOS"|"centos"|"Red Hat Enterprise Linux"*)
  975. distro_alias=centos
  976. ;;
  977. "Alpine Linux"|"Alpine"|"alpine")
  978. distro_alias=alpine
  979. ;;
  980. "Linux Mint"|"LinuxMint"|"linuxmint")
  981. distro_alias=mint
  982. ;;
  983. "Amazon Linux AMI"|"amazonlinux")
  984. distro_alias=amazonlinux
  985. ;;
  986. "AIX"|"aix")
  987. distro_alias=aix
  988. ;;
  989. "FreeBSD"|"freebsd")
  990. distro_alias=freebsd
  991. ;;
  992. "Darwin"|"darwin")
  993. distro_alias=darwin
  994. ;;
  995. esac
  996. printf "%s" "${distro_alias}"
  997. unset distro_name distro_alias
  998. }
  999. # @FUNCTION: posix_realpath
  1000. # @USAGE: <file>
  1001. # @DESCRIPTION:
  1002. # Portably gets the realpath and prints it to stdout.
  1003. # This was initially inspired by
  1004. # https://gist.github.com/tvlooy/cbfbdb111a4ebad8b93e
  1005. # and
  1006. # https://stackoverflow.com/a/246128
  1007. #
  1008. # If the file does not exist, just prints it appended to the current directory.
  1009. # @STDOUT: realpath of the given file
  1010. posix_realpath() {
  1011. [ -z "$1" ] && die "Internal error: no argument given to posix_realpath"
  1012. current_loop=0
  1013. max_loops=50
  1014. mysource=$1
  1015. while [ -h "${mysource}" ]; do
  1016. current_loop=$((current_loop+1))
  1017. mydir="$( cd -P "$( dirname "${mysource}" )" > /dev/null 2>&1 && pwd )"
  1018. mysource="$(readlink "${mysource}")"
  1019. [ "${mysource%${mysource#?}}"x != '/x' ] && mysource="${mydir}/${mysource}"
  1020. if [ ${current_loop} -gt ${max_loops} ] ; then
  1021. (>&2 echo "${1}: Too many levels of symbolic links")
  1022. break
  1023. fi
  1024. done
  1025. mydir="$( cd -P "$( dirname "${mysource}" )" > /dev/null 2>&1 && pwd )"
  1026. # TODO: better distinguish between "does not exist" and "permission denied"
  1027. if [ -z "${mydir}" ] ; then
  1028. (>&2 echo "${1}: Permission denied")
  1029. else
  1030. echo "${mydir%/}/$(basename "${mysource}")"
  1031. fi
  1032. unset current_loop max_loops mysource mydir
  1033. }
  1034. # @FUNCTION: get_meta_version_file
  1035. # @DESCRIPTION:
  1036. # Downloads the META_VERSION_URL
  1037. # in case it hasn't been downloaded
  1038. # during the execution of this script yet
  1039. # and checks the format version matches
  1040. # the expected one.
  1041. # @STDOUT: file location
  1042. get_meta_version_file() {
  1043. meta_file_name="$(basename "${META_VERSION_URL}")"
  1044. meta_filepath="${CACHE_LOCATION}/${meta_file_name}"
  1045. if [ ! -f "${meta_filepath}" ] ; then
  1046. (
  1047. edo cd "${CACHE_LOCATION}"
  1048. download_silent "${META_VERSION_URL}"
  1049. ) || die "downloading failed"
  1050. fi
  1051. check_meta_file_version "${meta_filepath}" "${META_VERSION_FORMAT}"
  1052. printf "%s" "${meta_filepath}"
  1053. unset meta_file_name meta_filepath
  1054. }
  1055. # @FUNCTION: get_meta_download_file
  1056. # @DESCRIPTION:
  1057. # Downloads the META_DOWNLOAD_URL
  1058. # in case it hasn't been downloaded
  1059. # during the execution of this script yet
  1060. # and checks the format version matches
  1061. # the expected one.
  1062. # @STDOUT: file location
  1063. get_meta_download_file() {
  1064. meta_file_name="$(basename "${META_DOWNLOAD_URL}")"
  1065. meta_filepath="${CACHE_LOCATION}/${meta_file_name}"
  1066. if [ ! -f "${meta_filepath}" ] ; then
  1067. (
  1068. edo cd "${CACHE_LOCATION}"
  1069. download_silent "${META_DOWNLOAD_URL}"
  1070. ) || die "downloading failed!"
  1071. fi
  1072. check_meta_file_version "${meta_filepath}" "${META_DOWNLOAD_FORMAT}"
  1073. printf "%s" "${CACHE_LOCATION}/${meta_file_name}"
  1074. unset meta_file_name meta_filepath
  1075. }
  1076. # @FUNCTION: known_tool_versions
  1077. # @USAGE: <tool>
  1078. # @DESCRIPTION:
  1079. # Prints the known tool versions from
  1080. # META_VERSION_URL.
  1081. # @STDOUT: known ghc versions
  1082. known_tool_versions() {
  1083. [ -z "$1" ] && die "Internal error: no argument given to posix_realpath"
  1084. mytool=$1
  1085. meta_file="$(get_meta_version_file)"
  1086. [ -z "${meta_file}" ] && die "failed to get meta file"
  1087. awk "
  1088. NF {
  1089. if (\$1 == \"${mytool}\") {
  1090. print \$2
  1091. }
  1092. }" "${meta_file}" || die "awk failed!"
  1093. unset mytool meta_file
  1094. }
  1095. # @FUNCTION: known_tool_tags
  1096. # @USAGE: <tool>
  1097. # @DESCRIPTION:
  1098. # Prints the known tool tags from
  1099. # META_VERSION_URL.
  1100. # @STDOUT: known tool tags
  1101. known_tool_tags() {
  1102. [ -z "$1" ] && die "Internal error: no argument given to known_tool_tags"
  1103. mytool=$1
  1104. meta_file="$(get_meta_version_file)"
  1105. [ -z "${meta_file}" ] && die "failed to get meta file"
  1106. awk "
  1107. NF {
  1108. if (\$1 == \"${mytool}\") {
  1109. split(\$3,a,\",\");
  1110. for (i in a) {
  1111. print a[i]
  1112. }
  1113. }
  1114. }" "${meta_file}" | sort -u || die "awk failed!"
  1115. unset mytool meta_file
  1116. }
  1117. # @FUNCTION: array_contains
  1118. # @USAGE: <element> <array>
  1119. # @DESCRIPTION:
  1120. # Checks whether the given elements
  1121. # is in the array.
  1122. # @RETURNS: returns 0 if the element is in the array, 1 otherwise
  1123. array_contains() {
  1124. { [ -z "$1" ] || [ -z "$2" ] ;} && die "Internal error: not enough arguments given to array_contains"
  1125. element=$1
  1126. array=$2
  1127. for e in ${array} ; do
  1128. if [ "${e}" = "${element}" ] ; then
  1129. unset e element array
  1130. return 0
  1131. fi
  1132. done
  1133. unset e element array
  1134. return 1
  1135. }
  1136. # @FUNCTION: show_ghc_installed
  1137. # @DESCRIPTION:
  1138. # Prints the currently selected GHC only as version string.
  1139. # @STDOUT: current GHC version
  1140. show_ghc_installed() {
  1141. current_ghc="${BIN_LOCATION}/ghc"
  1142. real_ghc=$(posix_realpath "${current_ghc}")
  1143. if [ -L "${current_ghc}" ] ; then # is symlink
  1144. if [ -f "${real_ghc}" ] ; then # exists (posix_realpath was called)
  1145. real_ghc="$(basename "${real_ghc}" | sed 's#ghc-##')"
  1146. printf "%s" "${real_ghc}"
  1147. else # is a broken symlink
  1148. red_message "broken symlink"
  1149. fi
  1150. fi
  1151. unset real_ghc current_ghc
  1152. }
  1153. # @FUNCTION: show_cabal_installed
  1154. # @DESCRIPTION:
  1155. # Prints the currently selected cabal only as version string.
  1156. # @STDOUT: current cabal version
  1157. show_cabal_installed() {
  1158. if [ -x "${BIN_LOCATION}/cabal" ] ; then
  1159. "${BIN_LOCATION}/cabal" --numeric-version
  1160. fi
  1161. }
  1162. # @FUNCTION: get_full_ghc_ver
  1163. # @USAGE: <ghcmajorversion>
  1164. # @DESCRIPTION:
  1165. # Get the latest full GHC version .
  1166. get_full_ghc_ver() {
  1167. [ -z "$1" ] && die "Internal error: no argument given to get_full_ghc_ver"
  1168. mymajorghcver=$1
  1169. latest_ghc=0
  1170. for current_ghc in "${BIN_LOCATION}/ghc-${mymajorghcver}."* ; do
  1171. [ -f "${current_ghc}" ] || break
  1172. real_ghc=$(posix_realpath "${current_ghc}")
  1173. real_ghc="$(basename "${real_ghc}" | sed 's#ghc-##')"
  1174. if [ "$(expr "${real_ghc}" \> "${latest_ghc}")" = 1 ] ; then
  1175. latest_ghc=${real_ghc}
  1176. fi
  1177. done
  1178. if [ "${latest_ghc}" != 0 ] ; then
  1179. printf "%s" "${latest_ghc}"
  1180. fi
  1181. unset mymajorghcver latest_ghc real_ghc current_ghc
  1182. }
  1183. # @FUNCTION: set_ghc_major
  1184. # @USAGE: <ghcversion>
  1185. # @DESCRIPTION:
  1186. # Sets a ghc-x.y major version to the latest ghc-x.y.z if any is installed.
  1187. set_ghc_major() {
  1188. [ -z "$1" ] && die "Internal error: no argument given to set_ghc_major"
  1189. full_ghc_ver="$(get_full_ghc_ver "${1%.*}")"
  1190. if [ -z "${full_ghc_ver}" ] ; then
  1191. die "Could not set GHC major symlink"
  1192. fi
  1193. set_ghc "${full_ghc_ver}" "-${1%.*}"
  1194. unset full_ghc_ver
  1195. }
  1196. ############################
  1197. #--[ Subcommand install ]--#
  1198. ############################
  1199. # @FUNCTION: install_ghc
  1200. # @USAGE: <ghcversion> [os-overwrite]
  1201. # @DESCRIPTION:
  1202. # Installs the given ghc version with a lot of side effects.
  1203. install_ghc() {
  1204. [ -z "$1" ] && die "Internal error: no argument given to install_ghc"
  1205. myghcver=$1
  1206. inst_location=$(get_ghc_location "$1")
  1207. [ -z "${inst_location}" ] && die "failed to get install location"
  1208. download_url=$(get_download_url "ghc" "${myghcver}" "$2")
  1209. if [ -z "${download_url}" ] ; then
  1210. die "Could not find an appropriate download for the requested GHC-${myghcver} on your system! Please report a bug at ${BUG_URL}"
  1211. fi
  1212. download_tarball_name=$(basename "${download_url}")
  1213. first_install=true
  1214. status_message "Installing GHC-${myghcver} for $(if [ -n "$2" ] ; then echo "$2" ; else get_distro_name ; fi) on architecture $(get_arch)"
  1215. if ghc_already_installed "${myghcver}" ; then
  1216. if ${FORCE} ; then
  1217. echo "GHC already installed in ${inst_location}, overwriting!"
  1218. else
  1219. warning_message "GHC already installed in ${inst_location}, use --force to overwrite"
  1220. exit 0
  1221. fi
  1222. first_install=false
  1223. fi
  1224. tmp_dir=$(mktempdir)
  1225. { [ -z "${tmp_dir}" ] || ! [ -d "${tmp_dir}" ] ; } && die "Failed to create temporary directory"
  1226. (
  1227. if ${CACHING} ; then
  1228. if [ ! -f "${CACHE_LOCATION}/${download_tarball_name}" ] ; then
  1229. download_to_cache "${download_url}"
  1230. fi
  1231. edo cd "${tmp_dir}"
  1232. unpack "${CACHE_LOCATION}/${download_tarball_name}"
  1233. else
  1234. edo cd "${tmp_dir}"
  1235. download "${download_url}"
  1236. unpack "${download_tarball_name}"
  1237. fi
  1238. edo cd "./ghc-${myghcver}"
  1239. debug_message "Installing GHC into ${inst_location}"
  1240. edo ./configure --prefix="${inst_location}"
  1241. emake install
  1242. # clean up
  1243. edo cd ..
  1244. [ -f "${tmp_dir}/${download_tarball_name}" ] && rm "${tmp_dir}/${download_tarball_name}"
  1245. [ -e "${tmp_dir}/ghc-${myghcver}" ] && rm -r "${tmp_dir}/ghc-${myghcver}"
  1246. ) || {
  1247. [ -f "${tmp_dir}/${download_tarball_name}" ] && rm "${tmp_dir}/${download_tarball_name}"
  1248. [ -e "${tmp_dir}/ghc-${myghcver}" ] && rm -r "${tmp_dir}/ghc-${myghcver}"
  1249. if ${first_install} ; then
  1250. [ -e "${inst_location}" ] && rm -r "${inst_location}"
  1251. else
  1252. warning_message "GHC force installation failed. The install might be broken."
  1253. warning_message "Consider running: ghcup rm ${myghcver}"
  1254. fi
  1255. die "Failed to install, consider updating this script via: ${SCRIPT} upgrade"
  1256. }
  1257. for f in "${inst_location}"/bin/*-"${myghcver}" ; do
  1258. [ -f "${f}" ] || die "Something went wrong, ${f} does not exist!"
  1259. fn=$(basename "${f}")
  1260. # shellcheck disable=SC2046
  1261. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}/bin/${fn}" "${BIN_LOCATION}/${fn}"
  1262. unset fn
  1263. done
  1264. # shellcheck disable=SC2046
  1265. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/runhaskell "${BIN_LOCATION}/runhaskell-${myghcver}"
  1266. # shellcheck disable=SC2046
  1267. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/hsc2hs "${BIN_LOCATION}/hsc2hs-${myghcver}"
  1268. # shellcheck disable=SC2046
  1269. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/hp2ps "${BIN_LOCATION}/hp2ps-${myghcver}"
  1270. # shellcheck disable=SC2046
  1271. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/hpc "${BIN_LOCATION}/hpc-${myghcver}"
  1272. if [ -f "${inst_location}/bin/haddock" ] ; then
  1273. # shellcheck disable=SC2046
  1274. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/haddock "${BIN_LOCATION}/haddock-${myghcver}"
  1275. fi
  1276. status_message "Done installing, run \"ghci-${myghcver}\" or set up your current GHC via: ${SCRIPT} set ${myghcver}"
  1277. unset inst_location f download_url download_tarball_name first_install tmp_dir
  1278. set_ghc_major "${myghcver}"
  1279. unset myghcver
  1280. }
  1281. ########################
  1282. #--[ Subcommand set ]--#
  1283. ########################
  1284. # @FUNCTION: set_ghc
  1285. # @USAGE: <ghcversion> [target-suffix]
  1286. # @DESCRIPTION:
  1287. # Sets the current ghc version by creating symlinks.
  1288. set_ghc() {
  1289. [ -z "$1" ] && die "Internal error: no argument given to set_ghc"
  1290. myghcver=$1
  1291. target_suffix=$2
  1292. inst_location=$(get_ghc_location "$1")
  1293. [ -z "${inst_location}" ] && die "failed to get install location"
  1294. [ -e "${inst_location}" ] || die "GHC ${myghcver} not installed yet, use: ${SCRIPT} install ${myghcver}"
  1295. status_message "Setting GHC${target_suffix} to ${myghcver}"
  1296. for f in "${inst_location}"/bin/*-"${myghcver}" ; do
  1297. [ -f "${f}" ] || die "Something went wrong, ${f} does not exist!"
  1298. source_fn=$(basename "${f}")
  1299. target_fn="$(echo "${source_fn}" | sed "s#-${myghcver}##")${target_suffix}"
  1300. # shellcheck disable=SC2046
  1301. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}/bin/${source_fn}" "${BIN_LOCATION}/${target_fn}"
  1302. unset source_fn target_fn
  1303. done
  1304. # shellcheck disable=SC2046
  1305. edo ln $(optionv "-v") -sf "runghc${target_suffix}" "${BIN_LOCATION}/runhaskell${target_suffix}"
  1306. # shellcheck disable=SC2046
  1307. edo ln $(optionv "-v") -sf "hsc2hs-${myghcver}" "${BIN_LOCATION}/hsc2hs${target_suffix}"
  1308. # shellcheck disable=SC2046
  1309. edo ln $(optionv "-v") -sf "hp2ps-${myghcver}" "${BIN_LOCATION}/hp2ps${target_suffix}"
  1310. # shellcheck disable=SC2046
  1311. edo ln $(optionv "-v") -sf "hpc-${myghcver}" "${BIN_LOCATION}/hpc${target_suffix}"
  1312. # not all bindists install haddock...
  1313. if [ -f "${inst_location}/bin/haddock" ] ; then
  1314. # shellcheck disable=SC2046
  1315. edo ln $(optionv "-v") -sf "haddock-ghc${target_suffix}" "${BIN_LOCATION}/haddock${target_suffix}"
  1316. fi
  1317. if [ -z "${target_suffix}" ] && [ -d "${inst_location}/share" ] ; then
  1318. # Make sure we don't link "${BIN_LOCATION}/../share/share".
  1319. # POSIX doesn't allow '-T' for 'ln'.
  1320. if [ -e "${BIN_LOCATION}/../share" ] ; then
  1321. edo rm "${BIN_LOCATION}/../share"
  1322. fi
  1323. # shellcheck disable=SC2046
  1324. edo ln $(optionv "-v") -sf ghc/"${myghcver}"/share "${BIN_LOCATION}/../share"
  1325. fi
  1326. status_message "Done"
  1327. unset myghcver inst_location f target_suffix
  1328. }
  1329. ############################
  1330. #--[ Subcommand upgrade ]--#
  1331. ############################
  1332. # @FUNCTION: upgrade
  1333. # @USAGE: <install-location>
  1334. # @DESCRIPTION:
  1335. # Downloads the latest version of this script and places it into
  1336. # the given directory.
  1337. upgrade() {
  1338. target_location=$1
  1339. [ -e "${target_location}" ] || die "Destination \"${target_location}\" does not exist, cannot update script"
  1340. status_message "Updating ${SCRIPT}"
  1341. (
  1342. edo cd "$(mktempdir)"
  1343. download "${SCRIPT_UPDATE_URL}"
  1344. edo chmod +x ghcup
  1345. edo mv -f ghcup "${target_location}"/ghcup
  1346. ) || die "failed to install"
  1347. status_message "Done"
  1348. unset target_location
  1349. }
  1350. #######################
  1351. #--[ Subcommand rm ]--#
  1352. #######################
  1353. # @FUNCTION: rm_ghc
  1354. # @USAGE: <ghcversion>
  1355. # @DESCRIPTION:
  1356. # Removes the given GHC version installed by ghcup.
  1357. rm_ghc() {
  1358. [ -z "$1" ] && die "Internal error: no argument given to rm_ghc"
  1359. myghcver=$1
  1360. inst_location=$(get_ghc_location "${myghcver}")
  1361. [ -z "${myghcver}" ] && die "We are paranoid, ghcver not set"
  1362. if ghc_already_installed "${myghcver}" ; then
  1363. [ -z "${inst_location}" ] && die "internal error: inst_location empty!"
  1364. if ! ${FORCE} ; then
  1365. if ! ask_for_confirmation "Really removing ${myghcver}? This will also recursively remove the following directory (please double-check): \"${inst_location}\"" ; then
  1366. warning_message "Not removing GHC..."
  1367. return 0
  1368. fi
  1369. fi
  1370. for f in "${BIN_LOCATION}"/*-"${myghcver}" ; do
  1371. # https://tanguy.ortolo.eu/blog/article113/test-symlink
  1372. [ ! -e "${f}" ] && [ ! -h "${f}" ] && {
  1373. warning_message "No existing symlinks for ${myghcver} in ${BIN_LOCATION}, skipping"
  1374. break
  1375. }
  1376. edo rm "${f}"
  1377. done
  1378. edo rm -r "${inst_location}"
  1379. status_message "Successfully removed GHC ${myghcver}."
  1380. # Only run set_ghc_major if there is at least one 8.6.x version left for 8.6.
  1381. if [ -n "$(get_full_ghc_ver "${myghcver%.*}")" ] ; then
  1382. set_ghc_major "${myghcver}"
  1383. fi
  1384. if [ -h "${BIN_LOCATION}/ghc-${myghcver%.*}" ] && [ ! -e "${BIN_LOCATION}/ghc-${myghcver%.*}" ] ; then
  1385. # TODO: known_tools is not very robust, but we want to avoid accidentially deleting
  1386. # unrelated things (even if those are dangling symlinks)
  1387. known_tools="ghc ghci ghc-pkg haddock haddock-ghc runghc runhaskell hp2ps hpc hsc2hs"
  1388. # remove dangling symlinks for ghc, ghci, ...
  1389. for t in ${known_tools} ; do
  1390. if [ -h "${BIN_LOCATION}/${t}-${myghcver%.*}" ] && [ ! -e "${BIN_LOCATION}/${t}-${myghcver%.*}" ] ; then
  1391. edo rm "${BIN_LOCATION}/${t}-${myghcver%.*}"
  1392. fi
  1393. done
  1394. unset t known_tools
  1395. fi
  1396. if [ -h "${BIN_LOCATION}/ghc" ] && [ ! -e "${BIN_LOCATION}/ghc" ] ; then
  1397. warning_message "Currently active GHC is a dangling symlink, removing..."
  1398. # TODO: known_tools is not very robust, but we want to avoid accidentially deleting
  1399. # unrelated things (even if those are dangling symlinks)
  1400. known_tools="ghc ghci ghc-pkg haddock haddock-ghc runghc runhaskell hp2ps hpc hsc2hs"
  1401. # remove dangling symlinks for ghc, ghci, ...
  1402. for t in ${known_tools} ; do
  1403. if [ -h "${BIN_LOCATION}/${t}" ] && [ ! -e "${BIN_LOCATION}/${t}" ] ; then
  1404. edo rm "${BIN_LOCATION}/${t}"
  1405. fi
  1406. done
  1407. unset t known_tools
  1408. # remove dangling symlink to share directory
  1409. if [ -h "${BIN_LOCATION}/../share" ] && [ ! -e "${BIN_LOCATION}/../share" ] ; then
  1410. edo rm "${BIN_LOCATION}/../share"
  1411. fi
  1412. warning_message "Done."
  1413. warning_message "You may now want to set currently active GHC to a different version via:"
  1414. warning_message " ghcup set <ghcver>"
  1415. fi
  1416. else
  1417. warning_message "${myghcver} doesn't appear to be installed, skipping"
  1418. fi
  1419. unset myghcver inst_location f
  1420. }
  1421. ############################
  1422. #--[ Subcommand install ]--#
  1423. ############################
  1424. # @FUNCTION: install_cabal
  1425. # @USAGE: <cabalversion> [os-overwrite]
  1426. # @DESCRIPTION:
  1427. # Installs the given cabal version.
  1428. install_cabal() {
  1429. [ -z "$1" ] && die "Internal error: no argument given to install_cabal"
  1430. mycabalver=$1
  1431. myarch=$(get_arch)
  1432. [ -z "${myarch}" ] && die "failed to get architecture"
  1433. inst_location=$BIN_LOCATION
  1434. download_url=$(get_download_url "cabal-install" "${mycabalver}" "$2")
  1435. download_tarball_name=$(basename "${download_url}")
  1436. if [ -z "${download_url}" ] ; then
  1437. die "Could not find an appropriate download for the requested cabal-install-${mycabalver} on your system! Please report a bug at ${BUG_URL}"
  1438. fi
  1439. status_message "Installing cabal-install-${mycabalver} for $(if [ -n "$2" ] ; then echo "$2" ; else get_distro_name ; fi) on architecture $(get_arch)"
  1440. edo mkdir -p "${inst_location}"
  1441. tmp_dir=$(mktempdir)
  1442. { [ -z "${tmp_dir}" ] || ! [ -d "${tmp_dir}" ] ; } && die "Failed to create temporary directory"
  1443. (
  1444. if ${CACHING} ; then
  1445. if [ ! -f "${CACHE_LOCATION}/${download_tarball_name}" ] ; then
  1446. download_to_cache "${download_url}"
  1447. fi
  1448. edo cd "${tmp_dir}"
  1449. unpack "${CACHE_LOCATION}/${download_tarball_name}"
  1450. else
  1451. edo cd "${tmp_dir}"
  1452. download "${download_url}"
  1453. unpack "${download_tarball_name}"
  1454. fi
  1455. edo mv -f cabal "${inst_location}"/cabal
  1456. if [ -f "${tmp_dir}/${download_tarball_name}" ] ; then
  1457. rm "${tmp_dir}/${download_tarball_name}"
  1458. fi
  1459. ) || die "Failed to install cabal-install"
  1460. status_message "Successfully installed cabal-install into"
  1461. status_message " ${BIN_LOCATION}"
  1462. status_message ""
  1463. unset mycabalver myarch inst_location download_url download_tarball_name tmp_dir
  1464. }
  1465. # @FUNCTION: compile_ghc
  1466. # @USAGE: <ghcversion> <bootstrap-ghc> [build.mk]
  1467. # @DESCRIPTION:
  1468. # Compile and installs the given GHC version with the
  1469. # specified GHC bootstrap version.
  1470. # Can additionally take a custom file that will be used
  1471. # as build configuration.
  1472. compile_ghc() {
  1473. { [ -z "$1" ] || [ -z "$2" ] ;} && die "Internal error: not enough arguments given to compile_ghc"
  1474. myghcver=$1
  1475. bootstrap_ghc=$2
  1476. inst_location=$(get_ghc_location "$1")
  1477. [ -z "${inst_location}" ] && die "failed to get install location"
  1478. download_url="https://downloads.haskell.org/~ghc/${myghcver}/ghc-${myghcver}-src.tar.xz"
  1479. download_tarball_name=$(basename "${download_url}")
  1480. if [ -n "$3" ] ; then
  1481. case "$3" in
  1482. /*) build_config=$3 ;;
  1483. *) build_config="$(pwd)/$3" ;;
  1484. esac
  1485. [ -f "${build_config}" ] || die "specified build config \"${build_config}\" file does not exist!"
  1486. fi
  1487. if ghc_already_installed "${myghcver}" ; then
  1488. if ${FORCE} ; then
  1489. echo "GHC already installed in ${inst_location}, overwriting!"
  1490. else
  1491. die "GHC already installed in ${inst_location}, use --force to overwrite"
  1492. fi
  1493. fi
  1494. status_message "Compiling GHC for version ${myghcver} from source"
  1495. tmp_dir=$(mktempdir)
  1496. { [ -z "${tmp_dir}" ] || ! [ -d "${tmp_dir}" ] ; } && die "Failed to create temporary directory"
  1497. (
  1498. if ${CACHING} ; then
  1499. if [ ! -f "${CACHE_LOCATION}/${download_tarball_name}" ] ; then
  1500. download_to_cache "${download_url}"
  1501. fi
  1502. edo cd "${tmp_dir}"
  1503. unpack "${CACHE_LOCATION}/${download_tarball_name}"
  1504. else
  1505. edo cd "${tmp_dir}"
  1506. download "${download_url}"
  1507. unpack "${download_tarball_name}"
  1508. fi
  1509. edo cd "./ghc-${myghcver}"
  1510. if [ -n "${build_config}" ] ; then
  1511. edo cat "${build_config}" > mk/build.mk
  1512. else
  1513. cat <<-EOF > mk/build.mk || die
  1514. V=0
  1515. BUILD_MAN = NO
  1516. BUILD_SPHINX_HTML = NO
  1517. BUILD_SPHINX_PDF = NO
  1518. HADDOCK_DOCS = YES
  1519. GhcWithLlvmCodeGen = YES
  1520. EOF
  1521. fi
  1522. edo ./boot
  1523. if [ "$(echo "${myghcver}" | awk -F. '{printf"%d%03d",$1,$2;}')" -ge 8008 ]; then
  1524. GHC=$(command -v "${bootstrap_ghc}") edo ./configure --prefix="${inst_location}"
  1525. else
  1526. edo ./configure --prefix="${inst_location}" --with-ghc="${bootstrap_ghc}"
  1527. fi
  1528. emake -j${JOBS}
  1529. emake install
  1530. # clean up
  1531. edo cd ..
  1532. [ -f "${tmp_dir}/${download_tarball_name}" ] && rm "${tmp_dir}/${download_tarball_name}"
  1533. [ -e "${tmp_dir}/ghc-${myghcver}" ] && rm -r "${tmp_dir}/ghc-${myghcver}"
  1534. ) || {
  1535. [ -f "${tmp_dir}/${download_tarball_name}" ] && rm "${tmp_dir}/${download_tarball_name}"
  1536. [ -e "${tmp_dir}/ghc-${myghcver}" ] && rm -r "${tmp_dir}/ghc-${myghcver}"
  1537. die "Failed to install, consider updating this script via:
  1538. ${SCRIPT} upgrade
  1539. Also check https://ghc.haskell.org/trac/ghc/wiki/Building/Preparation/Linux for build requirements and follow the instructions."
  1540. }
  1541. for f in "${inst_location}"/bin/*-"${myghcver}" ; do
  1542. [ -f "${f}" ] || die "Something went wrong, ${f} does not exist!"
  1543. fn=$(basename "${f}")
  1544. # shellcheck disable=SC2046
  1545. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}/bin/${fn}" "${BIN_LOCATION}/${fn}"
  1546. unset fn
  1547. done
  1548. # shellcheck disable=SC2046
  1549. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/runhaskell "${BIN_LOCATION}/runhaskell-${myghcver}"
  1550. # shellcheck disable=SC2046
  1551. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/hsc2hs "${BIN_LOCATION}/hsc2hs-${myghcver}"
  1552. # shellcheck disable=SC2046
  1553. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/hp2ps "${BIN_LOCATION}/hp2ps-${myghcver}"
  1554. # shellcheck disable=SC2046
  1555. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/hpc "${BIN_LOCATION}/hpc-${myghcver}"
  1556. if [ -f "${inst_location}/bin/haddock" ] ; then
  1557. # shellcheck disable=SC2046
  1558. edo ln $(optionv "-v") -sf ../ghc/"${myghcver}"/bin/haddock "${BIN_LOCATION}/haddock-${myghcver}"
  1559. fi
  1560. status_message "Done installing, run \"ghci-${myghcver}\" or set up your current GHC via: ${SCRIPT} set ${myghcver}"
  1561. unset bootstrap_ghc inst_location f download_url download_tarball_name tmp_dir
  1562. set_ghc_major "${myghcver}"
  1563. unset myghcver
  1564. }
  1565. ###############################
  1566. #--[ Subcommand debug-info ]--#
  1567. ###############################
  1568. # @FUNCTION: print_debug_info
  1569. # @DESCRIPTION:
  1570. # Print debug info (e.g. detected system/distro).
  1571. print_debug_info() {
  1572. echo "Script version: ${VERSION}"
  1573. echo
  1574. echo "Script variables:"
  1575. echo " GHC install location: ${GHC_LOCATION}"
  1576. echo " Binary install location: ${BIN_LOCATION}"
  1577. echo " Tarball cache location: ${CACHE_LOCATION}"
  1578. echo " Downloader: ${DOWNLOADER} ${DOWNLOADER_OPTS} <url>"
  1579. echo " Script update url: ${SCRIPT_UPDATE_URL}"
  1580. echo " GHC download baseurl: ${GHC_DOWNLOAD_BASEURL}"
  1581. echo " Meta download url ${META_DOWNLOAD_URL}"
  1582. echo " Meta download format ${META_DOWNLOAD_FORMAT}"
  1583. echo " Meta version url ${META_VERSION_URL}"
  1584. echo " Meta version format ${META_VERSION_FORMAT}"
  1585. echo
  1586. echo "Detected system information:"
  1587. echo " Architecture: $(get_arch)"
  1588. echo " Distribution: $(get_distro_name)"
  1589. echo " Distro alias: $(get_distro_alias "$(get_distro_name)")"
  1590. echo " Distro version: $(get_distro_ver)"
  1591. }
  1592. #########################
  1593. #--[ Subcommand list ]--#
  1594. #########################
  1595. # @FUNCTION: list
  1596. # @USAGE: <tool> <raw-format> <criteria>
  1597. # @DESCRIPTION:
  1598. # List available tools and their versions from upstream.
  1599. list() {
  1600. mytool=$1
  1601. raw_format=$2
  1602. criteria=$3
  1603. meta_file="$(get_meta_version_file)"
  1604. [ -z "${meta_file}" ] && die "failed to get meta file"
  1605. if ! ${raw_format} ; then
  1606. printf "\\033[1;32m%s\\033[0m\\n" "Available versions:"
  1607. fi
  1608. lines=$(
  1609. if [ "${mytool}" = "all" ] ; then
  1610. awk "
  1611. NF {
  1612. if (\$1 != \"#\") {
  1613. print \$1 \"\\t\" \$2 \"\\t\" \$3
  1614. }
  1615. }" "${meta_file}" || die "awk failed!"
  1616. else
  1617. awk "
  1618. NF {
  1619. if (\$1 == \"${mytool}\") {
  1620. print \$1 \"\\t\" \$2 \"\\t\" \$3
  1621. }
  1622. }" "${meta_file}" || die "awk failed!"
  1623. fi
  1624. )
  1625. _print_me() {
  1626. if ${raw_format} ; then
  1627. printf "%s\\n" "$1"
  1628. else
  1629. if [ "$2" = "available" ] ; then
  1630. printf "\\033[0;32m\342\234\224\\033[0m %s\\n" "$1"
  1631. elif [ "$2" = "set" ] ; then
  1632. printf "\\033[0;32m\342\234\224 \\033[0;34m%s\\033[0m\\n" "$1"
  1633. elif [ "$2" = "unavailable" ] ; then
  1634. printf "\\033[0;31m\342\234\227\\033[0m %s\\n" "$1"
  1635. fi
  1636. fi
  1637. }
  1638. if [ -z "${lines}" ] ; then
  1639. (>&2 echo "Nothing found for tool ${mytool}")
  1640. return
  1641. fi
  1642. echo "$lines" | while read -r l; do
  1643. tool=$(echo "${l}" | cut -f1)
  1644. version=$(echo "${l}" | cut -f2)
  1645. if [ "${criteria}" = "set" ] ; then
  1646. if [ "${tool}" = "ghc" ] && [ "${version}" = "$(show_ghc_installed)" ] ; then
  1647. _print_me "${l}" "set"
  1648. fi
  1649. if [ "${tool}" = "cabal-install" ] && [ "${version}" = "$(show_cabal_installed)" ] ; then
  1650. _print_me "${l}" "set"
  1651. fi
  1652. else
  1653. if tool_already_installed "${tool}" "${version}" ; then
  1654. if [ "${tool}" = "ghc" ] && [ "${version}" = "$(show_ghc_installed)" ] ; then
  1655. _print_me "${l}" "set"
  1656. elif [ "${tool}" = "cabal-install" ] && [ "${version}" = "$(show_cabal_installed)" ] ; then
  1657. _print_me "${l}" "set"
  1658. else
  1659. _print_me "${l}" "available"
  1660. fi
  1661. else
  1662. if [ "${criteria}" != "installed" ] ; then
  1663. _print_me "${l}" "unavailable"
  1664. fi
  1665. fi
  1666. fi
  1667. done
  1668. unset mytool meta_file l lines tool version raw_format installed_only criteria
  1669. }
  1670. ##############################
  1671. #--[ Subcommand changelog ]--#
  1672. ##############################
  1673. # @FUNCTION: changelog_url
  1674. # @USAGE: <ghcversion>
  1675. # @DESCRIPTION:
  1676. # Print the changelog url for the given GHC version to stdout.
  1677. # @STDOUT: the changelog url
  1678. changelog_url() {
  1679. [ -z "$1" ] && die "Internal error: no argument given to changelog"
  1680. printf "https://downloads.haskell.org/~ghc/%s/docs/html/users_guide/%s-notes.html" "$1" "$1"
  1681. }
  1682. # @FUNCTION: changelog
  1683. # @USAGE: <ghcversion>
  1684. # @DESCRIPTION:
  1685. # Opens the changelog for the given ghc version via xdg-open.
  1686. changelog() {
  1687. [ -z "$1" ] && die "Internal error: no argument given to changelog"
  1688. url=$(changelog_url "$1")
  1689. xdg-open "${url}" || die "failed to xdg-open the following url: ${url}"
  1690. unset url
  1691. }
  1692. ######################################
  1693. #--[ Subcommand print-system-reqs ]--#
  1694. ######################################
  1695. # @FUNCTION: system_reqs_url
  1696. # @USAGE: <distro-alias>
  1697. # @DESCRIPTION:
  1698. # Mapping of distro-alias to system requirements URL.
  1699. system_reqs_url() {
  1700. [ -z "$1" ] && die "Internal error: no argument given to system_reqs_url"
  1701. case "$1" in
  1702. "alpine")
  1703. printf "%s/.requirements/ghc/alpine" "${BASE_DOWNLOAD_URL}"
  1704. ;;
  1705. "debian"|"ubuntu")
  1706. printf "%s/.requirements/ghc/ubuntu" "${BASE_DOWNLOAD_URL}"
  1707. ;;
  1708. "darwin")
  1709. printf "%s/.requirements/ghc/darwin" "${BASE_DOWNLOAD_URL}"
  1710. ;;
  1711. *)
  1712. printf "%s/.requirements/ghc/default" "${BASE_DOWNLOAD_URL}"
  1713. ;;
  1714. esac
  1715. }
  1716. # @FUNCTION: print_system_reqs
  1717. # @DESCRIPTION:
  1718. # Print the system requirements (approximation).
  1719. print_system_reqs() {
  1720. mydistro=$(get_distro_alias "$(get_distro_name)")
  1721. reqs_url=$(system_reqs_url "${mydistro}")
  1722. download_to_stdout "${reqs_url}"
  1723. unset mydistro reqs_url
  1724. }
  1725. #######################
  1726. #--[ Sanity checks ]--#
  1727. #######################
  1728. if [ -z "${GHCUP_INSTALL_BASE_PREFIX}" ] ; then
  1729. die "GHCUP_INSTALL_BASE_PREFIX empty, cannot operate"
  1730. fi
  1731. if [ ! -d "${GHCUP_INSTALL_BASE_PREFIX}" ] ; then
  1732. die "${GHCUP_INSTALL_BASE_PREFIX} does not exist"
  1733. fi
  1734. ##############################################
  1735. #--[ Command line parsing and entry point ]--#
  1736. ##############################################
  1737. [ $# -lt 1 ] && usage
  1738. while [ $# -gt 0 ] ; do
  1739. case $1 in
  1740. -v|--verbose)
  1741. VERBOSE=true
  1742. shift 1
  1743. if [ $# -lt 1 ] ; then
  1744. usage
  1745. fi
  1746. ;;
  1747. -V|--version)
  1748. printf "%s\\n" "${VERSION}"
  1749. exit 0;;
  1750. --list-commands)
  1751. echo "changelog
  1752. compile
  1753. debug-info
  1754. install
  1755. install-cabal
  1756. list
  1757. print-system-reqs
  1758. rm
  1759. set
  1760. upgrade"
  1761. exit 0;;
  1762. -h|--help)
  1763. usage;;
  1764. -w|--wget)
  1765. DOWNLOADER="wget"
  1766. DOWNLOADER_OPTS=""
  1767. DOWNLOADER_STDOUT_OPTS="-qO-"
  1768. shift 1
  1769. if [ $# -lt 1 ] ; then
  1770. usage
  1771. fi
  1772. ;;
  1773. -c|--cache)
  1774. CACHING=true
  1775. shift 1
  1776. if [ $# -lt 1 ] ; then
  1777. usage
  1778. fi
  1779. ;;
  1780. -o|--os) MY_OS=$2
  1781. shift 2;;
  1782. *) ## startup tasks ##
  1783. edo mkdir -p "${INSTALL_BASE}"
  1784. edo mkdir -p "${BIN_LOCATION}"
  1785. edo mkdir -p "${CACHE_LOCATION}"
  1786. # clean up old meta files
  1787. if [ -f "${CACHE_LOCATION}/$(basename "${META_VERSION_URL}")" ] ; then
  1788. edo rm "${CACHE_LOCATION}/$(basename "${META_VERSION_URL}")"
  1789. fi
  1790. if [ -f "${CACHE_LOCATION}/$(basename "${META_DOWNLOAD_URL}")" ] ; then
  1791. edo rm "${CACHE_LOCATION}/$(basename "${META_DOWNLOAD_URL}")"
  1792. fi
  1793. # check for available commands
  1794. missing_commands="$(check_required_commands ${DOWNLOADER})"
  1795. if [ -n "${missing_commands}" ] ; then
  1796. die "Following commands are required, but missing, please install: ${missing_commands}"
  1797. fi
  1798. unset missing_commands
  1799. case $1 in
  1800. install)
  1801. shift 1
  1802. while [ $# -gt 0 ] ; do
  1803. case $1 in
  1804. -h|--help) install_usage;;
  1805. -f|--force) FORCE=true
  1806. shift 1;;
  1807. *) GHC_VER=$1
  1808. break;;
  1809. esac
  1810. done
  1811. if [ -z "${GHC_VER}" ] ; then
  1812. _tool_ver="$(get_tool_ver_from_tag "ghc" "recommended")"
  1813. if [ -z "${_tool_ver}" ] ; then
  1814. die "Could not find a recommended GHC version, please report a bug at ${BUG_URL}!"
  1815. fi
  1816. install_ghc "${_tool_ver}" "${MY_OS}"
  1817. else
  1818. # could be a version or a tag, let's check
  1819. if array_contains "${GHC_VER}" "$(known_tool_versions "ghc")" ; then
  1820. install_ghc "${GHC_VER}" "${MY_OS}"
  1821. elif array_contains "${GHC_VER}" "$(known_tool_tags "ghc")" ; then
  1822. install_ghc "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")" "${MY_OS}"
  1823. else
  1824. die "\"${GHC_VER}\" is not a known version or tag!"
  1825. fi
  1826. fi
  1827. break;;
  1828. set)
  1829. shift 1
  1830. while [ $# -gt 0 ] ; do
  1831. case $1 in
  1832. -h|--help) set_usage;;
  1833. *) GHC_VER=$1
  1834. break;;
  1835. esac
  1836. done
  1837. if [ -z "${GHC_VER}" ] ; then
  1838. _tool_ver="$(get_tool_ver_from_tag "ghc" "recommended")"
  1839. if [ -z "${_tool_ver}" ] ; then
  1840. die "Could not find a recommended GHC version, please report a bug at ${BUG_URL}!"
  1841. fi
  1842. set_ghc "${_tool_ver}"
  1843. else
  1844. # could be a version or a tag, let's check
  1845. if array_contains "${GHC_VER}" "$(known_tool_versions "ghc")" ; then
  1846. set_ghc "${GHC_VER}"
  1847. elif array_contains "${GHC_VER}" "$(known_tool_tags "ghc")" ; then
  1848. set_ghc "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")"
  1849. else
  1850. die "\"${GHC_VER}\" is not a known version or tag!"
  1851. fi
  1852. fi
  1853. break;;
  1854. upgrade)
  1855. IN_PLACE=false
  1856. shift 1
  1857. while [ $# -gt 0 ] ; do
  1858. case $1 in
  1859. -h|--help) upgrade_usage;;
  1860. -i|--inplace) IN_PLACE=true
  1861. shift 1 ;;
  1862. *) TARGET_LOCATION=$1
  1863. break;;
  1864. esac
  1865. done
  1866. if ${IN_PLACE} ; then
  1867. upgrade "$(dirname "$(posix_realpath "${SOURCE}")")"
  1868. elif [ -n "${TARGET_LOCATION}" ] ; then
  1869. upgrade "${TARGET_LOCATION}"
  1870. else
  1871. upgrade "${BIN_LOCATION}"
  1872. fi
  1873. break;;
  1874. rm)
  1875. shift 1
  1876. while [ $# -gt 0 ] ; do
  1877. case $1 in
  1878. -h|--help) rm_usage;;
  1879. -f|--force) FORCE=true
  1880. shift 1;;
  1881. *) GHC_VER=$1
  1882. break;;
  1883. esac
  1884. done
  1885. [ -n "${GHC_VER}" ] || rm_usage
  1886. rm_ghc "${GHC_VER}"
  1887. break;;
  1888. install-cabal)
  1889. shift 1
  1890. while [ $# -gt 0 ] ; do
  1891. case $1 in
  1892. -h|--help) install_cabal_usage;;
  1893. -f|--force) FORCE=true
  1894. shift 1;;
  1895. *) CABAL_VER=$1
  1896. break;;
  1897. esac
  1898. done
  1899. if [ -n "${CABAL_VER}" ] ; then
  1900. # could be a version or a tag, let's check
  1901. if array_contains "${CABAL_VER}" "$(known_tool_versions "cabal-install")" ; then
  1902. install_cabal "${CABAL_VER}" "${MY_OS}"
  1903. elif array_contains "${CABAL_VER}" "$(known_tool_tags "cabal-install")" ; then
  1904. install_cabal "$(get_tool_ver_from_tag "cabal-install" "${CABAL_VER}")" "${MY_OS}"
  1905. else
  1906. die "\"${CABAL_VER}\" is not a known version or tag!"
  1907. fi
  1908. else
  1909. _cabal_ver="$(get_tool_ver_from_tag "cabal-install" "recommended")"
  1910. if [ -z "${_cabal_ver}" ] ; then
  1911. die "Could not find a recommended cabal-install version, please report a bug at ${BUG_URL}!"
  1912. fi
  1913. install_cabal "${_cabal_ver}" "${MY_OS}"
  1914. fi
  1915. break;;
  1916. compile)
  1917. shift 1
  1918. while [ $# -gt 0 ] ; do
  1919. case $1 in
  1920. -h|--help) compile_usage;;
  1921. -f|--force) FORCE=true
  1922. shift 1;;
  1923. -j|--jobs) JOBS=$2
  1924. shift 2;;
  1925. -c|--build-config) BUILD_CONFIG=$2
  1926. shift 2;;
  1927. *) GHC_VER=$1
  1928. BOOTSTRAP_GHC=$2
  1929. break;;
  1930. esac
  1931. done
  1932. [ -n "${GHC_VER}" ] || compile_usage
  1933. [ -n "${BOOTSTRAP_GHC}" ] || compile_usage
  1934. compile_ghc "${GHC_VER}" "${BOOTSTRAP_GHC}" "${BUILD_CONFIG}"
  1935. break;;
  1936. debug-info)
  1937. shift 1
  1938. while [ $# -gt 0 ] ; do
  1939. case $1 in
  1940. -h|--help) debug_info_usage;;
  1941. *) debug_info_usage;;
  1942. esac
  1943. done
  1944. print_debug_info
  1945. break;;
  1946. list)
  1947. RAW_FORMAT=false
  1948. TOOL="ghc"
  1949. shift 1
  1950. while [ $# -gt 0 ] ; do
  1951. case $1 in
  1952. -h|--help) list_usage;;
  1953. -t|--tool) TOOL=$2
  1954. shift 2;;
  1955. -r|--raw-format) RAW_FORMAT=true
  1956. shift 1;;
  1957. -c|--show-criteria) SHOW_CRITERIA=$2
  1958. shift 2;;
  1959. *) list_usage;;
  1960. esac
  1961. done
  1962. list "${TOOL}" ${RAW_FORMAT} "${SHOW_CRITERIA}"
  1963. break;;
  1964. changelog)
  1965. shift 1
  1966. while [ $# -gt 0 ] ; do
  1967. case $1 in
  1968. -h|--help) changelog_usage;;
  1969. -f|--force) FORCE=true
  1970. shift 1;;
  1971. *) GHC_VER=$1
  1972. break;;
  1973. esac
  1974. done
  1975. if [ -z "${GHC_VER}" ] ; then
  1976. _tool_ver="$(get_tool_ver_from_tag "ghc" "latest")"
  1977. if [ -z "${_tool_ver}" ] ; then
  1978. die "Could not find a latest GHC version, please report a bug at ${BUG_URL}!"
  1979. fi
  1980. changelog "${_tool_ver}"
  1981. else
  1982. # could be a version or a tag, let's check
  1983. if array_contains "${GHC_VER}" "$(known_tool_versions "ghc")" ; then
  1984. changelog "${GHC_VER}"
  1985. elif array_contains "${GHC_VER}" "$(known_tool_tags "ghc")" ; then
  1986. changelog "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")"
  1987. else
  1988. die "\"${GHC_VER}\" is not a known version or tag!"
  1989. fi
  1990. fi
  1991. break;;
  1992. print-system-reqs)
  1993. shift 1
  1994. while [ $# -gt 0 ] ; do
  1995. case $1 in
  1996. -h|--help) print_system_reqs_usage;;
  1997. *) print_system_reqs_usage;;
  1998. esac
  1999. done
  2000. print_system_reqs
  2001. break;;
  2002. *) usage;;
  2003. esac
  2004. break;;
  2005. esac
  2006. done
  2007. # vim: tabstop=4 shiftwidth=4 expandtab