diff --git a/.travis.yml b/.travis.yml index 6723877176e3da58168de6fb27a894cceb0a1cb1..39606e1ce447d86dad69a695d3f92223581b524d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,13 @@ # This Travis job script has been generated by a script via # -# runghc make_travis_yml_2.hs '-o' '.travis.yml' '--ghc-head' '--env=7.0.4:NOINSTALLEDCONSTRAINTS=true TEST=--disable-tests BENCH=--disable-benchmarks' '--env=7.2.2:TEST=--disable-tests BENCH=--disable-benchmarks' 'cabal.project' +# haskell-ci '--output=.travis.yml' '--config=cabal.haskell-ci' 'cabal.project' # # For more information, see https://github.com/haskell-CI/haskell-ci # +# version: 0.3 +# language: c -sudo: false +dist: xenial git: submodules: false # whether to recursively clone submodules @@ -16,47 +18,37 @@ cache: - $HOME/.cabal/store before_cache: - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log + - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log # remove files that are regenerated by 'cabal update' - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.* - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx + - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.* + - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx - - rm -rfv $HOME/.cabal/packages/head.hackage + - rm -rfv $CABALHOME/packages/head.hackage matrix: include: - - compiler: "ghc-8.6.3" - # env: TEST=--disable-tests BENCH=--disable-benchmarks - addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3], sources: [hvr-ghc]}} + - compiler: "ghc-8.6.4" + addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.4], sources: [hvr-ghc]}} - compiler: "ghc-8.4.4" - # env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}} - compiler: "ghc-8.2.2" - # env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}} - compiler: "ghc-8.0.2" - # env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}} - compiler: "ghc-7.10.3" - # env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}} - compiler: "ghc-7.8.4" - # env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}} - compiler: "ghc-7.6.3" - # env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.6.3], sources: [hvr-ghc]}} - compiler: "ghc-7.4.2" - # env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.4.2], sources: [hvr-ghc]}} - compiler: "ghc-7.2.2" - env: TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.2.2], sources: [hvr-ghc]}} - compiler: "ghc-7.0.4" - env: NOINSTALLEDCONSTRAINTS=true TEST=--disable-tests BENCH=--disable-benchmarks addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.0.4], sources: [hvr-ghc]}} - compiler: "ghc-head" env: GHCHEAD=true @@ -66,58 +58,61 @@ matrix: - compiler: "ghc-head" before_install: - - HC=${CC} + - HC=/opt/ghc/bin/${CC} - HCPKG=${HC/ghc/ghc-pkg} - unset CC + - CABAL=/opt/ghc/bin/cabal + - CABALHOME=$HOME/.cabal + - export PATH="$CABALHOME/bin:$PATH" - ROOTDIR=$(pwd) - - mkdir -p $HOME/.local/bin - - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH" - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') )) - echo $HCNUMVER install: - - cabal --version + - ${CABAL} --version - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" - - BENCH=${BENCH---enable-benchmarks} - - TEST=${TEST---enable-tests} - - HADDOCK=${HADDOCK-true} - - UNCONSTRAINED=${UNCONSTRAINED-true} - - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false} + - TEST=--enable-tests + - if [ $HCNUMVER -lt 70400 ] ; then TEST=--disable-tests ; fi + - BENCH=--enable-benchmarks + - if [ $HCNUMVER -lt 70400 ] ; then BENCH=--disable-benchmarks ; fi - GHCHEAD=${GHCHEAD-false} - - travis_retry cabal update -v - - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config" + - travis_retry ${CABAL} update -v + - sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config - rm -fv cabal.project cabal.project.local # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage - | if $GHCHEAD; then - sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config - for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done + sed -i 's/-- allow-newer: .*/allow-newer: *:base/' $CABALHOME/config + for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" $CABALHOME/config; done - echo 'repository head.hackage' >> ${HOME}/.cabal/config - echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config - echo ' secure: True' >> ${HOME}/.cabal/config - echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config - echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config - echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config - echo ' key-threshold: 3' >> ${HOME}/.cabal.config + echo 'repository head.hackage' >> $CABALHOME/config + echo ' url: http://head.hackage.haskell.org/' >> $CABALHOME/config + echo ' secure: True' >> $CABALHOME/config + echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> $CABALHOME/config + echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> $CABALHOME/config + echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> $CABALHOME/config + echo ' key-threshold: 3' >> $CABALHOME.config - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$' + grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$' - cabal new-update head.hackage -v + ${CABAL} new-update head.hackage -v fi - - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$' - - "printf 'packages: \".\"\\n' > cabal.project" + - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$' + - rm -f cabal.project + - touch cabal.project + - "printf 'packages: \".\"\\n' >> cabal.project" - "printf 'write-ghc-environment-files: always\\n' >> cabal.project" - touch cabal.project.local - - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- hsc2hs | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(hsc2hs)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true - - if [ -f "./configure.ac" ]; then - (cd "." && autoreconf -i); - fi + - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi - rm -f cabal.project.freeze - - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all - - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all + - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dry + - "cat \"cabal.project.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'" + - rm "cabal.project.freeze" + - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all + - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all - rm -rf .ghc.environment.* "."/dist - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) @@ -125,28 +120,30 @@ install: # any command which exits with a non-zero exit code causes the build to fail. script: # test that source-distributions can be generated - - cabal new-sdist all + - ${CABAL} new-sdist all - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ - cd ${DISTDIR} || false - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \; - - "printf 'packages: hsc2hs-*/*.cabal\\n' > cabal.project" + - rm -f cabal.project + - touch cabal.project + - "printf 'packages: \"hsc2hs-*/*.cabal\"\\n' >> cabal.project" - "printf 'write-ghc-environment-files: always\\n' >> cabal.project" - touch cabal.project.local - - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- hsc2hs | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(hsc2hs)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" - cat cabal.project || true - cat cabal.project.local || true # this builds all libraries and executables (without tests/benchmarks) - - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all + - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all # build & run tests, build benchmarks - - cabal new-build -w ${HC} ${TEST} ${BENCH} all - - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi + - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all + - if [ $HCNUMVER -ge 70400 ] ; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all ; fi # cabal check - - (cd hsc2hs-* && cabal check) + - (cd hsc2hs-* && ${CABAL} check) # Build without installed constraints for packages in global-db - - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi + - if [ $HCNUMVER -ge 70200 ] ; then rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all ; fi -# REGENDATA ["-o",".travis.yml","--ghc-head","--env=7.0.4:NOINSTALLEDCONSTRAINTS=true TEST=--disable-tests BENCH=--disable-benchmarks","--env=7.2.2:TEST=--disable-tests BENCH=--disable-benchmarks","cabal.project"] +# REGENDATA ["--output=.travis.yml","--config=cabal.haskell-ci","cabal.project"] # EOF diff --git a/Common.hs b/Common.hs index 7c335ffe9754bfda5933a13991e674ae0aad59bc..aa0ec2b1ca01de9990770cb948a8a2d5d3af7286 100644 --- a/Common.hs +++ b/Common.hs @@ -1,14 +1,17 @@ +{-# LANGUAGE CPP #-} module Common where -import Control.Exception ( bracket_ ) import qualified Control.Exception as Exception import Control.Monad ( when ) import Data.Char ( isSpace ) import Data.List ( foldl' ) import System.IO - -import System.Process ( createProcess, proc, runProcess, waitForProcess ) - +#if defined(mingw32_HOST_OS) +import Control.Concurrent ( threadDelay ) +import System.IO.Error ( isPermissionError ) +#endif +import System.Process ( rawSystem, createProcess, waitForProcess + , proc, CreateProcess(..), StdStream(..) ) import System.Exit ( ExitCode(..), exitWith ) import System.Directory ( removeFile ) @@ -42,7 +45,17 @@ rawSystemWithStdOutL outDir action flg prog args outFile = withResponseFile outD let cmdLine = prog++" "++unwords args++" >"++outFile when flg (hPutStrLn stderr ("Executing: " ++ cmdLine)) hOut <- openFile outFile WriteMode - process <- runProcess prog ['@':rspFile] Nothing Nothing Nothing (Just hOut) Nothing + process <- runProcess prog Nothing Nothing Nothing (Just hOut) Nothing + (_ ,_ ,_ , process) <- + -- We use createProcess here instead of runProcess since we need to specify + -- a custom CreateProcess structure to turn on use_process_jobs when + -- available. + createProcess +#if MIN_VERSION_process (1,5,0) + (proc prog ['@':rspFile]){ use_process_jobs = True, std_out = UseHandle hOut } +#else + (proc prog ['@':rspFile]){ std_out = UseHandle hOut } +#endif exitStatus <- waitForProcess process hClose hOut case exitStatus of @@ -56,13 +69,39 @@ rawSystemWithStdOutL outDir action flg prog args outFile = withResponseFile outD -- just been exec'ed by a sub-process (Win32 only.) finallyRemove :: FilePath -> IO a -> IO a finallyRemove fp act = - bracket_ (return fp) + Exception.bracket_ (return fp) (noisyRemove fp) act where + max_retries :: Int + max_retries = 5 + + noisyRemove :: FilePath -> IO () noisyRemove fpath = - catchIO (removeFile fpath) + catchIO (removeFileInternal max_retries fpath) (\ e -> hPutStrLn stderr ("Failed to remove file " ++ fpath ++ "; error= " ++ show e)) + removeFileInternal _retries path = do +#if defined(mingw32_HOST_OS) + -- On Windows we have to retry the delete a couple of times. + -- The reason for this is that a FileDelete command just marks a + -- file for deletion. The file is really only removed when the last + -- handle to the file is closed. Unfortunately there are a lot of + -- system services that can have a file temporarily opened using a shared + -- read-only lock, such as the built in AV and search indexer. + -- + -- We can't really guarantee that these are all off, so what we can do is + -- whenever after an rm the file still exists to try again and wait a bit. + res <- Exception.try $ removeFile path + case res of + Right a -> return a + Left ex | isPermissionError ex && _retries > 1 -> do + let retries' = _retries - 1 + threadDelay ((max_retries - retries') * 200) + removeFileInternal retries' path + | otherwise -> Exception.throw ex +#else + removeFile path +#endif catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a catchIO = Exception.catch diff --git a/cabal.haskell-ci b/cabal.haskell-ci new file mode 100644 index 0000000000000000000000000000000000000000..0f2da1430fcae3ead9fbd4b626eeffbe0860b936 --- /dev/null +++ b/cabal.haskell-ci @@ -0,0 +1,4 @@ +ghc-head: True +tests: >=7.4 +benchmarks: >=7.4 +unconstrained: >=7.2 diff --git a/changelog.md b/changelog.md index 33e88774c741ce58e748ce2fd72fbef8dc229b99..d96902f11ce4083659ed151de9f27771d629516f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +## 0.68.6 + + - Temporary file removals on Windows are not a bit more reliable and should + throw less access denied errors. See #25 and + ([#9775](https://gitlab.haskell.org/ghc/ghc/issues/9775)) + ## 0.68.5 - Support response files regardless of which GHC `hsc2hs` was compiled diff --git a/hsc2hs.cabal b/hsc2hs.cabal index 191e66161b13606e27b66d6cbb2392a412107163..10d46b19e2a25137969e8e5b74523e6bcc9c5cf6 100644 --- a/hsc2hs.cabal +++ b/hsc2hs.cabal @@ -24,7 +24,7 @@ Description: Category: Development Data-Files: template-hsc.h build-type: Simple -tested-with: GHC==8.6.3, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2, GHC==7.2.2, GHC==7.0.4 +tested-with: GHC==8.6.4, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2, GHC==7.2.2, GHC==7.0.4 extra-source-files: changelog.md @@ -62,6 +62,9 @@ Executable hsc2hs filepath >= 1.2.0 && < 1.5, process >= 1.1.0 && < 1.7 + if os(windows) + Build-Depends: process >= 1.5.0 && < 1.7 + ghc-options: -Wall if flag(in-ghc-tree) cpp-options: -DIN_GHC_TREE