├── .ci └── stack-8.0.yaml ├── .circleci └── config.yml ├── .cirrus.yml ├── .github └── workflows │ └── packcheck.yml ├── .packcheck.ignore ├── Changelog.md ├── LICENSE ├── MAINTAINING.md ├── README.md ├── appveyor.yml ├── bench └── Main.hs ├── cabal.project ├── cabal.project.coveralls ├── cabal.project.ghc-head ├── packcheck-remote.sh ├── packcheck-safe.sh ├── packcheck.cabal ├── packcheck.sh ├── src └── Hello.hs ├── stack.yaml └── test └── Main.hs /.ci/stack-8.0.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-9.20 2 | packages: 3 | - '.' 4 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # packcheck-0.7.1 2 | # You can use any of the options supported by packcheck as environment 3 | # variables here. See https://github.com/composewell/packcheck for all 4 | # options and their explanation. 5 | 6 | version: 2 7 | 8 | #----------------------------------------------------------------------------- 9 | # Packcheck global environment variables 10 | #----------------------------------------------------------------------------- 11 | 12 | env: &env 13 | environment: 14 | # ------------------------------------------------------------------------ 15 | # Common options 16 | # ------------------------------------------------------------------------ 17 | # GHC_OPTIONS: "-Werror" 18 | # For updating see: https://downloads.haskell.org/~ghcup/ 19 | GHCUP_VERSION: 0.1.40.0 20 | CABAL_REINIT_CONFIG: "y" 21 | LC_ALL: "C.UTF-8" 22 | 23 | # ------------------------------------------------------------------------ 24 | # What to build 25 | # ------------------------------------------------------------------------ 26 | # DISABLE_TEST: "y" 27 | # DISABLE_BENCH: "y" 28 | # DISABLE_DOCS: "y" 29 | # DISABLE_SDIST_BUILD: "y" 30 | # DISABLE_SDIST_GIT_CHECK: "y" 31 | # DISABLE_DIST_CHECKS: "y" 32 | 33 | # ------------------------------------------------------------------------ 34 | # stack options 35 | # ------------------------------------------------------------------------ 36 | # Note requiring a specific version of stack using STACKVER may fail due to 37 | # github API limit while checking and upgrading/downgrading to the specific 38 | # version. 39 | #STACKVER: "1.6.5" 40 | #STACK_UPGRADE: "y" 41 | #RESOLVER: "lts-23" 42 | STACK_YAML: "stack.yaml" 43 | 44 | # ------------------------------------------------------------------------ 45 | # cabal options 46 | # ------------------------------------------------------------------------ 47 | CABAL_CHECK_RELAX: "y" 48 | CABAL_HACKAGE_MIRROR: "hackage.haskell.org:http://hackage.fpcomplete.com" 49 | CABAL_PROJECT: "cabal.project" 50 | 51 | # ------------------------------------------------------------------------ 52 | # Where to find the required tools 53 | # ------------------------------------------------------------------------ 54 | PATH: /opt/ghc/bin:/sbin:/usr/sbin:/bin:/usr/bin 55 | #TOOLS_DIR: /opt 56 | 57 | # ------------------------------------------------------------------------ 58 | # Location of packcheck.sh (the shell script invoked to perform CI tests ). 59 | # ------------------------------------------------------------------------ 60 | # You can either commit the packcheck.sh script at this path in your repo or 61 | # you can use it by specifying the PACKCHECK_REPO_URL option below in which 62 | # case it will be automatically copied from the packcheck repo to this path 63 | # during CI tests. In any case it is finally invoked from this path. 64 | PACKCHECK: "./packcheck.sh" 65 | # If you have not committed packcheck.sh in your repo at PACKCHECK 66 | # then it is automatically pulled from this URL. 67 | PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck" 68 | PACKCHECK_GITHUB_COMMIT: "03f9e753567d08835e342c8d24a406d5a1e1582e" 69 | 70 | # ubuntu seems to have better support than debian on CI systems 71 | docker: 72 | - image: ubuntu:latest 73 | 74 | #----------------------------------------------------------------------------- 75 | # Common utility stuff, not to be modified usually 76 | #----------------------------------------------------------------------------- 77 | 78 | preinstall: &preinstall 79 | run: | 80 | apt-get update 81 | # required for https/cache save and restore 82 | apt-get install -y ca-certificates 83 | 84 | # Alternative way of installing ghc and cabal, directly from 85 | # haskell.org instead of using ghcup. NOTE: this is for Debian 86 | # only and is debian release specific. 87 | # gnupg is required for apt-key to work 88 | #apt-get install -y gnupg 89 | #apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA3CBA3FFE22B574 90 | #echo "deb http://downloads.haskell.org/debian buster main" >> /etc/apt/sources.list 91 | #apt-get update 92 | #apt-get install -y ghc-8.10.4 93 | #apt-get install -y cabal-install-3.4 94 | 95 | # required for outbound https for stack and for stack setup 96 | apt-get install -y netbase xz-utils make 97 | apt-get install -y zlib1g-dev 98 | 99 | # For ghcup to install ghc 100 | if test -n "$GHCUP_VERSION" 101 | then 102 | apt-get install -y gcc 103 | apt-get install -y g++ 104 | fi 105 | 106 | # libgmp required by ghc for linking 107 | apt-get install -y libgmp-dev 108 | apt-get install -y libtinfo-dev 109 | 110 | # Required by cabal when git URL is specified in project file 111 | apt-get install -y git 112 | 113 | # Required for and by packcheck 114 | apt-get install -y curl 115 | 116 | # Get packcheck if needed 117 | if test ! -e "$PACKCHECK" 118 | then 119 | if test -z "$PACKCHECK_GITHUB_COMMIT" 120 | then 121 | die "PACKCHECK_GITHUB_COMMIT is not specified." 122 | fi 123 | PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh 124 | curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1 125 | chmod +x $PACKCHECK 126 | elif test ! -x "$PACKCHECK" 127 | then 128 | chmod +x $PACKCHECK 129 | fi 130 | 131 | restore: &restore 132 | # Needs to happen after installing ca-certificates 133 | restore_cache: 134 | key: v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }} 135 | 136 | save: &save 137 | save_cache: 138 | key: v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }} 139 | paths: 140 | - ~/.local 141 | - ~/.cabal 142 | - ~/.stack 143 | - ~/.ghcup 144 | 145 | #----------------------------------------------------------------------------- 146 | # Build matrix 147 | #----------------------------------------------------------------------------- 148 | 149 | jobs: 150 | cabal-ghc-9-8-4: 151 | <<: *env 152 | steps: 153 | - checkout 154 | - *restore 155 | - *preinstall 156 | - run: | 157 | bash -c "$PACKCHECK cabal GHCVER=9.8.4" 158 | - *save 159 | cabal-ghc-9-6-6: 160 | <<: *env 161 | steps: 162 | - checkout 163 | - *restore 164 | - *preinstall 165 | - run: | 166 | bash -c "$PACKCHECK cabal GHCVER=9.6.6" 167 | - *save 168 | cabal-ghc-9-4-8: 169 | <<: *env 170 | steps: 171 | - checkout 172 | - *restore 173 | - *preinstall 174 | - run: | 175 | bash -c "$PACKCHECK cabal GHCVER=9.4.8" 176 | - *save 177 | cabal-ghc-9-2-8: 178 | <<: *env 179 | steps: 180 | - checkout 181 | - *restore 182 | - *preinstall 183 | - run: | 184 | bash -c "$PACKCHECK cabal GHCVER=9.2.8" 185 | - *save 186 | cabal-ghc-9-0-1: 187 | <<: *env 188 | steps: 189 | - checkout 190 | - *restore 191 | - *preinstall 192 | - run: | 193 | bash -c "$PACKCHECK cabal GHCVER=9.0.1" 194 | - *save 195 | cabal-ghc-8-10-7: 196 | <<: *env 197 | steps: 198 | - checkout 199 | - *restore 200 | - *preinstall 201 | - run: | 202 | bash -c "$PACKCHECK cabal-v2 GHCVER=8.10.7" 203 | - *save 204 | stack-lts-23: 205 | <<: *env 206 | steps: 207 | - checkout 208 | - *restore 209 | - *preinstall 210 | - run: | 211 | bash -c "$PACKCHECK stack RESOLVER=lts-23" 212 | - *save 213 | 214 | workflows: 215 | version: 2 216 | build: 217 | jobs: 218 | # Uncomment the configs that you want to enable 219 | - cabal-ghc-9-8-4 220 | - cabal-ghc-9-6-6 221 | - cabal-ghc-9-4-8 222 | - cabal-ghc-9-2-8 223 | - cabal-ghc-9-0-1 224 | - cabal-ghc-8-10-7 225 | - stack-lts-23 226 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | freebsd_instance: 2 | image_family: freebsd-14-2 3 | 4 | task: 5 | name: FreeBSD+ghc-9.10.1+cabal 6 | env: 7 | LC_ALL: C.UTF-8 8 | BUILD: cabal 9 | GHCUP_VERSION: 0.1.40.0 10 | DOCSPEC_URL: https://github.com/phadej/cabal-extras/releases/tag/cabal-docspec-0.0.0.20240703 11 | DOCSPEC_OPTIONS: "--timeout 60 --check-properties --property-variables xs" 12 | # GHCUP_GHC_OPTIONS: ${{ matrix.ghcup_ghc_options }} 13 | GHCVER: 9.10.1 14 | CABALVER: 3.12.1.0 15 | DISABLE_DOCS: n 16 | ENABLE_DOCSPEC: n 17 | DISABLE_TEST: n 18 | DISABLE_BENCH: n 19 | DISABLE_DIST_CHECKS: y 20 | # SDIST_OPTIONS: ${{ matrix.sdist_options }} 21 | DISABLE_SDIST_BUILD: y 22 | 23 | # Cabal options 24 | CABAL_REINIT_CONFIG: y 25 | # CABAL_BUILD_OPTIONS: ${{ matrix.cabal_build_options }} --flag limit-build-mem 26 | # CABAL_BUILD_TARGETS: ${{ matrix.cabal_build_targets }} 27 | CABAL_PROJECT: cabal.project 28 | CABAL_CHECK_RELAX: y 29 | 30 | # Stack options 31 | # STACK_UPGRADE: "y" 32 | # RESOLVER: ${{ matrix.resolver }} 33 | # STACK_YAML: ${{ matrix.stack_yaml }} 34 | # STACK_BUILD_OPTIONS: ${{ matrix.stack_build_options }} 35 | 36 | # packcheck location and revision 37 | PACKCHECK: "./packcheck.sh" 38 | PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck" 39 | PACKCHECK_GITHUB_COMMIT: "03f9e753567d08835e342c8d24a406d5a1e1582e" 40 | 41 | # Pull token from "secrets" setting of the github repo 42 | # COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} 43 | # COVERAGE: ${{ matrix.coverage }} 44 | 45 | # hlint 46 | # HLINT_VERSION: 3.6.1 47 | # HLINT_OPTIONS: "lint" 48 | # HLINT_TARGETS: "core/src src test benchmark" 49 | 50 | # Subdir 51 | # SUBDIR: ${{ matrix.subdir }} 52 | 53 | deps_install_script: | 54 | pkg install -y gmake 55 | pkg install -y bash 56 | 57 | packcheck_install_script: | 58 | if test ! -e "$PACKCHECK" 59 | then 60 | if test -z "$PACKCHECK_GITHUB_COMMIT" 61 | then 62 | die "PACKCHECK_GITHUB_COMMIT is not specified." 63 | fi 64 | PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh 65 | curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1 66 | chmod +x $PACKCHECK 67 | elif test ! -x "$PACKCHECK" 68 | then 69 | chmod +x $PACKCHECK 70 | fi 71 | 72 | packcheck_run_script: | 73 | export PATH=$HOME/.local/bin:$HOME/.ghcup/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/opt/curl/bin 74 | bash -c "$PACKCHECK $BUILD" 75 | -------------------------------------------------------------------------------- /.github/workflows/packcheck.yml: -------------------------------------------------------------------------------- 1 | # packcheck-0.7.1 2 | # You can use any of the options supported by packcheck as environment 3 | # variables here. See https://github.com/composewell/packcheck for all 4 | # options and their explanation. 5 | 6 | name: packcheck 7 | 8 | #----------------------------------------------------------------------------- 9 | # Events on which the build should be triggered 10 | #----------------------------------------------------------------------------- 11 | 12 | on: 13 | push: 14 | branches: 15 | - master 16 | pull_request: 17 | 18 | 19 | #----------------------------------------------------------------------------- 20 | # Build matrix 21 | #----------------------------------------------------------------------------- 22 | 23 | jobs: 24 | build: 25 | name: >- 26 | ${{ matrix.name }} 27 | ${{ matrix.command }} 28 | ${{ matrix.runner }} 29 | ${{ matrix.ghc_version }} 30 | env: 31 | # ------------------------------------------------------------------------ 32 | # Common options 33 | # ------------------------------------------------------------------------ 34 | # GHC_OPTIONS: "-Werror" 35 | CABAL_REINIT_CONFIG: y 36 | LC_ALL: C.UTF-8 37 | 38 | # ------------------------------------------------------------------------ 39 | # What to build 40 | # ------------------------------------------------------------------------ 41 | # DISABLE_TEST: "y" 42 | # DISABLE_BENCH: "y" 43 | # DISABLE_DOCS: "y" 44 | # DISABLE_SDIST_BUILD: "y" 45 | # DISABLE_SDIST_GIT_CHECK: "y" 46 | # DISABLE_DIST_CHECKS: "y" 47 | 48 | # ------------------------------------------------------------------------ 49 | # Selecting tool versions 50 | # ------------------------------------------------------------------------ 51 | # For updating see: https://downloads.haskell.org/~ghcup/ 52 | GHCUP_VERSION: 0.1.40.0 53 | GHCVER: ${{ matrix.ghc_version }} 54 | GHCUP_GHC_OPTIONS: ${{ matrix.ghcup_ghc_options }} 55 | # RESOLVER: ${{ matrix.stack_resolver }} 56 | 57 | # ------------------------------------------------------------------------ 58 | # stack options 59 | # ------------------------------------------------------------------------ 60 | # Note requiring a specific version of stack using STACKVER may fail due to 61 | # github API limit while checking and upgrading/downgrading to the specific 62 | # version. 63 | #STACKVER: "1.6.5" 64 | #STACK_UPGRADE: "y" 65 | STACK_YAML: "stack.yaml" 66 | 67 | # ------------------------------------------------------------------------ 68 | # cabal options 69 | # ------------------------------------------------------------------------ 70 | CABAL_CHECK_RELAX: y 71 | CABAL_HACKAGE_MIRROR: "hackage.haskell.org:http://hackage.fpcomplete.com" 72 | CABAL_PROJECT: ${{ matrix.cabal_project }} 73 | 74 | # ------------------------------------------------------------------------ 75 | # Where to find the required tools 76 | # ------------------------------------------------------------------------ 77 | PATH: /opt/ghc/bin:/sbin:/usr/sbin:/bin:/usr/bin 78 | #TOOLS_DIR: /opt 79 | 80 | # ------------------------------------------------------------------------ 81 | # Location of packcheck.sh (the shell script invoked to perform CI tests ). 82 | # ------------------------------------------------------------------------ 83 | # You can either commit the packcheck.sh script at this path in your repo or 84 | # you can use it by specifying the PACKCHECK_REPO_URL option below in which 85 | # case it will be automatically copied from the packcheck repo to this path 86 | # during CI tests. In any case it is finally invoked from this path. 87 | PACKCHECK: "./packcheck.sh" 88 | # If you have not committed packcheck.sh in your repo at PACKCHECK 89 | # then it is automatically pulled from this URL. 90 | PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck" 91 | PACKCHECK_GITHUB_COMMIT: "03f9e753567d08835e342c8d24a406d5a1e1582e" 92 | 93 | # ------------------------------------------------------------------------ 94 | # Final build variables 95 | # ------------------------------------------------------------------------ 96 | PACKCHECK_COMMAND: ${{ matrix.command }} ${{ matrix.pack_options }} 97 | 98 | # ubuntu seems to have better support than debian on CI systems 99 | runs-on: ${{ matrix.runner }} 100 | strategy: 101 | fail-fast: false 102 | matrix: 103 | # The name of the CI is built using the name and other info from CI, 104 | # therefore, the "name" field is same for all tests here. 105 | # 106 | # The reason we have an explicit "name" field here is to force 107 | # an additional config instead of adding to an existing config 108 | # while adding additional configs. 109 | # Look at 110 | # for more info about adding matrix elements. 111 | # Adding any element to the list will increase the number of matrix 112 | # elements proportional to the cross product. 113 | include: 114 | 115 | - name: ci 116 | command: cabal 117 | runner: ubuntu-latest 118 | ghc_version: head 119 | cabal_project: cabal.project.ghc-head 120 | ghcup_ghc_options: -u https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-deb10-int_native-validate.tar.xz?job=x86_64-linux-deb10-int_native-validate 121 | 122 | - name: ci 123 | command: cabal 124 | runner: ubuntu-latest 125 | ghc_version: 9.8.4 126 | cabal_project: cabal.project 127 | 128 | - name: ci 129 | command: stack 130 | runner: ubuntu-latest 131 | cabal_project: cabal.project 132 | 133 | - name: ci 134 | ghc_version: 9.8.4 135 | command: cabal 136 | runner: macos-latest 137 | cabal_project: cabal.project 138 | 139 | - name: ci 140 | ghc_version: 9.10.1 141 | command: cabal 142 | runner: macos-latest 143 | cabal_project: cabal.project 144 | 145 | - name: ci 146 | ghc_version: 9.12.1 147 | command: cabal 148 | runner: macos-latest 149 | cabal_project: cabal.project 150 | 151 | - name: ci 152 | command: hlint 153 | runner: ubuntu-latest 154 | pack_options: >- 155 | HLINT_VERSION=3.6.1 156 | HLINT_OPTIONS="lint" 157 | HLINT_TARGETS="src" 158 | 159 | steps: 160 | - uses: actions/checkout@v2 161 | - uses: actions/cache@v3 162 | name: Cache common directories 163 | with: 164 | path: | 165 | ~/.local 166 | ~/.cabal 167 | ~/.stack 168 | ~/.ghcup 169 | key: ${{ matrix.command }}-${{ matrix.ghc_version }}-${{ matrix.runner }} 170 | 171 | - name: Download packcheck 172 | run: | 173 | if test ! -e "$PACKCHECK" 174 | then 175 | if test -z "$PACKCHECK_GITHUB_COMMIT" 176 | then 177 | die "PACKCHECK_GITHUB_COMMIT is not specified." 178 | fi 179 | PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh 180 | curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1 181 | chmod +x $PACKCHECK 182 | elif test ! -x "$PACKCHECK" 183 | then 184 | chmod +x $PACKCHECK 185 | fi 186 | 187 | - name: Run packcheck 188 | run: | 189 | bash -c "$PACKCHECK $PACKCHECK_COMMAND" 190 | -------------------------------------------------------------------------------- /.packcheck.ignore: -------------------------------------------------------------------------------- 1 | .packcheck.ignore 2 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | ## 0.7.1 (Feb 2025) 2 | 3 | * Set default "ghc" in PATH for cabal-docspec to work 4 | * FreeBSD support and Cirrus CI support for FreeBSD - see `.cirrus.yml` 5 | * `CABAL_TEST_OPTIONS` envvar to pass test-only options to cabal 6 | 7 | ## 0.7.0 (Dec 2023) 8 | 9 | ### Enhancements 10 | * Supports using ghcup to install ghc automatically if `GHCUP_VERSION` 11 | env var is specified. 12 | * Supports running cabal-docspec (doctest) in `cabal` build using the 13 | `ENABLE_DOCSPEC` option (Linux only). 14 | * `HLINT_VERSION` env var can be used to install a specific version of hlint 15 | 16 | ### Breaking Changes 17 | 18 | * Explicit `hlint` command was added. Use 19 | `packcheck hlint HLINT_OPTIONS="lint" ...` instead of 20 | `packcheck cabal-v2 HLINT_OPTIONS="lint" ...` to run hlint on the package. 21 | * Removed GHCJS, coveralls support 22 | 23 | ## 0.6.0 24 | 25 | ### Enhancements 26 | 27 | * `CABAL_DISABLE_DEPS` env var to disable dependencies install by cabal. This can 28 | be useful when we have dependencies already installed e.g. in a nix shell. 29 | * Add support for github CI 30 | * Add packcheck-remote.sh, a wrapper over packcheck that allows you 31 | to run packcheck on a remote repository by cloning it locally and 32 | optionally merging a branch into another branch (e.g. merging a PR 33 | branch into master). 34 | * Several fixes to make distribution builds safer and with more checks 35 | * Do a sanity check for the existence of files in .packcheck.ignore and 36 | .hlint.ignore 37 | 38 | ### Breaking Changes 39 | 40 | * "packcheck cabal" now defaults to "packcheck cabal-v2" 41 | * Support for `cabal-v1` is removed 42 | * CI now fails if `cabal-v1` is used as a command 43 | * `CABAL_CONFIGURE_OPTIONS` is removed 44 | * `CABAL_NO_SANDBOX` is removed 45 | * `packcheck cleanall` does not remove `.cabal-sandbox/` and 46 | `.cabal.sandbox.config` anymore 47 | * Support for `cabal-new` is removed 48 | * CI now fails if `cabal-new` is used as a command 49 | * `CABAL_NEWBUILD_OPTIONS` is removed 50 | * `CABAL_NEWBUILD_TARGETS` is removed 51 | * A new command `hlint` is introduced. The `hlint` build is only triggered by 52 | using this command. 53 | * `ENABLE_INSTALL` option has been removed. 54 | 55 | ## 0.5.1 56 | 57 | ### Bug Fixes 58 | 59 | * Fix breakage due to `DISABLE_SDIST_GIT_CHECK` option. Due to this bug, 60 | build was always failing by default and reported as success. 61 | 62 | ### Deprecations 63 | 64 | * `HLINT_COMMANDS` is deprecated and replaced by 65 | `HLINT_OPTIONS`/`HLINT_TARGETS` 66 | 67 | ### Enhancements 68 | 69 | * New `HLINT_OPTIONS`/`HLINT_TARGETS` env vars to specify hlint commands in 70 | a better way. 71 | 72 | ## 0.5.0 73 | 74 | ### Bug Fixes 75 | 76 | * `packcheck.sh` script itself was missing from the package, added. 77 | 78 | ### Breaking Changes 79 | 80 | * CI now fails if `DISABLE_SDIST_BUILD` is not set and the contents 81 | of the source distribution tar ball do not match the git repository 82 | contents. Either add any exceptions to `.packcheck.ignore` file or use 83 | `DISABLE_SDIST_GIT_CHECK=y` to disable this feature. Currently this check is 84 | done only if `git` and `tar` commands are available in the `PATH`. 85 | 86 | ### Deprecations 87 | 88 | * `cabal-v1` command now shows a deprecation message and is removed from help. 89 | This command will be removed in future. 90 | * `ENABLE_INSTALL` option now does nothing. This change is because of the new 91 | behavior in cabal-3. This option will be removed in future. 92 | 93 | ### Enhancements 94 | 95 | * Added a feature to detect if any files in the git repo are missing from the 96 | source distribution tarball. 97 | * Add `CABAL_PROJECT` environment variable to support specifying a cabal 98 | project file. 99 | 100 | ## 0.4.2 101 | 102 | ### Bug Fixes 103 | 104 | * When building from source distribution, it would not build again unless 105 | cleaned with `packcheck clean` if a file in the source has changed. 106 | 107 | ### Deprecations 108 | 109 | * Deprecate and replace the `cabal` command with `cabal-v1`, in future `cabal` 110 | will be used for `cabal-v2`. 111 | * Deprecate and replace the `cabal-new` command with `cabal-v2`. 112 | * Deprecate and rename `CABAL_NEWBUILD_OPTIONS` to `CABAL_BUILD_OPTIONS` 113 | * Deprecate and rename `CABAL_NEWBUILD_TARGETS` to `CABAL_BUILD_TARGETS` 114 | * Use STACK_BUILD_OPTIONS envvar in the dependency install phase as well 115 | * Remove stack yaml creation using stack init/solver 116 | 117 | ### Enhancements 118 | 119 | * Search for ghc among stack installed GHC binaries as well 120 | * Add GHCJS support. Use ENABLE_GHCJS=y option. 121 | * Add packcheck-safe.sh . The safe version does not trust or use any 122 | environment variables, all environment needs to be specified on the command 123 | line. It also catches any misspelled command line parameter names. 124 | * Allow boolean parameters to be specified with a lenient syntax allowing 125 | values y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON|n|N|no|No|NO|false|False|FALSE|off|Off|OFF 126 | 127 | ## 0.4.1 128 | 129 | * Disable hpc-coveralls by default 130 | 131 | ## 0.4.0 132 | 133 | * Add support for circle CI 134 | * Add support for multi-package stack as well as cabal repos 135 | * Add a version command 136 | * Add CABAL_NEWBUILD_TARGETS envvar to build specific targets 137 | * Add GHC 8.6.1 in build matrices 138 | 139 | ## 0.3.1 140 | 141 | * Add a new environment var option DISABLE_DIST_CHECKS to disable source 142 | distribution checks. This can be used as a workaround for a bug in stack 143 | causing "stack sdist" to fail. 144 | * For stack builds, use the same options (STACK_BUILD_OPTIONS) for install test 145 | as for build so that an extra rebuild does not occur during install. 146 | * Workaround to avoid depending on `cabal info` command; in certain cases this 147 | command crashes cabal. See issue #13. 148 | 149 | ## 0.3.0 150 | 151 | ### Enhancements 152 | * Add cabal new-build support. Use `packcheck.sh cabal-new` to use it. 153 | * Add knobs to disable tests or doc builds (`DISABLE_TEST`, `DISABLE_DOCS`) 154 | * Now you can specify multiple versions of GHC in PATH and packcheck 155 | automatically finds the right one based on GHCVER envvar. 156 | * Add TOOLS_DIR option to specify hvr-ghc style installation of ghc and 157 | cabal. A correct version of GHC is automatically picked from this directory. 158 | * GHCVER and CABALVER variables are now optional in travis config if you 159 | specify the cabal and ghc PPAs under apt sources. 160 | * Run `autoreconf` if there is a `configure.ac` in the package dir 161 | 162 | ### Deprecations 163 | * TEST_INSTALL option is deprecated, use ENABLE_INSTALL instead 164 | 165 | ## 0.2.0 166 | 167 | ### Breaking Changes 168 | * Make `STACK_BUILD_OPTIONS` and `CABAL_CONFIGURE_OPTIONS` append to the 169 | existing build/configure options instead of overriding them. 170 | * Do not enforce specific `stack` version in CI configs - this is done to avoid 171 | failures due to github API limits when upgrading or downgrading. 172 | 173 | ### Bug Fixes 174 | * Avoid build failures in cases when `cabal-install` has to be installed and 175 | its dependencies may conflict with the current project dependencies. 176 | 177 | ### Enhancements 178 | * Better documentation in travis and appveyor configs 179 | * Reduce the number of builds in default config from 11 to 6 180 | 181 | ## 0.1.1 182 | 183 | * _Enhancement_: Nix support; fix bash location to make it work on NixOS and 184 | potentially on other systems. 185 | 186 | ## 0.1.0 187 | 188 | * Initial release 189 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Harendra Kumar 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MAINTAINING.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | 3 | The commit-id of packcheck in the CI configs must be updated after any 4 | change in `packcheck.sh` otherwise CIs would be passing because they 5 | will be happily using an old version of packcheck. This should be the last 6 | commit in the PR. 7 | 8 | ## Release Process 9 | 10 | * See [the streamly maintanenace 11 | guide](https://github.com/composewell/streamly/blob/master/MAINTAINING.md) 12 | for general process. 13 | * Update the commit-id of the packcheck script in config files 14 | * Test the whole build matrix removing all the commented builds. 15 | * Test removing caching 16 | * Update the version number in config files 17 | * Update version number in the script 18 | * Update Changelog accordingly 19 | 20 | ## Post release tasks 21 | 22 | * Update the version number in config files to the next release target 23 | * Update version number in the script to the next release target 24 | * Add an unreleased section in the Changelog titled with the next release target 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # packcheck 2 | 3 | [![Hackage](https://img.shields.io/hackage/v/packcheck.svg?style=flat)](https://hackage.haskell.org/package/packcheck) 4 | [![Gitter chat](https://badges.gitter.im/composewell/gitter.svg)](https://gitter.im/composewell/streamly) 5 | [![Windows Build status](https://ci.appveyor.com/api/projects/status/f7c0ncy84cxp8lbe?svg=true)](https://ci.appveyor.com/project/harendra-kumar/packcheck) 6 | [![CircleCI](https://circleci.com/gh/composewell/packcheck/tree/master.svg?style=svg)](https://circleci.com/gh/composewell/packcheck/tree/master) 7 | 8 | ## Quick Start 9 | 10 | 13 | 14 | ### Build on CI 15 | 16 | To use packcheck for CI testing of your repo: 17 | 18 | 26 | 27 | #### CircleCI 28 | * Add your package repo to CircleCI as necessary (See 29 | https://circleci.com/docs/2.0/getting-started/) 30 | * Copy 31 | [.circleci/config.yml](https://github.com/composewell/packcheck/blob/master/.circleci/config.yml) 32 | to your package repo 33 | 34 | #### Appveyor (Windows) 35 | * Add your package repo to Appveyor as necessary (See 36 | https://www.appveyor.com/docs/server/) 37 | * Copy 38 | [appveyor.yml](https://github.com/composewell/packcheck/blob/master/appveyor.yml) 39 | to your package repo 40 | 41 | #### Github Actions 42 | * Add your package repo to Github as necessary (See 43 | https://docs.github.com/en/actions/quickstart) 44 | * Copy 45 | [.github/workflows/packcheck.yml](https://github.com/composewell/packcheck/blob/master/.github/workflows/packcheck.yml) 46 | to your package repo 47 | 48 | #### Cirrus (FreeBSD) 49 | * Add your package repo to Cirrus as necessary (See 50 | https://cirrus-ci.org/) 51 | * Copy 52 | [.cirrus.yml](https://github.com/composewell/packcheck/blob/master/.cirrus.yml) 53 | to your package repo 54 | 55 | CI should work out of the box for most packages. Uncomment the relevant lines 56 | in the CI config files or change the values of the environment variables for 57 | fine grained control or custom configuration. 58 | 59 | ### Build on Local Machine 60 | 61 | You can use packcheck to build or CI test a package on your local machine as 62 | well. For local use, copy 63 | [packcheck.sh](https://github.com/composewell/packcheck/blob/master/packcheck.sh) 64 | to your local machine (Linux/OSX/Windows), put it in your PATH, and run it 65 | from your package directory. You can pass the same environment variables that 66 | are used in CI files to run the exact same tests locally. Usage is as simple 67 | as: 68 | ``` 69 | $ packcheck.sh cabal 70 | $ packcheck.sh cabal GHCUP_VERSION=0.1.20.0 GHCVER=9.8.1 71 | $ packcheck.sh stack GHCVER=9.4 72 | ``` 73 | 74 | `packcheck` can automatically pick the requested version of GHC from: 75 | 76 | * multiple GHC path components in your PATH environment variable 77 | * stack installed ghc binaries 78 | 79 | ### Out of the box support 80 | 81 | | cabal | stack | 82 | |---------------|-----------| 83 | 84 | | Linux | OSX | Windows | FreeBSD | 85 | |---------------|-----------|---------------|---------| 86 | 87 | | Github | Appveyor | CircleCI | Local Machine | 88 | |---------------|-----------|---------------|---------------| 89 | 90 | The script can be easily adapted to any CI with a single line build command. 91 | 92 | ## Key Features 93 | 94 | * _Error messages:_ A lot of emphasis has been put on providing precise and 95 | detailed error messages when something fails so that the user can easily fix 96 | things. 97 | * _Informational:_ The output provides all the information that you may want to 98 | know, tool paths being used, their versions, how they are invoked, build 99 | options, time taken by each build step etc. You can even copy the commands 100 | from the output and paste them on your local host to reproduce the build or 101 | failure and debug quickly. 102 | * _Same tests everywhere:_ You can run exact same tests with same options or 103 | flags, in the same way, on all CI platforms. 104 | * _Choose options:_ Conveniently control all aspects of build through command 105 | line or environment variables, including tool options or whether to enable 106 | benchmarks, haddock, coverage, test etc. 107 | * _Picking GHC:_ Right GHC is picked up automatically from PATH or installed 108 | using ghcup by specifying GHCUP_VERSION and GHCVER env vars. Stack 109 | installed GHC binaries can be picked automatically when available. 110 | * _Test source distribution:_ `packcheck` creates the source distribution and 111 | builds the package from the generated tarball to make sure that you build 112 | what you release and don't miss adding a file to the distribution. Also, 113 | checks if any file in the git repo is missing in the source distribution. 114 | 119 | * _Non-destructive_: By default the script does not change any config or 120 | upgrade any tools on the host machine. 121 | * _Auto tool install_: `stack` and `ghc` can be installed automatically 122 | 123 | ## Introduction 124 | 125 | The package `packcheck` includes a script called `packcheck.sh`, it is a high 126 | level universal super build script to uniformly, consistently build and 127 | comprehensively sanity test a Haskell package across build tools (stack/cabal) 128 | and across all platforms (Linux/MacOS/Windows). You do not need to be familiar 129 | with any of the build tools to use it. 130 | 131 | To make sure that it works everywhere without installing anything it is 132 | deliberately written using the `bash` shell scripting language. Any of the 133 | parameters to control the builds can either be passed on the script command 134 | line or as environment variables for convenient use on CI systems. 135 | 136 | `packcheck` is also a minimal yet complete "hello world" Haskell package with 137 | model config files that can be used unmodified in any Haskell package. The CI 138 | configs can be modified **declaratively**, using environment variables, to adapt 139 | to **any** kind of build scenario you can imagine. 140 | 141 | This model package has everything that a Haskell package usually has; including 142 | tests, benchmarks and Linux/MacOS/Windows CI already working. It can be used as 143 | a starting point to develop a new package. Beginners can use it to learn about 144 | Haskell package metadata structure. 145 | 146 | ## What all does it do? 147 | 148 | An invocation of `packcheck.sh` performs a whole battery of tests, all aspects 149 | can be controlled via environment variables, command line. The flow goes 150 | roughly as follows: 151 | 152 | * Pick or install the requested version of GHC/cabal/stack 153 | * create source distribution package, unpack and test from it 154 | * Check the differences in git repo and source distribution 155 | * perform distribution checks 156 | * build source 157 | * build benchmarks 158 | * build haddock docs 159 | * run tests 160 | * run `hlint` 161 | * generate coverage report 162 | 163 | ## Usage Examples 164 | 165 | You can run these commands on your local machine as well as inside a CI script. 166 | You can try these commands in the `packcheck` package itself: 167 | ``` 168 | $ cd packcheck 169 | $ ./packcheck.sh cabal GHCUP_VERSION=0.1.20.0 GHCVER=9.8.1 170 | ``` 171 | 172 | ``` 173 | $ ./packcheck.sh stack RESOLVER=lts-21 174 | $ ./packcheck.sh stack GHCVER=8.6.5 175 | $ ./packcheck.sh stack RESOLVER=lts-21.24 STACK_YAML=stack-8.0.yaml STACK_BUILD_OPTIONS="--flag streamly:examples-sdl" CABALVER=3.10 176 | # You can also do a cabal build using stack installed ghc: 177 | $ stack exec ./packcheck.sh cabal RESOLVER=lts-21 178 | ``` 179 | 180 | Run hlint commands on the directories `src` and `test`: 181 | ``` 182 | $ ./packcheck.sh hlint HLINT_OPTIONS="lint" HLINT_TARGETS="src test" 183 | ``` 184 | 185 | 192 | 193 | ## Picking GHC versions 194 | 195 | When `GHCVER` parameter is not specified, `packcheck` looks for a binary named 196 | `ghc` in your `PATH` environment variable. It uses first such binary found in 197 | `PATH`. 198 | 199 | When `GHCVER` parameter is specified and is not set to `head`, it looks 200 | for `ghc` in the `PATH` and if `GHCVER` is a PREFIX of the actual 201 | version of `ghc` binary found then that `ghc` binary is used. Otherwise, 202 | `packcheck` tries to look for another `ghc` binary in the next PATH 203 | components until it finds a matching `ghc` version. 204 | 205 | If `GHCVER` is set to `head`, packcheck looks for `ghc-head` as the 206 | compiler and does not check the numeric version of the compiler. 207 | 208 | If `GHCUP_VERSION` is specified packcheck tries to use the existing `ghcup` 209 | to install the ghc, if `ghcup` is not found it installs the requested 210 | version and then installs the `GHCVER` using it. 211 | 212 | 219 | 220 | If all of the above fails `packcheck` looks for ghc in the `stack` install 221 | locations. 222 | 223 | ## packcheck-safe 224 | 225 | `packcheck-safe.sh` is a more robust wrapper over `packcheck.sh`, it does not 226 | trust or use any environment variables, all environment needs to be specified 227 | explicitly on the command line. Therefore, it ensures better reproducibility. 228 | 229 | It also catches any misspelled command line parameter names. For example, 230 | `packcheck.sh` won't catch it if you typed `GHCVWR=9.8` instead of 231 | `GHCVER=9.8`, it just assumes that `GHCVER` is not specified. 232 | `packcheck-safe.sh` would generate an error saying that `GHCVWR` is not 233 | recognized. Since it uses a clean environment you will have to specify PATH as 234 | well on the command line. For example, 235 | 236 | ``` 237 | $ ./packcheck-safe.sh cabal PATH=/bin:/usr/bin:/opt/ghc/bin 238 | ``` 239 | 240 | ## packcheck-remote 241 | 242 | `packcheck-remote.sh` is a wrapper over `packcheck.sh`. It allows you to run 243 | packcheck on a remote repository by cloning it locally and optionally merging a 244 | branch into another branch (e.g. merging a PR branch into master). 245 | 246 | ``` 247 | $ ./packcheck-remote.sh --force \ 248 | --remote=https://github.com/user/repo \ 249 | --checkout=origin/master \ 250 | --merge=origin/branch \ 251 | --directory=./repo.packcheck \ 252 | -- cabal GHCVER=9.8.1 253 | ``` 254 | 255 | Use `./packcheck-remote.sh --help` for more information. 256 | 257 | ## Full Reference 258 | 259 | Please use `cabal` version 2.4 or later. 260 | 261 | NOTE: Any of the parameters described below can either be passed on command 262 | line or as an environment variable. Passing options on command line is more 263 | convenient when running interactively, while environment variables are more 264 | convenient when running on a CI system. 265 | 266 | ``` 267 | $ ./packcheck.sh --help 268 | 269 | -------------------------------------------------- 270 | Usage 271 | -------------------------------------------------- 272 | packcheck.sh COMMAND [PARAMETER=VALUE ...] 273 | 274 | For example: 275 | packcheck.sh cabal GHCVER=9.8.1 276 | packcheck.sh stack RESOLVER=lts GHC_OPTIONS="-O0 -Werror" 277 | packcheck.sh hlint 278 | 279 | Ask questions: https://app.gitter.im/#/room/#composewell_streamly:gitter.im 280 | Report issues: https://github.com/composewell/packcheck/issues/new 281 | 282 | Control parameters can either be passed on command line or exported 283 | as environment variables. Parameters marked DESTRUCTIVE may modify 284 | your global user config or state. 285 | 286 | Boolean parameters can be specified as 287 | y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON for an affirmative value and as 288 | n|N|no|No|NO|false|False|FALSE|off|Off|OFF or empty for a negative value. 289 | 290 | -------------------------------------------------- 291 | Commands and flags 292 | -------------------------------------------------- 293 | cabal : build using cabal 294 | stack : build using stack 295 | hlint : run hlint 296 | clean : remove the .packcheck directory 297 | cleanall : remove .packcheck, .stack-work directories 298 | help | --help | -h : show this help message 299 | --version : show packcheck version 300 | 301 | -------------------------------------------------- 302 | Selecting tool versions 303 | -------------------------------------------------- 304 | GHCUP_VERSION : [a.b.c.d] ghcup version to install at $HOME/.ghcup/bin/ghcup (see https://downloads.haskell.org/~ghcup) 305 | GHCVER : [a.b.c | head] GHC version prefix (may not be enforced when using stack) 306 | CABALVER : [a.b.c.d] Cabal version (prefix) to use 307 | STACKVER : [a.b.c.d] Stack version (prefix) to use 308 | STACK_UPGRADE : [y] DESTRUCTIVE! Upgrades stack to latest version 309 | RESOLVER : Stack resolver to use for stack builds or cabal builds using stack 310 | HLINT_VERSION : hlint version to install at $HOME/.local/bin/hlint (see https://github.com/ndmitchell/hlint/releases) 311 | DOCSPEC_URL : cabal-docspec release URL to install at $HOME/.local/bin/cabal-docspec (see https://github.com/phadej/cabal-extras/releases/) 312 | 313 | -------------------------------------------------- 314 | Where to find the required tools 315 | -------------------------------------------------- 316 | PATH : [path] Set PATH explicitly for predictable builds 317 | 318 | -------------------------------------------------- 319 | Specifying common tool options 320 | -------------------------------------------------- 321 | GHCUP_GHC_OPTIONS : Used as in "ghcup install ghc " 322 | GHC_OPTIONS : Specify GHC options to use 323 | SDIST_OPTIONS : Arguments to stack/cabal sdist command 324 | 325 | -------------------------------------------------- 326 | Specifying what to build 327 | -------------------------------------------------- 328 | DISABLE_BENCH : [y] Do not build benchmarks, default is to build but not run 329 | DISABLE_TEST : [y] Do not run tests, default is to run tests 330 | DISABLE_DOCS : [y] Do not build haddocks, default is to build docs 331 | ENABLE_DOCSPEC : [y] Run cabal-docspec after the cabal build 332 | DISABLE_SDIST_BUILD : [y] Do not build from source distribution 333 | DISABLE_SDIST_PROJECT_CHECK: [y] Ignore project file and continue 334 | DISABLE_SDIST_GIT_CHECK : [y] Do not compare source distribution with git repo 335 | DISABLE_DIST_CHECKS : [y] Do not perform source distribution checks 336 | 337 | -------------------------------------------------- 338 | cabal options 339 | -------------------------------------------------- 340 | CABAL_REINIT_CONFIG : [y] DESTRUCTIVE! Remove old config to avoid incompatibility issues 341 | CABAL_PROJECT : Alternative cabal project file, path relative to project root 342 | CABAL_BUILD_OPTIONS : ADDITIONAL cabal build options to append to defaults 343 | CABAL_TEST_OPTIONS : ADDITIONAL cabal test options to append to defaults 344 | CABAL_DISABLE_DEPS : [y] Do not install dependencies, do not do cabal update 345 | CABAL_BUILD_TARGETS : cabal build targets, default is 'all' 346 | CABAL_CHECK_RELAX : [y] Do not fail if cabal check fails on the package. 347 | CABAL_HACKAGE_MIRROR : DESTRUCTIVE! Specify an alternative mirror, modifies the cabal config file. 348 | 349 | -------------------------------------------------- 350 | stack options 351 | -------------------------------------------------- 352 | STACK_YAML : Alternative stack config file path relative to project root 353 | STACK_OPTIONS : ADDITIONAL stack global options (e.g. -v) to append 354 | STACK_BUILD_OPTIONS : ADDITIONAL stack build command options to append 355 | 356 | -------------------------------------------------- 357 | hlint options 358 | -------------------------------------------------- 359 | HLINT_OPTIONS : hlint arguments e.g.'--datadir=. lint' 360 | HLINT_TARGETS : target directories to run hlint on e.g. 'src test' 361 | 362 | -------------------------------------------------- 363 | Coverage options 364 | -------------------------------------------------- 365 | COVERAGE : [y] Just generate coverage information 366 | 367 | -------------------------------------------------- 368 | Diagnostics options 369 | -------------------------------------------------- 370 | CHECK_ENV : [y] Treat unknown env variables as error, used with env -i 371 | BASE_TIME : System time to be used as base for timeline reporting 372 | ``` 373 | 374 | Build fails if `DISABLE_SDIST_BUILD` is not set and the contents 375 | of the source distribution tar ball do not match the git repository 376 | contents. Either add any exceptions to `.packcheck.ignore` file or use 377 | `DISABLE_SDIST_GIT_CHECK=y` to disable this feature. Currently this check is 378 | done only if `git` and `tar` commands are available in the `PATH`. 379 | 380 | Options marked `DESTRUCTIVE!` are fine in a CI environment. But on a 381 | local machine sometimes it may not be desirable as it will change the 382 | state of your global cabal config, so consider that before using these options. 383 | 384 | By default cabal builds are done using sandboxes. It creates any temporary 385 | files or build artifacts inside `.packcheck` directory. See the `clean` and 386 | `cleanall` commands to release the temporary space. 387 | 388 | `stack` is automatically installed and can be used to do cabal builds as well. 389 | If you specify `BUILD=cabal` and `RESOLVER` at the same time then the cabal 390 | build uses stack installed `cabal` and `ghc`, both are installed automatically 391 | when needed. 392 | 393 | For pure cabal builds i.e. when `BUILD=cabal` and `RESOLVER` is not 394 | specified, `cabal` and `ghc` must be pre-installed on the system before 395 | building. 396 | 397 | 414 | 415 | ## Diagnostics 416 | 417 | Sometimes you may run into issues due to some environment variables unknowingly 418 | set or some command line parameters or env variables being misspelled and 419 | therefore silently ignored. To avoid any such issues the robust way to invoke 420 | `packcheck` is to use a clean environment using `env -i` and passing 421 | `CHECK_ENV=y` parameter. When this parameter is set unwanted/misspelled 422 | variables are detected and reported. 423 | 424 | ``` 425 | $ env -i CHECK_ENV=y ./packcheck.sh stack 426 | ``` 427 | 428 | For performance diagnostics `packcheck` prints the time elapsed from the 429 | beginning at each build step performed. 430 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # packcheck-0.7.1 2 | # You can use any of the options supported by packcheck as environment 3 | # variables here. See https://github.com/composewell/packcheck for all 4 | # options and their explanation. 5 | 6 | environment: 7 | # ------------------------------------------------------------------------ 8 | # Global options, you can use these per build as well 9 | # ------------------------------------------------------------------------ 10 | global: 11 | # ------------------------------------------------------------------------ 12 | # Common options 13 | # ------------------------------------------------------------------------ 14 | # GHC_OPTIONS: "-Werror" 15 | CABAL_REINIT_CONFIG: "y" 16 | LC_ALL: "C.UTF-8" 17 | 18 | # ------------------------------------------------------------------------ 19 | # What to build 20 | # ------------------------------------------------------------------------ 21 | # DISABLE_TEST: "y" 22 | # DISABLE_BENCH: "y" 23 | # DISABLE_DOCS: "y" 24 | # DISABLE_SDIST_BUILD: "y" 25 | # This requires the "diff" utility, disabling for now. 26 | DISABLE_SDIST_GIT_CHECK: "y" 27 | # DISABLE_DIST_CHECKS: "y" 28 | DISABLE_SDIST_PROJECT_CHECK: "y" 29 | 30 | # ------------------------------------------------------------------------ 31 | # stack options 32 | # ------------------------------------------------------------------------ 33 | # Note requiring a specific version of stack using STACKVER may fail due to 34 | # github API limit while checking and upgrading/downgrading to the specific 35 | # version. 36 | #STACKVER: "1.6.5" 37 | STACK_UPGRADE: "y" 38 | # RESOLVER: "lts-15" 39 | STACK_ROOT: "c:\\sr" 40 | STACK_YAML: "stack.yaml" 41 | 42 | # ------------------------------------------------------------------------ 43 | # cabal options 44 | # ------------------------------------------------------------------------ 45 | CABAL_CHECK_RELAX: "y" 46 | CABAL_HACKAGE_MIRROR: "hackage.haskell.org:http://hackage.fpcomplete.com" 47 | CABAL_PROJECT: "cabal.project" 48 | 49 | # ------------------------------------------------------------------------ 50 | # Where to find the required tools 51 | # ------------------------------------------------------------------------ 52 | PATH: "%PATH%;%APPDATA%\\local\\bin" 53 | LOCAL_BIN: "%APPDATA%\\local\\bin" 54 | 55 | # ------------------------------------------------------------------------ 56 | # Location of packcheck.sh (the shell script invoked to perform CI tests ). 57 | # ------------------------------------------------------------------------ 58 | # You can either commit the packcheck.sh script at this path in your repo or 59 | # you can use it by specifying the PACKCHECK_REPO_URL option below in which 60 | # case it will be automatically copied from the packcheck repo to this path 61 | # during CI tests. In any case it is finally invoked from this path. 62 | PACKCHECK_LOCAL_PATH: "./packcheck.sh" 63 | # If you have not committed packcheck.sh in your repo at PACKCHECK_LOCAL_PATH 64 | # then it is automatically pulled from this URL. 65 | PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck" 66 | PACKCHECK_GITHUB_COMMIT: "03f9e753567d08835e342c8d24a406d5a1e1582e" 67 | 68 | # Override the temp directory to avoid sed escaping issues 69 | # See https://github.com/haskell/cabal/issues/5386 70 | TMP: "c:\\tmp" 71 | 72 | cache: 73 | - "%STACK_ROOT%" 74 | - "%LOCAL_BIN%" 75 | - "%APPDATA%\\cabal" 76 | - "%APPDATA%\\ghc" 77 | # - "%LOCALAPPDATA%\\Programs\\stack" 78 | 79 | clone_folder: "c:\\pkg" 80 | build: off 81 | 82 | before_test: 83 | - if not exist %PACKCHECK_LOCAL_PATH% curl -sSkL -o%PACKCHECK_LOCAL_PATH% %PACKCHECK_GITHUB_URL%/%PACKCHECK_GITHUB_COMMIT%/packcheck.sh 84 | - if not exist %LOCAL_BIN% mkdir %LOCAL_BIN% 85 | - where stack.exe || curl -sSkL -ostack.zip http://www.stackage.org/stack/windows-x86_64 && 7z x stack.zip stack.exe && move stack.exe %LOCAL_BIN% 86 | - if defined STACKVER (stack upgrade --binary-only --binary-version %STACKVER%) else (stack upgrade --binary-only || ver > nul) 87 | - stack --version 88 | 89 | test_script: 90 | - stack setup > nul 91 | - for /f "usebackq tokens=*" %%i in (`where 7z.exe`) do set PATH7Z=%%i\.. 92 | - for /f "usebackq tokens=*" %%i in (`where git.exe`) do set PATHGIT=%%i\.. 93 | - chcp 65001 && stack exec bash -- -c "chmod +x %PACKCHECK_LOCAL_PATH%; %PACKCHECK_LOCAL_PATH% stack PATH=/usr/bin:\"%PATH7Z%\":\"%PATHGIT%\"" 94 | -------------------------------------------------------------------------------- /bench/Main.hs: -------------------------------------------------------------------------------- 1 | import Hello 2 | 3 | main = hello 4 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: . 2 | 3 | package packcheck 4 | ghc-options: -Werror 5 | -------------------------------------------------------------------------------- /cabal.project.coveralls: -------------------------------------------------------------------------------- 1 | packages: . 2 | 3 | source-repository-package 4 | type: git 5 | location: https://github.com/composewell/hpc-coveralls 6 | tag: d9e20179579f0638f6e978816355d18568e6a1f0 7 | -------------------------------------------------------------------------------- /cabal.project.ghc-head: -------------------------------------------------------------------------------- 1 | -- See head.hackage: 2 | -- info at https://ghc.gitlab.haskell.org/head.hackage/ 3 | -- source at https://gitlab.haskell.org/ghc/head.hackage/ 4 | 5 | packages: packcheck.cabal 6 | 7 | repository head.hackage.ghc.haskell.org 8 | url: https://ghc.gitlab.haskell.org/head.hackage/ 9 | secure: True 10 | key-threshold: 3 11 | root-keys: 12 | f76d08be13e9a61a377a85e2fb63f4c5435d40f8feb3e12eb05905edb8cdea89 13 | 26021a13b401500c8eb2761ca95c61f2d625bfef951b939a8124ed12ecf07329 14 | 7541f32a4ccca4f97aea3b22f5e593ba2c0267546016b992dfadcd2fe944e55d 15 | 16 | active-repositories: hackage.haskell.org, head.hackage.ghc.haskell.org:override 17 | 18 | allow-newer: all 19 | -------------------------------------------------------------------------------- /packcheck-remote.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PACKCHECK_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 4 | PACKCHECK_EXE="$PACKCHECK_DIR/packcheck.sh" 5 | PWD="$(pwd)" 6 | DEFAULT_DIRECTORY_NAME="packcheck-remote-work" 7 | DEFAULT_DIRECTORY="$PWD/$DEFAULT_DIRECTORY_NAME" 8 | 9 | # $1: msg 10 | show_step() { 11 | echo 12 | echo "--------------------------------------------------" 13 | echo "$1" 14 | echo "--------------------------------------------------" 15 | } 16 | 17 | which_cmd() { 18 | hash -r && type -P "$1" || true 19 | } 20 | 21 | # $1: executable (eg. git) 22 | require_cmd () { 23 | local cmd_path 24 | cmd_path=$(which_cmd "$1") 25 | if test -z "$cmd_path" 26 | then 27 | echo "Required command [$1] not found in PATH [$PATH]." 28 | exit 1 29 | else 30 | echo "Using [$1] at [$cmd_path]" 31 | fi 32 | } 33 | 34 | # $1: command 35 | function run_verbose() { 36 | echo "$1" 37 | bash -c "$1" 38 | } 39 | 40 | # $1: Remote repository 41 | # $2: Revision to checkout 42 | # $3: Revision to merge into $2 43 | # $4: Directory to clone into 44 | # $5: Force mode 45 | # $6: Packcheck cli options 46 | try_git_clone_and_merge() { 47 | 48 | # Check for prerequisites 49 | require_cmd git 50 | 51 | local remote="$1" 52 | local rev="$2" 53 | local merge="$3" 54 | local dir="$4" 55 | local force_mode="$5" 56 | local packcheck_cli_opts="$6" 57 | 58 | if test -z "$dir" 59 | then 60 | dir="$DEFAULT_DIRECTORY" 61 | echo "No directory specified to clone to." 62 | echo "Using $dir" 63 | fi 64 | 65 | if test -z "$remote" 66 | then 67 | echo "No remote repository provided." 68 | echo "Skipping cloning." 69 | return 70 | fi 71 | 72 | if test -z "$rev" 73 | then 74 | echo "No revision given." 75 | echo "Defaulting to 'origin/master'." 76 | rev="origin/master" 77 | fi 78 | 79 | if test -d "$dir" 80 | then 81 | echo "$dir already exists" 82 | 83 | if [ "$force_mode" == "true" ]; 84 | then 85 | echo "Forcing the deletion of the directory." 86 | echo "Removing $dir" 87 | rm -rf "$dir" || exit 1 88 | else 89 | echo "Set the script to force mode to force the deletion." 90 | return 91 | fi 92 | fi 93 | 94 | mkdir -p "$dir" || exit 1 95 | 96 | run_verbose "git clone $remote $dir" || exit 1 97 | 98 | cd "$dir" || exit 1 99 | run_verbose "git branch $rev" || exit 1 100 | 101 | if test -n "$merge" 102 | then 103 | # This will fail if there are any merge conflicts 104 | run_verbose "git merge -X theirs $merge --no-edit --commit" || exit 1 105 | fi 106 | 107 | show_step "Running packcheck" 108 | # Run packcheck here 109 | run_verbose "$PACKCHECK_EXE $packcheck_cli_opts" 110 | } 111 | 112 | # Arguments for the command line 113 | FORCE="false" 114 | REMOTE="" 115 | CHECKOUT="" 116 | MERGE="" 117 | DIRECTORY="" 118 | PACKCHECK_CLI_OPTS_ARR=() 119 | PACKCHECK_CLI_OPTS="" 120 | 121 | function run_help() { 122 | local script 123 | script=$(basename "$0") 124 | 125 | echo 126 | echo "USAGE: $script --remote=url" 127 | echo " [--force]" 128 | echo " [--checkout=base_branch] [--merge=merge_branch]" 129 | echo " [--directory=path]" 130 | echo " -- [...packcheck_arguments]" 131 | echo 132 | echo "-f (or) --force: Puts the script in force mode. In this mode, the \ 133 | script forcefully replaces existing work created by packcheck and friends." 134 | echo "-h (or) --help: Print help." 135 | echo "--remote: Repository to clone." 136 | echo "--checkout: Revision to checkout. Defaults to 'origin/master'." 137 | echo "--merge: Revision to merge in the checked out branch." 138 | echo "--directory: Directory to clone the repository into." 139 | echo " Defaults to '$DEFAULT_DIRECTORY_NAME' under the present working directory." 140 | echo 141 | echo "All the arguments after '--' are passed to packcheck" 142 | echo 143 | 144 | echo 145 | echo "EXAMPLE:" 146 | echo 147 | echo "$script --force \\" 148 | echo " --remote=https://github.com/user/repo \\" 149 | echo " --checkout=origin/master \\" 150 | echo " --merge=origin/branch \\" 151 | echo " --directory=./repo.packcheck \\" 152 | echo " -- cabal-v2 GHCVER=8.8.3" 153 | echo 154 | } 155 | 156 | # Program entry point 157 | for i in "$@" 158 | do 159 | case $i in 160 | -f|--force) 161 | FORCE="true" 162 | shift 163 | ;; 164 | -h|--help) 165 | run_help 166 | exit 0 167 | ;; 168 | --remote=*) 169 | REMOTE="${i#*=}" 170 | shift 171 | ;; 172 | --checkout=*) 173 | CHECKOUT="${i#*=}" 174 | shift 175 | ;; 176 | --merge=*) 177 | MERGE="${i#*=}" 178 | shift 179 | ;; 180 | --directory=*) 181 | DIRECTORY="${i#*=}" 182 | shift 183 | ;; 184 | --) 185 | shift 186 | PACKCHECK_CLI_OPTS_ARR=("$@") 187 | break 188 | ;; 189 | *) 190 | echo "Unknown argument to packcheck-remote" 191 | run_help 192 | exit 1 193 | ;; 194 | esac 195 | done 196 | 197 | if test -z "$REMOTE" 198 | then 199 | run_help 200 | exit 1 201 | fi 202 | 203 | for i in "${PACKCHECK_CLI_OPTS_ARR[@]}" 204 | do 205 | case $i in 206 | *=*) 207 | key=${1%%=*} 208 | val=${1#*=} 209 | PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $key=\"$val\"" 210 | shift 211 | ;; 212 | *) 213 | PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $i" 214 | shift 215 | ;; 216 | esac 217 | done 218 | 219 | try_git_clone_and_merge "$REMOTE" "$CHECKOUT" "$MERGE" "$DIRECTORY" "$FORCE" "$PACKCHECK_CLI_OPTS" 220 | -------------------------------------------------------------------------------- /packcheck-safe.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PACKCHECK_DIR=$(dirname "$0") 4 | echo "Running ${PACKCHECK_DIR}/packcheck.sh with clean environment and CHECK_ENV on..." 5 | echo "No environment variables are honored, you have to specifiy ALL the" 6 | echo "parameters explicitly on the command line, including PATH." 7 | echo 8 | 9 | PACKCHECK_CLI_OPTS_ARR=("$@") 10 | PACKCHECK_CLI_OPTS="" 11 | 12 | for i in "${PACKCHECK_CLI_OPTS_ARR[@]}" 13 | do 14 | case $i in 15 | *=*) 16 | key=${1%%=*} 17 | val=${1#*=} 18 | PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $key=\"$val\"" 19 | shift 20 | ;; 21 | *) 22 | PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $i" 23 | shift 24 | ;; 25 | esac 26 | done 27 | 28 | eval "/usr/bin/env -i CHECK_ENV=y $PACKCHECK_DIR/packcheck.sh $PACKCHECK_CLI_OPTS" 29 | -------------------------------------------------------------------------------- /packcheck.cabal: -------------------------------------------------------------------------------- 1 | name: packcheck 2 | version: 0.7.1 3 | synopsis: Universal build and CI testing for Haskell packages 4 | description: 5 | This package contains a universal CI/build script @packcheck.sh@ and config 6 | files designed such that you can just copy over 7 | @.github\/workflows/packcheck.yml@, @appveyor.yml@, @.circleci/config.yml@ 8 | or @.cirrus.yml@ to your package repo and your package is CI ready 9 | in a jiffy. You can build and test packages on local machine as 10 | well. For local testing, copy @packcheck.sh@ to your local machine, 11 | put it in your PATH, and run it from your package directory: 12 | . 13 | > $ packcheck.sh cabal 14 | > $ packcheck.sh stack 15 | . 16 | You can try the script on this package itself. It builds and comprehensively 17 | sanity tests a Haskell package across build tools (stack/cabal), uniformly, 18 | consistently and across all platforms (Linux\/MacOS\/Windows). You do not 19 | need to be familiar with any of the build tools to use it. 20 | . 21 | This is also a minimal yet complete model package (with tests, benchmarks, CI 22 | already working) that can be used as a starting point to develop a new 23 | package. Beginners can use it to learn about haskell package metadata 24 | structure, benchmarks, tests, CI configs etc. 25 | . 26 | See the README for comprehensive documentation. 27 | 28 | homepage: https://github.com/composewell/packcheck 29 | bug-reports: https://github.com/composewell/packcheck/issues 30 | license: BSD3 31 | license-file: LICENSE 32 | tested-with: GHC==9.12.1 33 | , GHC==9.10.1 34 | , GHC==9.8.4 35 | , GHC==9.6.3 36 | , GHC==9.4.8 37 | , GHC==9.2.8 38 | , GHC==9.0.1 39 | , GHC==8.10.7 40 | author: Harendra Kumar 41 | maintainer: harendra.kumar@gmail.com 42 | copyright: 2017 Harendra Kumar 43 | category: Testing, CI 44 | stability: Experimental 45 | build-type: Simple 46 | cabal-version: >= 1.10 47 | 48 | extra-source-files: 49 | Changelog.md 50 | README.md 51 | stack.yaml 52 | .ci/stack-8.0.yaml 53 | .circleci/config.yml 54 | .github/workflows/packcheck.yml 55 | .cirrus.yml 56 | appveyor.yml 57 | cabal.project.coveralls 58 | cabal.project 59 | cabal.project.ghc-head 60 | packcheck.sh 61 | packcheck-safe.sh 62 | packcheck-remote.sh 63 | MAINTAINING.md 64 | 65 | source-repository head 66 | type: git 67 | location: https://github.com/composewell/packcheck 68 | 69 | flag dev 70 | description: Development build 71 | manual: True 72 | default: False 73 | 74 | library 75 | hs-source-dirs: src 76 | exposed-modules: Hello 77 | default-language: Haskell2010 78 | build-depends: base >= 4.8 && < 5 79 | 80 | test-suite test 81 | type: exitcode-stdio-1.0 82 | hs-source-dirs: test 83 | main-is: Main.hs 84 | build-depends: 85 | packcheck 86 | , base >= 4.8 && < 5 87 | default-language: Haskell2010 88 | if flag(dev) 89 | ghc-options: -Wall 90 | 91 | benchmark bench 92 | type: exitcode-stdio-1.0 93 | hs-source-dirs: bench 94 | main-is: Main.hs 95 | build-depends: 96 | packcheck 97 | , base >= 4.8 && < 5 98 | default-language: Haskell2010 99 | -------------------------------------------------------------------------------- /packcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #------------------------------------------------------------------------------ 4 | # Skip to the end for main flow of script 5 | #------------------------------------------------------------------------------ 6 | 7 | # See the Readme.md file for details on how it works and for the user guide. 8 | 9 | #------------------------------------------------------------------------------ 10 | # TODO 11 | #------------------------------------------------------------------------------ 12 | 13 | # FORCE_TOOL_INSTALL: an option to not use cached installs, force fresh 14 | # installs of tools What if we are using a tool from system path - 15 | # invert path? 16 | # NO_TOOL_INSTALL: do not download/install any tools 17 | # cabal build can use stack installed ghc if GHCVER is not specified 18 | 19 | #------------------------------------------------------------------------------ 20 | # Utility functions 21 | #------------------------------------------------------------------------------ 22 | 23 | PACKCHECK_VERSION=0.7.1 24 | 25 | show_version() { 26 | echo "packcheck version $PACKCHECK_VERSION" 27 | } 28 | 29 | # $1: varname 30 | show_var() { 31 | echo "$1=$(eval \"echo \$$1\")" 32 | } 33 | 34 | # $1: varname 35 | show_nonempty_var() { 36 | local var=$(eval "echo \$$1") 37 | if test -n "$var" 38 | then 39 | printf "%q\n" "$1=$var" 40 | fi 41 | } 42 | 43 | # $1: var name 44 | # $2: default value 45 | init_default() { 46 | local var=$(eval "echo \$$1") 47 | test -n "$var" || eval "export $1=\"$2\"" 48 | } 49 | 50 | # Rewrite deprecated environment variables 51 | rewrite_deprecated() { 52 | local oldvar=$1 53 | local oldval=$(eval "echo \$$1") 54 | local newvar=$2 55 | 56 | if test -n "$oldval" 57 | then 58 | echo "DEPRECATED [$oldvar] please use [$newvar] instead" 59 | eval "export $newvar=$oldval" 60 | unset $oldvar 61 | fi 62 | } 63 | 64 | require_file () { 65 | if test ! -f "$1" 66 | then 67 | echo "Required file [$1] does not exist." 68 | exit 1 69 | fi 70 | } 71 | 72 | # $1: message 73 | die () { 74 | >&2 echo -e "Error: $1" 75 | exit 1 76 | } 77 | 78 | retry_cmd() { 79 | cmd=$* 80 | $cmd || (sleep 2 && $cmd) || (sleep 10 && $cmd) 81 | } 82 | 83 | # MINGW 'which' does not seem to work when there are spaces in the 84 | # PATH. Note that "type" returns a cached path, so if something got 85 | # deleted we might still be returning a stale value (we can use hash -r 86 | # to clear the cache if needed). 87 | 88 | which_cmd() { 89 | hash -r && type -P "$1" || true 90 | } 91 | 92 | require_cmd () { 93 | if test -z "$(which_cmd $1)" 94 | then 95 | echo "Required command [$1] not found in PATH [$PATH]." 96 | exit 1 97 | else 98 | echo "Using [$1] at [$(which_cmd $1)]" 99 | fi 100 | } 101 | 102 | # $1: command 103 | function run_verbose() { 104 | # XXX redirecting to stderr leads to misaligned output because stdout and 105 | # stdin are printed at different times. 106 | echo "$*" 1>&2 107 | bash -c "$*" 108 | } 109 | 110 | function run_verbose_errexit_with() { 111 | local errScript="$1"; 112 | shift 113 | if ! run_verbose "$*"; 114 | then 115 | eval "$errScript" 116 | die "Command [$*] failed. Exiting." 117 | fi 118 | } 119 | 120 | function run_verbose_errexit() { 121 | run_verbose "$*" || die "Command [$*] failed. Exiting." 122 | } 123 | 124 | # $1: msg 125 | show_step1() { 126 | echo 127 | echo "--------------------------------------------------" 128 | echo "$1" 129 | echo "--------------------------------------------------" 130 | } 131 | 132 | # $1: msg 133 | show_step() { 134 | local reltime 135 | local disptime 136 | reltime=$(get_rel_time) 137 | if test -n "$reltime" 138 | then 139 | disptime="[$reltime sec]" 140 | fi 141 | show_step1 "$disptime $1" 142 | } 143 | 144 | # $1: file to ungztar 145 | win_ungztar() { 146 | local output=$(basename ${1%.gz}) 147 | run_verbose_errexit 7z e -y $1 && run_verbose_errexit 7z x -y $output 148 | run_verbose_errexit rm -f $output 149 | } 150 | 151 | set_os_specific_vars() { 152 | local os=$(uname) 153 | case "$os" in 154 | Darwin|Linux|FreeBSD) 155 | OS_HAS_TOOLS=tar 156 | OS_UNGZTAR_CMD="run_verbose_errexit tar xmzvf" 157 | OS_LOCAL_DIR=.local 158 | OS_CABAL_DIR=.cabal 159 | OS_APP_HOME=$HOME ;; 160 | MINGW*) 161 | require_cmd cygpath 162 | require_envvar APPDATA 163 | OS_HAS_TOOLS=cygpath 164 | OS_UNGZTAR_CMD=win_ungztar 165 | OS_LOCAL_DIR=local 166 | OS_CABAL_DIR=cabal 167 | OS_APP_HOME=`cygpath $APPDATA` ;; 168 | *) die "Unknown OS [$os]" ;; 169 | esac 170 | } 171 | 172 | show_machine_info() { 173 | local os=$(uname) 174 | case "$os" in 175 | Linux) 176 | #echo "OS: $os" 177 | run_verbose lsb_release -a || true 178 | echo 179 | run_verbose uname -a || true 180 | 181 | show_step "CPU" 182 | echo "lscpu" 183 | lscpu | grep "^Archi\|^CPU\|^Bogo\|^Hyper\|^Virtualiz" 184 | 185 | show_step "Memory" 186 | run_verbose free -h || true 187 | 188 | show_step "Container/cgroup information" 189 | # See https://stackoverflow.com/questions/20010199/determining-if-a-process-runs-inside-lxc-docker 190 | # For comprehensive detection see container-detect.conf in ubuntu 191 | #if test -f /.dockerenv 192 | #then 193 | # echo "Running inside Docker (found /.dockerenv)"; 194 | #fi 195 | #run_verbose head -n 1 /proc/1/cgroup 196 | sudo -n cat /proc/1/environ | tr '\0' '\n' | grep "^container=" || true 197 | run_verbose cat /sys/fs/cgroup/cpu/cpu.cfs_period_us || true 198 | run_verbose cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us || true 199 | run_verbose cat /sys/fs/cgroup/memory/memory.limit_in_bytes || true 200 | 201 | show_step "Filesystems" 202 | run_verbose mount || true 203 | 204 | show_step "Disk Usage" 205 | run_verbose df -T || true ;; 206 | 207 | FreeBSD) 208 | echo "OS: FreeBSD" 209 | 210 | show_step "Filesystems" 211 | run_verbose mount || true 212 | 213 | show_step "Disk Usage" 214 | run_verbose df || true ;; 215 | 216 | Darwin) 217 | echo "OS: MacOS" 218 | 219 | show_step "Filesystems" 220 | run_verbose mount || true 221 | 222 | show_step "Disk Usage" 223 | run_verbose df -Y || true ;; 224 | 225 | MINGW*) 226 | echo "OS: Windows (MINGW)" ;; 227 | *) die "OS: Unknown OS [$os]" ;; 228 | esac 229 | } 230 | 231 | #------------------------------------------------------------------------------ 232 | # Build config show and determine 233 | #------------------------------------------------------------------------------ 234 | 235 | SAFE_ENVVARS="\ 236 | RESOLVER \ 237 | ENABLE_GHCJS \ 238 | GHCUP_VERSION \ 239 | GHCVER \ 240 | CABALVER \ 241 | GHC_OPTIONS \ 242 | GHCUP_GHC_OPTIONS \ 243 | SDIST_OPTIONS \ 244 | DISABLE_SDIST_BUILD \ 245 | DISABLE_SDIST_PROJECT_CHECK \ 246 | DISABLE_SDIST_GIT_CHECK \ 247 | DISABLE_BENCH \ 248 | DISABLE_TEST \ 249 | DISABLE_DOCS \ 250 | ENABLE_DOCSPEC \ 251 | DISABLE_DIST_CHECKS \ 252 | PATH \ 253 | TOOLS_DIR \ 254 | STACKVER \ 255 | STACK_YAML \ 256 | STACK_OPTIONS \ 257 | STACK_BUILD_OPTIONS \ 258 | CABAL_PROJECT \ 259 | CABAL_CHECK_RELAX \ 260 | CABAL_USE_STACK_SDIST \ 261 | CABAL_BUILD_OPTIONS \ 262 | CABAL_TEST_OPTIONS \ 263 | CABAL_DISABLE_DEPS \ 264 | CABAL_BUILD_TARGETS \ 265 | COVERAGE \ 266 | COVERALLS_OPTIONS \ 267 | HLINT_VERSION \ 268 | HLINT_BUILD \ 269 | HLINT_COMMANDS \ 270 | HLINT_OPTIONS \ 271 | HLINT_TARGETS \ 272 | DOCSPEC_URL \ 273 | DOCSPEC_OPTIONS \ 274 | CHECK_ENV \ 275 | LANG \ 276 | LC_ALL \ 277 | BASE_TIME \ 278 | " 279 | 280 | UNSAFE_ENVVARS="\ 281 | ENABLE_INSTALL \ 282 | STACK_UPGRADE \ 283 | CABAL_REINIT_CONFIG \ 284 | CABAL_HACKAGE_MIRROR \ 285 | " 286 | 287 | ENVVARS="$SAFE_ENVVARS $UNSAFE_ENVVARS" 288 | 289 | # $1: varname 290 | # $2: list of vars to find in 291 | find_var() { 292 | for v in $2 293 | do 294 | test $v != "$1" || return 0 295 | done 296 | return 1 297 | } 298 | 299 | error_novar() { 300 | find_var "$1" "$ENVVARS" || die "Unknown parameter or environment variable [$1]\nTry --help for supported parameters" 301 | } 302 | 303 | error_clean_env() { 304 | echo "Error: environment variable [$1] is set." 305 | echo "Error: No environment variables except [$ALLOW_ENVVARS] are allowed to be set when using CHECK_ENV=y" 306 | die "Please use a clean environment (e.g. env -i) with CHECK_ENV." 307 | } 308 | 309 | error_clean_param() { 310 | die "Unknown parameter [$1] specified on command line.\nTry --help for supported parameters" 311 | } 312 | 313 | ALLOW_ENVVARS="CHECK_ENV STACK_ROOT APPDATA PWD SHLVL _" 314 | 315 | check_clean_env() { 316 | local vars=$(env | cut -f1 -d=) 317 | for i in $vars 318 | do 319 | find_var $i "$ALLOW_ENVVARS" || error_clean_env "$i" 320 | done 321 | } 322 | 323 | # $1: varname 324 | require_envvar() { 325 | local var=$(eval "echo \$$1") 326 | test -n "$var" || die "Parameter or environment variable [$1] must be set. Try --help for usage." 327 | } 328 | 329 | # $1: envvar name 330 | check_boolean_var() { 331 | error_novar "$1" 332 | local var=$(eval "echo \$$1") 333 | 334 | if test -n "$var" 335 | then 336 | case "$var" in 337 | y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON) export $1=y ;; 338 | n|N|no|No|NO|false|False|FALSE|off|Off|OFF) export $1= ;; 339 | *) die "Boolean parameter or environment variable [$1=$var] can only be set to either an empty value or one of the following boolean values: y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON|n|N|no|No|NO|false|False|FALSE|off|Off|OFF" 340 | esac 341 | fi 342 | } 343 | 344 | # $1: cmdname 345 | # $2: help text 346 | help_cmd() { 347 | printf "%-24s: %s\n" "$1" "$2" 348 | } 349 | 350 | # $1: varname 351 | # $2: help text 352 | help_envvar() { 353 | error_novar $1 354 | printf "%-24s: %s\n" "$1" "$2" 355 | } 356 | 357 | short_help() { 358 | local script=$(basename $0) 359 | echo "$script COMMAND [PARAMETER=VALUE ...]" 360 | echo 361 | echo "For example:" 362 | echo "$script cabal GHCVER=9.8.1" 363 | echo "$script stack RESOLVER=lts GHC_OPTIONS=\"-O0 -Werror\"" 364 | echo "$script hlint" 365 | echo 366 | echo "Ask questions: https://app.gitter.im/#/room/#composewell_streamly:gitter.im" 367 | echo "Report issues: https://github.com/composewell/packcheck/issues/new" 368 | } 369 | 370 | # Please add the boolean options to check_boolean_var as well 371 | show_help() { 372 | show_step1 "Usage" 373 | short_help 374 | 375 | echo 376 | echo "Control parameters can either be passed on command line or exported" 377 | echo "as environment variables. Parameters marked DESTRUCTIVE may modify" 378 | echo "your global user config or state." 379 | echo 380 | echo "Boolean parameters can be specified as " 381 | echo "y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON for an affirmative value and as" 382 | echo "n|N|no|No|NO|false|False|FALSE|off|Off|OFF or empty for a negative value." 383 | 384 | show_step1 "Commands and flags" 385 | #help_cmd cabal-v2 "build using cabal v2-build" 386 | help_cmd cabal "build using cabal" 387 | #help_cmd cabal-new "Deprecated alias to cabal-v2" 388 | #help_cmd cabal-v1 "Deprecated: build using cabal v1-build" 389 | help_cmd stack "build using stack" 390 | help_cmd hlint "run hlint" 391 | # TODO add hlint as a tool 392 | help_cmd clean "remove the .packcheck directory" 393 | help_cmd cleanall "remove .packcheck, .stack-work directories" 394 | help_cmd "help | --help | -h" "show this help message" 395 | help_cmd "--version" "show packcheck version" 396 | 397 | show_step1 "Selecting tool versions" 398 | # untested/unsupported 399 | #help_envvar ENABLE_GHCJS "[y] Use GHCJS instead of GHC to build" 400 | help_envvar GHCUP_VERSION "[a.b.c.d] ghcup version to install at $GHCUP_PATH (see $GHCUP_URL_PREFIX)" 401 | help_envvar GHCVER "[a.b.c | head] GHC version prefix (may not be enforced when using stack)" 402 | help_envvar CABALVER "[a.b.c.d] Cabal version (prefix) to use" 403 | help_envvar STACKVER "[a.b.c.d] Stack version (prefix) to use" 404 | help_envvar STACK_UPGRADE "[y] DESTRUCTIVE! Upgrades stack to latest version" 405 | help_envvar RESOLVER "Stack resolver to use for stack builds or cabal builds using stack" 406 | help_envvar HLINT_VERSION "hlint version to install at $HLINT_PATH (see $HLINT_URL_PREFIX)" 407 | help_envvar DOCSPEC_URL "cabal-docspec release URL to install at $DOCSPEC_PATH (see $DOCSPEC_URL_PREFIX)" 408 | 409 | show_step1 "Where to find the required tools" 410 | help_envvar PATH "[path] Set PATH explicitly for predictable builds" 411 | # Untested/unsupported 412 | #help_envvar TOOLS_DIR "[dir] Find ghc|cabal by version as in TOOLS_DIR/ghc//bin" 413 | 414 | show_step1 "Specifying common tool options" 415 | # TODO 416 | # help_envvar TOOL_OPTIONS "Specify the tool specific (stack or cabal) options to use." 417 | # help_envvar BUILD_OPTIONS "Specify the tool specific build (stack build or cabal new-build) options to use." 418 | help_envvar GHCUP_GHC_OPTIONS "Used as in \"ghcup install ghc \"" 419 | help_envvar GHC_OPTIONS "Specify GHC options to use" 420 | help_envvar SDIST_OPTIONS "Arguments to stack/cabal sdist command" 421 | 422 | show_step1 "Specifying what to build" 423 | help_envvar DISABLE_BENCH "[y] Do not build benchmarks, default is to build but not run" 424 | help_envvar DISABLE_TEST "[y] Do not run tests, default is to run tests" 425 | help_envvar DISABLE_DOCS "[y] Do not build haddocks, default is to build docs" 426 | help_envvar ENABLE_DOCSPEC "[y] Run cabal-docspec after the cabal build" 427 | help_envvar DISABLE_SDIST_BUILD "[y] Do not build from source distribution" 428 | help_envvar DISABLE_SDIST_PROJECT_CHECK "[y] Ignore project file and continue" 429 | help_envvar DISABLE_SDIST_GIT_CHECK "[y] Do not compare source distribution with git repo" 430 | help_envvar DISABLE_DIST_CHECKS "[y] Do not perform source distribution checks" 431 | #help_envvar ENABLE_INSTALL "[y] DESTRUCTIVE! Install the package after building" 432 | 433 | show_step1 "cabal options" 434 | # XXX this applies to both stack and cabal builds 435 | help_envvar CABAL_REINIT_CONFIG "[y] DESTRUCTIVE! Remove old config to avoid incompatibility issues" 436 | help_envvar CABAL_PROJECT "Alternative cabal project file, path relative to project root" 437 | #help_envvar CABAL_USE_STACK_SDIST "[y] Use stack sdist (to use --pvp-bounds)" 438 | help_envvar CABAL_BUILD_OPTIONS "ADDITIONAL cabal build options to append to defaults" 439 | help_envvar CABAL_TEST_OPTIONS "ADDITIONAL cabal test options to append to defaults" 440 | help_envvar CABAL_DISABLE_DEPS "[y] Do not install dependencies, do not do cabal update" 441 | help_envvar CABAL_BUILD_TARGETS "cabal build targets, default is 'all'" 442 | help_envvar CABAL_CHECK_RELAX "[y] Do not fail if cabal check fails on the package." 443 | help_envvar CABAL_HACKAGE_MIRROR "DESTRUCTIVE! Specify an alternative mirror, modifies the cabal config file." 444 | 445 | show_step1 "stack options" 446 | help_envvar STACK_YAML "Alternative stack config file path relative to project root" 447 | help_envvar STACK_OPTIONS "ADDITIONAL stack global options (e.g. -v) to append" 448 | help_envvar STACK_BUILD_OPTIONS "ADDITIONAL stack build command options to append" 449 | 450 | show_step1 "hlint options" 451 | #help_envvar HLINT_COMMANDS "hlint commands e.g.'hlint lint src; hlint lint test'" 452 | # XXX this is broken 453 | #help_envvar HLINT_BUILD "Build latest hlint from hackage source" 454 | help_envvar HLINT_OPTIONS "hlint arguments e.g.'--datadir=. lint'" 455 | help_envvar HLINT_TARGETS "target directories to run hlint on e.g. 'src test'" 456 | 457 | show_step1 "Coverage options" 458 | # Untested/unsupported should be removed 459 | #help_envvar COVERALLS_OPTIONS "hpc-coveralls args and options, usually just test suite names" 460 | help_envvar COVERAGE "[y] Just generate coverage information" 461 | 462 | show_step1 "Diagnostics options" 463 | # To catch spelling mistakes in envvar names passed, otherwise they will be 464 | # silently ignored and we will be wondering why the script is not working. 465 | help_envvar CHECK_ENV "[y] Treat unknown env variables as error, used with env -i" 466 | help_envvar BASE_TIME "System time to be used as base for timeline reporting" 467 | } 468 | 469 | check_all_boolean_vars () { 470 | check_boolean_var STACK_UPGRADE 471 | check_boolean_var DISABLE_SDIST_BUILD 472 | check_boolean_var DISABLE_DIST_CHECKS 473 | check_boolean_var DISABLE_SDIST_PROJECT_CHECK 474 | check_boolean_var DISABLE_SDIST_GIT_CHECK 475 | check_boolean_var CABAL_USE_STACK_SDIST 476 | check_boolean_var CABAL_REINIT_CONFIG 477 | check_boolean_var CABAL_CHECK_RELAX 478 | check_boolean_var CABAL_DISABLE_DEPS 479 | if test -n "$TEST_INSTALL" 480 | then 481 | echo "WARNING! TEST_INSTALL is deprecated. Please use ENABLE_INSTALL instead" 482 | ENABLE_INSTALL="$TEST_INSTALL" 483 | unset TEST_INSTALL 484 | fi 485 | check_boolean_var ENABLE_GHCJS 486 | check_boolean_var ENABLE_INSTALL 487 | if test -n "$ENABLE_INSTALL" 488 | then 489 | echo "WARNING! ENABLE_INSTALL is deprecated and will be removed in future" 490 | echo "WARNING! ENABLE_INSTALL is a no op, it will do nothing" 491 | unset ENABLE_INSTALL 492 | fi 493 | check_boolean_var DISABLE_BENCH 494 | check_boolean_var DISABLE_TEST 495 | check_boolean_var DISABLE_DOCS 496 | check_boolean_var ENABLE_DOCSPEC 497 | check_boolean_var COVERAGE 498 | check_boolean_var CHECK_ENV 499 | } 500 | 501 | show_build_command() { 502 | check_all_boolean_vars 503 | 504 | echo "You can use the following command to reproduce this build:" 505 | echo 506 | echo -n "$0 $BUILD " 507 | for i in $SAFE_ENVVARS 508 | do 509 | local val="$(show_nonempty_var $i)" 510 | test -z "$val" || echo -n "$val " 511 | done 512 | echo 513 | 514 | local unsafe 515 | for i in $UNSAFE_ENVVARS 516 | do 517 | if test -n "$(show_nonempty_var $i)" 518 | then 519 | unsafe=y 520 | fi 521 | done 522 | 523 | if test -n "$unsafe" 524 | then 525 | echo 526 | echo "The above command has omitted the following unsafe options." 527 | echo "If you know what you are doing, you can also add these to the" 528 | echo "above command to reproduce this build more faithfully." 529 | echo 530 | echo "Unsafe options may modify your config and " 531 | echo "are usually meant to be used in a CI setup:" 532 | for i in $UNSAFE_ENVVARS 533 | do 534 | show_nonempty_var $i 535 | done 536 | echo 537 | fi 538 | } 539 | 540 | show_build_config() { 541 | check_all_boolean_vars 542 | for i in $ENVVARS 543 | do 544 | show_nonempty_var $i 545 | done 546 | } 547 | 548 | show_build_env() { 549 | show_nonempty_var HOME 550 | show_nonempty_var APPDATA 551 | show_nonempty_var STACK_ROOT 552 | } 553 | 554 | need_stack() { 555 | if test "$BUILD" = stack -o -n "$RESOLVER" -o -n "$CABAL_USE_STACK_SDIST" 556 | then 557 | echo true 558 | fi 559 | } 560 | 561 | # $1: be verbose about why we need cabal 562 | dont_need_cabal() { 563 | if test "$BUILD" = "cabal-v2" 564 | then 565 | test -z "$1" || echo "Need cabal-install because 'BUILD=$BUILD'" 566 | return 1 567 | fi 568 | 569 | if test -z "$DISABLE_SDIST_BUILD" 570 | then 571 | test -z "$1" || echo "Need cabal-install because 'DISABLE_SDIST_BUILD=$DISABLE_SDIST_BUILD'" 572 | return 1 573 | fi 574 | 575 | return 0 576 | } 577 | 578 | # $1: varname 579 | cabal_only_var() { 580 | error_novar $1 581 | local var=$(eval "echo \$$1") 582 | test -z "$var" || die "[$1] is meaningful only for cabal build" 583 | } 584 | 585 | # $1: varname 586 | stack_only_var() { 587 | error_novar $1 588 | local var=$(eval "echo \$$1") 589 | test -z "$var" || die "[$1] is meaningful only when stack is used" 590 | } 591 | 592 | verify_build_config() { 593 | test -z "$COVERALLS_OPTIONS" || COVERAGE=y 594 | if test -n "$ENABLE_GHCJS" 595 | then 596 | test "$BUILD" = "cabal-v2" || die "ENABLE_GHCJS works only with build type 'cabal-v2'" 597 | COMPILER=ghcjs 598 | GHCJS_FLAG=--ghcjs 599 | else 600 | if test "$GHCVER" = "head" 601 | then 602 | COMPILER=ghc-head 603 | else 604 | COMPILER=ghc 605 | fi 606 | fi 607 | 608 | if test "$BUILD" = stack 609 | then 610 | STACK_DEP_OPTIONS="$STACK_BUILD_OPTIONS --only-dependencies" 611 | test -n "$DISABLE_TEST" || STACK_DEP_OPTIONS="$STACK_DEP_OPTIONS --test" 612 | test -n "$DISABLE_BENCH" || STACK_DEP_OPTIONS="$STACK_DEP_OPTIONS --bench" 613 | 614 | STACK_BUILD_OPTIONS_ORIG=$STACK_BUILD_OPTIONS 615 | STACK_BUILD_OPTIONS=$(cat << EOF 616 | $(test -n "$DISABLE_DOCS" || echo "--haddock --no-haddock-deps") 617 | $(test -n "$DISABLE_TEST" || echo "--test") 618 | $(test -n "$DISABLE_BENCH" || echo "--bench --no-run-benchmarks") 619 | $(test -z "${COVERAGE}" || echo --coverage) 620 | $(test -z "${GHC_OPTIONS}" || echo --ghc-options=\"$GHC_OPTIONS\") 621 | $STACK_BUILD_OPTIONS 622 | EOF 623 | ) 624 | elif test "$BUILD" = "cabal-v2" 625 | then 626 | test -n "$CABAL_BUILD_TARGETS" || CABAL_BUILD_TARGETS=all 627 | 628 | CABAL_BUILD_OPTIONS=$(cat << EOF 629 | $(test -n "$DISABLE_TEST" || echo "--enable-tests") 630 | $(test -n "$DISABLE_BENCH" || echo "--enable-benchmarks") 631 | $(test -z "${GHC_OPTIONS}" || echo --ghc-options=\"$GHC_OPTIONS\") 632 | $CABAL_BUILD_OPTIONS 633 | EOF 634 | ) 635 | CABAL_DEP_OPTIONS="$CABAL_BUILD_OPTIONS --only-dependencies --reorder-goals --max-backjumps=-1" 636 | 637 | test -z "${COVERAGE}" || \ 638 | CABAL_BUILD_OPTIONS="$CABAL_BUILD_OPTIONS --enable-coverage" 639 | else 640 | die "Bug: Unknown build type [$BUILD]" 641 | fi 642 | 643 | # These variables are now combined with other options so clear them 644 | # so that we do not show them in the effective config 645 | COVERAGE= 646 | GHC_OPTIONS= 647 | 648 | test "$BUILD" = stack -o \ 649 | "$BUILD" = "cabal-v2" || \ 650 | die "build [$BUILD] can only be 'stack', or 'cabal-v2'" 651 | 652 | if test -n "$CHECK_ENV" 653 | then 654 | if test "$BUILD" != "cabal-v2" 655 | then 656 | cabal_only_var CABAL_PROJECT 657 | cabal_only_var CABALVER 658 | cabal_only_var CABAL_USE_STACK_SDIST 659 | cabal_only_var CABAL_CHECK_RELAX 660 | cabal_only_var CABAL_HACKAGE_MIRROR 661 | 662 | cabal_only_var CABAL_BUILD_OPTIONS 663 | cabal_only_var CABAL_TEST_OPTIONS 664 | cabal_only_var CABAL_DISABLE_DEPS 665 | cabal_only_var CABAL_BUILD_TARGETS 666 | fi 667 | 668 | if test -z "$(need_stack)" 669 | then 670 | stack_only_var STACK_YAML 671 | stack_only_var STACK_UPGRADE 672 | stack_only_var STACK_OPTIONS 673 | stack_only_var STACK_BUILD_OPTIONS 674 | fi 675 | fi 676 | } 677 | 678 | #------------------------------------------------------------------------------ 679 | # Stack fetch and install etc. 680 | #------------------------------------------------------------------------------ 681 | 682 | #ensure_msys_tools() { 683 | # if [[ `uname` = MINGW* ]] 684 | # then 685 | # # retry?? 686 | # for i in "$1" 687 | # do 688 | # if test -z "$(which_cmd $i)" 689 | # then 690 | # stack exec pacman -- -S --noconfirm $i 691 | # fi 692 | # done 693 | # fi 694 | #} 695 | 696 | fetch_stack_osx() { 697 | curl --fail -sSkL https://www.stackage.org/stack/osx-x86_64 \ 698 | | tar xz --strip-components=1 -C $1 --include '*/stack' 699 | } 700 | 701 | fetch_stack_linux() { 702 | curl --fail -sSkL https://www.stackage.org/stack/linux-x86_64 \ 703 | | tar xz --strip-components=1 -C $1 --wildcards '*/stack' 704 | } 705 | 706 | fetch_stack_windows() { 707 | curl --fail -sSkL http://www.stackage.org/stack/windows-i386 \ 708 | | 7z x -si stack.exe 709 | } 710 | 711 | # $1: directory to place stack executable in 712 | fetch_stack() { 713 | mkdir -p $1 714 | local os=$(uname) 715 | case "$os" in 716 | Darwin) retry_cmd fetch_stack_osx $1 ;; 717 | Linux) retry_cmd fetch_stack_linux $1 ;; 718 | MINGW*) retry_cmd fetch_stack_windows $1 ;; 719 | *) die "Unknown OS [$os]" ;; 720 | esac 721 | } 722 | 723 | # $1: directory to place stack executable in 724 | ensure_stack() { 725 | if test -z "$(which_cmd stack)" 726 | then 727 | echo "Downloading stack to [$1]..." 728 | fetch_stack $1 729 | fi 730 | require_cmd stack 731 | 732 | # Empty STACK_YAML variable does not go well 733 | if test -z "$STACK_YAML" 734 | then 735 | unset STACK_YAML 736 | fi 737 | if test -n "$STACK_UPGRADE" 738 | then 739 | echo "Upgrading stack to the required version" 740 | if test -n "$STACKVER" 741 | then 742 | local curver=$(stack --numeric-version) 743 | if test "${curver#$STACKVER}" = ${curver} 744 | then 745 | run_verbose stack --no-terminal upgrade --binary-only --binary-version $STACKVER 746 | fi 747 | else 748 | run_verbose stack --no-terminal upgrade --binary-only || fetch_stack $1 749 | fi 750 | fi 751 | 752 | test -z "$STACKVER" || check_version_die stack $STACKVER 753 | # Set the real version of stack 754 | STACKVER=$(stack --numeric-version) || exit 1 755 | 756 | STACKCMD="stack --no-terminal $STACK_OPTIONS" 757 | STACKCMD_TOOL_INSTALL="$STACKCMD" 758 | $STACKCMD --version 759 | 760 | if test -n "$RESOLVER" 761 | then 762 | STACKCMD="$STACKCMD --resolver $RESOLVER" 763 | fi 764 | } 765 | 766 | use_stack_paths() { 767 | # Need the bin path (not just compiler-path) on mingw to find gcc 768 | # some packages may have a configure script looking for gcc, so we need to 769 | # use bin path so that on windows we will find the stack installed mingw gcc 770 | run_verbose_errexit $STACKCMD path --bin-path 771 | local BINPATH=`$STACKCMD path --bin-path` 772 | if [[ `uname` = MINGW* ]] 773 | then 774 | # Need for 7z on windows 775 | local GHCPATHS=`$STACKCMD path --programs` 776 | # Convert the path to MINGW format from windows native format 777 | BINPATH=$(cygpath -u -p $BINPATH) 778 | GHCPATHS=$(cygpath -u -p $GHCPATHS) 779 | if test -n "$GHCPATHS" 780 | then 781 | echo "Adding [$GHCPATHS] in front of PATH..." 782 | export PATH=$GHCPATHS:$PATH 783 | fi 784 | fi 785 | if test -n "$BINPATH" 786 | then 787 | echo "Adding [$BINPATH] in front of PATH..." 788 | export PATH=$BINPATH:$PATH 789 | fi 790 | } 791 | 792 | #------------------------------------------------------------------------------ 793 | # Ensure ghc, cabal are available and the right versions when requested 794 | #------------------------------------------------------------------------------ 795 | 796 | # $1: version to compare 797 | # $2: version to compare with 798 | # true (returns 0) if $1 <= $2 799 | verlte () { 800 | [ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ] 801 | } 802 | 803 | # $1: tool name (used only for ghc, cabal and stack) 804 | # $2: expected version 805 | check_version() { 806 | local real_ver=$($1 --numeric-version) 807 | 808 | # Match that the expected version is a prefix of the real version. 809 | # Do not check when the expected version is head. 810 | if test "${real_ver#$2}" != "${real_ver}" -o "$2" = head 811 | then 812 | return 0 813 | else 814 | echo "Found $1 version [$real_ver] expecting [$2]" 815 | return 1 816 | fi 817 | } 818 | 819 | # $1: tool name (used only for ghc, cabal and stack) 820 | # $2: expected version 821 | check_version_die() { 822 | check_version $1 $2 || \ 823 | die "Wrong $1 version [$($1 --numeric-version)] expected [$2]" 824 | } 825 | 826 | # Remove a path from the PATH envvar 827 | # $1: path component to remove 828 | function path_remove { 829 | # Delete path by parts so we can never accidentally remove sub paths 830 | PATH=${PATH//":$1:"/":"} # delete any instances in the middle 831 | PATH=${PATH/#"$1:"/} # delete any instance at the beginning 832 | PATH=${PATH/%":$1"/} # delete any instance at the end 833 | } 834 | 835 | # Find ghc/cabal having the given version prefix in PATH or in a version 836 | # suffixed directory at TOOLS_DIR, and check if it matches the requested 837 | # version. If we found the requested binary we make sure that it is in the 838 | # PATH. 839 | # 840 | # $1: binary name (e.g. ghc or cabal) 841 | # $2: binary version prefix (e.g. 8 or 8.0 or 8.0.1) 842 | find_binary () { 843 | local binary 844 | local path=$PATH 845 | local removed_path 846 | 847 | echo "Looking for binary [$1] in PATH..." 848 | binary=$(which_cmd $1) 849 | while test -n "$binary" 850 | do 851 | if test -z "$2" || check_version $binary $2 852 | then 853 | PATH=$PATH$removed_path 854 | echo "Found. PATH set to [$PATH]..." 855 | return 0 856 | else 857 | # remove it from the path and search again 858 | echo "Mismatching $1 version found at [$(dirname $binary)], removing it from PATH and trying again" 859 | removed_path=$removed_path:$(dirname $binary) 860 | path_remove $(dirname $binary) 861 | binary="$(which_cmd $1)" 862 | fi 863 | done 864 | echo "Not found." 865 | 866 | # If we did not find the binary, restore the PATH for better error reporting 867 | PATH=$path 868 | 869 | if test -n "$TOOLS_DIR" 870 | then 871 | echo "Looking for binary [$1] in [$TOOLS_DIR]..." 872 | # Find if we have a binary in TOOLS_DIR 873 | local dir 874 | if test -n "$2" 875 | then 876 | dir=$(echo ${TOOLS_DIR}/$1/$2*/ | tr ' ' '\n' | sort | tail -1) 877 | else 878 | dir=$(echo ${TOOLS_DIR}/$1/[0-9]*/ | tr ' ' '\n' | sort | tail -1) 879 | test -x "${dir}/bin/$1" || dir="${TOOLS_DIR}/$1/head" 880 | fi 881 | 882 | if test -x "${dir}/bin/$1" 883 | then 884 | if test -z "$2" || check_version "${dir}/bin/$1" $2 885 | then 886 | if [[ $dir != /* ]] 887 | then 888 | dir=`pwd`/$dir 889 | fi 890 | PATH=$dir/bin:$PATH 891 | echo "Found. PATH set to [$PATH]..." 892 | export PATH 893 | return 0 894 | fi 895 | fi 896 | echo "Not found." 897 | fi 898 | 899 | STACK_ROOT_PATH="~/.stack" 900 | if test -n "$STACK_ROOT" 901 | then 902 | STACK_ROOT_PATH=$STACK_ROOT 903 | fi 904 | # XXX what if there are multiple arch dirs in programs? 905 | if test -d $STACK_ROOT_PATH -a "$1" = "ghc" 906 | then 907 | echo "Looking for binary [$1] in [$STACK_ROOT_PATH]..." 908 | # XXX We should pick the highest version by default 909 | local dir=$(echo $STACK_ROOT_PATH/programs/*/${1}-${2}*/) 910 | if test -x "${dir}/bin/$1" 911 | then 912 | if test -z "$2" || check_version "${dir}/bin/$1" $2 913 | then 914 | if [[ $dir != /* ]] 915 | then 916 | dir=`pwd`/$dir 917 | fi 918 | PATH=$dir/bin:$PATH 919 | echo "Found. PATH set to [$PATH]..." 920 | export PATH 921 | return 0 922 | fi 923 | fi 924 | echo "Not found." 925 | fi 926 | return 1 927 | } 928 | 929 | ghcup_install() { 930 | local tool=$1 931 | local tool_ver=$2 932 | local GHCUP_ARCH 933 | local ghcup_path 934 | 935 | # XXX add only if not already on PATH 936 | PATH=$GHCUP_BIN:$PATH 937 | echo "Added $GHCUP_BIN to PATH [$PATH]" 938 | ghcup_path=$(which_cmd ghcup) 939 | 940 | if test -n "$ghcup_path" 941 | then 942 | echo "Using existing $ghcup_path in PATH" 943 | else 944 | # User can either add it in the PATH or we can use the full path of the 945 | # tool either as found in PATH or use GHCUP_PATH directly. We should 946 | # probably fix each tool's location as found and use that rather 947 | # than using it from PATH. 948 | if test -e "$GHCUP_PATH" 949 | then 950 | die "$GHCUP_PATH already exists, not overwriting." 951 | fi 952 | 953 | # Determine GHCUP_ARCH 954 | os=$(uname -s -m) 955 | case "$os" in 956 | "Linux x86_64") GHCUP_ARCH="x86_64-linux" ;; 957 | "FreeBSD x86_64" | "FreeBSD amd64") GHCUP_ARCH=x86_64-portbld-freebsd ;; 958 | "Darwin x86_64") GHCUP_ARCH="x86_64-apple-darwin" ;; 959 | "Darwin arm64") GHCUP_ARCH="aarch64-apple-darwin" ;; 960 | *) echo "Unknown OS/Arch: $os"; exit 1;; 961 | esac 962 | 963 | # Check available versions here: https://downloads.haskell.org/~ghcup/ 964 | URL="$GHCUP_URL_PREFIX/$GHCUP_VERSION/${GHCUP_ARCH}-ghcup-$GHCUP_VERSION" 965 | echo "Downloading $URL to $GHCUP_PATH" 966 | # XXX Download to a temp location and move when successful? 967 | #TEMP=$(mktemp -d .ghcup-XXXXXX) 968 | #cleanup(){ 969 | # rm -r $TEMP 970 | #} 971 | #trap cleanup EXIT 972 | mkdir -p $(dirname $GHCUP_PATH) 973 | retry_cmd curl --fail --progress-bar --location -o $GHCUP_PATH $URL 974 | if test $? -ne 0 975 | then 976 | rm -f $GHCUP_PATH 977 | echo "Failed to download ghcup" 978 | exit 1 979 | fi 980 | chmod +x $GHCUP_PATH 981 | # Avoid changing the user's setup 982 | #$GHCUP_PATH set $tool $tool_ver 983 | fi 984 | 985 | if test "$tool" = "ghc" 986 | then 987 | run_verbose_errexit_with "cat /usr/local/.ghcup/logs/*" \ 988 | ghcup install ghc $GHCUP_GHC_OPTIONS $tool_ver 989 | else 990 | run_verbose_errexit ghcup install $tool $tool_ver 991 | fi 992 | } 993 | 994 | ensure_default_ghc() { 995 | local ghc 996 | ghc="$(which_cmd ghc)" 997 | if test -z "$ghc" -a -n "$GHCUP_VERSION" 998 | then 999 | echo "No default ghc found in PATH. Setting it using ghcup" 1000 | ghcup set ghc $GHCVER 1001 | fi 1002 | } 1003 | 1004 | ensure_ghc() { 1005 | local found 1006 | local compiler 1007 | # If there is a ghc in PATH then use that otherwise install it using 1008 | # ghcup or stack 1009 | find_binary "$COMPILER" "$GHCVER" 1010 | found=$? 1011 | if test "$found" -ne 0 1012 | then 1013 | if test -n "$GHCVER" 1014 | then 1015 | find_binary "$COMPILER-$GHCVER" "$GHCVER" 1016 | found=$? 1017 | if test "$found" -eq 0 1018 | then 1019 | COMPILER="$COMPILER-$GHCVER" 1020 | fi 1021 | fi 1022 | 1023 | if test "$found" -ne 0 1024 | then 1025 | if test -n "$(need_stack)" 1026 | then 1027 | # Use stack supplied ghc 1028 | echo "$STACKCMD setup" 1029 | retry_cmd $STACKCMD setup || die "stack setup falied" 1030 | use_stack_paths 1031 | echo 1032 | else 1033 | ghcup_install ghc $GHCVER 1034 | if test -n "$GHCVER" -a "$COMPILER" != "ghc-head" 1035 | then 1036 | COMPILER="$COMPILER-$GHCVER" 1037 | fi 1038 | fi 1039 | fi 1040 | fi 1041 | 1042 | compiler="$(which_cmd $COMPILER)" 1043 | if test -z "$compiler" 1044 | then 1045 | local msg 1046 | msg="$COMPILER $GHCVER not found in PATH [$PATH]" 1047 | if test -n "$TOOLS_DIR" 1048 | then 1049 | msg="$msg or in $TOOLS_DIR/$COMPILER/$GHCVER*/bin" 1050 | fi 1051 | die "$msg" 1052 | fi 1053 | 1054 | if test -n "$GHCVER" 1055 | then 1056 | check_version_die $COMPILER $GHCVER 1057 | fi 1058 | 1059 | if test -n "$GHCVER" -a -n "$(need_stack)" 1060 | then 1061 | echo "GHCVER is specified, using '$STACKCMD --system-ghc'." 1062 | echo "Clear GHCVER if you want to use stack supplied ghc." 1063 | # If the user specified GHCVER then use it as system-ghc 1064 | # Stack will still silently choose its own ghc if the ghc does not match 1065 | # the snapshot. 1066 | STACKCMD="$STACKCMD --system-ghc" 1067 | fi 1068 | 1069 | if test -n "$(need_stack)" 1070 | then 1071 | compiler=$($STACKCMD path --compiler-exe) || exit 1 1072 | fi 1073 | 1074 | export COMPILER_EXE_PATH="$compiler" 1075 | echo "Using $COMPILER at $compiler" 1076 | echo "$($compiler --version) [$($compiler --print-project-git-commit-id 2> /dev/null || echo '?')]" 1077 | # Use the real version, the user might have specified a version prefix in 1078 | # GHCVER 1079 | GHCVER=$($compiler --numeric-version) || exit 1 1080 | 1081 | # cabal info command requires "ghc" to be in PATH 1082 | if test -z "$DISABLE_SDIST_BUILD" 1083 | then 1084 | ensure_default_ghc 1085 | fi 1086 | 1087 | if test -n "$ENABLE_GHCJS" 1088 | then 1089 | local tmpdir 1090 | local shebang 1091 | local nodepath 1092 | local output 1093 | 1094 | # XXX the temp file may not get removed on crash/interrupt 1095 | tmpdir=`mktemp -d 2>/dev/null || mktemp -d -t 'packcheck'` 1096 | echo "main=putStrLn \"hello\"" > $tmpdir/test.hs 1097 | $compiler -build-runner -o ${tmpdir}/run $tmpdir/test.hs 1098 | shebang=$(head -1 ${tmpdir}/run) 1099 | nodepath=${shebang:2} 1100 | echo "$compiler uses node at [${nodepath}] for executing js output" 1101 | if test -x $nodepath 1102 | then 1103 | echo "$nodepath is version [$($nodepath --version)]" || die "Cannot determine node version" 1104 | else 1105 | rm -rf $tmpdir 1106 | die "[$nodepath] not found or not executable" 1107 | fi 1108 | echo "Trying to run a test executable produced by [$compiler]..." 1109 | output=$(${tmpdir}/run) 1110 | rm -rf $tmpdir 1111 | 1112 | if test "$output" != "hello" 1113 | then 1114 | die "[$compiler] cannot produce executables runnable by [$nodepath]" 1115 | else 1116 | echo "[$compiler] produces executables runnable by [$nodepath]" 1117 | fi 1118 | fi 1119 | } 1120 | 1121 | # XXX/TODO this may not work for cabal 1.24 config 1122 | # $1: mirror URL 1123 | cabal_use_mirror() { 1124 | local CABAL_CONFIG=${OS_APP_HOME}/${OS_CABAL_DIR}/config 1125 | if test -f $CABAL_CONFIG 1126 | then 1127 | local inplace 1128 | if [ `uname` = "Darwin" ] 1129 | then 1130 | inplace="-i orig" 1131 | else 1132 | inplace="--in-place" 1133 | fi 1134 | echo "Adding hackage mirror [$1] to [$CABAL_CONFIG]" 1135 | sed $inplace -e "s%^remote-repo:.*%remote-repo: $1%" $CABAL_CONFIG 1136 | else 1137 | die "cabal config file [$CABAL_CONFIG] not found." 1138 | fi 1139 | } 1140 | 1141 | # We need to ignore the project's stack.yaml when installing 1142 | # cabal-install, otherwise it may fail due to dependency conflicts. 1143 | # 1144 | # On CI machines if our repo is cloned in the top dir then there is no way to 1145 | # teach stack to ignore the project's stack.yaml. We cannot change our 1146 | # directory to go above it. Because we have a project stack.yaml, stack does 1147 | # not even create a global stack.yaml. So we work it around by creating a new 1148 | # package and installing cabal via that. 1149 | # 1150 | # $1 tool name 1151 | stack_install_tool () { 1152 | rm -rf .packcheck/tool-install 1153 | mkdir -p .packcheck/tool-install || exit 1 1154 | cd .packcheck/tool-install || exit 1 1155 | 1156 | local res 1157 | # Where possible we want to use the same resolver for build as well as 1158 | # tools to share the snapshot when building. 1159 | # But nightlies may not have cabal-install all the time 1160 | if [[ "$RESOLVER" != "" && "$RESOLVER" != nightly* ]] 1161 | then 1162 | res="--resolver $RESOLVER" 1163 | fi 1164 | 1165 | if test ! -e stack.yaml 1166 | then 1167 | run_verbose_errexit $STACKCMD_TOOL_INSTALL $res \ 1168 | new --bare tool-install 1169 | fi 1170 | run_verbose_errexit $STACKCMD_TOOL_INSTALL $res install $1 1171 | cd ../.. 1172 | } 1173 | 1174 | # $1: Directory to install cabal in 1175 | # We are not using the param as currently it is always the dir where stack 1176 | # installs binaries. 1177 | ensure_cabal() { 1178 | # We can only do this after ghc is installed. 1179 | # We need cabal to retrieve the package version 1180 | # We are assuming CI cache will be per resolver so we can cache the bin 1181 | 1182 | find_binary $CABAL_BINARY_NAME "$CABALVER" 1183 | found=$? 1184 | if test "$found" -ne 0 1185 | then 1186 | if test -n "$CABALVER" 1187 | then 1188 | find_binary "$CABAL_BINARY_NAME-$CABALVER" "$CABALVER" 1189 | found=$? 1190 | if test "$found" -eq 0 1191 | then 1192 | CABAL_BINARY_NAME="$CABAL_BINARY_NAME-$CABALVER" 1193 | fi 1194 | fi 1195 | 1196 | if test "$found" -ne 0 1197 | then 1198 | if test -n "$(need_stack)" 1199 | then 1200 | stack_install_tool cabal-install 1201 | elif test -n "$GHCUP_VERSION" 1202 | then 1203 | ghcup_install cabal $CABALVER 1204 | if test -n "$CABALVER" 1205 | then 1206 | CABAL_BINARY_NAME="$CABAL_BINARY_NAME-$CABALVER" 1207 | fi 1208 | fi 1209 | fi 1210 | fi 1211 | 1212 | require_cmd $CABAL_BINARY_NAME 1213 | $CABAL_BINARY_NAME --version 1214 | test -z "$CABALVER" || check_version_die $CABAL_BINARY_NAME $CABALVER 1215 | # Set the real version of cabal 1216 | CABALVER=$($CABAL_BINARY_NAME --numeric-version) || exit 1 1217 | 1218 | MIN_CABALVER="1.24.0.0" 1219 | verlte $MIN_CABALVER $CABALVER || \ 1220 | die "Cabal version should at least be $MIN_CABALVER" 1221 | } 1222 | 1223 | # Find a file in the parent/ancestor directories 1224 | find_in_parents () { 1225 | local curdir="$1" # must be absolute path without .. 1226 | local file="$2" 1227 | 1228 | if test -e "$curdir/$file" 1229 | then echo "$curdir/$file" 1230 | elif test "$curdir" != "/" 1231 | then find_in_parents "$(dirname "$curdir")" "$file" 1232 | fi 1233 | } 1234 | 1235 | ensure_cabal_project() { 1236 | CABALCMD=$CABAL_BINARY_NAME 1237 | 1238 | if test -n "$CABAL_PROJECT" 1239 | then 1240 | require_file "$CABAL_PROJECT" 1241 | echo "Using user specified cabal project file at [$CABAL_PROJECT]" 1242 | if test -n "$DISABLE_SDIST_BUILD" 1243 | then # regular build 1244 | SDIST_CABALCMD="$CABALCMD --project-file=$CABAL_PROJECT" 1245 | else # sdist build 1246 | # We run the command from .packcheck/ dir after unpacking 1247 | # sdist 1248 | SDIST_CABALCMD="$CABALCMD --project-file=../../$CABAL_PROJECT" 1249 | echo "WARNING!! You should not test a distribution build with a cabal" \ 1250 | "project file. It may not be reproducible." 1251 | fi 1252 | CABALCMD="$CABALCMD --project-file=$CABAL_PROJECT" 1253 | else 1254 | SDIST_CABALCMD="$CABALCMD" 1255 | local implicit_proj_file 1256 | implicit_proj_file=$(find_in_parents "$(pwd)" cabal.project) 1257 | if test -n "$implicit_proj_file" 1258 | then 1259 | echo "Implicit cabal project file found at [$implicit_proj_file]" 1260 | fi 1261 | fi 1262 | echo "Using cabal command [$CABALCMD]" 1263 | echo "Using sdist cabal command [$SDIST_CABALCMD]" 1264 | } 1265 | 1266 | ensure_stack_yaml() { 1267 | if test -n "$STACK_YAML" 1268 | then 1269 | require_file $STACK_YAML 1270 | echo "Using user specified stack.yaml file at [$STACK_YAML]" 1271 | if test -n "$DISABLE_SDIST_BUILD" 1272 | then # regular build 1273 | SDIST_STACKCMD="$STACKCMD --stack-yaml $STACK_YAML" 1274 | else # sdist build 1275 | # We run the stack command from .packcheck/ dir after unpacking 1276 | # sdist 1277 | SDIST_STACKCMD="$STACKCMD --stack-yaml ../../$STACK_YAML" 1278 | echo "WARNING!! You should not test a distribution build with a" \ 1279 | "stack.yaml file. It may not be reproducible." 1280 | fi 1281 | STACKCMD="$STACKCMD --stack-yaml $STACK_YAML" 1282 | else 1283 | SDIST_STACKCMD="$STACKCMD" 1284 | local implicit_stack_yaml 1285 | implicit_stack_yaml=$(find_in_parents "$(pwd)" stack.yaml) 1286 | if test -n "$implicit_stack_yaml" 1287 | then 1288 | echo "Implicit stack.yaml file found at [$implicit_stack_yaml]" 1289 | else 1290 | if test -n "$DISABLE_SDIST_BUILD" 1291 | then # regular build 1292 | run_verbose_errexit $STACKCMD init 1293 | fi 1294 | fi 1295 | fi 1296 | echo "Using stack command [$STACKCMD]" 1297 | echo "Using sdist stack command [$SDIST_STACKCMD]" 1298 | } 1299 | 1300 | #------------------------------------------------------------------------------ 1301 | # Create a dist, install deps and test 1302 | #------------------------------------------------------------------------------ 1303 | 1304 | get_pkg_name() { 1305 | local name=$(echo *.cabal) 1306 | test -f "$name" || die "One and only one .cabal file is required in the current directory." 1307 | name=${name%.cabal} 1308 | test -f "${name}.cabal" || \ 1309 | die "Cannot determine package name. File [${name}.cabal] does not exist" 1310 | echo $name 1311 | } 1312 | 1313 | get_pkg_full_name() { 1314 | local pkgname 1315 | local full_name 1316 | pkgname=$(get_pkg_name) || exit 1 1317 | full_name=$($CABAL_BINARY_NAME info . | awk '{ if ($1 == "*") {print $2; exit}}') || true 1318 | if test -z "$full_name" 1319 | then 1320 | if test -z "$DISABLE_SDIST_BUILD" 1321 | then 1322 | die "'cabal info' command failed to determine package name.\nPlease use 'DISABLE_SDIST_BUILD=y' to avoid this issue." 1323 | fi 1324 | else 1325 | if test "${pkgname}${full_name#$pkgname}" != "${full_name}" 1326 | then 1327 | die "Inconsistent package name [$pkgname] and package full name [$full_name]" 1328 | fi 1329 | fi 1330 | 1331 | echo $full_name 1332 | } 1333 | 1334 | # XXX use MULTI_PACKAGE_PROJECT as a CLI option, instead of trying to determine 1335 | # it here. 1336 | determine_build_type() { 1337 | MULTI_PACKAGE_PROJECT=false 1338 | echo "Looking for cabal or stack build files in the current directory..." 1339 | local name=$(echo *.cabal) 1340 | if test "$name" = "*.cabal" 1341 | then 1342 | if test -f "package.yaml" -a -n "$STACKCMD" 1343 | then 1344 | echo "No cabal file found but a package.yaml file found" 1345 | echo "Generating cabal file from package.yaml" 1346 | # Generate cabal file from package.yaml 1347 | run_verbose "$STACKCMD query > /dev/null 2>&1" 1348 | else 1349 | if test $BUILD = "stack" -a -f "stack.yaml" 1350 | then 1351 | echo "No cabal file found but a stack.yaml file found, assuming a multipackage project." 1352 | echo "Setting DISABLE_SDIST_BUILD=y and DISABLE_DIST_CHECKS=y" 1353 | MULTI_PACKAGE_PROJECT=true 1354 | DISABLE_SDIST_BUILD=y 1355 | DISABLE_DIST_CHECKS=y 1356 | else 1357 | if test $BUILD = "cabal-v2" -a -f "cabal.project" 1358 | then 1359 | echo "No cabal file found but a cabal.project file found, assuming a multipackage project." 1360 | echo "Setting DISABLE_SDIST_BUILD=y and DISABLE_DIST_CHECKS=y" 1361 | MULTI_PACKAGE_PROJECT=true 1362 | DISABLE_SDIST_BUILD=y 1363 | DISABLE_DIST_CHECKS=y 1364 | else 1365 | echo "No valid build config file cabal/cabal.project/package.yaml/stack.yaml found." 1366 | die "Make sure you are using BUILD=cabal-v2 if you are using a cabal.project file" 1367 | fi 1368 | fi 1369 | fi 1370 | else 1371 | if test ! -f "$name" 1372 | then 1373 | echo "Either there are multiple cabal files in the directory" 1374 | echo "or the cabal file is not a regular file." 1375 | die "cabal files: $name" 1376 | else 1377 | echo "Found [$name]" 1378 | fi 1379 | fi 1380 | } 1381 | 1382 | ensure_cabal_config() { 1383 | # When cabal versions change across builds on a CI host its safer to remove 1384 | # the old config so that the build does not error out. 1385 | local cfg="${OS_APP_HOME}/${OS_CABAL_DIR}/config" 1386 | if test "$CABAL_REINIT_CONFIG" = y 1387 | then 1388 | echo "Removing old cabal config [$cfg]" 1389 | run_verbose_errexit rm -f "$cfg" 1390 | fi 1391 | 1392 | # this generates it in ~/.config which creates issues for cabal-docspec and 1393 | # some other issues. 1394 | if test ! -e $cfg 1395 | then 1396 | run_verbose $CABAL_BINARY_NAME user-config init || true 1397 | if test ! -f $cfg 1398 | then 1399 | if test -f ${OS_APP_HOME}/.config/cabal/config 1400 | then 1401 | mkdir -p $(dirname $cfg) 1402 | run_verbose_errexit ln -s ${OS_APP_HOME}/.config/cabal/config $cfg 1403 | fi 1404 | fi 1405 | fi 1406 | 1407 | if test "$BUILD" = "cabal-v2" 1408 | then 1409 | if test -n "$CABAL_HACKAGE_MIRROR" 1410 | then 1411 | cabal_use_mirror $CABAL_HACKAGE_MIRROR 1412 | fi 1413 | 1414 | if test -z "$CABAL_DISABLE_DEPS" 1415 | then 1416 | echo 1417 | echo "cabal v2-update" 1418 | if test -n "$CABAL_PROJECT" 1419 | then 1420 | retry_cmd $CABAL_BINARY_NAME v2-update --project-file "$CABAL_PROJECT" 1421 | else 1422 | retry_cmd $CABAL_BINARY_NAME v2-update 1423 | fi 1424 | fi 1425 | fi 1426 | } 1427 | 1428 | # $1: dir where they are installed 1429 | remove_pkg_executables() { 1430 | test -n "$(which_cmd $CABAL_BINARY_NAME)" || return 0 1431 | 1432 | exes=$($CABAL_BINARY_NAME info . | awk '{if ($1 == "Executables:") { print $2; exit }}') || exit 1 1433 | echo "Remove installed binaries" 1434 | for i in $exes 1435 | do 1436 | # The executables may be under a cabal flag and may not have been installed 1437 | # unless we used that flag. So just use "rm -f" to remove silently. 1438 | run_verbose_errexit rm -f "$1"/"$i" 1439 | done 1440 | } 1441 | 1442 | sdist_remove_project_file () { 1443 | if test -e "$1" 1444 | then 1445 | echo "WARNING! Avoid including $1 in a distribution" 1446 | test -n "$DISABLE_SDIST_PROJECT_CHECK" || \ 1447 | die "Use DISABLE_SDIST_PROJECT_CHECK=y to allow this" 1448 | run_verbose rm -f "$1" 1449 | fi 1450 | } 1451 | 1452 | # $1: package full name (name + ver) 1453 | create_and_unpack_pkg_dist() { 1454 | local pkgtar=${1}.tar.gz 1455 | local opts 1456 | local SDIST_DIR 1457 | local SDIST_CMD 1458 | 1459 | test -z "$SDIST_OPTIONS" || opts="$SDIST_OPTIONS" 1460 | 1461 | if test "$BUILD" = stack 1462 | then 1463 | # When custom setup is used we need to configure before we can use sdist. 1464 | if test -z "$STACK_YAML" 1465 | then 1466 | test -e stack.yaml || run_verbose_errexit $STACKCMD init 1467 | fi 1468 | run_verbose_errexit $STACKCMD build --only-configure 1469 | SDIST_CMD="$STACKCMD sdist $opts" 1470 | SDIST_DIR=$($STACKCMD path --dist-dir) || exit 1 1471 | elif test -n "$CABAL_USE_STACK_SDIST" 1472 | then 1473 | # When custom setup is used we need to configure before we can use sdist. 1474 | run_verbose_errexit $STACKCMD --compiler=$COMPILER-$GHCVER build --only-configure 1475 | SDIST_CMD="$STACKCMD --compiler=$COMPILER-$GHCVER sdist $opts" 1476 | SDIST_DIR=$($STACKCMD --compiler=$COMPILER-$GHCVER path --dist-dir) || exit 1 1477 | else 1478 | # XXX We need to configure to use sdist and we need to install 1479 | # dependencies to configure. So to just create the sdist we will 1480 | # have to go through the whole process once and then again after 1481 | # unpacking the sdist and to build it. 1482 | SDIST_CMD="$CABALCMD v2-sdist $opts" 1483 | SDIST_DIR=dist-newstyle/sdist 1484 | fi 1485 | 1486 | # stack commands return path in windows format 1487 | [[ `uname` != MINGW* ]] || SDIST_DIR=`cygpath ${SDIST_DIR}` 1488 | 1489 | local tarpath=${SDIST_DIR}/${pkgtar} 1490 | rm -f $tarpath 1491 | echo 1492 | run_verbose_errexit $SDIST_CMD 1493 | if test ! -f $tarpath 1494 | then 1495 | echo "$SDIST_CMD did not create [$tarpath]" 1496 | exit 1 1497 | fi 1498 | 1499 | # Unpack the tar inside .packcheck directory 1500 | mkdir -p .packcheck || exit 1 1501 | 1502 | # XXX Move the git repo comparison code to a separate function 1503 | local gitcmd="$(which_cmd git)" 1504 | test -n "$gitcmd" || echo "WARNING! git command not found skipping source distribution comparison with git repo" 1505 | 1506 | if test -d ".git" -a -n "$gitcmd" -a -n "$(which_cmd tar)" 1507 | then 1508 | echo ".git directory found, assuming git repo" 1509 | echo "Comparing distribution contents against git ls-files..." 1510 | tar -ztf $tarpath \ 1511 | | sed -e "s%^$PACKAGE_FULL_NAME/%%" \ 1512 | | grep -v '/$' \ 1513 | | grep -v '^$' \ 1514 | > .packcheck/tar-ztf.txt 1515 | if test -f .packcheck.ignore 1516 | then 1517 | local pi_files_exist="" 1518 | local pi_files_n_exist="" 1519 | while read p; do 1520 | if [ ! -f "$p" -a ! -d "$p" ] 1521 | then 1522 | pi_files_n_exist="$p\n$pi_files_n_exist" 1523 | fi 1524 | x=`git ls-files $p` 1525 | pi_files_exist="$x\n$pi_files_exist" 1526 | done <.packcheck.ignore 1527 | if test -n "$pi_files_n_exist" 1528 | then 1529 | echo "WARNING: The following files or directories don't exist but are mentioned in \ 1530 | your .packcheck.ignore file." 1531 | printf "$pi_files_n_exist" 1532 | fi 1533 | local sane_ignore_file=".packcheck/sane-ignore" 1534 | printf "$pi_files_exist" > "$sane_ignore_file" 1535 | cat "$sane_ignore_file" .packcheck/tar-ztf.txt \ 1536 | | sort | grep -v '^$' > .packcheck/tar-ztf1.txt 1537 | else 1538 | cat .packcheck/tar-ztf.txt \ 1539 | | sort | grep -v '^$' > .packcheck/tar-ztf1.txt 1540 | fi 1541 | git ls-files | sort | grep -v '^$' > .packcheck/git-ls-files.txt 1542 | local diff_res_file=".packcheck/tar-git-diff.txt" 1543 | local tar_minus_git 1544 | local git_minus_tar 1545 | diff -B --suppress-common-lines .packcheck/tar-ztf1.txt .packcheck/git-ls-files.txt > "$diff_res_file" || 1546 | { echo "WARNING! Source distribution tar and git repo contents differ." 1547 | tar_minus_git="$(awk '$0~/^< .+$/' "$diff_res_file")" 1548 | git_minus_tar="$(awk '$0~/^> .+$/' "$diff_res_file")" 1549 | if test -n "$tar_minus_git" 1550 | then 1551 | echo 1552 | echo "The following files exist in your source distribution but \ 1553 | are not committed to the git repository." 1554 | echo "$tar_minus_git" 1555 | echo "Please consider committing them to the git repository." 1556 | echo "Or clean the git workspace of unwanted files before creating \ 1557 | the source distribution." 1558 | echo "Wildcards in 'extra-sources-files' section and \ 1559 | 'extra-doc-files' section of the cabal file may pickup any files lying around." 1560 | fi 1561 | if test -n "$git_minus_tar" 1562 | then 1563 | echo 1564 | echo "The following files are committed to the git repository \ 1565 | but do not exist in the source distribution." 1566 | echo "$git_minus_tar" 1567 | echo "Please consider adding them to your cabal file under \ 1568 | 'extra-source-files' or 'extra-doc-files'." 1569 | echo "If you do not want to add them in the source distribution \ 1570 | then add them to .packcheck.ignore file at the root of the git repository." 1571 | fi 1572 | if test -z "$DISABLE_SDIST_GIT_CHECK" 1573 | then 1574 | echo 1575 | echo "Exiting. Use DISABLE_SDIST_GIT_CHECK=y to disable this check." 1576 | exit 1 1577 | fi 1578 | } 1579 | 1580 | rm -f .packcheck/tar-ztf.txt \ 1581 | .packcheck/tar-ztf1.txt \ 1582 | .packcheck/git-ls-files.txt \ 1583 | "$diff_res_file" \ 1584 | "$sane_ignore_file" 1585 | fi 1586 | 1587 | echo 1588 | echo "cd .packcheck" 1589 | cd .packcheck || exit 1 1590 | test "${tarpath:0:1}" == / || tarpath=../$tarpath 1591 | $OS_UNGZTAR_CMD $tarpath 1592 | echo 1593 | 1594 | # NOTE: We are entering the sdist directory inside .packcheck now. The 1595 | # original tree is intact. When we remove the cabal.project or stack.yaml 1596 | # files here, it is removed from the sdist copy not from the original tree. 1597 | # However, the CABAL_PROJECT and STACK_YAML variables are pointing to a path 1598 | # relative to the original tree root. 1599 | cd ${1} 1600 | if test "$BUILD" = stack 1601 | then 1602 | if test -z "$STACK_YAML" 1603 | then 1604 | sdist_remove_project_file stack.yaml 1605 | run_verbose_errexit $SDIST_STACKCMD init 1606 | fi 1607 | else 1608 | if test -z "$CABAL_PROJECT" 1609 | then 1610 | # Is there a way in cabal to ignore project file? 1611 | # XXX how does cabal build dependencies with project files? 1612 | sdist_remove_project_file cabal.project 1613 | sdist_remove_project_file cabal.project.local 1614 | sdist_remove_project_file cabal.project.freeze 1615 | 1616 | # We want to ensure that our build is not affected by the 1617 | # implicit cabal.project file. This is likely because the 1618 | # .packcheck directory is created inside the project repo which 1619 | # may have a cabal.project file. 1620 | local implicit_proj_file 1621 | implicit_proj_file=$(find_in_parents "$(pwd)" cabal.project) 1622 | 1623 | if test -n "$implicit_proj_file" 1624 | then 1625 | echo "WARNING! Implicit cabal.project found at [$implicit_proj_file]" 1626 | echo "Creating an explicit cabal.project file to ensure that we" \ 1627 | "do not use the implicit one for a distribution build" 1628 | echo "packages: ." > cabal.project 1629 | run_verbose cat cabal.project 1630 | echo 1631 | fi 1632 | fi 1633 | fi 1634 | 1635 | show_step "Package info [sdist $SDIST_OPTIONS]" 1636 | run_verbose $CABAL_BINARY_NAME info . || true 1637 | 1638 | if test -f "./configure.ac" 1639 | then 1640 | show_step "Run autoreconf" 1641 | run_verbose_errexit autoreconf -i 1642 | fi 1643 | } 1644 | 1645 | install_deps() { 1646 | case "$BUILD" in 1647 | stack) run_verbose_errexit $SDIST_STACKCMD build $STACK_DEP_OPTIONS ;; 1648 | cabal-v2) 1649 | if test -z "$CABAL_DISABLE_DEPS" 1650 | then 1651 | run_verbose_errexit $SDIST_CABALCMD v2-build \ 1652 | --with-compiler "$COMPILER_EXE_PATH" \ 1653 | $GHCJS_FLAG $CABAL_DEP_OPTIONS $CABAL_BUILD_TARGETS 1654 | else 1655 | echo "Skipping. CABAL_DISABLE_DEPS is on." 1656 | fi ;; 1657 | esac 1658 | } 1659 | 1660 | build_and_test() { 1661 | case "$BUILD" in 1662 | stack) run_verbose_errexit $SDIST_STACKCMD build $STACK_BUILD_OPTIONS ;; 1663 | cabal-v2) 1664 | run_verbose_errexit $SDIST_CABALCMD v2-build \ 1665 | --with-compiler "$COMPILER_EXE_PATH" \ 1666 | $GHCJS_FLAG $CABAL_BUILD_OPTIONS $CABAL_BUILD_TARGETS 1667 | echo 1668 | test -n "$DISABLE_DOCS" || \ 1669 | run_verbose_errexit $SDIST_CABALCMD v2-haddock \ 1670 | --with-compiler "$COMPILER_EXE_PATH" \ 1671 | $GHCJS_FLAG $CABAL_BUILD_OPTIONS $CABAL_BUILD_TARGETS 1672 | if test -z "$DISABLE_TEST" 1673 | then 1674 | local version 1675 | local SHOW_DETAILS 1676 | version=$($CABAL_BINARY_NAME --numeric-version|cut -f1,2 -d'.'|sed s/\\.//g) 1677 | if test $version -ge 25 1678 | then 1679 | SHOW_DETAILS="--test-show-details=streaming" 1680 | fi 1681 | echo 1682 | run_verbose_errexit $SDIST_CABALCMD v2-test \ 1683 | --with-compiler "$COMPILER_EXE_PATH" \ 1684 | $SHOW_DETAILS $GHCJS_FLAG $CABAL_BUILD_OPTIONS $CABAL_TEST_OPTIONS $CABAL_BUILD_TARGETS 1685 | fi ;; 1686 | esac 1687 | } 1688 | 1689 | dist_checks() { 1690 | case "$BUILD" in 1691 | stack) run_verbose $SDIST_STACKCMD sdist $SDIST_OPTIONS ;; 1692 | cabal-v2) 1693 | echo 1694 | if test -n "$CABAL_CHECK_RELAX" 1695 | then 1696 | run_verbose $CABAL_BINARY_NAME check || true 1697 | else 1698 | run_verbose $CABAL_BINARY_NAME check || \ 1699 | die "Use CABAL_CHECK_RELAX=y to ignore this error" 1700 | fi 1701 | run_verbose $SDIST_CABALCMD v2-sdist $CABAL_BUILD_TARGETS $SDIST_OPTIONS 1702 | ;; 1703 | esac 1704 | } 1705 | 1706 | # XXX The downloading code is common among different tools (e.g. ghcup). 1707 | # we can write a common function to do it. 1708 | # 1709 | # hlint install from Neil Mitchell's github repo, script taken from 1710 | # https://raw.githubusercontent.com/ndmitchell/neil/master/misc/run.sh 1711 | install_hlint() { 1712 | show_step "Installing hlint version $HLINT_VERSION" 1713 | 1714 | case "$(uname)" in 1715 | "Darwin") 1716 | OS=osx;; 1717 | MINGW*|MSYS*) 1718 | OS=windows;; 1719 | Linux) 1720 | OS=linux;; 1721 | *) die "install_hlint: unknown os";; 1722 | esac 1723 | 1724 | if [ "$OS" = "windows" ]; then 1725 | EXT=.zip 1726 | ESCEXT=\.zip 1727 | else 1728 | EXT=.tar.gz 1729 | ESCEXT=\.tar\.gz 1730 | fi 1731 | 1732 | local PACKAGE=hlint 1733 | VERSION=$HLINT_VERSION 1734 | 1735 | URL="$HLINT_URL_PREFIX/download/v$VERSION/hlint-$VERSION-x86_64-$OS$EXT" 1736 | TEMP=$(mktemp -d .$PACKAGE-XXXXXX) 1737 | 1738 | cleanup(){ 1739 | rm -r $TEMP 1740 | } 1741 | trap cleanup EXIT 1742 | 1743 | echo $URL 1744 | retry_cmd curl --fail --progress-bar --location -o$TEMP/$PACKAGE$EXT $URL 1745 | if test "$?" -ne 0 1746 | then 1747 | die "Failed to download $URL" 1748 | fi 1749 | 1750 | if test -e "$HLINT_PATH" 1751 | then 1752 | die "$HLINT_PATH already exists, not overwriting." 1753 | fi 1754 | 1755 | mkdir -p ${LOCAL_BIN} 1756 | if [ "$OS" = "windows" ]; then 1757 | 7z x -y $TEMP/$PACKAGE$EXT -o${TEMP} hlint-$VERSION/hlint.exe > /dev/null 1758 | mv ${TEMP}/hlint-${VERSION}/hlint.exe ${LOCAL_BIN} 1759 | elif [ "$OS" = "osx" ]; then 1760 | tar -xzvf $TEMP/$PACKAGE$EXT -C${TEMP} --include '*/hlint' 1761 | mv ${TEMP}/hlint-${VERSION}/hlint ${LOCAL_BIN} 1762 | else 1763 | tar -xzvf $TEMP/$PACKAGE$EXT -C${TEMP} --wildcards '*/hlint' 1764 | mv ${TEMP}/hlint-${VERSION}/hlint ${LOCAL_BIN} 1765 | fi 1766 | chmod +x $HLINT_PATH 1767 | } 1768 | 1769 | build_hlint() { 1770 | show_step "Installing hlint..." 1771 | if test -n "$(need_stack)" 1772 | then 1773 | ensure_stack ${LOCAL_BIN} 1774 | ensure_ghc 1775 | stack_install_tool hlint 1776 | else 1777 | ensure_cabal ${LOCAL_BIN} 1778 | ensure_cabal_config 1779 | ensure_ghc 1780 | case "$BUILD" in 1781 | cabal-v2) run_verbose_errexit $CABALCMD v2-install hlint ;; 1782 | *) echo "Bug: unknown build type: $BUILD" ;; 1783 | esac 1784 | fi 1785 | } 1786 | 1787 | run_hlint() { 1788 | show_step "Running hlint ..." 1789 | 1790 | # Old method 1791 | if test -n "$HLINT_COMMANDS" 1792 | then 1793 | echo "DEPRECATED env var HLINT_COMMANDS! Please use HLINT_OPTIONS and HLINT_TARGETS instead." 1794 | run_verbose_errexit "$HLINT_COMMANDS" 1795 | elif test -f .hlint.ignore 1796 | then 1797 | # Check if all files mentioned in .hlint.ignore exist. 1798 | local hi_files_exist="" 1799 | local hi_files_n_exist="" 1800 | while read p; do 1801 | if test -f "$p" 1802 | then 1803 | hi_files_exist="$p\n$hi_files_exist" 1804 | else 1805 | if test -n "$p" 1806 | then 1807 | hi_files_n_exist="$p\n$hi_files_n_exist" 1808 | fi 1809 | fi 1810 | done <.hlint.ignore 1811 | if test -n "$hi_files_n_exist" 1812 | then 1813 | echo "WARNING: The following files don't exist but are mentioned in \ 1814 | your .hlint.ignore file." 1815 | printf "$hi_files_n_exist" 1816 | fi 1817 | 1818 | local files 1819 | local found 1820 | local i 1821 | local target 1822 | for target in $HLINT_TARGETS 1823 | do 1824 | files=$(find $target -name "*.hs") 1825 | for i in $files 1826 | do 1827 | # XXX ignore whitespace at the end 1828 | found=$(grep "^$i$" .hlint.ignore) || true 1829 | if test -z "$found" 1830 | then 1831 | run_verbose_errexit "hlint $HLINT_OPTIONS $i" 1832 | fi 1833 | done 1834 | done 1835 | else 1836 | local target 1837 | for target in $HLINT_TARGETS 1838 | do 1839 | run_verbose_errexit "hlint $HLINT_OPTIONS $target" 1840 | done 1841 | fi 1842 | } 1843 | 1844 | install_docspec() { 1845 | show_step "Installing docspec from $DOCSPEC_URL" 1846 | case "$(uname)" in 1847 | Linux) 1848 | OS=linux;; 1849 | *) die "install_docspec: unknown or unsupported os";; 1850 | esac 1851 | 1852 | TEMP=$(mktemp -d docspec-XXXXXX) 1853 | cleanup(){ 1854 | rm -r $TEMP 1855 | } 1856 | trap cleanup EXIT 1857 | 1858 | # docspec does not have consistent release naming, therefore, use URL 1859 | # directly instead of version. 1860 | #URL="$DOCSPEC_URL_PREFIX/download/cabal-docspec-$DOCSPEC_VERSION/cabal-docspec-$DOCSPEC_VERSION-x86_64-$OS.xz" 1861 | #echo "Downloading $URL ..." 1862 | retry_cmd curl --fail --progress-bar --location -o$TEMP/cabal-docspec.xz $DOCSPEC_URL 1863 | if test "$?" -ne 0 1864 | then 1865 | rm -f $TEMP/cabal-docspec.xz 1866 | die "Failed to download $DOCSPEC_URL" 1867 | fi 1868 | 1869 | if test -e "$DOCSPEC_PATH" 1870 | then 1871 | die "$DOCSPEC_PATH already exists, not overwriting." 1872 | else 1873 | mkdir -p $(dirname ${DOCSPEC_PATH}) 1874 | xz -d < $TEMP/cabal-docspec.xz > $DOCSPEC_PATH 1875 | rm -f $TEMP/cabal-docspec.xz 1876 | chmod +x $DOCSPEC_PATH 1877 | fi 1878 | } 1879 | 1880 | # We run it only after a stack or cabal build so we are sure that stack or 1881 | # cabal are already installed. 1882 | coveralls_io() { 1883 | if test -z "$(which_cmd hpc-coveralls)" 1884 | then 1885 | if test "$BUILD" = stack 1886 | then 1887 | stack_install_tool hpc-coveralls 1888 | else 1889 | case "$BUILD" in 1890 | cabal-v2) run_verbose_errexit $CABALCMD v2-install hpc-coveralls ;; 1891 | *) echo "Bug: unknown build type: $BUILD" ;; 1892 | esac 1893 | fi 1894 | fi 1895 | run_verbose_errexit hpc-coveralls $COVERALLS_OPTIONS 1896 | } 1897 | 1898 | determine_package_full_name() { 1899 | PACKAGE_FULL_NAME=$(get_pkg_full_name) || die "PACKAGE_FULL_NAME" 1900 | echo "Package name and version: [$PACKAGE_FULL_NAME]" 1901 | } 1902 | 1903 | # stack or cabal build (i.e. not hlint) 1904 | build_compile () { 1905 | # ---------Install any tools needed-------- 1906 | show_step "Check and install build tools" 1907 | 1908 | test -z "$(need_stack)" \ 1909 | || ensure_stack ${LOCAL_BIN} \ 1910 | && echo 1911 | # The tar installed by pacman does not seem to work. Maybe we need to have it 1912 | # packed with msys itself. 1913 | # ensure_msys_tools "tar" && require_cmd tar 1914 | 1915 | # This may use STACKCMD so happens after stack install 1916 | determine_build_type && echo 1917 | ensure_ghc && echo 1918 | dont_need_cabal 'verbose' || ensure_cabal ${LOCAL_BIN} 1919 | 1920 | # use the stack installed 7z instead. depends on ensure ghc where we setup 1921 | # stack paths. 1922 | [[ `uname` != MINGW* ]] || require_cmd 7z 1923 | 1924 | show_step "Effective build config" 1925 | show_build_config 1926 | 1927 | # ---------Create dist, unpack, install deps, test-------- 1928 | show_step "Build tools: package level and global configuration" 1929 | dont_need_cabal || ensure_cabal_project 1930 | dont_need_cabal || ensure_cabal_config 1931 | if test -z "$DISABLE_SDIST_BUILD" 1932 | then 1933 | determine_package_full_name 1934 | fi 1935 | test -z "$(need_stack)" || ensure_stack_yaml 1936 | 1937 | # Note: from here on we should be using SDIST_CABALCMD or SDIST_STACKCMD to 1938 | # invoke cabal or stack unless we want to use the command without 1939 | # cabal-project or stack.yaml argument in which case CABALCMD or STACKCMD 1940 | # should be used. 1941 | if test -z "$DISABLE_SDIST_BUILD" 1942 | then 1943 | if test -f "./configure.ac" 1944 | then 1945 | echo "Package contains a 'configure.ac' file." 1946 | echo "Checking for availability of 'autoreconf' command to regenerate " 1947 | echo "the configure script before testing. Use DISABLE_SDIST_BUILD " 1948 | echo "option if you do not have 'autoreconf' available" 1949 | require_cmd autoreconf 1950 | fi 1951 | # Note this function leaves us in the package dir unpacked from sdist 1952 | show_step "Prepare to build from source distribution" 1953 | create_and_unpack_pkg_dist $PACKAGE_FULL_NAME 1954 | fi 1955 | 1956 | show_step "Install package dependencies" 1957 | install_deps 1958 | 1959 | show_step "Build and test" 1960 | build_and_test 1961 | 1962 | if test -n "$COVERALLS_OPTIONS" 1963 | then 1964 | show_step "Send coverage info to coveralls.io" 1965 | coveralls_io 1966 | fi 1967 | 1968 | if test -z "$DISABLE_DIST_CHECKS" 1969 | then 1970 | show_step "Package distribution checks" 1971 | dist_checks || die "Use DISABLE_DIST_CHECKS=y to disable this check" 1972 | fi 1973 | } 1974 | 1975 | eval_env() { 1976 | while test -n "$1" 1977 | do 1978 | case "$1" in 1979 | (*=*) 1980 | key=${1%%=*} 1981 | val=${1#*=} 1982 | find_var $key "$ENVVARS $ALLOW_ENVVARS" || error_clean_param "$key" 1983 | eval "$key=\"$val\"" 1984 | ;; 1985 | (*) 1986 | die "Expecting key=value pair, got [$1]" 1987 | ;; 1988 | esac 1989 | shift 1990 | done 1991 | } 1992 | 1993 | # $1: prompt 1994 | get_confirmation() 1995 | { 1996 | echo -n "$1" 1997 | read -p "(y/n) :" ANSWER 1998 | echo 1999 | 2000 | if test $ANSWER != "y" 2001 | then 2002 | echo "Aborted." 2003 | exit 2004 | fi 2005 | } 2006 | 2007 | get_sys_time() { 2008 | local os=$(uname) 2009 | case "$os" in 2010 | # Do not use floating point values so that we can avoid using bc for 2011 | # computations. 2012 | #Linux | MINGW*) date +%s.%N ;; 2013 | Linux | FreeBSD | MINGW*) date +%s ;; 2014 | Darwin) date +%s ;; 2015 | *) die "Unknown OS [$os]" ;; 2016 | esac 2017 | } 2018 | 2019 | get_rel_time() { 2020 | echo $((`get_sys_time` - ${BASE_TIME})) 2021 | } 2022 | 2023 | #------------------------------------------------------------------------------ 2024 | # Main flow of script starts here 2025 | #------------------------------------------------------------------------------ 2026 | 2027 | # This leads to unexpected and undebuggable behavior especially when shell 2028 | # functions are returning non-zero exit codes. 2029 | #set -e 2030 | set -o pipefail 2031 | test -n "$BASE_TIME" || BASE_TIME=$(get_sys_time) 2032 | 2033 | test -n "$1" \ 2034 | || { short_help; echo -e "\nTry --help for detailed help"; exit 1; } 2035 | 2036 | #------------------------------------------------------------------------------ 2037 | 2038 | # Need these to produce help 2039 | # Determine home independent of the environment 2040 | export HOME=$(echo ~) 2041 | set_os_specific_vars # depends on HOME 2042 | 2043 | LOCAL_BIN=$OS_APP_HOME/$OS_LOCAL_DIR/bin 2044 | HLINT_PATH="${LOCAL_BIN}/hlint" 2045 | HLINT_URL_PREFIX="https://github.com/ndmitchell/hlint/releases" 2046 | 2047 | DOCSPEC_PATH="${LOCAL_BIN}/cabal-docspec" 2048 | DOCSPEC_URL_PREFIX="https://github.com/phadej/cabal-extras/releases/" 2049 | 2050 | # XXX On windows this should be ghcup instead of .ghcup? See 2051 | # set_os_specific_vars 2052 | GHCUP_BIN="${OS_APP_HOME}/.ghcup/bin" 2053 | GHCUP_PATH="${GHCUP_BIN}/ghcup" 2054 | GHCUP_URL_PREFIX="https://downloads.haskell.org/~ghcup" 2055 | 2056 | #------------------------------------------------------------------------------ 2057 | 2058 | case $1 in 2059 | cabal) shift; eval_env "$@"; BUILD=cabal-v2;; 2060 | cabal-v1) die "cabal-v1 is not supported, please use cabal-v2 instead";; 2061 | cabal-new) die "cabal-new is not supported, please use cabal-v2 instead";; 2062 | cabal-v2) shift; eval_env "$@"; BUILD=cabal-v2;; 2063 | stack) shift; eval_env "$@"; BUILD=stack;; 2064 | hlint) shift; eval_env "$@"; BUILD=hlint;; 2065 | clean) rm -rf .packcheck; exit;; 2066 | cleanall) 2067 | rm -rf .packcheck .stack-work 2068 | exit;; 2069 | -h | --help | help) show_help; exit;; 2070 | --version) show_version; exit;; 2071 | *) echo -e "Error: First argument must be a command\n" 2072 | short_help; exit 1 ;; 2073 | esac 2074 | 2075 | test -z "$CHECK_ENV" || check_boolean_var CHECK_ENV 2076 | test -z "$CHECK_ENV" || check_clean_env 2077 | 2078 | echo 2079 | bash --version 2080 | 2081 | show_step "Build command" 2082 | show_build_command 2083 | 2084 | TOOLS="awk cat curl cut date env head mkdir printf rm sleep tr which sort \ 2085 | $OS_HAS_TOOLS" 2086 | 2087 | show_step "Check basic tools" 2088 | require_cmd bash 2089 | for i in $TOOLS; do require_cmd $i; done 2090 | 2091 | show_step "Build host machine information" 2092 | show_machine_info 2093 | 2094 | # ---------Show, process and verify the config------------ 2095 | show_step "Build environment" 2096 | show_build_env 2097 | 2098 | # Set path for installed utilities, e.g. stack, cabal, hpc-coveralls 2099 | echo 2100 | echo "Original PATH is [$PATH]..." 2101 | if test "$BUILD" = "cabal-v2" 2102 | then 2103 | export PATH=$OS_APP_HOME/$OS_CABAL_DIR/bin:$PATH 2104 | fi 2105 | export PATH=$LOCAL_BIN:$PATH 2106 | echo 2107 | echo "Added $LOCAL_BIN to PATH [$PATH]" 2108 | 2109 | # if we are running from a stack environment remove GHC_PACKAGE_PATH so that 2110 | # cabal does not complain 2111 | # XXX this should be done from outside via env 2112 | unset GHC_PACKAGE_PATH 2113 | # stack does not work well with empty STACK_YAML env var 2114 | test -n "$STACK_YAML" || unset STACK_YAML 2115 | 2116 | CABAL_BINARY_NAME=cabal 2117 | if test "$BUILD" = "hlint" 2118 | then 2119 | hlint_path=$(which_cmd hlint) 2120 | if test -n "$hlint_path" 2121 | then 2122 | echo 2123 | echo "WARNING! Using hlint in PATH at $hlint_path" 2124 | elif test -n "$HLINT_VERSION" 2125 | then 2126 | install_hlint 2127 | elif test -n "$HLINT_BUILD" 2128 | then 2129 | # XXX This is broken as it expects BUILD to be either stack or cabal. 2130 | # Also, we need to initialize COMPILER variable before this which is 2131 | # done in verify_build_config happening after this. 2132 | build_hlint 2133 | else 2134 | echo "hlint not found." 2135 | die "Use HLINT_VERSION option to install." 2136 | fi 2137 | run_verbose_errexit hlint --version 2138 | run_hlint 2139 | else 2140 | verify_build_config 2141 | build_compile 2142 | if test -n "$ENABLE_DOCSPEC" 2143 | then 2144 | if test $BUILD = "cabal-v2" 2145 | then 2146 | docspec_path=$(which_cmd cabal-docspec) 2147 | if test -n "$docspec_path" 2148 | then 2149 | echo "WARNING! Using cabal-docspec in PATH at $docspec_path" 2150 | elif test -n "$DOCSPEC_URL" 2151 | then 2152 | install_docspec 2153 | else 2154 | echo "cabal-docspec not found." 2155 | die "Use DOCSPEC_URL option to install." 2156 | fi 2157 | fi 2158 | ensure_ghc 2159 | run_verbose_errexit cabal-docspec --version 2160 | run_verbose_errexit cabal-docspec $DOCSPEC_OPTIONS \ 2161 | --with-compiler "$COMPILER_EXE_PATH" 2162 | fi 2163 | fi 2164 | 2165 | show_step "Done" 2166 | -------------------------------------------------------------------------------- /src/Hello.hs: -------------------------------------------------------------------------------- 1 | module Hello 2 | (hello) 3 | where 4 | 5 | hello = print "Hello world!" 6 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-23.9 2 | packages: 3 | - '.' 4 | -------------------------------------------------------------------------------- /test/Main.hs: -------------------------------------------------------------------------------- 1 | import Hello 2 | 3 | main = hello 4 | --------------------------------------------------------------------------------