├── cabal.haskell-ci ├── .ghci ├── .gitignore ├── Setup.lhs ├── CHANGELOG.md ├── fileLeakTest.hs ├── test └── Spec.hs ├── LICENSE ├── README.md ├── silently.cabal ├── src └── System │ └── IO │ └── Silently.hs └── .github └── workflows └── haskell-ci.yml /cabal.haskell-ci: -------------------------------------------------------------------------------- 1 | branches: master 2 | -------------------------------------------------------------------------------- /.ghci: -------------------------------------------------------------------------------- 1 | :set -isrc -itest -idist/build/autogen -DUNIX 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.hi 2 | *.o 3 | dist/ 4 | dist-newstyle/ 5 | .stack-work/ 6 | stack*.yaml.lock -------------------------------------------------------------------------------- /Setup.lhs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/runhaskell 2 | > module Main where 3 | > import Distribution.Simple 4 | > main :: IO () 5 | > main = defaultMain 6 | 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.2.5.4 November 2024 2 | 3 | * Bump `cabal-version` to 1.18. 4 | * README: alert of malfunction in multi-threaded settings. 5 | * Tested with GHC 8.0 - 9.12.0. 6 | 7 | # 1.2.5.3 August 2022 8 | 9 | * Tested with GHC 7.0 - 9.4.1. 10 | * Remove remnants of GHC 6.x support. 11 | * Silence incomplete pattern matching warning, refactor code. 12 | * Add section about limitations to README. 13 | 14 | # 1.2.5.2 November 2021 15 | 16 | * Tested with GHC 7.0 - 9.2. 17 | * Silence warning caused by missing `other-modules` in cabal file. 18 | * Add README and CHANGELOG to dist. 19 | 20 | # 1.2.5.1 July 2019 21 | 22 | No changelog for this and earlier versions. 23 | -------------------------------------------------------------------------------- /fileLeakTest.hs: -------------------------------------------------------------------------------- 1 | import System.IO.Silently (silence) 2 | import System.IO (withFile, IOMode(..)) 3 | import System.Directory (removeFile) 4 | import System.Info (os) 5 | 6 | silentTest n m 7 | | n == m = return m 8 | | otherwise = safely n (silence (print "testing" >> silentTest (n+1) m)) 9 | 10 | fileTest n m 11 | | n == m = return m 12 | | otherwise = safely n (withFile (show n ++ ".tst") AppendMode (\ _ -> fileTest (n+1) m)) 13 | 14 | safely n io = catch io (\ _ -> return (n-1)) 15 | 16 | main = do 17 | let m = 10000 18 | putStrLn $ "Current OS: " ++ os 19 | fileMax <- fileTest 1 m 20 | putStrLn $ "Maximum nested 'withFile' calls: " ++ show fileMax 21 | silentMax <- silentTest 1 m 22 | putStrLn $ "Maximum nested 'silence' calls: " ++ show silentMax 23 | mapM_ (\ n -> removeFile (show n ++ ".tst")) [1..fileMax] -------------------------------------------------------------------------------- /test/Spec.hs: -------------------------------------------------------------------------------- 1 | module Main (main) where 2 | 3 | import Test.Hspec 4 | import System.IO 5 | import System.IO.Silently 6 | import System.Directory 7 | import System.IO.Temp 8 | 9 | import Control.Exception 10 | import Control.Monad 11 | 12 | main :: IO () 13 | main = hspec spec 14 | 15 | spec :: Spec 16 | spec = do 17 | 18 | describe "hSilence" $ do 19 | it "prevents output to a given handle" $ let file = "foo.txt" in do 20 | h <- openFile file ReadWriteMode 21 | hSilence [h] $ do 22 | hPutStrLn h "foo bar baz" 23 | hFlush h 24 | hSeek h AbsoluteSeek 0 25 | hGetContents h `shouldReturn` "" 26 | `finally` removeFile file 27 | 28 | describe "capture" $ do 29 | it "captures stdout" $ do 30 | capture (putStr "foo" >> return 23) `shouldReturn` ("foo", 23 :: Int) 31 | 32 | describe "hCapture" $ do 33 | forM_ [NoBuffering, LineBuffering, BlockBuffering Nothing] $ \buffering -> do 34 | it ("preserves " ++ show buffering) $ do 35 | withSystemTempFile "silently" $ \_file h -> do 36 | hSetBuffering h buffering 37 | _ <- hCapture [h] (return ()) 38 | hGetBuffering h `shouldReturn` buffering 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 2 | 3 | 4 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 5 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 6 | The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. 7 | 8 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Hackage version](https://img.shields.io/hackage/v/silently.svg?label=Hackage&color=informational)](http://hackage.haskell.org/package/silently) 2 | [![silently on Stackage Nightly](https://stackage.org/package/silently/badge/nightly)](https://stackage.org/nightly/package/silently) 3 | [![Stackage LTS version](https://www.stackage.org/package/silently/badge/lts?label=Stackage)](https://www.stackage.org/package/silently) 4 | [![Cabal build](https://github.com/hspec/silently/workflows/Haskell-CI/badge.svg)](https://github.com/hspec/silently/actions) 5 | 6 | # silently 7 | 8 | Silently is a package that allows you to run an `IO` action and 9 | prevent it from writing to `stdout`, or any other handle, by using 10 | `silence`. Or you can capture the output for yourself using `capture`. 11 | 12 | For example, the program 13 | ```haskell 14 | import System.IO.Silently 15 | 16 | main = do 17 | putStr "putStrLn: " >> putStrLn "puppies!" 18 | putStr "silenced: " >> silence (putStrLn "kittens!") 19 | putStrLn "" 20 | (captured, result) <- capture (putStr "wookies!" >> return 123) 21 | putStr "captured: " >> putStrLn captured 22 | putStr "returned: " >> putStrLn (show result) 23 | ``` 24 | will print: 25 | ``` 26 | putStrLn: puppies! 27 | silenced: 28 | captured: wookies! 29 | returned: 123 30 | ``` 31 | 32 | ## Not thread-safe 33 | 34 | Since all threads of a process share the standard output handles `stdout` and `stderr`, 35 | capturing output to these handle will capture the output of __all threads__, 36 | not just the one running the action under `capture`. 37 | 38 | In essence, this library does not work in a situation where multiple threads are writing to the handle 39 | whose output _produced by the given action_ we want to capture. 40 | 41 | See: 42 | - https://github.com/hspec/silently/issues/6 43 | 44 | ## Further limitations 45 | 46 | Capturing/silencing might not work as expected if the action uses the FFI 47 | or conceals output under `unsafePerformIO` or similar unsafe operations. 48 | 49 | Examples: 50 | - FFI: https://github.com/hspec/silently/issues/3 51 | - `unsafePerformIO`: https://github.com/bos/filemanip/issues/22 52 | -------------------------------------------------------------------------------- /silently.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.18 2 | name: silently 3 | version: 1.2.5.4 4 | build-type: Simple 5 | license: BSD3 6 | license-file: LICENSE 7 | author: Trystan Spangler 8 | copyright: (c) Trystan Spangler 2011 9 | maintainer: Simon Hengel , Andreas Abel 10 | homepage: https://github.com/hspec/silently 11 | package-url: https://github.com/hspec/silently 12 | bug-reports: https://github.com/hspec/silently/issues 13 | synopsis: Prevent or capture writing to stdout and other handles. 14 | description: Prevent or capture writing to stdout, stderr, and other handles. 15 | category: System, Testing 16 | 17 | tested-with: 18 | GHC == 9.12.2 19 | GHC == 9.10.2 20 | GHC == 9.8.4 21 | GHC == 9.6.7 22 | GHC == 9.4.8 23 | GHC == 9.2.8 24 | GHC == 9.0.2 25 | GHC == 8.10.7 26 | GHC == 8.8.4 27 | GHC == 8.6.5 28 | GHC == 8.4.4 29 | GHC == 8.2.2 30 | GHC == 8.0.2 31 | 32 | extra-doc-files: 33 | CHANGELOG.md 34 | README.md 35 | 36 | source-repository head 37 | type: git 38 | location: https://github.com/hspec/silently 39 | 40 | Library 41 | hs-source-dirs: 42 | src 43 | default-language: 44 | Haskell98 45 | exposed-modules: 46 | System.IO.Silently 47 | 48 | build-depends: 49 | base >= 4.3 && < 5 50 | , directory 51 | , deepseq 52 | 53 | if os(windows) 54 | cpp-options: -DWINDOWS 55 | if os(linux) || os(osx) || os(freebsd) || os(openbsd) || os(netbsd) 56 | cpp-options: -DUNIX 57 | 58 | ghc-options: 59 | -Wall 60 | if impl(ghc >= 8) 61 | ghc-options: 62 | -Wcompat 63 | 64 | -- This tests the platform specific implementation. 65 | -- 66 | -- NOTE: Cabal 1.10 can not deal with conditional (== if-else) options. This 67 | -- is why we depend on silently to test the platform specific implementation. 68 | -- 69 | -- As a consequence we can not use Hspec for testing, as this would result in 70 | -- depending on two different versions of silently at the same time! 71 | test-suite spec-specific 72 | type: 73 | exitcode-stdio-1.0 74 | hs-source-dirs: 75 | test 76 | main-is: 77 | Spec.hs 78 | default-language: 79 | Haskell98 80 | ghc-options: 81 | -Wall -threaded 82 | build-depends: 83 | base >= 4.3 && < 5 84 | , silently 85 | , directory 86 | , nanospec 87 | , temporary 88 | 89 | -- This tests the generic implementation, that should work on all platforms. 90 | test-suite spec-generic 91 | type: 92 | exitcode-stdio-1.0 93 | hs-source-dirs: 94 | src 95 | , test 96 | main-is: 97 | Spec.hs 98 | default-language: 99 | Haskell98 100 | ghc-options: 101 | -Wall -threaded 102 | other-modules: 103 | System.IO.Silently 104 | 105 | build-depends: 106 | base >= 4.3 && < 5 107 | , deepseq 108 | , directory 109 | , nanospec 110 | , temporary 111 | -------------------------------------------------------------------------------- /src/System/IO/Silently.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | 4 | -- | Need to prevent output to the terminal, a file, or stderr? 5 | -- Need to capture it and use it for your own means? 6 | -- Now you can, with 'silence' and 'capture'. 7 | 8 | module System.IO.Silently 9 | ( silence, hSilence 10 | , capture, capture_, hCapture, hCapture_ 11 | ) where 12 | 13 | import Prelude 14 | 15 | import qualified Control.Exception as E 16 | import Control.DeepSeq 17 | ( deepseq ) 18 | 19 | import GHC.IO.Handle 20 | ( hDuplicate, hDuplicateTo ) 21 | 22 | import System.Directory 23 | ( getTemporaryDirectory, removeFile ) 24 | import System.IO 25 | ( Handle, IOMode(AppendMode), SeekMode(AbsoluteSeek) 26 | , hClose, hFlush, hGetBuffering, hGetContents, hSeek, hSetBuffering 27 | , openFile, openTempFile, stdout 28 | ) 29 | 30 | mNullDevice :: Maybe FilePath 31 | #ifdef WINDOWS 32 | mNullDevice = Just "\\\\.\\NUL" 33 | #elif UNIX 34 | mNullDevice = Just "/dev/null" 35 | #else 36 | mNullDevice = Nothing 37 | #endif 38 | 39 | -- | Run an IO action while preventing all output to stdout. 40 | silence :: IO a -> IO a 41 | silence = hSilence [stdout] 42 | 43 | -- | Run an IO action while preventing all output to the given handles. 44 | hSilence :: forall a. [Handle] -> IO a -> IO a 45 | hSilence handles action = 46 | case mNullDevice of 47 | Just nullDevice -> 48 | E.bracket (openFile nullDevice AppendMode) 49 | hClose 50 | prepareAndRun 51 | Nothing -> withTempFile "silence" prepareAndRun 52 | where 53 | prepareAndRun :: Handle -> IO a 54 | prepareAndRun tmpHandle = go handles 55 | where 56 | go [] = action 57 | go (h:hs) = goBracket go tmpHandle h hs 58 | 59 | 60 | -- Provide a tempfile for the given action and remove it afterwards. 61 | withTempFile :: String -> (Handle -> IO a) -> IO a 62 | withTempFile tmpName action = do 63 | tmpDir <- getTempOrCurrentDirectory 64 | E.bracket (openTempFile tmpDir tmpName) 65 | cleanup 66 | (action . snd) 67 | where 68 | cleanup :: (FilePath, Handle) -> IO () 69 | cleanup (tmpFile, tmpHandle) = do 70 | hClose tmpHandle 71 | removeFile tmpFile 72 | 73 | getTempOrCurrentDirectory :: IO String 74 | getTempOrCurrentDirectory = getTemporaryDirectory `catchIOError` (\_ -> return ".") 75 | where 76 | -- NOTE: We can not use `catchIOError` from "System.IO.Error", it is only 77 | -- available in base >= 4.4. 78 | catchIOError :: IO a -> (IOError -> IO a) -> IO a 79 | catchIOError = E.catch 80 | 81 | -- | Run an IO action while preventing and capturing all output to stdout. 82 | -- This will, as a side effect, create and delete a temp file in the temp directory 83 | -- or current directory if there is no temp directory. 84 | capture :: IO a -> IO (String, a) 85 | capture = hCapture [stdout] 86 | 87 | -- | Like `capture`, but discards the result of given action. 88 | capture_ :: IO a -> IO String 89 | capture_ = fmap fst . capture 90 | 91 | -- | Like `hCapture`, but discards the result of given action. 92 | hCapture_ :: [Handle] -> IO a -> IO String 93 | hCapture_ handles = fmap fst . hCapture handles 94 | 95 | -- | Run an IO action while preventing and capturing all output to the given handles. 96 | -- This will, as a side effect, create and delete a temp file in the temp directory 97 | -- or current directory if there is no temp directory. 98 | hCapture :: forall a. [Handle] -> IO a -> IO (String, a) 99 | hCapture handles action = withTempFile "capture" prepareAndRun 100 | where 101 | prepareAndRun :: Handle -> IO (String, a) 102 | prepareAndRun tmpHandle = go handles 103 | where 104 | go [] = do 105 | a <- action 106 | mapM_ hFlush handles 107 | hSeek tmpHandle AbsoluteSeek 0 108 | str <- hGetContents tmpHandle 109 | str `deepseq` return (str, a) 110 | go (h:hs) = goBracket go tmpHandle h hs 111 | 112 | goBracket :: ([Handle] -> IO a) -> Handle -> Handle -> [Handle] -> IO a 113 | goBracket go tmpHandle h hs = do 114 | buffering <- hGetBuffering h 115 | let redirect = do 116 | old <- hDuplicate h 117 | hDuplicateTo tmpHandle h 118 | return old 119 | restore old = do 120 | hDuplicateTo old h 121 | hSetBuffering h buffering 122 | hClose old 123 | E.bracket redirect restore (\_ -> go hs) 124 | -------------------------------------------------------------------------------- /.github/workflows/haskell-ci.yml: -------------------------------------------------------------------------------- 1 | # This GitHub workflow config has been generated by a script via 2 | # 3 | # haskell-ci 'github' 'silently.cabal' 4 | # 5 | # To regenerate the script (for example after adjusting tested-with) run 6 | # 7 | # haskell-ci regenerate 8 | # 9 | # For more information, see https://github.com/haskell-CI/haskell-ci 10 | # 11 | # version: 0.19.20250506 12 | # 13 | # REGENDATA ("0.19.20250506",["github","silently.cabal"]) 14 | # 15 | name: Haskell-CI 16 | on: 17 | push: 18 | branches: 19 | - master 20 | pull_request: 21 | branches: 22 | - master 23 | jobs: 24 | linux: 25 | name: Haskell-CI - Linux - ${{ matrix.compiler }} 26 | runs-on: ubuntu-24.04 27 | timeout-minutes: 28 | 60 29 | container: 30 | image: buildpack-deps:jammy 31 | continue-on-error: ${{ matrix.allow-failure }} 32 | strategy: 33 | matrix: 34 | include: 35 | - compiler: ghc-9.12.2 36 | compilerKind: ghc 37 | compilerVersion: 9.12.2 38 | setup-method: ghcup 39 | allow-failure: false 40 | - compiler: ghc-9.10.2 41 | compilerKind: ghc 42 | compilerVersion: 9.10.2 43 | setup-method: ghcup 44 | allow-failure: false 45 | - compiler: ghc-9.8.4 46 | compilerKind: ghc 47 | compilerVersion: 9.8.4 48 | setup-method: ghcup 49 | allow-failure: false 50 | - compiler: ghc-9.6.7 51 | compilerKind: ghc 52 | compilerVersion: 9.6.7 53 | setup-method: ghcup 54 | allow-failure: false 55 | - compiler: ghc-9.4.8 56 | compilerKind: ghc 57 | compilerVersion: 9.4.8 58 | setup-method: ghcup 59 | allow-failure: false 60 | - compiler: ghc-9.2.8 61 | compilerKind: ghc 62 | compilerVersion: 9.2.8 63 | setup-method: ghcup 64 | allow-failure: false 65 | - compiler: ghc-9.0.2 66 | compilerKind: ghc 67 | compilerVersion: 9.0.2 68 | setup-method: ghcup 69 | allow-failure: false 70 | - compiler: ghc-8.10.7 71 | compilerKind: ghc 72 | compilerVersion: 8.10.7 73 | setup-method: ghcup 74 | allow-failure: false 75 | - compiler: ghc-8.8.4 76 | compilerKind: ghc 77 | compilerVersion: 8.8.4 78 | setup-method: ghcup 79 | allow-failure: false 80 | - compiler: ghc-8.6.5 81 | compilerKind: ghc 82 | compilerVersion: 8.6.5 83 | setup-method: ghcup 84 | allow-failure: false 85 | - compiler: ghc-8.4.4 86 | compilerKind: ghc 87 | compilerVersion: 8.4.4 88 | setup-method: ghcup 89 | allow-failure: false 90 | - compiler: ghc-8.2.2 91 | compilerKind: ghc 92 | compilerVersion: 8.2.2 93 | setup-method: ghcup 94 | allow-failure: false 95 | - compiler: ghc-8.0.2 96 | compilerKind: ghc 97 | compilerVersion: 8.0.2 98 | setup-method: ghcup 99 | allow-failure: false 100 | fail-fast: false 101 | steps: 102 | - name: apt-get install 103 | run: | 104 | apt-get update 105 | apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5 libnuma-dev 106 | - name: Install GHCup 107 | run: | 108 | mkdir -p "$HOME/.ghcup/bin" 109 | curl -sL https://downloads.haskell.org/ghcup/0.1.50.1/x86_64-linux-ghcup-0.1.50.1 > "$HOME/.ghcup/bin/ghcup" 110 | chmod a+x "$HOME/.ghcup/bin/ghcup" 111 | - name: Install cabal-install 112 | run: | 113 | "$HOME/.ghcup/bin/ghcup" install cabal 3.14.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false) 114 | echo "CABAL=$HOME/.ghcup/bin/cabal-3.14.2.0 -vnormal+nowrap" >> "$GITHUB_ENV" 115 | - name: Install GHC (GHCup) 116 | if: matrix.setup-method == 'ghcup' 117 | run: | 118 | "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false) 119 | HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER") 120 | HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#') 121 | HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#') 122 | echo "HC=$HC" >> "$GITHUB_ENV" 123 | echo "HCPKG=$HCPKG" >> "$GITHUB_ENV" 124 | echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV" 125 | env: 126 | HCKIND: ${{ matrix.compilerKind }} 127 | HCNAME: ${{ matrix.compiler }} 128 | HCVER: ${{ matrix.compilerVersion }} 129 | - name: Set PATH and environment variables 130 | run: | 131 | echo "$HOME/.cabal/bin" >> $GITHUB_PATH 132 | echo "LANG=C.UTF-8" >> "$GITHUB_ENV" 133 | echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV" 134 | echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV" 135 | HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))') 136 | echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV" 137 | echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV" 138 | echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV" 139 | echo "HEADHACKAGE=false" >> "$GITHUB_ENV" 140 | echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV" 141 | env: 142 | HCKIND: ${{ matrix.compilerKind }} 143 | HCNAME: ${{ matrix.compiler }} 144 | HCVER: ${{ matrix.compilerVersion }} 145 | - name: env 146 | run: | 147 | env 148 | - name: write cabal config 149 | run: | 150 | mkdir -p $CABAL_DIR 151 | cat >> $CABAL_CONFIG <> $CABAL_CONFIG < cabal-plan.xz 184 | echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c - 185 | xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan 186 | rm -f cabal-plan.xz 187 | chmod a+x $HOME/.cabal/bin/cabal-plan 188 | cabal-plan --version 189 | - name: checkout 190 | uses: actions/checkout@v4 191 | with: 192 | path: source 193 | - name: initial cabal.project for sdist 194 | run: | 195 | touch cabal.project 196 | echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project 197 | cat cabal.project 198 | - name: sdist 199 | run: | 200 | mkdir -p sdist 201 | $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist 202 | - name: unpack 203 | run: | 204 | mkdir -p unpacked 205 | find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \; 206 | - name: generate cabal.project 207 | run: | 208 | PKGDIR_silently="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/silently-[0-9.]*')" 209 | echo "PKGDIR_silently=${PKGDIR_silently}" >> "$GITHUB_ENV" 210 | rm -f cabal.project cabal.project.local 211 | touch cabal.project 212 | touch cabal.project.local 213 | echo "packages: ${PKGDIR_silently}" >> cabal.project 214 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo "package silently" >> cabal.project ; fi 215 | if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo " ghc-options: -Werror=missing-methods" >> cabal.project ; fi 216 | cat >> cabal.project <> cabal.project.local 219 | cat cabal.project 220 | cat cabal.project.local 221 | - name: dump install plan 222 | run: | 223 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all 224 | cabal-plan 225 | - name: restore cache 226 | uses: actions/cache/restore@v4 227 | with: 228 | key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} 229 | path: ~/.cabal/store 230 | restore-keys: ${{ runner.os }}-${{ matrix.compiler }}- 231 | - name: install dependencies 232 | run: | 233 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all 234 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all 235 | - name: build w/o tests 236 | run: | 237 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all 238 | - name: build 239 | run: | 240 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always 241 | - name: tests 242 | run: | 243 | $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct 244 | - name: cabal check 245 | run: | 246 | cd ${PKGDIR_silently} || false 247 | ${CABAL} -vnormal check 248 | - name: haddock 249 | run: | 250 | $CABAL v2-haddock --disable-documentation --haddock-all $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all 251 | - name: unconstrained build 252 | run: | 253 | rm -f cabal.project.local 254 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all 255 | - name: save cache 256 | if: always() 257 | uses: actions/cache/save@v4 258 | with: 259 | key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} 260 | path: ~/.cabal/store 261 | --------------------------------------------------------------------------------