├── test ├── test-data │ ├── project-with-package-env │ │ ├── lib │ │ │ └── .gitkeep │ │ ├── dist-newstyle │ │ │ └── packagedb │ │ │ │ └── ghc-8.4.3 │ │ │ │ ├── package.cache │ │ │ │ └── flycheck-haskell-test-0.1.0.0-inplace.conf │ │ ├── foo.cabal │ │ ├── .ghc.environment.x86_64-darwin-8.4.3 │ │ └── .ghc.environment.x86_64-linux-8.4.3 │ ├── project-with-cabal-file │ │ ├── cabal.config │ │ ├── cabal.sandbox.config │ │ └── flycheck-haskell-test.cabal │ ├── project-with-prelude-module │ │ ├── foo.cabal │ │ └── Prelude.hs │ └── project-with-package-yaml │ │ └── package.yaml └── flycheck-haskell-test.el ├── .hlint.yaml ├── stack-7.8.yaml ├── stack-7.10.yaml ├── stack-8.0.yaml ├── stack-8.2.yaml ├── Cask ├── .gitignore ├── .mailmap ├── .dir-locals.el ├── Makefile ├── .github └── workflows │ └── ci.yml ├── README.md ├── CHANGES.md ├── flycheck-haskell.el ├── COPYING └── get-cabal-configuration.hs /test/test-data/project-with-package-env/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.hlint.yaml: -------------------------------------------------------------------------------- 1 | 2 | - ignore: {name: "Use fewer imports"} 3 | -------------------------------------------------------------------------------- /stack-7.8.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-2.22 2 | require-stack-version: '>= 0.1.8.0' -------------------------------------------------------------------------------- /stack-7.10.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-6.35 2 | require-stack-version: '>= 0.1.8.0' 3 | -------------------------------------------------------------------------------- /stack-8.0.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-9.21 2 | require-stack-version: '>= 0.1.8.0' 3 | -------------------------------------------------------------------------------- /stack-8.2.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-11.22 2 | require-stack-version: '>= 1.5' 3 | -------------------------------------------------------------------------------- /test/test-data/project-with-cabal-file/cabal.config: -------------------------------------------------------------------------------- 1 | with-compiler: /foo/bar/ghc-7.10 2 | -------------------------------------------------------------------------------- /test/test-data/project-with-package-env/dist-newstyle/packagedb/ghc-8.4.3/package.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flycheck/flycheck-haskell/HEAD/test/test-data/project-with-package-env/dist-newstyle/packagedb/ghc-8.4.3/package.cache -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa) 3 | 4 | (package-file "flycheck-haskell.el") 5 | 6 | (files 7 | "flycheck-haskell.el" 8 | "get-flags.hs" 9 | "get-cabal-configuration.hs") 10 | 11 | (development 12 | (depends-on "cl-lib")) 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | *.o 3 | *.hi 4 | 5 | /get-cabal-configuration 6 | /get-flags 7 | 8 | /.cask/ 9 | /dist/ 10 | /build/ 11 | 12 | /.stack-work/ 13 | 14 | test/test-data/project-with-cabal-file/dist-newstyle/ 15 | test/test-data/project-with-package-env/dist-newstyle/cache/ 16 | test/test-data/project-with-prelude-module/dist-newstyle/ 17 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Alex Rozenshteyn 2 | Gracjan Polak 3 | Mark Karpov 4 | Michael Alan Dorman 5 | Sebastian Wiesner 6 | Sebastian Wiesner 7 | Steve Purcell 8 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;;; Directory Local Variables 2 | ;;; For more information see (info "(emacs) Directory Variables") 3 | 4 | ((nil 5 | (bug-reference-url-format . "https://github.com/flycheck/flycheck-haskell/issues/%s")) 6 | (haskell-mode 7 | (hindent-style . "johan-tibell")) 8 | (markdown-mode 9 | (bug-reference-bug-regexp . "\\[GH-\\(?2:[[:digit:]]+\\)]"))) 10 | -------------------------------------------------------------------------------- /test/test-data/project-with-package-env/foo.cabal: -------------------------------------------------------------------------------- 1 | -- A Cabal file for use in the test cases of Flycheck Haskell. 2 | 3 | name: flycheck-haskell-test 4 | version: 0.1.0.0 5 | build-type: Simple 6 | cabal-version: >=1.10 7 | 8 | library 9 | hs-source-dirs: lib/ 10 | extensions: OverloadedStrings 11 | build-depends: base >=4.7 && <4.13 12 | default-language: Haskell2010 13 | ghc-options: -fwarn-haha-no-such-option -Wall 14 | -------------------------------------------------------------------------------- /test/test-data/project-with-prelude-module/foo.cabal: -------------------------------------------------------------------------------- 1 | -- A Cabal file for use in the test cases of Flycheck Haskell. 2 | 3 | name: flycheck-haskell-test 4 | version: 0.1.0.0 5 | build-type: Simple 6 | cabal-version: >=1.10 7 | 8 | library 9 | hs-source-dirs: lib/ 10 | extensions: OverloadedStrings 11 | build-depends: base >=4.7 && <4.8 12 | default-language: Haskell2010 13 | ghc-options: -fwarn-haha-no-such-option -Wall 14 | -------------------------------------------------------------------------------- /test/test-data/project-with-prelude-module/Prelude.hs: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | -- | 3 | -- Module : Prelude 4 | -- Copyright : (c) Sergey Vinokurov 2017 5 | -- License : BSD3-style (see LICENSE) 6 | -- 7 | -- Maintainer : serg.foo@gmail.com 8 | -- Created : 17 June 2017 9 | -- Stability : 10 | -- Portability : 11 | -- 12 | -- 13 | ---------------------------------------------------------------------------- 14 | 15 | module Prelude (foo) where 16 | 17 | import Prelude () 18 | 19 | foo :: a -> a 20 | foo x = x 21 | -------------------------------------------------------------------------------- /test/test-data/project-with-package-env/.ghc.environment.x86_64-darwin-8.4.3: -------------------------------------------------------------------------------- 1 | -- This is a GHC environment file written by cabal. This means you can 2 | -- run ghc or ghci and get the environment of the project as a whole. 3 | -- But you still need to use cabal repl $target to get the environment 4 | -- of specific components (libs, exes, tests etc) because each one can 5 | -- have its own source dirs, cpp flags etc. 6 | -- 7 | clear-package-db 8 | global-package-db 9 | package-db dist-newstyle/packagedb/ghc-8.4.3 10 | package-id flycheck-haskell-test-0.1.0.0-inplace 11 | package-id base-4.11.1.0 12 | package-id rts 13 | package-id ghc-prim-0.5.2.0 14 | package-id integer-gmp-1.0.2.0 15 | -------------------------------------------------------------------------------- /test/test-data/project-with-package-env/.ghc.environment.x86_64-linux-8.4.3: -------------------------------------------------------------------------------- 1 | -- This is a GHC environment file written by cabal. This means you can 2 | -- run ghc or ghci and get the environment of the project as a whole. 3 | -- But you still need to use cabal repl $target to get the environment 4 | -- of specific components (libs, exes, tests etc) because each one can 5 | -- have its own source dirs, cpp flags etc. 6 | -- 7 | clear-package-db 8 | global-package-db 9 | package-db dist-newstyle/packagedb/ghc-8.4.3 10 | package-id flycheck-haskell-test-0.1.0.0-inplace 11 | package-id base-4.11.1.0 12 | package-id rts 13 | package-id ghc-prim-0.5.2.0 14 | package-id integer-gmp-1.0.2.0 15 | -------------------------------------------------------------------------------- /test/test-data/project-with-cabal-file/cabal.sandbox.config: -------------------------------------------------------------------------------- 1 | -- This is a Cabal package environment file, for use in our unit test suite. 2 | 3 | local-repo: /foo/bar/.cabal-sandbox/packages 4 | logs-dir: /foo/bar/.cabal-sandbox/logs 5 | world-file: /foo/bar/.cabal-sandbox/world 6 | user-install: False 7 | package-db: /foo/bar/.cabal-sandbox/foo-packages.conf.d 8 | build-summary: /foo/bar/.cabal-sandbox/logs/build.log 9 | 10 | install-dirs 11 | prefix: /foo/bar/.cabal-sandbox 12 | bindir: $prefix/bin 13 | libdir: $prefix/lib 14 | libsubdir: $abi/$pkgkey 15 | libexecdir: $prefix/libexec 16 | datadir: $prefix/share 17 | datasubdir: $abi/$pkgid 18 | docdir: $datadir/doc/$abi/$pkgid 19 | htmldir: $docdir/html 20 | haddockdir: $htmldir 21 | sysconfdir: $prefix/etc 22 | -------------------------------------------------------------------------------- /test/test-data/project-with-package-env/dist-newstyle/packagedb/ghc-8.4.3/flycheck-haskell-test-0.1.0.0-inplace.conf: -------------------------------------------------------------------------------- 1 | name: flycheck-haskell-test 2 | version: 0.1.0.0 3 | id: flycheck-haskell-test-0.1.0.0-inplace 4 | key: flycheck-haskell-test-0.1.0.0-inplace 5 | abi: inplace 6 | exposed: True 7 | data-dir: /home/danny/Develop/emacs/flycheck-haskell/test/test-data/project-with-package-env 8 | depends: 9 | base-4.11.1.0 10 | abi-depends: base-4.11.1.0=9da3f387598d41928f3ebf8097acf9b3 11 | haddock-interfaces: /home/danny/Develop/emacs/flycheck-haskell/test/test-data/project-with-package-env/dist-newstyle/build/x86_64-linux/ghc-8.4.3/flycheck-haskell-test-0.1.0.0/doc/html/flycheck-haskell-test/flycheck-haskell-test.haddock 12 | haddock-html: /home/danny/Develop/emacs/flycheck-haskell/test/test-data/project-with-package-env/dist-newstyle/build/x86_64-linux/ghc-8.4.3/flycheck-haskell-test-0.1.0.0/doc/html/flycheck-haskell-test 13 | -------------------------------------------------------------------------------- /test/test-data/project-with-cabal-file/flycheck-haskell-test.cabal: -------------------------------------------------------------------------------- 1 | -- A Cabal file for use in the test cases of Flycheck Haskell. 2 | 3 | name: flycheck-haskell-test 4 | version: 0.1.0.0 5 | build-type: Simple 6 | cabal-version: >=1.10 7 | 8 | library 9 | hs-source-dirs: lib/ 10 | extensions: OverloadedStrings 11 | build-depends: base >=4.7 && <4.8 12 | default-language: Haskell98 13 | cpp-options: -DDEBUG=1 14 | 15 | executable flycheck-haskell-test 16 | hs-source-dirs: src/ 17 | default-extensions: GeneralizedNewtypeDeriving 18 | build-depends: base >=4.7 && <4.8, 19 | bytestring 20 | default-language: Haskell2010 21 | ghc-options: -Wall 22 | 23 | executable flycheck-haskell-unknown-stuff 24 | -- This shall test the handling of unknown language and extension values 25 | build-depends: base >=4.7 && <4.8 26 | extensions: YouDontKnowThisOne 27 | default-language: SpamLanguage 28 | -------------------------------------------------------------------------------- /test/test-data/project-with-package-yaml/package.yaml: -------------------------------------------------------------------------------- 1 | name: flycheck-haskell-hpack-test 2 | version: 0.0.0.2 3 | category: "Test" 4 | synopsis: Testing how flycheck works with hpack's package.yaml 5 | license: "BSD3" 6 | author: "Sergey Vinokurov" 7 | 8 | ghc-options: -Wall -fwarn-name-shadowing -Werror 9 | 10 | extra-source-files: 11 | - docs/* 12 | 13 | dependencies: 14 | - base == 4.* 15 | - vector 16 | 17 | library: 18 | source-dirs: 19 | - lib/ 20 | exposed-modules: 21 | - Test1 22 | - Test2 23 | cpp-options: 24 | - "-DDEBUG=1" 25 | default-extensions: OverloadedStrings 26 | 27 | executables: 28 | flycheck-haskell-test: 29 | source-dirs: "src/" 30 | main: Test.hs 31 | default-extensions: GeneralizedNewtypeDeriving 32 | dependencies: 33 | - base >=4.7 && <4.8 34 | - bytestring 35 | ghc-options: -Wcompat 36 | 37 | flycheck-haskell-unknown-stuff: 38 | main: FlycheckUnknown.hs 39 | # This shall test the handling of unknown language and extension values 40 | dependencies: base >=4.7 && <4.8 41 | default-extensions: YouDontKnowThisOne 42 | 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EMACS = emacs 2 | EMACSFLAGS = 3 | GHC = ghc 4 | GHCFLAGS = -Wall -Werror -O1 5 | HLINT = hlint 6 | HLINTFLAGS = 7 | CASK = cask 8 | PKGDIR := $(shell EMACS=$(EMACS) $(CASK) package-directory) 9 | 10 | # Export the used EMACS to recipe environments 11 | export EMACS 12 | 13 | HS_BUILDDIR = build/hs 14 | EL_SRCS = flycheck-haskell.el 15 | EL_OBJS = $(EL_SRCS:.el=.elc) 16 | HS_SRCS = get-cabal-configuration.hs 17 | HS_OBJS = $(HS_SRCS:.hs=) 18 | PACKAGE = flycheck-haskell-$(VERSION).tar 19 | 20 | EMACSBATCH = $(EMACS) -Q --batch $(EMACSFLAGS) 21 | 22 | .PHONY: compile dist \ 23 | lint test \ 24 | clean clean-elc clean-dist clean-deps \ 25 | deps \ 26 | check 27 | 28 | # Build targets 29 | compile : $(EL_OBJS) $(HS_OBJS) 30 | 31 | dist : 32 | $(CASK) package 33 | 34 | # Test targets 35 | lint : 36 | $(HLINT) $(HLINTFLAGS) $(HS_SRCS) 37 | 38 | test : $(EL_OBJS) 39 | $(CASK) exec $(EMACSBATCH) -l flycheck-haskell.elc \ 40 | -l test/flycheck-haskell-test.el -f ert-run-tests-batch-and-exit 41 | 42 | # Support targets 43 | deps : $(PKGDIR) 44 | 45 | # Cleanup targets 46 | clean : clean-elc clean-hs clean-dist clean-deps 47 | 48 | clean-elc : 49 | rm -rf $(EL_OBJS) 50 | 51 | clean-hs: 52 | rm -rf $(HS_OBJS) $(HS_BUILDDIR) 53 | 54 | clean-dist : 55 | rm -rf dist/ 56 | 57 | clean-deps : 58 | rm -rf $(PKGDIR) 59 | 60 | # File targets 61 | %.elc : %.el $(PKGDIR) 62 | $(CASK) exec $(EMACSBATCH) -f batch-byte-compile $< 63 | 64 | %: %.hs 65 | $(GHC) $(GHCFLAGS) -outputdir $(HS_BUILDDIR) -o $@ $< 66 | 67 | check: $(EL_OBJS) 68 | $(GHC) $(GHCFLAGS) -fno-code get-cabal-configuration.hs 69 | 70 | $(PKGDIR) : Cask 71 | $(CASK) install 72 | touch $(PKGDIR) 73 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: {} # Validate all PRs 7 | schedule: 8 | # Run once per month. 9 | - cron: '0 0 1 * *' 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | jobs: 16 | build: 17 | name: ${{ matrix.os }} - ${{ matrix.ghc }} 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | fail-fast: true 21 | matrix: 22 | os: ['ubuntu-latest'] 23 | ghc: ['7.10', '8.0', '8.2', '8.4', '8.6', '8.8', '8.10', '9.0', '9.2'] 24 | hpack: [false] 25 | link: [false] 26 | include: 27 | - os: 'macOS-latest' 28 | ghc: 'latest' 29 | hpack: false 30 | link: true 31 | # Configuration with hpack enabled 32 | - os: 'ubuntu-latest' 33 | ghc: 'latest' 34 | hpack: true 35 | link: true 36 | steps: 37 | - uses: actions/checkout@v2 38 | - uses: haskell/actions/setup@v1.2 39 | id: setup-haskell-cabal 40 | with: 41 | ghc-version: ${{ matrix.ghc }} 42 | - name: Update cabal package database 43 | run: cabal update 44 | 45 | - uses: actions/setup-python@v2 46 | with: 47 | python-version: '3.6' 48 | architecture: 'x64' 49 | 50 | - uses: purcell/setup-emacs@master 51 | with: 52 | version: '28.1' 53 | 54 | - uses: conao3/setup-cask@master 55 | with: 56 | version: '0.8.8' 57 | 58 | 59 | - name: Check 60 | if: ${{ !matrix.link }} 61 | run: make check 62 | 63 | - name: Compile 64 | if: ${{ matrix.link }} 65 | run: make compile 66 | 67 | - name: 'Set up HLint' 68 | uses: haskell/actions/hlint-setup@v2 69 | 70 | - name: 'Run HLint' 71 | uses: haskell/actions/hlint-run@v2 72 | 73 | - uses: actions/cache@v2 74 | name: Cache cabal stuff 75 | if: ${{ matrix.hpack }} 76 | with: 77 | path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }} 78 | key: ${{ runner.os }}-${{ matrix.ghc }} 79 | 80 | - name: Install hpack 81 | if: ${{ matrix.hpack }} 82 | run: cabal install hpack 83 | 84 | - name: Run tests 85 | run: | 86 | PATH="/home/runner/.cabal/bin/:$PATH" make test 87 | 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | flycheck-haskell — Flycheck for Haskell 2 | ======================================= 3 | 4 | [![License GPL 3][badge-license]][copying] 5 | [![MELPA][badge-melpa]](http://melpa.org/#/flycheck-haskell) 6 | [![MELPA Stable][badge-melpa-stable]](http://stable.melpa.org/#/flycheck-haskell) 7 | [![Build Status][badge-travis]](https://travis-ci.org/flycheck/flycheck-haskell) 8 | 9 | Automatically configure [Flycheck][] for Haskell. 10 | 11 | Installation 12 | ------------ 13 | 14 | Install `flycheck-haskell` from [MELPA][] or [MELPA Stable][] and add the 15 | following to your `init.el`: 16 | 17 | ```cl 18 | (add-hook 'haskell-mode-hook #'flycheck-haskell-setup) 19 | ``` 20 | 21 | Supported GHC versions 22 | ---------------------- 23 | 24 | Tested with GHC `7.2.2`, `7.4.2`, `7.6.3`, `7.8.4`, `7.10.3`, `8.0.2`, `8.2.2`, `8.4.1`, `8.6.5`, `8.8.1`. 25 | 26 | Usage 27 | ----- 28 | 29 | Just use Flycheck as usual in your Cabal projects. 30 | 31 | To explicitly configure Haskell syntax checking for the current buffer, type 32 | M-x flycheck-haskell-configure. You should run this command after 33 | major changes to the Cabal file. 34 | 35 | Customization 36 | ------------- 37 | 38 | - M-x customize-group RET flycheck-haskell 39 | 40 | License 41 | ------- 42 | 43 | This program is free software: you can redistribute it and/or modify it under 44 | the terms of the GNU General Public License as published by the Free Software 45 | Foundation, either version 3 of the License, or (at your option) any later 46 | version. 47 | 48 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 49 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 50 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 51 | 52 | You should have received a copy of the GNU General Public License along with 53 | this program. If not, see http://www.gnu.org/licenses/. 54 | 55 | See [`COPYING`][copying] for details. 56 | 57 | [badge-license]: https://img.shields.io/badge/license-GPL_3-green.svg?dummy 58 | [COPYING]: https://github.com/flycheck/flycheck-haskell/blob/master/COPYING 59 | [badge-melpa]: http://melpa.org/packages/flycheck-haskell-badge.svg 60 | [badge-melpa-stable]: http://stable.melpa.org/packages/flycheck-haskell-badge.svg 61 | [badge-travis]: https://travis-ci.org/flycheck/flycheck-haskell.svg?branch=master 62 | [Flycheck]: https://www.flycheck.org 63 | [Cask]: https://github.com/cask/cask 64 | [MELPA]: http://melpa.org 65 | [MELPA Stable]: http://stable.melpa.org 66 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | master (in development) 2 | ======================= 3 | 4 | - Support GHC 8.2 with Cabal 2.0 [GH-66] 5 | - Use `cpp-options` cabal file field to get more options for GHC [GH-68] 6 | - Fix obtaining configuration from cabal file if current directory has Prelude module in it [GH-70] 7 | - Get correct location of stack’s dist directory. Fixes the case when GHC was installed via stack [GH-73] 8 | - Improve `flycheck-haskell-setup` to perform initialization the first time a Haskell file is opened [https://github.com/flycheck/flycheck/issues/1346] 9 | - Support `hpack` package description format [GH-85] 10 | - Properly collect options from foreign library sections available starting with Cabal 2.0 11 | - Fix escaping issues with paths on Windows [GH-102] 12 | - Support GHC 9.2.2 with Cabal 3.6 [GH-114] 13 | 14 | 0.8 (May 24, 2016) 15 | ================== 16 | 17 | - Remove `flycheck-haskell-runhaskell` in favour of `flycheck-haskell-runghc` 18 | - Use `stack runghc` by default 19 | - Only make dependent packages visible to GHC [GH-47] [GH-48] 20 | - Add Stack build directories to GHC search path [GH-48] 21 | - Add Cabal macros to GHC and hlint [GH-51] 22 | - Make sure to use the right Cabal library with Stack [GH-60] 23 | 24 | 0.7.2 (Jun 02, 2015) 25 | ==================== 26 | 27 | - Don’t choke when a configuration key is missing [GH-37] 28 | 29 | 0.7.1 (May 30, 2015) 30 | ==================== 31 | 32 | - Don’t choke when sandbox no is present [GH-35] 33 | - Don’t change GHC executable when compiler is not configured 34 | 35 | 0.7 (May 28, 2015) 36 | ================== 37 | 38 | - Extract compiler from `cabal.config` [GH-28] [GH-29] 39 | - Handle Cabal conditionals [GH-31] 40 | 41 | 0.6 (Apr 04, 2015) 42 | ================== 43 | 44 | - Fix error when `default-directory` does not exist 45 | - Extract various additional GHC options [GH-25] [GH-26] 46 | - Extract dependencies to avoid package conflicts [GH-25] [GH-26] 47 | 48 | 0.5.1 (Dec 27, 2014) 49 | ==================== 50 | 51 | - Explicitly set local values of variables 52 | 53 | 0.5 (Oct 3, 2014) 54 | ================= 55 | 56 | - Extract language extensions from Cabal projects [GH-3] 57 | - Set the language from Cabal [GH-9] 58 | - Merge all helpers into a single one [GH-13] 59 | - Cache cabal configurations [GH-16] [GH-18] 60 | 61 | 0.4 (Apr 25, 2014) 62 | ================== 63 | 64 | - Add build files from executables to GHC path 65 | - Add interactive `flycheck-haskell-configure` to explicitly re-configure 66 | Flycheck 67 | 68 | 0.3 (Apr 14, 2014) 69 | ================== 70 | 71 | - Use sandboxes even without Cabal files 72 | - Add build files from Cabal to GHC path 73 | 74 | 0.2 (Apr 3, 2014) 75 | ================= 76 | 77 | - Postpone setup until after local variables were set up 78 | - Add auto-generated files from Cabal to GHC path 79 | 80 | 0.1 (Jan 13, 2014) 81 | ================== 82 | 83 | Initial release 84 | -------------------------------------------------------------------------------- /flycheck-haskell.el: -------------------------------------------------------------------------------- 1 | ;;; flycheck-haskell.el --- Flycheck: Automatic Haskell configuration -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016-2018 Sergey Vinokurov 4 | ;; Copyright (C) 2014-2016 Sebastian Wiesner 5 | ;; Copyright (C) 2016-2018 Danny Navarro 6 | ;; Copyright (C) 2015 Mark Karpov 7 | ;; Copyright (C) 2015 Michael Alan Dorman 8 | ;; Copyright (C) 2015 Alex Rozenshteyn 9 | ;; Copyright (C) 2014 Gracjan Polak 10 | 11 | ;; Author: Sebastian Wiesner 12 | ;; URL: https://github.com/flycheck/flycheck-haskell 13 | ;; Keywords: tools, convenience 14 | ;; Version: 0.9-cvs 15 | ;; Package-Requires: ((emacs "24.3") (flycheck "0.25") (haskell-mode "13.7") (dash "2.4.0") (seq "1.11") (let-alist "1.0.1")) 16 | 17 | ;; This file is not part of GNU Emacs. 18 | 19 | ;; This program is free software; you can redistribute it and/or modify 20 | ;; it under the terms of the GNU General Public License as published by 21 | ;; the Free Software Foundation, either version 3 of the License, or 22 | ;; (at your option) any later version. 23 | 24 | ;; This program is distributed in the hope that it will be useful, 25 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | ;; GNU General Public License for more details. 28 | 29 | ;; You should have received a copy of the GNU General Public License 30 | ;; along with this program. If not, see . 31 | 32 | ;;; Commentary: 33 | 34 | ;; Automatically configure Flycheck for Haskell. 35 | 36 | ;;;; Cabal support 37 | 38 | ;; Try to find a Cabal file for the current Haskell buffer, and configure syntax 39 | ;; checking according to the Cabal project settings. 40 | 41 | ;;;; Cabal sandboxes 42 | 43 | ;; Try to find a Cabal sandbox configuration for this project, and configure the 44 | ;; Haskell syntax checkers in Flycheck to use the package database from the 45 | ;; Sandbox. 46 | 47 | ;;;; Stack support 48 | 49 | ;; Try to find a stack.yaml file for current project and configure stack projct 50 | ;; according to the Stack project settings. 51 | 52 | ;;;; Setup 53 | 54 | ;; (add-hook 'flycheck-mode-hook #'flycheck-haskell-setup) 55 | 56 | ;;; Code: 57 | 58 | (eval-when-compile 59 | (require 'rx) 60 | (require 'let-alist)) 61 | 62 | (require 'seq) 63 | (require 'haskell-cabal) 64 | (require 'flycheck) 65 | (require 'dash) 66 | 67 | 68 | ;;; Customization 69 | 70 | (defgroup flycheck-haskell nil 71 | "Haskell support for Flycheck." 72 | :prefix "flycheck-haskell-" 73 | :group 'flycheck 74 | :link '(url-link :tag "Github" "https://github.com/flycheck/flycheck-haskell")) 75 | 76 | (defcustom flycheck-haskell-runghc-command 77 | (let ((stack-exe (funcall flycheck-executable-find "stack")) 78 | (runghc-exe (funcall flycheck-executable-find "runghc"))) 79 | (cond 80 | (stack-exe 81 | `(,stack-exe "--verbosity" "silent" "runghc" "--no-ghc-package-path" "--" "-i" 82 | "-packageCabal" 83 | "-packageCabal-syntax" 84 | "-packagebase" 85 | "-packagebytestring" 86 | "-packagecontainers" 87 | "-packageprocess" 88 | "-packagedirectory" 89 | "-packagefilepath")) 90 | (runghc-exe 91 | `(,runghc-exe "--" "-i" 92 | "-packageCabal" 93 | "-packageCabal-syntax" 94 | "-packagebase" 95 | "-packagebytestring" 96 | "-packagecontainers" 97 | "-packageprocess" 98 | "-packagedirectory" 99 | "-packagefilepath")) 100 | (t 101 | ;; A reasonable default. 102 | '("runghc" "-i")))) 103 | "Command for `runghc'. 104 | 105 | This library uses `runghc' to run various Haskell helper scripts 106 | to extract information from Cabal files. This option provides 107 | the command to invoke `runghc'. The default is to use `stack' 108 | and otherwise fall back to standard `runghc'." 109 | :type '(repeat (string :tag "Command")) 110 | :risky t 111 | :group 'flycheck-haskell) 112 | 113 | (defcustom flycheck-haskell-hpack-executable (funcall flycheck-executable-find "hpack") 114 | "Path to the `hpack' executable. 115 | 116 | This library uses `hpack' to get package configuration if `package.yaml' file 117 | is present. This option provides the path to the `hpack' executable. The nil 118 | value will make this library ignore `package.yaml' file, even if it's present." 119 | :type 'string 120 | :risky t 121 | :group 'flycheck-haskell) 122 | 123 | (defcustom flycheck-haskell-hpack-preference 'prefer-hpack 124 | "How to handle projects with both `.cabal' and `package.yaml' files present. 125 | 126 | This option controls which configuration file this library will pick for 127 | a project that has both `.cabal' and `package.yaml' files present. 128 | The default, 'prefer-hpack, will make it pick `package.yaml' file as the source 129 | of configuration parameters. Another possible value, 'prefer-cabal will 130 | make it pick `.cabal' file in such a case." 131 | :group 'flycheck-haskell 132 | :type '(set (const :tag "Prefer hpack's \"package.yaml\" file" prefer-hpack) 133 | (const :tag "Prefer cabal's \".cabal\" file" prefer-cabal)) 134 | :safe #'symbolp) 135 | 136 | 137 | ;;; Cabal support 138 | (defconst flycheck-haskell-directory 139 | (file-name-directory (if load-in-progress 140 | load-file-name 141 | (buffer-file-name))) 142 | "The package directory of flycheck-haskell.") 143 | 144 | (defconst flycheck-haskell-helper 145 | (expand-file-name "get-cabal-configuration.hs" flycheck-haskell-directory) 146 | "The helper to dump the Cabal configuration.") 147 | 148 | (defun flycheck-haskell-runghc-command (args) 149 | "Create a runghc command with ARGS. 150 | 151 | Take the base command from `flycheck-haskell-runghc-command'." 152 | (append flycheck-haskell-runghc-command args nil)) 153 | 154 | (defun flycheck-haskell--read-configuration-with-helper (command) 155 | ;; Copy enivronment variables into the new process, since 156 | ;; with-temp-buffer will re-use the variables' defaults, even if 157 | ;; they have been changed in this buffer by e.g. envrc-mode. 158 | ;; See https://github.com/purcell/envrc/issues/12. 159 | (let ((env process-environment) 160 | (path exec-path)) 161 | (with-temp-buffer 162 | ;; Copy the entire environment just in case there's something we need. 163 | (setq-local process-environment env) 164 | ;; Set path so we can find the command. 165 | (setq-local exec-path path) 166 | ;; Hack around call-process' limitation handling standard error 167 | (let ((error-file (make-temp-file "flycheck-haskell-errors"))) 168 | (pcase (apply 'call-process (car command) nil (list t error-file) nil (cdr command)) 169 | (0 (delete-file error-file) 170 | (goto-char (point-min)) 171 | (read (current-buffer))) 172 | (retcode (insert-file-contents error-file) 173 | (delete-file error-file) 174 | (message "Reading Haskell configuration failed with exit code %s and output:\n%s" 175 | retcode (buffer-string)) 176 | nil)))))) 177 | 178 | (defun flycheck-haskell-read-cabal-configuration (cabal-file) 179 | "Read the Cabal configuration from CABAL-FILE." 180 | (let ((args (list flycheck-haskell-helper "--cabal-file" (expand-file-name cabal-file)))) 181 | (flycheck-haskell--read-configuration-with-helper 182 | (flycheck-haskell-runghc-command args)))) 183 | 184 | (defun flycheck-haskell-read-hpack-configuration (hpack-file) 185 | "Read the hpack configuration from HPACK-FILE." 186 | (cl-assert flycheck-haskell-hpack-executable) 187 | (let ((args (list flycheck-haskell-helper 188 | "--hpack-exe" flycheck-haskell-hpack-executable 189 | "--hpack-file" (expand-file-name hpack-file)))) 190 | (flycheck-haskell--read-configuration-with-helper 191 | (flycheck-haskell-runghc-command args)))) 192 | 193 | (defun flycheck-haskell--delete-dups (xs) 194 | "Remove duplicates from a list XS using `equal'. Leaves initial 195 | list unchanged." 196 | (copy-sequence (delete-dups xs))) 197 | 198 | 199 | ;;; Cabal configuration caching 200 | (defconst flycheck-haskell-config-cache (make-hash-table :test 'equal) 201 | "Cache of Cabal configuration. 202 | 203 | A hash table, mapping the name of a cabal file to a 204 | cons-cell `(MODTIME . CONFIG)', where MODTIME is the modification 205 | time of the cabal file, and CONFIG the extracted configuration.") 206 | 207 | (defun flycheck-haskell-clear-config-cache () 208 | "Clear the cache of configurations." 209 | (interactive) 210 | (clrhash flycheck-haskell-config-cache)) 211 | 212 | (defun flycheck-haskell-get-cached-configuration (config-file) 213 | "Get the cached configuration for CABAL-FILE. 214 | 215 | Return the cached configuration, or nil, if there is no cache 216 | entry, or if the cache entry is outdated." 217 | (pcase-let* ((cache-entry (gethash config-file flycheck-haskell-config-cache)) 218 | (`(,modtime . ,config) cache-entry)) 219 | (when (and modtime (file-exists-p config-file)) 220 | (let ((current-modtime (nth 5 (file-attributes config-file)))) 221 | (if (time-less-p modtime current-modtime) 222 | ;; The entry is outdated, drop it. `remhash' always 223 | ;; returns nil, so we are safe to use it here. 224 | (remhash config-file flycheck-haskell-config-cache) 225 | ;; The configuration is up to date, use it 226 | config))))) 227 | 228 | (defun flycheck-haskell-read-and-cache-configuration (config-file) 229 | "Read and cache configuration from CABAL-FILE. 230 | 231 | Return the configuration." 232 | (let ((modtime (nth 5 (file-attributes config-file))) 233 | (config (if (equal "yaml" (file-name-extension config-file)) 234 | (flycheck-haskell-read-hpack-configuration config-file) 235 | (flycheck-haskell-read-cabal-configuration config-file)))) 236 | (puthash config-file (cons modtime config) flycheck-haskell-config-cache) 237 | config)) 238 | 239 | (defun flycheck-haskell-get-configuration (config-file) 240 | "Get the Cabal configuration from CABAL-FILE. 241 | 242 | Get the configuration either from our cache, or by reading the 243 | CABAL-FILE. 244 | 245 | Return the configuration." 246 | (or (flycheck-haskell-get-cached-configuration config-file) 247 | (flycheck-haskell-read-and-cache-configuration config-file))) 248 | 249 | 250 | ;;; Cabal sandbox support 251 | (defconst flycheck-haskell-cabal-config "cabal.config" 252 | "The file name of a Cabal configuration.") 253 | 254 | (defconst flycheck-haskell-cabal-config-keys '(with-compiler) 255 | "Keys to parse from a Cabal configuration file.") 256 | 257 | (defconst flycheck-haskell-sandbox-config "cabal.sandbox.config" 258 | "The file name of a Cabal sandbox configuration.") 259 | 260 | (defconst flycheck-haskell-sandbox-config-keys '(package-db) 261 | "Keys to parse from a Cabal sandbox configuration.") 262 | 263 | (defmacro flycheck-haskell-with-config-file-buffer (file-name &rest body) 264 | "Eval BODY in a buffer with the contents of FILE-NAME." 265 | (declare (indent 1)) 266 | `(with-temp-buffer 267 | (insert-file-contents ,file-name) 268 | (goto-char (point-min)) 269 | ,@body)) 270 | 271 | (defun flycheck-haskell-get-config-value (key) 272 | "Get the value of a configuration KEY from this buffer. 273 | 274 | KEY is a symbol denoting the key whose value to get. Return 275 | a `(KEY . VALUE)' cons cell." 276 | (save-excursion 277 | (goto-char (point-min)) 278 | (let ((setting (haskell-cabal--get-field (symbol-name key)))) 279 | (when setting 280 | (cons key (substring-no-properties setting)))))) 281 | 282 | (defun flycheck-haskell-parse-config-file (keys config-file) 283 | "Parse KEYS from CONFIG-FILE. 284 | 285 | KEYS is a list of symbols. Return an alist with all parsed 286 | KEYS." 287 | (flycheck-haskell-with-config-file-buffer config-file 288 | (mapcar #'flycheck-haskell-get-config-value keys))) 289 | 290 | (defun flycheck-haskell-find-config (config-file) 291 | "Find a CONFIG-FILE for the current buffer. 292 | 293 | Return the absolute path of CONFIG-FILE as string, or nil if 294 | CONFIG-FILE was not found." 295 | (let ((root-dir (locate-dominating-file (buffer-file-name) config-file))) 296 | (when root-dir 297 | (expand-file-name config-file root-dir)))) 298 | 299 | (defun flycheck-haskell-get-cabal-config () 300 | "Get Cabal configuration for the current buffer. 301 | 302 | Return an alist with the Cabal configuration for the current 303 | buffer." 304 | (let ((file-name (flycheck-haskell-find-config 305 | flycheck-haskell-cabal-config))) 306 | (when file-name 307 | (flycheck-haskell-parse-config-file flycheck-haskell-cabal-config-keys 308 | file-name)))) 309 | 310 | (defun flycheck-haskell-get-sandbox-config () 311 | "Get sandbox configuration for the current buffer. 312 | 313 | Return an alist with the sandbox configuration for the current 314 | buffer." 315 | (let ((file-name (flycheck-haskell-find-config 316 | flycheck-haskell-sandbox-config))) 317 | (when file-name 318 | (flycheck-haskell-parse-config-file flycheck-haskell-sandbox-config-keys 319 | file-name)))) 320 | 321 | 322 | ;;; Buffer setup 323 | (defun flycheck-haskell-process-configuration (config) 324 | "Process the a Cabal CONFIG." 325 | (let-alist config 326 | (setq-local flycheck-ghc-search-path 327 | (flycheck-haskell--delete-dups 328 | (append .build-directories .source-directories 329 | flycheck-ghc-search-path))) 330 | (setq-local flycheck-ghc-language-extensions 331 | (flycheck-haskell--delete-dups 332 | (append .extensions .languages 333 | flycheck-ghc-language-extensions))) 334 | (setq-local flycheck-ghc-args 335 | (flycheck-haskell--delete-dups 336 | (append .other-options 337 | (seq-map (apply-partially #'concat "-I") 338 | .autogen-directories) 339 | (when (car .should-include-version-header) 340 | '("-optP-include" "-optPcabal_macros.h")) 341 | (when (not (car .package-env-exists)) 342 | (cons "-hide-all-packages" 343 | (seq-map (apply-partially #'concat "-package=") 344 | .dependencies))) 345 | (seq-map (apply-partially #'concat "-I") 346 | .include-directories) 347 | flycheck-ghc-args))) 348 | (setq-local flycheck-hlint-args 349 | (flycheck-haskell--delete-dups 350 | (append (seq-map (apply-partially #'concat "--cpp-include=") 351 | .autogen-directories) 352 | '("--cpp-file=cabal_macros.h")))))) 353 | 354 | (defun flycheck-haskell-configure () 355 | "Set paths and package database for the current project." 356 | (interactive) 357 | (when (and (buffer-file-name) (file-directory-p default-directory)) 358 | (let ((config-file (flycheck-haskell--find-config-file))) 359 | (when config-file 360 | (let ((config (flycheck-haskell-get-configuration config-file))) 361 | (when config 362 | (flycheck-haskell-process-configuration config))))) 363 | 364 | (let-alist (flycheck-haskell-get-cabal-config) 365 | (when .with-compiler 366 | (setq-local flycheck-haskell-ghc-executable .with-compiler))) 367 | 368 | (let-alist (flycheck-haskell-get-sandbox-config) 369 | (when .package-db 370 | (setq-local flycheck-ghc-package-databases 371 | (flycheck-haskell--delete-dups 372 | (cons .package-db flycheck-ghc-package-databases))) 373 | (setq-local flycheck-ghc-no-user-package-database t))))) 374 | 375 | (defun flycheck-haskell--find-config-file () 376 | (let* ((cabal-file (haskell-cabal-find-file)) 377 | (hpack-dir 378 | (and flycheck-haskell-hpack-executable 379 | (locate-dominating-file default-directory "package.yaml"))) 380 | (hpack-file 381 | (when hpack-dir 382 | (concat hpack-dir "/package.yaml")))) 383 | (if cabal-file 384 | (if hpack-file 385 | (cond 386 | ((eq 'prefer-hpack flycheck-haskell-hpack-preference) 387 | hpack-file) 388 | (t 389 | cabal-file)) 390 | cabal-file) 391 | hpack-file))) 392 | 393 | ;;;###autoload 394 | (defun flycheck-haskell-setup () 395 | "Setup Haskell support for Flycheck. 396 | 397 | If the current file is part of a Cabal project, configure 398 | Flycheck to take the module paths of the Cabal projects into 399 | account. 400 | 401 | Also search for Cabal sandboxes and add them to the module search 402 | path as well." 403 | (flycheck-haskell-configure) 404 | (add-hook 'hack-local-variables-hook #'flycheck-haskell-configure)) 405 | 406 | (provide 'flycheck-haskell) 407 | 408 | ;; Local Variables: 409 | ;; indent-tabs-mode: nil 410 | ;; coding: utf-8 411 | ;; End: 412 | 413 | ;;; flycheck-haskell.el ends here 414 | -------------------------------------------------------------------------------- /test/flycheck-haskell-test.el: -------------------------------------------------------------------------------- 1 | ;;; flycheck-haskell-test.el --- Flycheck Haskell: Test suite -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2018 Sergey Vinokurov 4 | ;; Copyright (C) 2014, 2015 Sebastian Wiesner 5 | 6 | ;; Author: Sebastian Wiesner 7 | 8 | ;; This file is not part of GNU Emacs. 9 | 10 | ;; This program is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation, either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;; The test suite for Flycheck Haskell. 26 | 27 | ;;; Code: 28 | 29 | (require 'flycheck-haskell) 30 | 31 | (require 'let-alist) 32 | (require 'cl-lib) 33 | (require 'ert) 34 | 35 | 36 | ;;; Directories 37 | 38 | (defun flycheck-haskell--concat-dirs (&rest dirs) 39 | "Combine multiple relative directory names into one. 40 | 41 | Combine directory names in DIRS into one long directory name 42 | using directory separator." 43 | (cl-reduce 44 | #'concat 45 | (seq-map #'file-name-as-directory dirs) 46 | :initial-value nil)) 47 | 48 | (defconst flycheck-haskell-test-dir 49 | (file-name-directory (if load-in-progress load-file-name (buffer-file-name))) 50 | "Directory of the test suite.") 51 | 52 | (defconst flycheck-haskell-cabal-test-dir 53 | (flycheck-haskell--concat-dirs flycheck-haskell-test-dir 54 | "test-data" 55 | "project-with-cabal-file") 56 | "Directory where tests are run by default.") 57 | 58 | (defconst flycheck-haskell-hpack-test-dir 59 | (flycheck-haskell--concat-dirs flycheck-haskell-test-dir 60 | "test-data" 61 | "project-with-package-yaml") 62 | "Directory where tests are run by default.") 63 | 64 | (defconst flycheck-haskell-test-cabal-file 65 | (expand-file-name "flycheck-haskell-test.cabal" 66 | flycheck-haskell-cabal-test-dir) 67 | "Cabal file for our test suite.") 68 | 69 | (defconst flycheck-haskell-test-hpack-file 70 | (expand-file-name "package.yaml" 71 | flycheck-haskell-hpack-test-dir) 72 | "package.yaml file for our test suite.") 73 | 74 | (defconst flycheck-haskell-test-sandbox-file 75 | (expand-file-name "cabal.sandbox.config" 76 | flycheck-haskell-cabal-test-dir) 77 | "Sandbox configuration file for our test suite.") 78 | 79 | (defconst flycheck-haskell-test-config-file 80 | (expand-file-name "cabal.config" 81 | flycheck-haskell-cabal-test-dir) 82 | "Cabal configuration file for our test suite.") 83 | 84 | 85 | ;;; Helpers 86 | 87 | (defun flycheck-haskell-read-test-cabal-config () 88 | "Read the Cabal configuration from the test file." 89 | (flycheck-haskell-read-cabal-configuration flycheck-haskell-test-cabal-file)) 90 | 91 | (defun flycheck-haskell-read-test-hpack-config () 92 | "Read the Cabal configuration from the test file." 93 | (flycheck-haskell-read-hpack-configuration flycheck-haskell-test-hpack-file)) 94 | 95 | (defmacro flycheck-haskell-test-with-cache (&rest body) 96 | "Run BODY and clear the config cache afterwards." 97 | (declare (indent 0)) 98 | `(unwind-protect (progn ,@body) 99 | (flycheck-haskell-clear-config-cache))) 100 | 101 | (defmacro flycheck-haskell-test-with-fake-file (&rest body) 102 | "Run BODY with a fake file buffer." 103 | (declare (indent 0)) 104 | `(with-temp-buffer 105 | (let* ((default-directory flycheck-haskell-cabal-test-dir) 106 | (buffer-file-name (expand-file-name "test.hs"))) 107 | ,@body))) 108 | 109 | (defun flycheck-haskell--sort-strs (xs) 110 | (seq-sort #'string< xs)) 111 | 112 | 113 | ;;; Cabal and hpack support 114 | (ert-deftest flycheck-haskell-read-cabal-configuration/has-all-extensions () 115 | (let-alist (flycheck-haskell-read-test-cabal-config) 116 | (should (equal (flycheck-haskell--sort-strs .extensions) 117 | (flycheck-haskell--sort-strs 118 | '("OverloadedStrings" 119 | "YouDontKnowThisOne" 120 | "GeneralizedNewtypeDeriving")))))) 121 | 122 | (ert-deftest flycheck-haskell-read-hpack-configuration/has-all-extensions () 123 | (skip-unless flycheck-haskell-hpack-executable) 124 | (let-alist (flycheck-haskell-read-test-hpack-config) 125 | (should (equal (flycheck-haskell--sort-strs .extensions) 126 | (flycheck-haskell--sort-strs 127 | '("OverloadedStrings" 128 | "YouDontKnowThisOne" 129 | "GeneralizedNewtypeDeriving")))))) 130 | 131 | (ert-deftest flycheck-haskell-read-cabal-configuration/has-all-languages () 132 | (let-alist (flycheck-haskell-read-test-cabal-config) 133 | (should-not (seq-difference .languages '("Haskell98" 134 | "SpamLanguage" 135 | "Haskell2010"))))) 136 | 137 | (ert-deftest flycheck-haskell-read-hpack-configuration/has-all-languages () 138 | (skip-unless flycheck-haskell-hpack-executable) 139 | (let-alist (flycheck-haskell-read-test-hpack-config) 140 | (should-not (seq-difference .languages '("Haskell98" 141 | "SpamLanguage" 142 | "Haskell2010"))))) 143 | 144 | (ert-deftest flycheck-haskell-read-cabal-configuration/source-dirs () 145 | (let-alist (flycheck-haskell-read-test-cabal-config) 146 | (should-not (seq-difference 147 | (flycheck-haskell--sort-strs .source-directories) 148 | (flycheck-haskell--sort-strs 149 | (seq-map (lambda (fn) 150 | (file-name-as-directory 151 | (expand-file-name fn flycheck-haskell-cabal-test-dir))) 152 | '("lib/" "." "src/"))))))) 153 | 154 | (ert-deftest flycheck-haskell-read-hpack-configuration/source-dirs () 155 | (skip-unless flycheck-haskell-hpack-executable) 156 | (let-alist (flycheck-haskell-read-test-hpack-config) 157 | (should-not (seq-difference 158 | (flycheck-haskell--sort-strs .source-directories) 159 | (flycheck-haskell--sort-strs 160 | (seq-map (lambda (fn) 161 | (file-name-as-directory 162 | (expand-file-name fn flycheck-haskell-hpack-test-dir))) 163 | '("lib/" "." "src/"))))))) 164 | 165 | (ert-deftest flycheck-haskell-read-cabal-configuration/build-dirs () 166 | (let* ((builddirs '("build" "build/autogen" 167 | "build/flycheck-haskell-unknown-stuff/flycheck-haskell-unknown-stuff-tmp" 168 | "build/flycheck-haskell-test/flycheck-haskell-test-tmp"))) 169 | (let-alist (flycheck-haskell-read-test-cabal-config) 170 | (dolist (dir builddirs) 171 | (let ((stack-re (format "\\.stack-work/.*/%s\\'" (regexp-quote dir))) 172 | (cabal-re (format "dist/%s\\'" (regexp-quote dir)))) 173 | (should (seq-find (apply-partially #'string-match-p stack-re) 174 | .build-directories)) 175 | (should (seq-find (apply-partially #'string-match-p cabal-re) 176 | .build-directories))))))) 177 | 178 | (ert-deftest flycheck-haskell-read-hpack-configuration/build-dirs () 179 | (skip-unless flycheck-haskell-hpack-executable) 180 | (let* ((builddirs '("build" "build/autogen" 181 | "build/flycheck-haskell-unknown-stuff/flycheck-haskell-unknown-stuff-tmp" 182 | "build/flycheck-haskell-test/flycheck-haskell-test-tmp"))) 183 | (let-alist (flycheck-haskell-read-test-hpack-config) 184 | (dolist (dir builddirs) 185 | (let ((stack-re (format "\\.stack-work/.*/%s\\'" (regexp-quote dir))) 186 | (cabal-re (format "dist/%s\\'" (regexp-quote dir)))) 187 | (should (seq-find (apply-partially #'string-match-p stack-re) 188 | .build-directories)) 189 | (should (seq-find (apply-partially #'string-match-p cabal-re) 190 | .build-directories))))))) 191 | 192 | (ert-deftest flycheck-haskell-read-cabal-configuration/cpp-options () 193 | (let-alist (flycheck-haskell-read-test-cabal-config) 194 | (should (member "-DDEBUG=1" .other-options)))) 195 | 196 | (ert-deftest flycheck-haskell-read-hpack-configuration/cpp-options () 197 | (skip-unless flycheck-haskell-hpack-executable) 198 | (let-alist (flycheck-haskell-read-test-hpack-config) 199 | (should (member "-DDEBUG=1" .other-options)))) 200 | 201 | (ert-deftest flycheck-haskell-read-cabal-configuration/ghc-options () 202 | (let-alist (flycheck-haskell-read-test-cabal-config) 203 | (should (member "-Wall" .other-options)))) 204 | 205 | (ert-deftest flycheck-haskell-read-hpack-configuration/ghc-options () 206 | (skip-unless flycheck-haskell-hpack-executable) 207 | (let-alist (flycheck-haskell-read-test-hpack-config) 208 | (should (member "-Wall" .other-options)))) 209 | 210 | (ert-deftest flycheck-haskell-get-configuration/no-cache-entry () 211 | (let* ((cabal-file flycheck-haskell-test-cabal-file)) 212 | (cl-letf (((symbol-function 'flycheck-haskell-read-cabal-configuration) 213 | (lambda (_) 'dummy))) 214 | (flycheck-haskell-test-with-cache 215 | (should-not (flycheck-haskell-get-cached-configuration cabal-file)) 216 | (should (eq (flycheck-haskell-get-configuration cabal-file) 'dummy)) 217 | (should (eq (flycheck-haskell-get-cached-configuration cabal-file) 218 | 'dummy)))))) 219 | 220 | (ert-deftest flycheck-haskell-read-cabal-configuration/read-from-dir-that-has-prelude-module () 221 | (let* ((test-dir (flycheck-haskell--concat-dirs flycheck-haskell-test-dir 222 | "test-data" 223 | "project-with-prelude-module")) 224 | (default-directory test-dir)) 225 | (cl-assert (file-regular-p (expand-file-name "foo.cabal" test-dir))) 226 | (flycheck-haskell-read-cabal-configuration "foo.cabal") 227 | (let ((conf (flycheck-haskell-read-cabal-configuration "foo.cabal"))) 228 | (let-alist conf 229 | (should (equal .dependencies '("base"))) 230 | (should (equal .extensions '("OverloadedStrings"))) 231 | (should (equal .languages '("Haskell2010"))) 232 | (should (member "-Wall" .other-options)) 233 | (should (member "-fwarn-haha-no-such-option" .other-options)) 234 | (should (equal .source-directories (list (expand-file-name "lib/" test-dir)))))))) 235 | 236 | 237 | ;;; Package environment support 238 | (ert-deftest flycheck-haskell-read-cabal-configuration/read-from-dir-that-has-package-env () 239 | (let* ((test-dir (flycheck-haskell--concat-dirs flycheck-haskell-test-dir 240 | "test-data" 241 | "project-with-package-env")) 242 | (default-directory test-dir)) 243 | (cl-assert (file-regular-p (expand-file-name "foo.cabal" test-dir))) 244 | (flycheck-haskell-read-cabal-configuration "foo.cabal") 245 | (let ((conf (flycheck-haskell-read-cabal-configuration "foo.cabal"))) 246 | (let-alist conf 247 | (should (equal .dependencies '("base"))) 248 | (should (equal .extensions '("OverloadedStrings"))) 249 | (should (equal .languages '("Haskell2010"))) 250 | (should (member "-Wall" .other-options)) 251 | (should (member "-fwarn-haha-no-such-option" .other-options)) 252 | (should (equal .source-directories (list (expand-file-name "lib/" test-dir)))))))) 253 | 254 | 255 | ;;; Configuration caching 256 | (ert-deftest flycheck-haskell-clear-config-cache () 257 | (unwind-protect 258 | (progn 259 | (puthash "foo" "bar" flycheck-haskell-config-cache) 260 | (should (= (hash-table-count flycheck-haskell-config-cache) 1)) 261 | (flycheck-haskell-clear-config-cache) 262 | (should (= (hash-table-count flycheck-haskell-config-cache) 0))) 263 | (clrhash flycheck-haskell-config-cache))) 264 | 265 | (ert-deftest flycheck-haskell-get-cached-configuration/no-cache-entry () 266 | (should-not (flycheck-haskell-get-cached-configuration 267 | flycheck-haskell-test-cabal-file))) 268 | 269 | (ert-deftest flycheck-haskell-get-cached-configuration/cached-cabal-config () 270 | (flycheck-haskell-test-with-cache 271 | (flycheck-haskell-read-and-cache-configuration 272 | flycheck-haskell-test-cabal-file) 273 | (should (= (hash-table-count flycheck-haskell-config-cache) 1)) 274 | (let ((config (flycheck-haskell-get-cached-configuration 275 | flycheck-haskell-test-cabal-file))) 276 | (should (equal config 277 | (flycheck-haskell-read-cabal-configuration 278 | flycheck-haskell-test-cabal-file)))))) 279 | 280 | (ert-deftest flycheck-haskell-get-cached-configuration/cached-hpack-config () 281 | (skip-unless flycheck-haskell-hpack-executable) 282 | (flycheck-haskell-test-with-cache 283 | (flycheck-haskell-read-and-cache-configuration 284 | flycheck-haskell-test-hpack-file) 285 | (should (= (hash-table-count flycheck-haskell-config-cache) 1)) 286 | (let ((config (flycheck-haskell-get-cached-configuration 287 | flycheck-haskell-test-hpack-file))) 288 | (should (equal config 289 | (flycheck-haskell-read-hpack-configuration 290 | flycheck-haskell-test-hpack-file)))))) 291 | 292 | (ert-deftest flycheck-haskell-get-cached-configuration/file-is-modified () 293 | (flycheck-haskell-test-with-cache 294 | (flycheck-haskell-read-and-cache-configuration 295 | flycheck-haskell-test-cabal-file) 296 | (should (flycheck-haskell-get-cached-configuration 297 | flycheck-haskell-test-cabal-file)) 298 | ;; Wait a second, to ensure that the current time advances 299 | (sleep-for 1) 300 | (set-file-times flycheck-haskell-test-cabal-file) 301 | (should-not (flycheck-haskell-get-cached-configuration 302 | flycheck-haskell-test-cabal-file)) 303 | (should (= (hash-table-count flycheck-haskell-config-cache) 0)))) 304 | 305 | (ert-deftest flycheck-haskell-get-configuration/has-cache-entry () 306 | (let* ((cabal-file flycheck-haskell-test-cabal-file) 307 | (mtime (nth 6 (file-attributes cabal-file)))) 308 | (cl-letf (((symbol-function 'flycheck-haskell-read-cabal-configuration) 309 | (lambda (_) 'dummy))) 310 | (flycheck-haskell-test-with-cache 311 | ;; Create a fake hash entry, which is guaranteed to be newer than the 312 | ;; actual file 313 | (puthash cabal-file (cons (time-add mtime (seconds-to-time 1)) 314 | 'cached-dummy) 315 | flycheck-haskell-config-cache) 316 | (should (eq (flycheck-haskell-get-configuration cabal-file) 317 | 'cached-dummy)) 318 | (flycheck-haskell-clear-config-cache) 319 | (should (eq (flycheck-haskell-get-configuration cabal-file) 320 | 'dummy)))))) 321 | 322 | 323 | ;;; Cabal sandbox support 324 | (ert-deftest flycheck-haskell-get-config-value/returns-value () 325 | (with-temp-buffer 326 | (insert "spam: with eggs\n") 327 | (goto-char (point-min)) 328 | (should (equal (flycheck-haskell-get-config-value 'spam) 329 | '(spam . "with eggs"))))) 330 | 331 | (ert-deftest flycheck-haskell-get-config-value/no-text-properties () 332 | (with-temp-buffer 333 | (insert "spam: with eggs\n") 334 | (goto-char (point-min)) 335 | (add-text-properties 6 (line-end-position) '(face 'bold)) 336 | (let ((value (cdr (flycheck-haskell-get-config-value 'spam)))) 337 | (should-not (text-properties-at 0 value))))) 338 | 339 | (ert-deftest flycheck-haskell-get-config-value/no-such-key () 340 | (with-temp-buffer 341 | (insert "spam: with eggs\n") 342 | (goto-char (point-min)) 343 | (should-not (flycheck-haskell-get-config-value 'foo)))) 344 | 345 | (ert-deftest flycheck-haskell-get-cabal-config () 346 | (flycheck-haskell-test-with-fake-file 347 | (let ((config (flycheck-haskell-get-cabal-config))) 348 | (should (equal config '((with-compiler . "/foo/bar/ghc-7.10"))))))) 349 | 350 | (ert-deftest flycheck-haskell-get-sandbox-config () 351 | (flycheck-haskell-test-with-fake-file 352 | (let ((config (flycheck-haskell-get-sandbox-config)) 353 | (db "/foo/bar/.cabal-sandbox/foo-packages.conf.d")) 354 | (should (equal config `((package-db . ,db))))))) 355 | 356 | 357 | ;;; Buffer setup 358 | (ert-deftest flycheck-haskell-process-configuration/cabal-language-extensions () 359 | (with-temp-buffer ; To scope the variables 360 | (flycheck-haskell-process-configuration (flycheck-haskell-read-test-cabal-config)) 361 | (should-not (seq-difference flycheck-ghc-language-extensions 362 | '("OverloadedStrings" 363 | "YouDontKnowThisOne" 364 | "GeneralizedNewtypeDeriving" 365 | "Haskell98" 366 | "SpamLanguage" 367 | "Haskell2010"))) 368 | (should (local-variable-p 'flycheck-ghc-language-extensions)))) 369 | 370 | (ert-deftest flycheck-haskell-process-configuration/hpack-language-extensions () 371 | (skip-unless flycheck-haskell-hpack-executable) 372 | (with-temp-buffer ; To scope the variables 373 | (flycheck-haskell-process-configuration (flycheck-haskell-read-test-hpack-config)) 374 | (should-not (seq-difference flycheck-ghc-language-extensions 375 | '("OverloadedStrings" 376 | "YouDontKnowThisOne" 377 | "GeneralizedNewtypeDeriving" 378 | "Haskell98" 379 | "SpamLanguage" 380 | "Haskell2010"))) 381 | (should (local-variable-p 'flycheck-ghc-language-extensions)))) 382 | 383 | (ert-deftest flycheck-haskell-process-configuration/cabal-search-path () 384 | (let* ((config (flycheck-haskell-read-test-cabal-config)) 385 | (sourcedirs (seq-map 386 | (lambda (d) 387 | (file-name-as-directory 388 | (expand-file-name d flycheck-haskell-cabal-test-dir))) 389 | '("lib/" "." "src/")))) 390 | (let-alist config 391 | (with-temp-buffer 392 | (flycheck-haskell-process-configuration config) 393 | (should (local-variable-p 'flycheck-ghc-search-path)) 394 | (should (cl-subsetp sourcedirs flycheck-ghc-search-path :test #'equal)) 395 | (should (cl-subsetp .build-directories flycheck-ghc-search-path 396 | :test #'equal)))))) 397 | 398 | (ert-deftest flycheck-haskell-process-configuration/hpack-search-path () 399 | (skip-unless flycheck-haskell-hpack-executable) 400 | (let* ((config (flycheck-haskell-read-test-hpack-config)) 401 | (sourcedirs (seq-map 402 | (lambda (d) 403 | (file-name-as-directory 404 | (expand-file-name d flycheck-haskell-hpack-test-dir))) 405 | '("lib/" "." "src/")))) 406 | (let-alist config 407 | (with-temp-buffer 408 | (flycheck-haskell-process-configuration config) 409 | (should (local-variable-p 'flycheck-ghc-search-path)) 410 | (should (cl-subsetp sourcedirs flycheck-ghc-search-path :test #'equal)) 411 | (should (cl-subsetp .build-directories flycheck-ghc-search-path 412 | :test #'equal)))))) 413 | 414 | (ert-deftest flycheck-haskell-process-configuration/hides-all-packages () 415 | (with-temp-buffer 416 | (flycheck-haskell-process-configuration (flycheck-haskell-read-test-cabal-config)) 417 | (should (member "-hide-all-packages" flycheck-ghc-args)) 418 | (should (local-variable-p 'flycheck-ghc-args)))) 419 | 420 | (ert-deftest flycheck-haskell-process-configuration/includes-dependenty-packages () 421 | (with-temp-buffer 422 | (flycheck-haskell-process-configuration (flycheck-haskell-read-test-cabal-config)) 423 | (should (member "-package=bytestring" flycheck-ghc-args)) 424 | (should (member "-package=base" flycheck-ghc-args)) 425 | (should (local-variable-p 'flycheck-ghc-args)))) 426 | 427 | (ert-deftest flycheck-haskell-configure/ghc-executable () 428 | (flycheck-haskell-test-with-fake-file 429 | (flycheck-haskell-configure) 430 | (should (equal flycheck-haskell-ghc-executable "/foo/bar/ghc-7.10")) 431 | (should (local-variable-p 'flycheck-haskell-ghc-executable)))) 432 | 433 | (ert-deftest flycheck-haskell-configure/package-database () 434 | (flycheck-haskell-test-with-fake-file 435 | (flycheck-haskell-configure) 436 | (should (equal flycheck-ghc-package-databases 437 | '("/foo/bar/.cabal-sandbox/foo-packages.conf.d"))) 438 | (should (local-variable-p 'flycheck-ghc-package-databases)) 439 | (should (equal flycheck-ghc-no-user-package-database t)) 440 | (should (local-variable-p 'flycheck-ghc-no-user-package-database)))) 441 | 442 | (provide 'flycheck-haskell-test) 443 | 444 | ;;; flycheck-haskell-test.el ends here 445 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /get-cabal-configuration.hs: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2016-2022 Sergey Vinokurov 2 | -- Copyright (C) 2014-2016 Sebastian Wiesner 3 | -- Copyright (C) 2016-2018 Danny Navarro 4 | -- Copyright (C) 2015 Mark Karpov 5 | -- Copyright (C) 2015 Michael Alan Dorman 6 | -- Copyright (C) 2014 Gracjan Polak 7 | 8 | -- This file is not part of GNU Emacs. 9 | 10 | -- This program is free software; you can redistribute it and/or modify it under 11 | -- the terms of the GNU General Public License as published by the Free Software 12 | -- Foundation, either version 3 of the License, or (at your option) any later 13 | -- version. 14 | 15 | -- This program is distributed in the hope that it will be useful, but WITHOUT 16 | -- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17 | -- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 18 | -- details. 19 | 20 | -- You should have received a copy of the GNU General Public License along with 21 | -- this program. If not, see . 22 | 23 | {-# LANGUAGE BangPatterns #-} 24 | {-# LANGUAGE CPP #-} 25 | {-# LANGUAGE FlexibleInstances #-} 26 | {-# LANGUAGE NamedFieldPuns #-} 27 | {-# LANGUAGE OverloadedStrings #-} 28 | {-# LANGUAGE ScopedTypeVariables #-} 29 | 30 | module Main (main) where 31 | 32 | #if __GLASGOW_HASKELL__ >= 800 33 | #define GHC_INCLUDES_VERSION_MACRO 1 34 | #endif 35 | 36 | #if defined(GHC_INCLUDES_VERSION_MACRO) 37 | 38 | # if MIN_VERSION_Cabal(3, 8, 0) 39 | # define Cabal38OrLater 1 40 | # define Cabal36OrLater 1 41 | # define Cabal32OrLater 1 42 | # define Cabal30OrLater 1 43 | # define Cabal24OrLater 1 44 | # define Cabal22OrLater 1 45 | # define Cabal20OrLater 1 46 | # elif MIN_VERSION_Cabal(3, 6, 0) 47 | # define Cabal36OrLater 1 48 | # define Cabal32OrLater 1 49 | # define Cabal30OrLater 1 50 | # define Cabal24OrLater 1 51 | # define Cabal22OrLater 1 52 | # define Cabal20OrLater 1 53 | # elif MIN_VERSION_Cabal(3, 2, 0) 54 | # define Cabal32OrLater 1 55 | # define Cabal30OrLater 1 56 | # define Cabal24OrLater 1 57 | # define Cabal22OrLater 1 58 | # define Cabal20OrLater 1 59 | # elif MIN_VERSION_Cabal(3, 0, 0) 60 | # define Cabal30OrLater 1 61 | # define Cabal24OrLater 1 62 | # define Cabal22OrLater 1 63 | # define Cabal20OrLater 1 64 | # elif MIN_VERSION_Cabal(2, 3, 0) 65 | # define Cabal24OrLater 1 66 | # define Cabal22OrLater 1 67 | # define Cabal20OrLater 1 68 | # elif MIN_VERSION_Cabal(2, 1, 0) 69 | # define Cabal22 1 70 | # define Cabal22OrLater 1 71 | # define Cabal20OrLater 1 72 | # elif MIN_VERSION_Cabal(2, 0, 0) 73 | # define Cabal20OrLater 1 74 | # endif 75 | 76 | # if MIN_VERSION_bytestring(0, 10, 2) 77 | # define bytestring_10_2_or_later 1 78 | # define bytestring_10_0_or_later 1 79 | # elif MIN_VERSION_bytestring(0, 10, 0) 80 | # define bytestring_10_0_or_later 1 81 | # endif 82 | 83 | #else 84 | 85 | # if __GLASGOW_HASKELL__ > 810 86 | # define bytestring_10_2_or_later 1 87 | # define bytestring_10_0_or_later 1 88 | # elif __GLASGOW_HASKELL__ > 704 89 | # define bytestring_10_0_or_later 1 90 | # define bytestring_10_0_or_later 1 91 | # endif 92 | 93 | #endif 94 | 95 | #if __GLASGOW_HASKELL__ >= 704 96 | # define Cabal114OrMore 1 97 | #endif 98 | 99 | #if __GLASGOW_HASKELL__ < 710 100 | # define Cabal118OrLess 1 101 | #endif 102 | 103 | #if __GLASGOW_HASKELL__ <= 706 104 | #undef HAVE_DATA_FUNCTOR_IDENTITY 105 | #else 106 | #define HAVE_DATA_FUNCTOR_IDENTITY 107 | #endif 108 | 109 | import qualified Data.ByteString.Char8 as C8 110 | #if defined(bytestring_10_2_or_later) 111 | import qualified Data.ByteString.Builder as BS.Builder 112 | #elif defined(bytestring_10_0_or_later) 113 | import qualified Data.ByteString.Lazy.Builder as BS.Builder 114 | #else 115 | import qualified Data.ByteString.Lazy.Char8 as CL8 116 | #endif 117 | 118 | import Distribution.ModuleName (ModuleName) 119 | import qualified Distribution.ModuleName as ModuleName 120 | import Distribution.PackageDescription () 121 | 122 | #if defined(Cabal20OrLater) 123 | import Distribution.Types.Benchmark (benchmarkBuildInfo, benchmarkInterface) 124 | import Distribution.Types.ForeignLib (foreignLibBuildInfo) 125 | import Distribution.Types.TestSuite (testBuildInfo, testInterface) 126 | 127 | import Distribution.PackageDescription (allLibraries, libName) 128 | #else 129 | import Distribution.PackageDescription (library) 130 | #endif 131 | 132 | 133 | import qualified Control.Applicative as A 134 | import Control.Exception (SomeException, try) 135 | import Control.Monad (when) 136 | #if defined(Cabal22OrLater) 137 | import qualified Data.ByteString as BS 138 | #endif 139 | import Data.Char (isSpace) 140 | #if defined(HAVE_DATA_FUNCTOR_IDENTITY) 141 | import Data.Functor.Identity 142 | #endif 143 | import Data.List (nub, foldl', intersperse) 144 | import Data.Maybe (maybeToList) 145 | #if __GLASGOW_HASKELL__ < 710 146 | import Data.Monoid 147 | #endif 148 | #if defined(Cabal32OrLater) 149 | import Data.List.NonEmpty (toList) 150 | #endif 151 | import Data.Set (Set) 152 | import qualified Data.Set as S 153 | #if defined(Cabal118OrLess) 154 | import Distribution.Compiler 155 | (CompilerFlavor(GHC), CompilerId(CompilerId), buildCompilerFlavor) 156 | #else 157 | import Distribution.Compiler 158 | (AbiTag(NoAbiTag), CompilerFlavor(GHC), CompilerId(CompilerId), 159 | CompilerInfo, buildCompilerFlavor, unknownCompilerInfo) 160 | #endif 161 | import Distribution.Package 162 | (pkgName, Dependency(..)) 163 | import Distribution.PackageDescription 164 | (GenericPackageDescription, PackageDescription(..), 165 | TestSuiteInterface(..), BuildInfo(..), Library, Executable, 166 | allBuildInfo, usedExtensions, allLanguages, hcOptions, exeName, 167 | buildInfo, modulePath, libBuildInfo, exposedModules) 168 | import Distribution.Simple.BuildPaths (defaultDistPref) 169 | import Distribution.Simple.Utils (cabalVersion) 170 | import Distribution.System (buildPlatform) 171 | #if defined(Cabal20OrLater) 172 | import Distribution.System (OS(OSX), buildArch, buildOS) 173 | #endif 174 | import Distribution.Text (display) 175 | #if defined(Cabal20OrLater) 176 | import Distribution.Types.PackageId (PackageId) 177 | #endif 178 | import Distribution.Verbosity (silent) 179 | import Language.Haskell.Extension (Extension(..),Language(..)) 180 | import System.Console.GetOpt 181 | import System.Environment (getArgs) 182 | import System.Exit (ExitCode(..), exitFailure, exitSuccess) 183 | import System.FilePath ((), dropFileName, normalise, isPathSeparator) 184 | import System.Info (compilerVersion) 185 | import System.IO (Handle, hGetContents, hPutStrLn, stderr, stdout) 186 | import System.Process (readProcessWithExitCode) 187 | import qualified System.Process as Process 188 | 189 | #if __GLASGOW_HASKELL__ >= 710 && !defined(Cabal20OrLater) && !defined(Cabal22OrLater) 190 | import Data.Version (Version) 191 | #endif 192 | 193 | #if defined(Cabal24OrLater) 194 | import Distribution.PackageDescription (allBuildDepends) 195 | #endif 196 | 197 | #if defined(Cabal114OrMore) 198 | import Distribution.PackageDescription (BenchmarkInterface(..),) 199 | #endif 200 | 201 | #if defined(Cabal20OrLater) 202 | import Control.Monad (filterM) 203 | import Distribution.Package (unPackageName, depPkgName, PackageName) 204 | import Distribution.PackageDescription.Configuration (finalizePD) 205 | import Distribution.Types.ComponentRequestedSpec (ComponentRequestedSpec(..)) 206 | import Distribution.Types.ForeignLib (ForeignLib(foreignLibName)) 207 | import Distribution.Types.UnqualComponentName (unUnqualComponentName) 208 | import qualified Distribution.Version as CabalVersion 209 | import Distribution.Types.Benchmark (Benchmark(benchmarkName)) 210 | import Distribution.Types.TestSuite (TestSuite(testName)) 211 | import System.Directory (doesDirectoryExist, doesFileExist) 212 | #else 213 | import Control.Arrow (second) 214 | import Data.Version (showVersion) 215 | import Distribution.Package (PackageName(..)) 216 | import Distribution.PackageDescription.Configuration 217 | (finalizePackageDescription, mapTreeData) 218 | 219 | # if defined(Cabal114OrMore) 220 | import Distribution.PackageDescription 221 | (TestSuite(..), Benchmark(..), 222 | condTestSuites, condBenchmarks, benchmarkEnabled, testEnabled) 223 | # else 224 | import Distribution.PackageDescription 225 | (TestSuite(..), condTestSuites, testEnabled) 226 | # endif 227 | #endif 228 | 229 | #if defined(Cabal22OrLater) 230 | import Distribution.Pretty (prettyShow) 231 | #endif 232 | 233 | #if defined(Cabal32OrLater) 234 | import Distribution.PackageDescription (mkFlagAssignment) 235 | #elif defined(Cabal22OrLater) 236 | import Distribution.Types.GenericPackageDescription (mkFlagAssignment) 237 | #endif 238 | 239 | #if defined(Cabal22OrLater) 240 | # if defined(Cabal38OrLater) 241 | import Distribution.Simple.PackageDescription 242 | (readGenericPackageDescription) 243 | import Distribution.PackageDescription.Parsec 244 | (runParseResult, parseGenericPackageDescription) 245 | # else 246 | import Distribution.PackageDescription.Parsec 247 | (runParseResult, readGenericPackageDescription, parseGenericPackageDescription) 248 | # endif 249 | # if defined(Cabal30OrLater) 250 | import Distribution.Parsec.Error (showPError) 251 | #else 252 | import Distribution.Parsec.Common (showPError) 253 | # endif 254 | #elif defined(Cabal20OrLater) 255 | import Distribution.PackageDescription.Parse 256 | (ParseResult(..), readGenericPackageDescription, parseGenericPackageDescription) 257 | import Distribution.ParseUtils (locatedErrorMsg) 258 | #else 259 | import Distribution.PackageDescription.Parse 260 | (ParseResult(..), parsePackageDescription, readPackageDescription) 261 | import Distribution.ParseUtils (locatedErrorMsg) 262 | #endif 263 | 264 | #if defined(Cabal30OrLater) 265 | import Distribution.Types.LibraryName (libraryNameString) 266 | #endif 267 | 268 | #if defined(Cabal36OrLater) 269 | import Distribution.Utils.Path (getSymbolicPath) 270 | #endif 271 | 272 | newtype UnixFilepath = UnixFilepath { unUnixFilepath :: C8.ByteString } 273 | deriving (Eq, Ord) 274 | 275 | mkUnixFilepath :: FilePath -> UnixFilepath 276 | mkUnixFilepath = UnixFilepath . C8.map (\c -> if isPathSeparator c then '/' else c) . C8.pack . normalise 277 | 278 | data Sexp 279 | = SList [Sexp] 280 | | SString C8.ByteString 281 | | SSymbol C8.ByteString 282 | 283 | data TargetTool = Cabal | Stack 284 | #if defined(Cabal20OrLater) 285 | | CabalNew PackageId GhcVersion 286 | 287 | type GhcVersion = String 288 | #endif 289 | 290 | #if defined(bytestring_10_0_or_later) 291 | type Builder = BS.Builder.Builder 292 | 293 | builderFromByteString :: C8.ByteString -> Builder 294 | builderFromByteString = BS.Builder.byteString 295 | 296 | builderFromChar :: Char -> Builder 297 | builderFromChar = BS.Builder.char8 298 | 299 | hPutBuilder :: Handle -> Builder -> IO () 300 | hPutBuilder = BS.Builder.hPutBuilder 301 | 302 | #else 303 | type Builder = Endo CL8.ByteString 304 | 305 | builderFromByteString :: C8.ByteString -> Builder 306 | builderFromByteString x = Endo (CL8.fromChunks [x] `mappend`) 307 | 308 | builderFromChar :: Char -> Builder 309 | builderFromChar c = Endo (CL8.singleton c `mappend`) 310 | 311 | hPutBuilder :: Handle -> Builder -> IO () 312 | hPutBuilder h (Endo f) = CL8.hPut h $ f CL8.empty 313 | #endif 314 | 315 | sym :: C8.ByteString -> Sexp 316 | sym = SSymbol 317 | 318 | renderSexp :: Sexp -> Builder 319 | renderSexp (SSymbol s) = builderFromByteString s 320 | renderSexp (SString s) = dquote `mappend` builderFromByteString s `mappend` dquote 321 | where 322 | dquote = builderFromChar '"' 323 | renderSexp (SList xs) = 324 | lparen `mappend` mconcat (intersperse space (map renderSexp xs)) `mappend` rparen 325 | where 326 | lparen = builderFromChar '(' 327 | rparen = builderFromChar ')' 328 | space = builderFromChar ' ' 329 | 330 | class ToSexp a where 331 | toSexp :: a -> Sexp 332 | 333 | instance ToSexp C8.ByteString where 334 | toSexp = SString 335 | 336 | instance ToSexp UnixFilepath where 337 | toSexp = SString . unUnixFilepath 338 | 339 | instance ToSexp Extension where 340 | toSexp (EnableExtension ext) = toSexp (C8.pack (show ext)) 341 | toSexp (DisableExtension ext) = toSexp ("No" `mappend` C8.pack (show ext)) 342 | toSexp (UnknownExtension ext) = toSexp (C8.pack ext) 343 | 344 | instance ToSexp Language where 345 | toSexp (UnknownLanguage lang) = toSexp (C8.pack lang) 346 | toSexp lang = toSexp (C8.pack (show lang)) 347 | 348 | instance ToSexp Dependency where 349 | toSexp = toSexp . C8.pack . unPackageName' . depPkgName' 350 | 351 | instance ToSexp Sexp where 352 | toSexp = id 353 | 354 | instance ToSexp Bool where 355 | toSexp b = SSymbol $ if b then "t" else "nil" 356 | 357 | instance ToSexp a => ToSexp [a] where 358 | toSexp = SList . map toSexp 359 | 360 | instance ToSexp a => ToSexp (Maybe a) where 361 | toSexp Nothing = SSymbol "nil" 362 | toSexp (Just x) = toSexp x 363 | 364 | instance ToSexp ModuleName where 365 | toSexp = toSexp . map C8.pack . ModuleName.components 366 | 367 | instance (ToSexp a, ToSexp b, ToSexp c, ToSexp d) => ToSexp (a, b, c, d) where 368 | toSexp (a, b, c, d) = SList [toSexp a, toSexp b, toSexp c, toSexp d] 369 | 370 | cons :: (ToSexp a, ToSexp b) => a -> [b] -> Sexp 371 | cons h t = SList (toSexp h : map toSexp t) 372 | 373 | -- | Get possible dist directory 374 | distDir :: TargetTool -> IO FilePath 375 | distDir Cabal = return defaultDistPref 376 | distDir Stack = do 377 | res <- try $ readProcessWithExitCode "stack" ["path", "--dist-dir"] [] 378 | return $ case res of 379 | Left (_ :: SomeException) -> defaultDistDir 380 | Right (ExitSuccess, stdOut, _) -> stripWhitespace stdOut 381 | Right (ExitFailure _, _, _) -> defaultDistDir 382 | where 383 | defaultDistDir :: FilePath 384 | defaultDistDir = 385 | ".stack-work" defaultDistPref 386 | display buildPlatform 387 | "Cabal-" ++ cabalVersion' 388 | #if defined(Cabal20OrLater) 389 | distDir (CabalNew packageId ghcVersion) = 390 | return $ "dist-newstyle/build" display buildPlatform 391 | "ghc-" ++ ghcVersion 392 | display packageId 393 | #endif 394 | 395 | getBuildDirectories 396 | :: TargetTool 397 | -> PackageDescription 398 | -> FilePath 399 | -> IO ([UnixFilepath], [UnixFilepath]) 400 | getBuildDirectories tool pkgDesc cabalDir = do 401 | distDir' <- distDir tool 402 | let buildDir :: FilePath 403 | buildDir = cabalDir distDir' "build" 404 | 405 | componentNames :: [String] 406 | componentNames = 407 | getExeNames pkgDesc ++ 408 | getTestNames pkgDesc ++ 409 | getBenchmarkNames pkgDesc ++ 410 | getForeignLibNames pkgDesc 411 | 412 | autogenDirs <- getAutogenDirs buildDir componentNames 413 | 414 | let componentBuildDir :: String -> FilePath 415 | componentBuildDir componentName = 416 | #if defined(Cabal20OrLater) 417 | case tool of 418 | CabalNew _ _ -> cabalDir distDir' 419 | "build" 420 | componentName 421 | (componentName ++ "-tmp") 422 | _ -> buildDir componentName (componentName ++ "-tmp") 423 | #else 424 | buildDir componentName (componentName ++ "-tmp") 425 | #endif 426 | 427 | buildDirs :: [FilePath] 428 | buildDirs = 429 | autogenDirs ++ map componentBuildDir componentNames 430 | 431 | buildDirs' = case library pkgDesc of 432 | Just _ -> buildDir : buildDirs 433 | Nothing -> buildDirs 434 | return (map mkUnixFilepath buildDirs', map mkUnixFilepath autogenDirs) 435 | 436 | getAutogenDirs :: FilePath -> [String] -> IO [FilePath] 437 | getAutogenDirs buildDir componentNames = 438 | (autogenDir :) A.<$> componentsAutogenDirs buildDir componentNames 439 | where 440 | -- 'dist/bulid/autogen' OR 441 | -- '.stack-work/dist/x86_64-linux/Cabal-1.24.2.0/build/autogen' OR 442 | -- ./dist-newstyle/build/x86_64-linux/ghc-8.4.3/lens-4.17/build/autogen 443 | autogenDir :: FilePath 444 | autogenDir = buildDir "autogen" 445 | 446 | getSourceDirectories :: [BuildInfo] -> FilePath -> [String] 447 | getSourceDirectories buildInfo cabalDir = 448 | map (cabalDir ) (concatMap hsSourceDirs' buildInfo) 449 | 450 | getIncludeDirectories :: [BuildInfo] -> FilePath -> [String] 451 | getIncludeDirectories buildInfo cabalDir = 452 | map (cabalDir ) (concatMap includeDirs buildInfo) 453 | 454 | hsSourceDirs' :: BuildInfo -> [FilePath] 455 | hsSourceDirs' = 456 | #if defined(Cabal36OrLater) 457 | map getSymbolicPath . hsSourceDirs 458 | #else 459 | hsSourceDirs 460 | #endif 461 | 462 | #if defined(Cabal20OrLater) 463 | doesPackageEnvExist :: GhcVersion -> FilePath -> IO Bool 464 | doesPackageEnvExist ghcVersion projectDir = doesFileExist $ projectDir packageEnvFn 465 | where 466 | packageEnvFn = 467 | -- The filename for package environments in MacOS uses the synonym "darwin 468 | -- "instead of the "official" buildPlatform, "osx". 469 | case buildOS of 470 | OSX -> ".ghc.environment." ++ display buildArch ++ "-" ++ "darwin" ++ "-" ++ ghcVersion 471 | _ -> ".ghc.environment." ++ display buildPlatform ++ "-" ++ ghcVersion 472 | 473 | #endif 474 | 475 | allowedOptions :: Set C8.ByteString 476 | allowedOptions = S.fromList 477 | [ "-w" 478 | , "-fglasgow-exts" 479 | , "-fpackage-trust" 480 | , "-fhelpful-errors" 481 | , "-F" 482 | , "-cpp" 483 | ] 484 | 485 | allowedOptionPrefixes :: [C8.ByteString] 486 | allowedOptionPrefixes = 487 | [ "-fwarn-" 488 | , "-fno-warn-" 489 | , "-W" 490 | , "-fcontext-stack=" 491 | , "-firrefutable-tuples" 492 | , "-D" 493 | , "-U" 494 | , "-I" 495 | , "-fplugin=" 496 | , "-fplugin-opt=" 497 | , "-pgm" 498 | , "-opt" 499 | ] 500 | 501 | forbiddenOptions :: Set C8.ByteString 502 | forbiddenOptions = S.fromList 503 | [ "-Wmissing-home-modules" 504 | , "-Werror=missing-home-modules" 505 | ] 506 | 507 | isAllowedOption :: C8.ByteString -> Bool 508 | isAllowedOption opt = 509 | S.member opt allowedOptions || any (`C8.isPrefixOf` opt) allowedOptionPrefixes && S.notMember opt forbiddenOptions 510 | 511 | dumpPackageDescription :: PackageDescription -> FilePath -> IO Sexp 512 | dumpPackageDescription pkgDesc projectDir = do 513 | (cabalDirs, cabalAutogen) <- getBuildDirectories Cabal pkgDesc projectDir 514 | (stackDirs, stackAutogen) <- getBuildDirectories Stack pkgDesc projectDir 515 | #if defined(Cabal20OrLater) 516 | ghcVersion <- getGhcVersion 517 | (cabalNewDirs, cabalNewAutogen) <- getBuildDirectories (CabalNew (package pkgDesc) ghcVersion) pkgDesc projectDir 518 | packageEnvExists <- doesPackageEnvExist ghcVersion projectDir 519 | let buildDirs = cabalDirs ++ stackDirs ++ cabalNewDirs 520 | autogenDirs = cabalAutogen ++ stackAutogen ++ cabalNewAutogen 521 | #else 522 | let buildDirs = cabalDirs ++ stackDirs 523 | autogenDirs = cabalAutogen ++ stackAutogen 524 | #endif 525 | let packageName = C8.pack $ unPackageName' $ pkgName $ package pkgDesc 526 | return $ 527 | SList 528 | [ cons (sym "build-directories") (ordNub (buildDirs :: [UnixFilepath])) 529 | , cons (sym "source-directories") (sourceDirs :: [UnixFilepath]) 530 | , cons (sym "extensions") exts 531 | , cons (sym "languages") langs 532 | , cons (sym "dependencies") deps 533 | , cons (sym "other-options") (cppOpts ++ ghcOpts) 534 | , cons (sym "autogen-directories") (autogenDirs :: [UnixFilepath]) 535 | , cons (sym "should-include-version-header") [not ghcIncludesVersionMacro] 536 | #if defined(Cabal20OrLater) 537 | , cons (sym "package-env-exists") [packageEnvExists] 538 | #endif 539 | , cons (sym "package-name") [packageName] 540 | , cons (sym "components") $ getComponents packageName pkgDesc 541 | , cons (sym "include-directories") (includeDirs :: [UnixFilepath]) 542 | ] 543 | where 544 | buildInfo :: [BuildInfo] 545 | buildInfo = allBuildInfo pkgDesc 546 | 547 | sourceDirs :: [UnixFilepath] 548 | sourceDirs = ordNub (map mkUnixFilepath (getSourceDirectories buildInfo projectDir)) 549 | 550 | includeDirs :: [UnixFilepath] 551 | includeDirs = ordNub (map mkUnixFilepath (getIncludeDirectories buildInfo projectDir)) 552 | 553 | exts :: [Extension] 554 | exts = nub (concatMap usedExtensions buildInfo) 555 | 556 | langs :: [Language] 557 | langs = nub (concatMap allLanguages buildInfo) 558 | 559 | thisPackage :: PackageName 560 | thisPackage = pkgName (package pkgDesc) 561 | 562 | deps :: [Dependency] 563 | deps = 564 | nub (filter ((/= thisPackage) . depPkgName') (buildDepends' pkgDesc)) 565 | 566 | -- The "cpp-options" configuration field. 567 | cppOpts :: [C8.ByteString] 568 | cppOpts = 569 | ordNub (filter isAllowedOption (map C8.pack (concatMap cppOptions buildInfo))) 570 | 571 | -- The "ghc-options" configuration field. 572 | ghcOpts :: [C8.ByteString] 573 | ghcOpts = 574 | ordNub (filter isAllowedOption (map C8.pack (concatMap (hcOptions GHC) buildInfo))) 575 | 576 | #if defined(Cabal20OrLater) 577 | -- We don't care about the stack ghc compiler because we don't need it for 578 | -- the stack checker 579 | getGhcVersion :: IO String 580 | getGhcVersion = 581 | go "cabal" 582 | ["new-exec", "ghc", "--", "--numeric-version"] 583 | (go "ghc" ["--numeric-version"] A.empty) 584 | where 585 | go :: String -> [String] -> IO String -> IO String 586 | go comm opts cont = do 587 | res <- try $ readProcessWithExitCode comm opts [] 588 | case res of 589 | Left (_ :: SomeException) -> cont 590 | Right (ExitSuccess, stdOut, _) -> return $ stripWhitespace stdOut 591 | Right (ExitFailure _, _, _) -> cont 592 | #endif 593 | 594 | data ComponentType 595 | = CTLibrary 596 | | CTForeignLibrary 597 | | CTExecutable 598 | | CTTestSuite 599 | | CTBenchmark 600 | deriving (Eq, Ord, Show, Enum, Bounded) 601 | 602 | componentTypePrefix :: ComponentType -> C8.ByteString 603 | componentTypePrefix x = case x of 604 | CTLibrary -> "lib" 605 | CTForeignLibrary -> "flib" 606 | CTExecutable -> "exe" 607 | CTTestSuite -> "test" 608 | CTBenchmark -> "bench" 609 | 610 | instance ToSexp ComponentType where 611 | toSexp = toSexp . componentTypePrefix 612 | 613 | -- | Gather files and modules that constitute each component. 614 | getComponents :: C8.ByteString -> PackageDescription -> [(ComponentType, C8.ByteString, Maybe C8.ByteString, [ModuleName])] 615 | getComponents packageName pkgDescr = 616 | [ (CTLibrary, name, Nothing, exposedModules lib ++ biMods bi) 617 | | lib <- allLibraries' pkgDescr 618 | , let bi = libBuildInfo lib 619 | , let name = maybe packageName C8.pack $ libName' lib 620 | ] ++ 621 | #if defined(Cabal20OrLater) 622 | [ (CTForeignLibrary, C8.pack (foreignLibName' flib), Nothing, biMods bi) 623 | | flib <- foreignLibs pkgDescr 624 | , let bi = foreignLibBuildInfo flib 625 | ] ++ 626 | #endif 627 | [ (CTExecutable, C8.pack (exeName' exe), Just (C8.pack (modulePath exe)), biMods bi) 628 | | exe <- executables pkgDescr 629 | , let bi = buildInfo exe 630 | ] ++ 631 | [ (CTTestSuite, C8.pack (testName' tst), exeFile, maybeToList extraMod ++ biMods bi) 632 | | tst <- testSuites pkgDescr 633 | , let bi = testBuildInfo tst 634 | , let (exeFile, extraMod) = case testInterface tst of 635 | TestSuiteExeV10 _ path -> (Just (C8.pack path), Nothing) 636 | TestSuiteLibV09 _ modName -> (Nothing, Just modName) 637 | TestSuiteUnsupported{} -> (Nothing, Nothing) 638 | ] 639 | #if defined(Cabal114OrMore) 640 | ++ 641 | [ (CTBenchmark, C8.pack (benchmarkName' tst), exeFile, biMods bi) 642 | | tst <- benchmarks pkgDescr 643 | , let bi = benchmarkBuildInfo tst 644 | , let exeFile = case benchmarkInterface tst of 645 | BenchmarkExeV10 _ path -> Just (C8.pack path) 646 | BenchmarkUnsupported{} -> Nothing 647 | ] 648 | #endif 649 | where 650 | #if defined(Cabal20OrLater) 651 | biMods bi = 652 | otherModules bi 653 | #if defined(Cabal22OrLater) 654 | ++ virtualModules bi 655 | #endif 656 | ++ autogenModules bi 657 | #else 658 | biMods = otherModules 659 | #endif 660 | 661 | getCabalConfiguration :: HPackExe -> ConfigurationFile -> IO Sexp 662 | getCabalConfiguration hpackExe configFile = do 663 | genericDesc <- 664 | case configFile of 665 | HPackFile path -> readHPackPkgDescr hpackExe path projectDir 666 | CabalFile path -> readGenericPkgDescr path 667 | case getConcretePackageDescription genericDesc of 668 | Left e -> die' $ "Issue with package configuration\n" ++ show e 669 | Right pkgDesc -> dumpPackageDescription pkgDesc projectDir 670 | where 671 | projectDir :: FilePath 672 | projectDir = dropFileName $ configFilePath configFile 673 | 674 | readHPackPkgDescr :: HPackExe -> FilePath -> FilePath -> IO GenericPackageDescription 675 | readHPackPkgDescr exe configFile projectDir = do 676 | (Nothing, Just out, Just err, procHandle) <- Process.createProcess p 677 | cabalFileContents <- readCabalFileContentsFromHandle out 678 | exitCode <- Process.waitForProcess procHandle 679 | case exitCode of 680 | ExitFailure{} -> do 681 | err' <- hGetContents err 682 | die' $ "Failed to obtain cabal configuration by running hpack on '" ++ configFile ++ "':\n" ++ err' 683 | ExitSuccess -> 684 | case parsePkgDescr "" cabalFileContents of 685 | Left msgs -> 686 | die' $ "Failed to parse cabal file produced by hpack from '" ++ configFile ++ "':\n" ++ 687 | unlines msgs 688 | Right x -> return x 689 | where 690 | p = (Process.proc (unHPackExe exe) [configFile, "-"]) 691 | { Process.std_in = Process.Inherit 692 | , Process.std_out = Process.CreatePipe 693 | , Process.std_err = Process.CreatePipe 694 | , Process.cwd = Just projectDir 695 | } 696 | 697 | buildDepends' :: PackageDescription -> [Dependency] 698 | buildDepends' = 699 | #if defined(Cabal24OrLater) 700 | allBuildDepends 701 | #else 702 | buildDepends 703 | #endif 704 | 705 | readGenericPkgDescr :: FilePath -> IO GenericPackageDescription 706 | readGenericPkgDescr = 707 | #if defined(Cabal20OrLater) 708 | readGenericPackageDescription silent 709 | #else 710 | readPackageDescription silent 711 | #endif 712 | 713 | newtype CabalFileContents = CabalFileContents 714 | { unCabalFileContents :: 715 | #if defined(Cabal22OrLater) 716 | BS.ByteString 717 | #else 718 | String 719 | #endif 720 | } 721 | 722 | readCabalFileContentsFromHandle :: Handle -> IO CabalFileContents 723 | readCabalFileContentsFromHandle = 724 | fmap CabalFileContents . 725 | #if defined(Cabal22OrLater) 726 | BS.hGetContents 727 | #else 728 | hGetContents 729 | #endif 730 | 731 | parsePkgDescr :: FilePath -> CabalFileContents -> Either [String] GenericPackageDescription 732 | parsePkgDescr _fileName cabalFileContents = 733 | #if defined(Cabal32OrLater) 734 | case runParseResult $ parseGenericPackageDescription $ unCabalFileContents cabalFileContents of 735 | (_warnings, res) -> 736 | case res of 737 | Left (_version, errs) -> Left $ map (showPError _fileName) $ toList errs 738 | Right x -> return x 739 | #elif defined(Cabal22OrLater) 740 | case runParseResult $ parseGenericPackageDescription $ unCabalFileContents cabalFileContents of 741 | (_warnings, res) -> 742 | case res of 743 | Left (_version, errs) -> Left $ map (showPError _fileName) errs 744 | Right x -> return x 745 | #elif defined(Cabal20OrLater) 746 | case parseGenericPackageDescription $ unCabalFileContents cabalFileContents of 747 | ParseFailed failure -> 748 | let (_line, msg) = locatedErrorMsg failure 749 | in Left [msg] 750 | ParseOk _warnings x -> Right x 751 | #else 752 | case parsePackageDescription $ unCabalFileContents cabalFileContents of 753 | ParseFailed failure -> 754 | let (_line, msg) = locatedErrorMsg failure 755 | in Left [msg] 756 | ParseOk _warnings x -> Right x 757 | #endif 758 | 759 | getConcretePackageDescription 760 | :: GenericPackageDescription 761 | -> Either [Dependency] PackageDescription 762 | getConcretePackageDescription genericDesc = do 763 | #if defined(Cabal22OrLater) 764 | let enabled :: ComponentRequestedSpec 765 | enabled = ComponentRequestedSpec 766 | { testsRequested = True 767 | , benchmarksRequested = True 768 | } 769 | fst A.<$> finalizePD 770 | (mkFlagAssignment []) -- Flag assignment 771 | enabled -- Enable all components 772 | (const True) -- Whether given dependency is available 773 | buildPlatform 774 | buildCompilerId 775 | [] -- Additional constraints 776 | genericDesc 777 | #elif defined(Cabal20OrLater) 778 | let enabled :: ComponentRequestedSpec 779 | enabled = ComponentRequestedSpec 780 | { testsRequested = True 781 | , benchmarksRequested = True 782 | } 783 | fst A.<$> finalizePD 784 | [] -- Flag assignment 785 | enabled -- Enable all components 786 | (const True) -- Whether given dependency is available 787 | buildPlatform 788 | buildCompilerId 789 | [] -- Additional constraints 790 | genericDesc 791 | #elif Cabal114OrMore 792 | -- This let block is eerily like one in Cabal.Distribution.Simple.Configure 793 | let enableTest :: TestSuite -> TestSuite 794 | enableTest t = t { testEnabled = True } 795 | enableBenchmark :: Benchmark -> Benchmark 796 | enableBenchmark bm = bm { benchmarkEnabled = True } 797 | flaggedTests = 798 | map (second (mapTreeData enableTest)) (condTestSuites genericDesc) 799 | flaggedBenchmarks = 800 | map 801 | (second (mapTreeData enableBenchmark)) 802 | (condBenchmarks genericDesc) 803 | genericDesc' = 804 | genericDesc 805 | { condTestSuites = flaggedTests 806 | , condBenchmarks = flaggedBenchmarks 807 | } 808 | fst A.<$> finalizePackageDescription 809 | [] 810 | (const True) 811 | buildPlatform 812 | buildCompilerId 813 | [] 814 | genericDesc' 815 | #else 816 | -- This let block is eerily like one in Cabal.Distribution.Simple.Configure 817 | let enableTest :: TestSuite -> TestSuite 818 | enableTest t = t { testEnabled = True } 819 | flaggedTests = 820 | map (second (mapTreeData enableTest)) (condTestSuites genericDesc) 821 | genericDesc' = 822 | genericDesc 823 | { condTestSuites = flaggedTests 824 | } 825 | fst A.<$> finalizePackageDescription 826 | [] 827 | (const True) 828 | buildPlatform 829 | buildCompilerId 830 | [] 831 | genericDesc' 832 | #endif 833 | 834 | componentsAutogenDirs :: FilePath -> [String] -> IO [FilePath] 835 | #if defined(Cabal20OrLater) 836 | componentsAutogenDirs buildDir componentNames = 837 | filterM doesDirectoryExist $ 838 | map (\path -> buildDir path "autogen") componentNames 839 | #else 840 | componentsAutogenDirs _ _ = return [] 841 | #endif 842 | 843 | #if defined(Cabal118OrLess) 844 | buildCompilerId :: CompilerId 845 | buildCompilerId = CompilerId buildCompilerFlavor compilerVersion 846 | #else 847 | buildCompilerId :: CompilerInfo 848 | buildCompilerId = unknownCompilerInfo compId NoAbiTag 849 | where 850 | compId :: CompilerId 851 | compId = CompilerId buildCompilerFlavor compVersion 852 | # if defined(Cabal20OrLater) 853 | compVersion :: CabalVersion.Version 854 | compVersion = CabalVersion.mkVersion' compilerVersion 855 | # else 856 | compVersion :: Version 857 | compVersion = compilerVersion 858 | # endif 859 | #endif 860 | 861 | allLibraries' :: PackageDescription -> [Library] 862 | allLibraries' = 863 | #if defined(Cabal20OrLater) 864 | allLibraries 865 | #else 866 | maybeToList . library 867 | #endif 868 | 869 | libName' :: Library -> Maybe String 870 | libName' = 871 | #if defined(Cabal30OrLater) 872 | fmap unUnqualComponentName . libraryNameString . libName 873 | #elif defined(Cabal20OrLater) 874 | fmap unUnqualComponentName . libName 875 | #else 876 | const Nothing 877 | #endif 878 | 879 | exeName' :: Executable -> String 880 | exeName' = 881 | #if defined(Cabal20OrLater) 882 | unUnqualComponentName . exeName 883 | #else 884 | exeName 885 | #endif 886 | 887 | #if defined(Cabal20OrLater) 888 | foreignLibName' :: ForeignLib -> String 889 | foreignLibName' = 890 | unUnqualComponentName . foreignLibName 891 | #endif 892 | 893 | testName' :: TestSuite -> String 894 | testName' = 895 | #if defined(Cabal20OrLater) 896 | unUnqualComponentName . testName 897 | #else 898 | testName 899 | #endif 900 | 901 | #if defined(Cabal114OrMore) 902 | benchmarkName' :: Benchmark -> FilePath 903 | benchmarkName' = 904 | # if defined(Cabal20OrLater) 905 | unUnqualComponentName . benchmarkName 906 | # else 907 | benchmarkName 908 | # endif 909 | #endif 910 | 911 | 912 | getExeNames :: PackageDescription -> [String] 913 | getExeNames = 914 | map exeName' . executables 915 | 916 | getForeignLibNames :: PackageDescription -> [String] 917 | getForeignLibNames = 918 | #if defined(Cabal20OrLater) 919 | map foreignLibName' . foreignLibs 920 | #else 921 | const [] 922 | #endif 923 | 924 | getTestNames :: PackageDescription -> [String] 925 | getTestNames = 926 | map testName' . testSuites 927 | 928 | getBenchmarkNames :: PackageDescription -> [String] 929 | getBenchmarkNames = 930 | #if defined(Cabal114OrMore) 931 | map benchmarkName' . benchmarks 932 | #else 933 | const [] 934 | #endif 935 | 936 | depPkgName' :: Dependency -> PackageName 937 | depPkgName' = 938 | #if defined(Cabal20OrLater) 939 | depPkgName 940 | #else 941 | let f (Dependency x _) = x in f 942 | #endif 943 | 944 | unPackageName' :: PackageName -> String 945 | unPackageName' = 946 | #if defined(Cabal20OrLater) 947 | unPackageName 948 | #else 949 | let f (PackageName x) = x in f 950 | #endif 951 | 952 | 953 | -- Textual representation of cabal version 954 | cabalVersion' :: String 955 | cabalVersion' = 956 | #if defined(Cabal22OrLater) 957 | prettyShow cabalVersion 958 | #elif defined(Cabal20OrLater) 959 | CabalVersion.showVersion cabalVersion 960 | #else 961 | showVersion cabalVersion 962 | #endif 963 | 964 | ghcIncludesVersionMacro :: Bool 965 | ghcIncludesVersionMacro = 966 | #if defined(GHC_INCLUDES_VERSION_MACRO) 967 | True 968 | #else 969 | False 970 | #endif 971 | 972 | ordNub :: forall a. Ord a => [a] -> [a] 973 | ordNub = go S.empty 974 | where 975 | go :: Set a -> [a] -> [a] 976 | go _ [] = [] 977 | go !acc (x:xs) 978 | | S.member x acc = go acc xs 979 | | otherwise = x : go (S.insert x acc) xs 980 | 981 | stripWhitespace :: String -> String 982 | stripWhitespace = reverse . dropWhile isSpace . reverse . dropWhile isSpace 983 | 984 | die' :: String -> IO a 985 | die' msg = do 986 | hPutStrLn stderr msg 987 | exitFailure 988 | 989 | #if !defined(HAVE_DATA_FUNCTOR_IDENTITY) 990 | newtype Identity a = Identity { runIdentity :: a } 991 | #endif 992 | 993 | data ConfigurationFile = 994 | CabalFile FilePath 995 | | HPackFile FilePath 996 | 997 | configFilePath :: ConfigurationFile -> FilePath 998 | configFilePath (CabalFile path) = path 999 | configFilePath (HPackFile path) = path 1000 | 1001 | newtype HPackExe = HPackExe { unHPackExe :: FilePath } 1002 | 1003 | data Config f = Config 1004 | { cfgInputFile :: f ConfigurationFile 1005 | , cfgHPackExe :: HPackExe 1006 | } 1007 | 1008 | reifyConfig :: Config Maybe -> IO (Config Identity) 1009 | reifyConfig Config{cfgInputFile, cfgHPackExe} = do 1010 | cfgInputFile' <- case cfgInputFile of 1011 | Nothing -> die' "Input file not specified. Use --cabal-file or --hpack-file to specify one." 1012 | Just path -> return path 1013 | return Config 1014 | { cfgInputFile = Identity cfgInputFile' 1015 | , cfgHPackExe 1016 | } 1017 | 1018 | optionDescr :: [OptDescr (Config Maybe -> Config Maybe)] 1019 | optionDescr = 1020 | [ Option 1021 | [] 1022 | ["cabal-file"] 1023 | (ReqArg (\path cfg -> cfg { cfgInputFile = Just (CabalFile path) }) "FILE") 1024 | "Cabal file to process" 1025 | , Option 1026 | [] 1027 | ["hpack-file"] 1028 | (ReqArg (\path cfg -> cfg { cfgInputFile = Just (HPackFile path) }) "FILE") 1029 | "HPack package.yaml file to process" 1030 | , Option 1031 | [] 1032 | ["hpack-exe"] 1033 | (ReqArg (\path cfg -> cfg { cfgHPackExe = HPackExe path }) "FILE") 1034 | "Path to 'hpack' executable" 1035 | ] 1036 | 1037 | defaultConfig :: Config Maybe 1038 | defaultConfig = Config 1039 | { cfgInputFile = Nothing 1040 | , cfgHPackExe = HPackExe "hpack" 1041 | } 1042 | 1043 | main' :: Config Identity -> IO () 1044 | main' Config{cfgInputFile, cfgHPackExe} = 1045 | hPutBuilder stdout . renderSexp =<< 1046 | getCabalConfiguration cfgHPackExe (runIdentity cfgInputFile) 1047 | 1048 | main :: IO () 1049 | main = do 1050 | args <- getArgs 1051 | when (any (`elem` ["-h", "--help"]) args) $ do 1052 | putStrLn usage 1053 | exitSuccess 1054 | case getOpt' RequireOrder optionDescr args of 1055 | (fs, [], [], []) -> do 1056 | let cfg = foldl' (flip ($)) defaultConfig fs 1057 | main' =<< reifyConfig cfg 1058 | (_, x:_, [], []) -> 1059 | die' $ "Unrecognised argument: " ++ x 1060 | (_, [], y:_, []) -> 1061 | die' $ "Unrecognised command-line option: " ++ y 1062 | (_, _, _, es) -> 1063 | die' $ "Failed to parse command-line options:\n" ++ unlines es 1064 | where 1065 | header = "Usage: get-cabal-configuration [OPTION...]" 1066 | usage = usageInfo header optionDescr 1067 | --------------------------------------------------------------------------------