summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsolpeth <>2019-02-20 09:59:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2019-02-20 09:59:00 (GMT)
commit20e7448ff7f01621d84f457200f8218958e291d3 (patch)
treeda928f4239a88dac6b8dc8ed94c4436a7335b9f3
parentc23e9bc0c8b549df99aa7a593af63f5fb9fe4fc0 (diff)
version 0.60.6
-rw-r--r--Merge.hs12
-rw-r--r--Merge/Dependencies.hs27
-rw-r--r--Portage/EBuild.hs4
-rw-r--r--Portage/GHCCore.hs113
-rw-r--r--Portage/Metadata.hs12
-rw-r--r--cabal/Cabal/Distribution/PackageDescription/Configuration.hs5
-rw-r--r--hackage-security/.appveyor.yml31
-rw-r--r--hackage-security/.travis.yml10
-rw-r--r--hackage-security/README.md2
-rw-r--r--hackage-security/example-client/example-client.cabal2
-rw-r--r--hackage-security/hackage-repo-tool/ChangeLog.md9
-rw-r--r--hackage-security/hackage-repo-tool/hackage-repo-tool.cabal68
-rw-r--r--hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Options.hs7
-rw-r--r--hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Util/IO.hs55
-rw-r--r--hackage-security/hackage-repo-tool/src/Main.hs11
-rw-r--r--hackage-security/hackage-root-tool/hackage-root-tool.cabal2
-rw-r--r--hackage-security/hackage-security-HTTP/hackage-security-HTTP.cabal9
-rw-r--r--hackage-security/hackage-security-curl/hackage-security-curl.cabal2
-rw-r--r--hackage-security/hackage-security-http-client/hackage-security-http-client.cabal2
-rw-r--r--hackage-security/hackage-security/ChangeLog.md15
-rw-r--r--hackage-security/hackage-security/hackage-security.cabal54
-rw-r--r--hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Cache.hs50
-rw-r--r--hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Remote.hs38
-rw-r--r--hackage-security/hackage-security/src/Hackage/Security/Util/Checked.hs41
-rw-r--r--hackage-security/hackage-security/src/Hackage/Security/Util/Exit.hs40
-rw-r--r--hackage-security/hackage-security/src/Hackage/Security/Util/FileLock.hsc202
-rw-r--r--hackage-security/hackage-security/src/Hackage/Security/Util/IO.hs41
-rw-r--r--hackage-security/hackage-security/tests/TestSuite.hs1
-rw-r--r--hackage-security/hackage-security/tests/TestSuite/JSON.hs21
-rw-r--r--hackport.cabal5
30 files changed, 735 insertions, 156 deletions
diff --git a/Merge.hs b/Merge.hs
index a2bf6bf..487857c 100644
--- a/Merge.hs
+++ b/Merge.hs
@@ -383,7 +383,7 @@ mergeGenericPackageDescription verbosity overlayPath cat pkgGenericDesc fetch us
Just ucf -> (\e -> e { E.used_options = E.used_options e ++ [("flags", ucf)] }))
$ C2E.cabal2ebuild cat pkgDesc
- mergeEbuild verbosity existing_meta pkgdir ebuild
+ mergeEbuild verbosity existing_meta pkgdir ebuild cabal_flag_descs
when fetch $ do
let cabal_pkgId = Cabal.packageId pkgDesc
norm_pkgName = Cabal.packageName (Portage.normalizeCabalPackageId cabal_pkgId)
@@ -421,14 +421,18 @@ to_unstable kw =
'-':_ -> kw
_ -> '~':kw
-mergeEbuild :: Verbosity -> EM.EMeta -> FilePath -> E.EBuild -> IO ()
-mergeEbuild verbosity existing_meta pkgdir ebuild = do
+-- | Generate a list of tuples containing Cabal flag names and descriptions
+metaFlags :: [Cabal.Flag] -> [(String, String)]
+metaFlags flags = zip (Cabal.unFlagName . Cabal.flagName <$> flags) (Cabal.flagDescription <$> flags)
+
+mergeEbuild :: Verbosity -> EM.EMeta -> FilePath -> E.EBuild -> [Cabal.Flag] -> IO ()
+mergeEbuild verbosity existing_meta pkgdir ebuild flags = do
let edir = pkgdir
elocal = E.name ebuild ++"-"++ E.version ebuild <.> "ebuild"
epath = edir </> elocal
emeta = "metadata.xml"
mpath = edir </> emeta
- default_meta = BL.pack $ Portage.makeDefaultMetadata (E.long_desc ebuild)
+ default_meta = BL.pack $ Portage.makeDefaultMetadata (E.long_desc ebuild) (metaFlags flags)
createDirectoryIfMissing True edir
now <- TC.getCurrentTime
diff --git a/Merge/Dependencies.hs b/Merge/Dependencies.hs
index d49ed98..d7f7a5e 100644
--- a/Merge/Dependencies.hs
+++ b/Merge/Dependencies.hs
@@ -16,6 +16,7 @@ import qualified Distribution.Package as Cabal
import qualified Distribution.PackageDescription as Cabal
import qualified Distribution.Version as Cabal
import qualified Distribution.Text as Cabal
+import qualified Distribution.Types.ExeDependency as Cabal
import qualified Distribution.Types.LegacyExeDependency as Cabal
import qualified Distribution.Types.PkgconfigDependency as Cabal
@@ -104,7 +105,7 @@ resolveDependencies overlay pkg compiler_info ghc_package_names merged_cabal_pkg
then []
else [any_c_p "virtual" "pkgconfig"]
build_tools :: Portage.Dependency
- build_tools = Portage.DependAllOf $ pkg_config_tools : buildToolsDependencies pkg
+ build_tools = Portage.DependAllOf $ pkg_config_tools : legacyBuildToolsDependencies pkg ++ hackageBuildToolsDependencies overlay pkg
setup_deps :: Portage.Dependency
setup_deps = PN.normalize_depend $ Portage.DependAllOf $
@@ -352,14 +353,16 @@ staticTranslateExtraLib lib = lookup lib m
, ("notify", any_c_p "x11-libs" "libnotify")
, ("SDL2", any_c_p " media-libs" "libsdl2")
, ("SDL2_mixer", any_c_p "media-libs" "sdl2-mixer")
+ , ("blas", any_c_p "virtual" "blas")
+ , ("lapack", any_c_p "virtual" "lapack")
]
---------------------------------------------------------------
--- Build Tools
+-- Build Tools (legacy, a list of well-known tools)
---------------------------------------------------------------
-buildToolsDependencies :: Cabal.PackageDescription -> [Portage.Dependency]
-buildToolsDependencies (Cabal.PackageDescription { Cabal.library = lib, Cabal.executables = exes }) = L.nub $
+legacyBuildToolsDependencies :: Cabal.PackageDescription -> [Portage.Dependency]
+legacyBuildToolsDependencies (Cabal.PackageDescription { Cabal.library = lib, Cabal.executables = exes }) = L.nub $
[ case pkg of
Just p -> p
Nothing -> trace ("WARNING: Unknown build tool '" ++ Cabal.display exe ++ "'. Check the generated ebuild.")
@@ -395,6 +398,20 @@ buildToolsTable =
buildToolsProvided :: [String]
buildToolsProvided = ["hsc2hs"]
+---------------------------------------------------------------
+-- Hackage Build Tools (behind `build-tool-depends`)
+---------------------------------------------------------------
+
+hackageBuildToolsDependencies :: Portage.Overlay -> Cabal.PackageDescription -> [Portage.Dependency]
+hackageBuildToolsDependencies overlay (Cabal.PackageDescription { Cabal.library = lib, Cabal.executables = exes }) =
+ haskellDependencies overlay $ L.nub $
+ [ Cabal.Dependency pn versionRange
+ | Cabal.ExeDependency pn _component versionRange <- cabalDeps
+ ]
+ where
+ cabalDeps = depL ++ depE
+ depL = concatMap (Cabal.buildToolDepends . Cabal.libBuildInfo) $ maybe [] return lib
+ depE = concatMap Cabal.buildToolDepends (filter Cabal.buildable (map Cabal.buildInfo exes))
---------------------------------------------------------------
-- pkg-config
@@ -436,6 +453,8 @@ pkgconfig_table =
,("gmodule-export-2.0", ("dev-libs", "glib", Portage.GivenSlot "2"))
,("gmodule-no-export-2.0", ("dev-libs", "glib", Portage.GivenSlot "2"))
,("gobject-2.0", ("dev-libs", "glib", Portage.GivenSlot "2"))
+ ,("gobject-introspection-1.0", ("dev-libs", "gobject-introspection",
+ Portage.AnySlot))
,("gthread-2.0", ("dev-libs", "glib", Portage.GivenSlot "2"))
,("gtk+-2.0", ("x11-libs", "gtk+", Portage.GivenSlot "2"))
diff --git a/Portage/EBuild.hs b/Portage/EBuild.hs
index 38c9690..0f81cbe 100644
--- a/Portage/EBuild.hs
+++ b/Portage/EBuild.hs
@@ -91,10 +91,10 @@ src_uri e =
showEBuild :: TC.UTCTime -> EBuild -> String
showEBuild now ebuild =
- ss ("# Copyright 1999-" ++ this_year ++ " Gentoo Foundation"). nl.
+ ss ("# Copyright 1999-" ++ this_year ++ " Gentoo Authors"). nl.
ss "# Distributed under the terms of the GNU General Public License v2". nl.
nl.
- ss "EAPI=6". nl.
+ ss "EAPI=7". nl.
nl.
ss ("# ebuild generated by hackport " ++ hackportVersion ebuild). nl.
sconcat (map (\(k, v) -> ss "#hackport: " . ss k . ss ": " . ss v . nl) $ used_options ebuild).
diff --git a/Portage/GHCCore.hs b/Portage/GHCCore.hs
index 6ff6447..5ebfeec 100644
--- a/Portage/GHCCore.hs
+++ b/Portage/GHCCore.hs
@@ -31,7 +31,7 @@ import Debug.Trace
-- It means that first ghc in this list is a minmum default.
ghcs :: [(DC.CompilerInfo, InstalledPackageIndex)]
ghcs = modern_ghcs
- where modern_ghcs = [ghc741, ghc742, ghc761, ghc762, ghc782, ghc7101, ghc7102, ghc801, ghc802, ghc821]
+ where modern_ghcs = [ghc741, ghc742, ghc761, ghc762, ghc782, ghc7101, ghc7102, ghc801, ghc802, ghc821, ghc843, ghc861, ghc863]
cabalFromGHC :: [Int] -> Maybe Cabal.Version
cabalFromGHC ver = lookup ver table
@@ -45,6 +45,8 @@ cabalFromGHC ver = lookup ver table
, ([8,0,1], Cabal.mkVersion [1,24,0,0])
, ([8,0,2], Cabal.mkVersion [1,24,2,0])
, ([8,2,1], Cabal.mkVersion [2,0,0,2])
+ , ([8,4,3], Cabal.mkVersion [2,2,0,1])
+ , ([8,6,1], Cabal.mkVersion [2,4,0,1])
]
platform :: Platform
@@ -114,6 +116,15 @@ ghc :: [Int] -> DC.CompilerInfo
ghc nrs = DC.unknownCompilerInfo c_id DC.NoAbiTag
where c_id = CompilerId GHC (mkVersion nrs)
+ghc863 :: (DC.CompilerInfo, InstalledPackageIndex)
+ghc863 = (ghc [8,6,3], mkIndex ghc863_pkgs)
+
+ghc861 :: (DC.CompilerInfo, InstalledPackageIndex)
+ghc861 = (ghc [8,6,1], mkIndex ghc861_pkgs)
+
+ghc843 :: (DC.CompilerInfo, InstalledPackageIndex)
+ghc843 = (ghc [8,4,3], mkIndex ghc843_pkgs)
+
ghc821 :: (DC.CompilerInfo, InstalledPackageIndex)
ghc821 = (ghc [8,2,1], mkIndex ghc821_pkgs)
@@ -148,6 +159,106 @@ ghc741 = (ghc [7,4,1], mkIndex ghc741_pkgs)
-- Source: http://haskell.org/haskellwiki/Libraries_released_with_GHC
-- and our binary tarballs (package.conf.d.initial subdir)
+
+ghc863_pkgs :: [Cabal.PackageIdentifier]
+ghc863_pkgs =
+ [ p "array" [0,5,3,0]
+ , p "base" [4,12,0,0]
+ , p "binary" [0,8,6,0] -- used by libghc
+ , p "bytestring" [0,10,8,2]
+-- , p "Cabal" [2,4,0,1] package is upgradeable
+ , p "containers" [0,6,0,1]
+ , p "deepseq" [1,4,4,0] -- used by time
+ , p "directory" [1,3,3,0]
+ , p "filepath" [1,4,2,1]
+ , p "ghc-boot" [8,6,3]
+ , p "ghc-boot-th" [8,6,3]
+ , p "ghc-compact" [0,1,0,0]
+ , p "ghc-prim" [0,5,3,0]
+ , p "ghci" [8,6,3]
+-- , p "haskeline" [0,7,4,3] package is upgradeable
+ , p "hpc" [0,6,0,3] -- used by libghc
+ , p "integer-gmp" [1,0,2,0]
+ -- , p "mtl" [2,2,2] package is upgradeable(?)
+ -- , p "parsec" [3,1,13,0] package is upgradeable(?)
+ , p "pretty" [1,1,3,6]
+ , p "process" [1,6,3,0]
+ -- , p "stm" [2,5,0,0] package is upgradeable(?)
+ , p "template-haskell" [2,14,0,0] -- used by libghc
+ -- , p "terminfo" [0,4,1,2]
+ -- , p "text" [1,2,3,1] dependency of Cabal library
+ , p "time" [1,8,0,2] -- used by unix, directory, hpc, ghc. unsafe to upgrade
+ , p "transformers" [0,5,5,0] -- used by libghc
+ , p "unix" [2,7,2,2]
+-- , p "xhtml" [3000,2,2,1]
+ ]
+
+ghc861_pkgs :: [Cabal.PackageIdentifier]
+ghc861_pkgs =
+ [ p "array" [0,5,2,0]
+ , p "base" [4,12,0,0]
+ , p "binary" [0,8,6,0] -- used by libghc
+ , p "bytestring" [0,10,8,2]
+-- , p "Cabal" [2,4,0,1] package is upgradeable
+ , p "containers" [0,6,0,1]
+ , p "deepseq" [1,4,4,0] -- used by time
+ , p "directory" [1,3,3,0]
+ , p "filepath" [1,4,2,1]
+ , p "ghc-boot" [8,6,1]
+ , p "ghc-boot-th" [8,6,1]
+ , p "ghc-compact" [0,1,0,0]
+ , p "ghc-prim" [0,5,3,0]
+ , p "ghci" [8,6,1]
+-- , p "haskeline" [0,7,4,3] package is upgradeable
+ , p "hpc" [0,6,0,3] -- used by libghc
+ , p "integer-gmp" [1,0,2,0]
+ -- , p "mtl" [2,2,2] package is upgradeable(?)
+ -- , p "parsec" [3,1,13,0] package is upgradeable(?)
+ , p "pretty" [1,1,3,6]
+ , p "process" [1,6,3,0]
+ -- , p "stm" [2,5,0,0] package is upgradeable(?)
+ , p "template-haskell" [2,14,0,0] -- used by libghc
+ -- , p "terminfo" [0,4,1,2]
+ -- , p "text" [1,2,3,1] dependency of Cabal library
+ , p "time" [1,8,0,2] -- used by unix, directory, hpc, ghc. unsafe to upgrade
+ , p "transformers" [0,5,5,0] -- used by libghc
+ , p "unix" [2,7,2,2]
+-- , p "xhtml" [3000,2,2,1]
+ ]
+
+ghc843_pkgs :: [Cabal.PackageIdentifier]
+ghc843_pkgs =
+ [ p "array" [0,5,2,0]
+ , p "base" [4,11,1,0]
+ , p "binary" [0,8,5,1] -- used by libghc
+ , p "bytestring" [0,10,8,2]
+-- , p "Cabal" [2,2,0,1] package is upgradeable
+ , p "containers" [0,5,11,2]
+ , p "deepseq" [1,4,3,0] -- used by time
+ , p "directory" [1,3,1,5]
+ , p "filepath" [1,4,2]
+ , p "ghc-boot" [8,4,3]
+ , p "ghc-boot-th" [8,4,3]
+ , p "ghc-compact" [0,1,0,0]
+ , p "ghc-prim" [0,5,2,0]
+ , p "ghci" [8,4,3]
+-- , p "haskeline" [0,7,4,2] package is upgradeable
+ , p "hpc" [0,6,0,3] -- used by libghc
+ , p "integer-gmp" [1,0,2,0]
+ -- , p "mtl" [2,2,2] package is upgradeable(?)
+ -- , p "parsec" [3,1,13,0] package is upgradeable(?)
+ , p "pretty" [1,1,3,6]
+ , p "process" [1,6,3,0]
+ -- , p "stm" [2,4,5,0] package is upgradeable(?)
+ , p "template-haskell" [2,13,0,0] -- used by libghc
+ -- , p "terminfo" [0,4,1,1]
+ -- , p "text" [1,2,3,0]
+ , p "time" [1,8,0,2] -- used by unix, directory, hpc, ghc. unsafe to upgrade
+ , p "transformers" [0,5,5,0] -- used by libghc
+ , p "unix" [2,7,2,2]
+-- , p "xhtml" [3000,2,2,1]
+ ]
+
ghc821_pkgs :: [Cabal.PackageIdentifier]
ghc821_pkgs =
[ p "array" [0,5,2,0]
diff --git a/Portage/Metadata.hs b/Portage/Metadata.hs
index 9b59b13..5bccf6a 100644
--- a/Portage/Metadata.hs
+++ b/Portage/Metadata.hs
@@ -25,9 +25,13 @@ parseMetadata :: Element -> Maybe Metadata
parseMetadata xml =
return Metadata { metadata_emails = map strContent (findElements (unqual "email") xml) }
+formatFlags :: (String, String) -> String
+formatFlags (name, description) = "\t\t<flag name=\"" ++ name ++
+ "\">" ++ description ++ "</flag>"
+
-- don't use Text.XML.Light as we like our own pretty printer
-makeDefaultMetadata :: String -> String
-makeDefaultMetadata long_description =
+makeDefaultMetadata :: String -> [(String, String)] -> String
+makeDefaultMetadata long_description flags =
unlines [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
, "<!DOCTYPE pkgmetadata SYSTEM \"http://www.gentoo.org/dtd/metadata.dtd\">"
, "<pkgmetadata>"
@@ -35,6 +39,10 @@ makeDefaultMetadata long_description =
, "\t\t<email>haskell@gentoo.org</email>"
, "\t\t<name>Gentoo Haskell</name>"
, "\t</maintainer>"
+ , if (formatFlags <$> flags) == [""]
+ then "\t<use>\n\t</use>"
+ else "\t<use>\n" ++ (unlines $ formatFlags <$> flags) ++
+ "\t</use>"
, (init {- strip trailing newline-}
. unlines
. map (\l -> if l `elem` ["<longdescription>", "</longdescription>"]
diff --git a/cabal/Cabal/Distribution/PackageDescription/Configuration.hs b/cabal/Cabal/Distribution/PackageDescription/Configuration.hs
index c57c98c..5efcfc7 100644
--- a/cabal/Cabal/Distribution/PackageDescription/Configuration.hs
+++ b/cabal/Cabal/Distribution/PackageDescription/Configuration.hs
@@ -56,7 +56,8 @@ import Distribution.Types.CondTree
import Distribution.Types.Condition
import Distribution.Types.DependencyMap
-import qualified Data.Map as Map
+import qualified Data.Map.Strict as Map.Strict
+import qualified Data.Map.Lazy as Map
import Data.Tree ( Tree(Node) )
------------------------------------------------------------------------------
@@ -219,7 +220,7 @@ resolveWithFlags dom enabled os arch impl constrs trees checkDeps =
mp m@(Right _) _ = m
mp _ m@(Right _) = m
mp (Left xs) (Left ys) =
- let union = Map.foldrWithKey (Map.insertWith' combine)
+ let union = Map.foldrWithKey (Map.Strict.insertWith combine)
(unDepMapUnion xs) (unDepMapUnion ys)
combine x y = simplifyVersionRange $ unionVersionRanges x y
in union `seq` Left (DepMapUnion union)
diff --git a/hackage-security/.appveyor.yml b/hackage-security/.appveyor.yml
new file mode 100644
index 0000000..c678c17
--- /dev/null
+++ b/hackage-security/.appveyor.yml
@@ -0,0 +1,31 @@
+version: "{build}"
+clone_folder: "c:\\WORK"
+
+environment:
+ global:
+ CABOPTS: "--store-dir=C:\\SR --http-transport=plain-http"
+ matrix:
+ - GHCVER: "8.2.2"
+ - GHCVER: "8.0.2"
+# temporarily disabled until https://github.com/snoyberg/http-client/issues/327 is resolved
+# - GHCVER: "7.10.3.2"
+# - GHCVER: "7.8.4"
+
+cache:
+ - "C:\\SR"
+
+install:
+ - "choco install -y ghc --version %GHCVER%"
+ - "refreshenv"
+ - "set PATH=C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;%PATH%"
+ - "cabal --version"
+ - "ghc --version"
+ - "cabal %CABOPTS% update -vverbose+nowrap"
+
+build: off
+
+test_script:
+ - "cabal %CABOPTS% new-build -j1 -vnormal+nowrap --dry all"
+ - ps: Push-AppveyorArtifact dist-newstyle\cache\plan.json
+ - "cabal %CABOPTS% new-build -j1 -vnormal+nowrap all"
+ - "cabal %CABOPTS% new-test -j1 -vnormal+nowrap all"
diff --git a/hackage-security/.travis.yml b/hackage-security/.travis.yml
index 868838e..c1a4933 100644
--- a/hackage-security/.travis.yml
+++ b/hackage-security/.travis.yml
@@ -26,18 +26,18 @@ matrix:
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.8.4], sources: [hvr-ghc]}}
- compiler: "ghc-7.10.3"
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.10.3], sources: [hvr-ghc]}}
- - compiler: "ghc-8.0.1"
- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.1], sources: [hvr-ghc]}}
- compiler: "ghc-8.0.2"
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
- - compiler: "ghc-8.2.1"
- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.1], sources: [hvr-ghc]}}
+ - compiler: "ghc-8.2.2"
+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
+ - compiler: "ghc-8.4.3"
+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.4.3], sources: [hvr-ghc]}}
before_install:
- HC=${CC}
- unset CC
- PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$PATH
-
+
install:
- cabal --version
- echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
diff --git a/hackage-security/README.md b/hackage-security/README.md
index d31d3a4..819e395 100644
--- a/hackage-security/README.md
+++ b/hackage-security/README.md
@@ -1,4 +1,4 @@
-# Hackage Security
+# Hackage Security [![Hackage version](https://img.shields.io/hackage/v/hackage-security.svg?label=Hackage)](https://hackage.haskell.org/package/hackage-security) [![Stackage version](https://www.stackage.org/package/hackage-security/badge/lts?label=Stackage)](https://www.stackage.org/package/hackage-security) [![Build Status](https://secure.travis-ci.org/haskell/hackage-security.svg?branch=master)](http://travis-ci.org/haskell/hackage-security) [![Windows build status](https://ci.appveyor.com/api/projects/status/github/haskell/cabal?branch=master&svg=true)](https://ci.appveyor.com/project/hvr/hackage-security)
This is a library for Hackage security based on [TUF, The Update
Framework][TUF].
diff --git a/hackage-security/example-client/example-client.cabal b/hackage-security/example-client/example-client.cabal
index b364fc9..a628bb1 100644
--- a/hackage-security/example-client/example-client.cabal
+++ b/hackage-security/example-client/example-client.cabal
@@ -43,6 +43,6 @@ executable example-client
-- see comments in hackage-security.cabal
if flag(use-network-uri)
build-depends: network-uri >= 2.6 && < 2.7,
- network >= 2.6 && < 2.7
+ network >= 2.6 && < 2.8
else
build-depends: network >= 2.5 && < 2.6
diff --git a/hackage-security/hackage-repo-tool/ChangeLog.md b/hackage-security/hackage-repo-tool/ChangeLog.md
index 1ebc48e..78f63b3 100644
--- a/hackage-security/hackage-repo-tool/ChangeLog.md
+++ b/hackage-security/hackage-repo-tool/ChangeLog.md
@@ -1,11 +1,16 @@
+0.1.1.1
+-------
+* Make `hackage-repo-tool` buildable on Windows (#175)
+* Fix build-failure w/ `directory-1.2`
+
0.1.1
-----
-* Update for hackage-security-0.5
+* Update for `hackage-security-0.5`
0.1.0.1
-------
* Add missing module to other-modules (#100)
-* Allow for hackage-security-0.3
+* Allow for `hackage-security-0.3`
* Include ChangeLog.md in sdist (#98)
0.1.0.0
diff --git a/hackage-security/hackage-repo-tool/hackage-repo-tool.cabal b/hackage-security/hackage-repo-tool/hackage-repo-tool.cabal
index 9533640..362b266 100644
--- a/hackage-security/hackage-repo-tool/hackage-repo-tool.cabal
+++ b/hackage-security/hackage-repo-tool/hackage-repo-tool.cabal
@@ -1,36 +1,44 @@
+cabal-version: 1.12
name: hackage-repo-tool
-version: 0.1.1
+version: 0.1.1.1
+
synopsis: Utility to manage secure file-based package repositories
-description: This utility can be used to manage secure file-based
- repositories (creating TUF metadata as well as a Hackage
- index tarball). Currently it also provides various
- lower level utilities for creating and signing TUF files.
+description: This utility can be used to manage secure file-based package
+ repositories (creating [TUF](https://theupdateframework.github.io/)
+ metadata as well as a Hackage index tarball) which can be used by
+ clients such as [cabal-install](http://hackage.haskell.org/package/cabal-install).
+ Currently it also provides various lower level utilities for creating
+ and signing TUF files.
.
- This is part of the Hackage Security infrastructure.
-homepage: http://github.com/well-typed/hackage-security/
+ This is part of the [Hackage Security](https://github.com/haskell/hackage-security#readme)
+ infrastructure.
license: BSD3
license-file: LICENSE
author: Edsko de Vries
-maintainer: edsko@well-typed.com
+maintainer: cabal-devel@haskell.org
copyright: Copyright 2015 Well-Typed LLP
category: Distribution
-homepage: https://github.com/well-typed/hackage-security
-bug-reports: https://github.com/well-typed/hackage-security/issues
+homepage: https://github.com/haskell/hackage-security
+bug-reports: https://github.com/haskell/hackage-security/issues
build-type: Simple
-cabal-version: >=1.10
extra-source-files:
ChangeLog.md
source-repository head
type: git
- location: https://github.com/well-typed/hackage-security.git
+ location: https://github.com/haskell/hackage-security.git
flag use-network-uri
- description: Are we using network-uri?
+ description: Are we using @network-uri@?
+ manual: False
+
+flag use-old-time
+ description: Are we using @old-time@?
manual: False
executable hackage-repo-tool
+ hs-source-dirs: src
main-is: Main.hs
other-modules: Hackage.Security.RepoTool.Options
Hackage.Security.RepoTool.Layout
@@ -38,18 +46,33 @@ executable hackage-repo-tool
Hackage.Security.RepoTool.Paths
Hackage.Security.RepoTool.Util.IO
Prelude
- build-depends: base >= 4.4 && < 5,
- Cabal >= 1.12 && < 2.1,
+
+ build-depends: base >= 4.5 && < 5,
+ Cabal >= 1.14 && < 2.4,
bytestring >= 0.9 && < 0.11,
directory >= 1.1 && < 1.4,
filepath >= 1.2 && < 1.5,
- optparse-applicative >= 0.11 && < 0.14,
+ optparse-applicative >= 0.11 && < 0.15,
tar >= 0.4 && < 0.6,
time >= 1.2 && < 1.9,
- unix >= 2.5 && < 2.8,
zlib >= 0.5 && < 0.7,
hackage-security >= 0.5 && < 0.6
- hs-source-dirs: src
+ if !os(windows)
+ build-depends: unix >= 2.5 && < 2.8
+
+ if flag(use-old-time)
+ build-depends: directory < 1.2
+ , old-time == 1.1.*
+ else
+ build-depends: directory >= 1.2
+
+ -- see comments in hackage-security.cabal
+ if flag(use-network-uri)
+ build-depends: network-uri >= 2.6 && < 2.7,
+ network >= 2.6 && < 2.8
+ else
+ build-depends: network >= 2.5 && < 2.6
+
default-language: Haskell2010
default-extensions: DeriveDataTypeable
FlexibleContexts
@@ -58,12 +81,5 @@ executable hackage-repo-tool
ScopedTypeVariables
StandaloneDeriving
RecordWildCards
- other-extensions: TemplateHaskell
- ghc-options: -Wall
- -- see comments in hackage-security.cabal
- if flag(use-network-uri)
- build-depends: network-uri >= 2.6 && < 2.7,
- network >= 2.6 && < 2.7
- else
- build-depends: network >= 2.5 && < 2.6
+ ghc-options: -Wall
diff --git a/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Options.hs b/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Options.hs
index af65529..d9a4af5 100644
--- a/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Options.hs
+++ b/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Options.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
module Hackage.Security.RepoTool.Options (
GlobalOpts(..)
, Command(..)
@@ -60,8 +61,10 @@ data Command =
-- | Create mirrors metadata
| CreateMirrors KeysLoc (Path Absolute) [URI]
+#ifndef mingw32_HOST_OS
-- | Create a directory with symlinks in cabal-local-rep layout
| SymlinkCabalLocalRepo RepoLoc RepoLoc
+#endif
-- | Sign an individual file
| Sign [KeyLoc] DeleteExistingSignatures (Path Absolute)
@@ -123,6 +126,7 @@ parseCreateMirrors = CreateMirrors
])
<*> (many $ argument (str >>= readURI) (metavar "MIRROR"))
+#ifndef mingw32_HOST_OS
parseSymlinkCabalLocalRepo :: Parser Command
parseSymlinkCabalLocalRepo = SymlinkCabalLocalRepo
<$> parseRepoLoc
@@ -130,6 +134,7 @@ parseSymlinkCabalLocalRepo = SymlinkCabalLocalRepo
long "cabal-repo"
, help "Location of the cabal repo"
])
+#endif
parseSign :: Parser Command
parseSign = Sign
@@ -181,8 +186,10 @@ parseGlobalOptions = GlobalOpts
progDesc "Create root metadata"
, command "create-mirrors" $ info (helper <*> parseCreateMirrors) $
progDesc "Create mirrors metadata. All MIRRORs specified on the command line will be written to the file."
+#ifndef mingw32_HOST_OS
, command "symlink-cabal-local-repo" $ info (helper <*> parseSymlinkCabalLocalRepo) $
progDesc "Create a directory in cabal-local-repo layout with symlinks to the specified repository."
+#endif
, command "sign" $ info (helper <*> parseSign) $
progDesc "Sign a file"
])
diff --git a/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Util/IO.hs b/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Util/IO.hs
index 263c673..b6cd031 100644
--- a/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Util/IO.hs
+++ b/hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Util/IO.hs
@@ -4,32 +4,41 @@ module Hackage.Security.RepoTool.Util.IO (
-- * Miscellaneous
compress
, getFileModTime
+#ifndef mingw32_HOST_OS
, createSymbolicLink
+#endif
-- * Tar archives
, TarGzError
, tarExtractFile
) where
-import Control.Exception
-import Data.Typeable
-import System.IO.Error
-import qualified Codec.Archive.Tar as Tar
-import qualified Codec.Archive.Tar.Entry as Tar
-import qualified Codec.Compression.GZip as GZip
-import qualified Data.ByteString.Lazy as BS.L
-
--- Unlike the hackage-security library properly,
--- this currently works on unix systems only
-import System.Posix.Types (EpochTime)
-import qualified System.Posix.Files as Posix
+import qualified Codec.Archive.Tar as Tar
+import qualified Codec.Archive.Tar.Entry as Tar
+import qualified Codec.Compression.GZip as GZip
+import Control.Exception
+import qualified Data.ByteString.Lazy as BS.L
+import Data.Typeable
+import qualified System.Directory as Directory
+import System.IO.Error
-- hackage-security
-import Hackage.Security.Util.Path
+import Hackage.Security.Util.Path
-- hackage-repo-tool
-import Hackage.Security.RepoTool.Options
-import Hackage.Security.RepoTool.Layout
-import Hackage.Security.RepoTool.Paths
+import Hackage.Security.RepoTool.Layout
+import Hackage.Security.RepoTool.Options
+import Hackage.Security.RepoTool.Paths
+
+import System.Posix.Types (EpochTime)
+#ifndef mingw32_HOST_OS
+import qualified System.Posix.Files as Posix
+#endif
+
+#if MIN_VERSION_directory(1,2,0)
+import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
+#else
+import System.Time (ClockTime (TOD))
+#endif
-- | Get the modification time of the specified file
--
@@ -37,7 +46,17 @@ import Hackage.Security.RepoTool.Paths
getFileModTime :: GlobalOpts -> RepoLoc -> TargetPath' -> IO EpochTime
getFileModTime opts repoLoc targetPath =
handle handler $
- Posix.modificationTime <$> Posix.getFileStatus (toFilePath fp)
+ -- Underlying implementation of 'Directory.getModificationTime' converts
+ -- from POSIX seconds, so there shouldn't be loss of precision.
+ -- NB: Apparently, this has low clock resolution on GHC < 7.8.
+ -- I don't think we care.
+#if MIN_VERSION_directory(1,2,0)
+ fromInteger . floor . utcTimeToPOSIXSeconds
+ <$> Directory.getModificationTime (toFilePath fp)
+#else
+ Directory.getModificationTime (toFilePath fp) >>= \(TOD s _) ->
+ return (fromInteger s)
+#endif
where
fp :: Path Absolute
fp = anchorTargetPath' opts repoLoc targetPath
@@ -51,6 +70,7 @@ compress src dst =
withFile dst WriteMode $ \h ->
BS.L.hPut h =<< GZip.compress <$> readLazyByteString src
+#ifndef mingw32_HOST_OS
-- | Create a symbolic link (unix only)
--
-- Create the directory for the target if it does not exist.
@@ -66,6 +86,7 @@ createSymbolicLink linkTarget linkLoc = do
linkTarget' <- toAbsoluteFilePath linkTarget
linkLoc' <- toAbsoluteFilePath linkLoc
Posix.createSymbolicLink linkTarget' linkLoc'
+#endif
{-------------------------------------------------------------------------------
Working with tar archives
diff --git a/hackage-security/hackage-repo-tool/src/Main.hs b/hackage-security/hackage-repo-tool/src/Main.hs
index 866c7e0..90c5a07 100644
--- a/hackage-security/hackage-repo-tool/src/Main.hs
+++ b/hackage-security/hackage-repo-tool/src/Main.hs
@@ -9,10 +9,13 @@ import Data.Time
import GHC.Conc.Sync (setUncaughtExceptionHandler)
import Network.URI (URI)
import System.Exit
-import System.IO.Error (isAlreadyExistsError)
import qualified Data.ByteString.Lazy as BS.L
import qualified System.FilePath as FilePath
+#ifndef mingw32_HOST_OS
+import System.IO.Error (isAlreadyExistsError)
+#endif
+
-- Cabal
import Distribution.Package
import Distribution.Text
@@ -54,8 +57,10 @@ main = do
createRoot opts keysLoc rootLoc
CreateMirrors keysLoc mirrorsLoc mirrors ->
createMirrors opts keysLoc mirrorsLoc mirrors
+#ifndef mingw32_HOST_OS
SymlinkCabalLocalRepo repoLoc cabalRepoLoc ->
symlinkCabalLocalRepo opts repoLoc cabalRepoLoc
+#endif
Sign keys deleteExisting file ->
signFile keys deleteExisting file
@@ -489,7 +494,7 @@ data WhenWrite =
-- version number) we don't overwrite it and return Nothing; otherwise we
-- increment the version number, write the file, and (if it's in the index)
-- copy it to the unpacked index directory.
-updateFile :: forall a. (ToJSON WriteJSON (Signed a), HasHeader a)
+updateFile :: forall a. (ToJSON WriteJSON a, HasHeader a)
=> GlobalOpts
-> RepoLoc
-> WhenWrite
@@ -597,6 +602,7 @@ checkRepoLayout opts repoLoc = liftM and . mapM checkPackage
expectedTarGz :: TargetPath'
expectedTarGz = InRepPkg repoLayoutPkgTarGz pkgId
+#ifndef mingw32_HOST_OS
{-------------------------------------------------------------------------------
Creating Cabal-local-repo
-------------------------------------------------------------------------------}
@@ -621,6 +627,7 @@ symlinkCabalLocalRepo opts@GlobalOpts{..} repoLoc cabalRepoLoc = do
target = anchorRepoPath opts repoLoc file
loc = anchorRepoPath opts' cabalRepoLoc file
opts' = opts { globalRepoLayout = cabalLocalRepoLayout }
+#endif
{-------------------------------------------------------------------------------
Signing individual files
diff --git a/hackage-security/hackage-root-tool/hackage-root-tool.cabal b/hackage-security/hackage-root-tool/hackage-root-tool.cabal
index 1bb66cc..86da37d 100644
--- a/hackage-security/hackage-root-tool/hackage-root-tool.cabal
+++ b/hackage-security/hackage-root-tool/hackage-root-tool.cabal
@@ -27,7 +27,7 @@ executable hackage-root-tool
main-is: Main.hs
build-depends: base >= 4.4 && < 5,
filepath >= 1.2 && < 1.5,
- optparse-applicative >= 0.11 && < 0.14,
+ optparse-applicative >= 0.11 && < 0.15,
hackage-security >= 0.5 && < 0.6
default-language: Haskell2010
other-extensions: CPP, ScopedTypeVariables, RecordWildCards
diff --git a/hackage-security/hackage-security-HTTP/hackage-security-HTTP.cabal b/hackage-security/hackage-security-HTTP/hackage-security-HTTP.cabal
index 6956543..9aef5f9 100644
--- a/hackage-security/hackage-security-HTTP/hackage-security-HTTP.cabal
+++ b/hackage-security/hackage-security-HTTP/hackage-security-HTTP.cabal
@@ -5,15 +5,14 @@ description: The hackage security library provides a 'HttpLib'
abstraction to allow to bind against different HTTP
libraries. This library implements this abstraction using
the @HTTP@ library.
-homepage: http://github.com/well-typed/hackage-security/
license: BSD3
license-file: LICENSE
author: Edsko de Vries
maintainer: edsko@well-typed.com
copyright: Copyright 2015 Well-Typed LLP
category: Distribution
-homepage: https://github.com/well-typed/hackage-security
-bug-reports: https://github.com/well-typed/hackage-security/issues
+homepage: https://github.com/haskell/hackage-security
+bug-reports: https://github.com/haskell/hackage-security/issues
build-type: Simple
cabal-version: >=1.10
@@ -22,7 +21,7 @@ extra-source-files:
source-repository head
type: git
- location: https://github.com/well-typed/hackage-security.git
+ location: https://github.com/haskell/hackage-security.git
flag use-network-uri
description: Are we using network-uri?
@@ -50,6 +49,6 @@ library
-- See comments in hackage-security.cabal
if flag(use-network-uri)
build-depends: network-uri >= 2.6 && < 2.7,
- network >= 2.6 && < 2.7
+ network >= 2.6 && < 2.8
else
build-depends: network >= 2.5 && < 2.6
diff --git a/hackage-security/hackage-security-curl/hackage-security-curl.cabal b/hackage-security/hackage-security-curl/hackage-security-curl.cabal
index 0148615..ec03ce7 100644
--- a/hackage-security/hackage-security-curl/hackage-security-curl.cabal
+++ b/hackage-security/hackage-security-curl/hackage-security-curl.cabal
@@ -28,6 +28,6 @@ library
-- See comments in hackage-security.cabal
if flag(use-network-uri)
build-depends: network-uri >= 2.6 && < 2.7,
- network >= 2.6 && < 2.7
+ network >= 2.6 && < 2.8
else
build-depends: network >= 2.5 && < 2.6
diff --git a/hackage-security/hackage-security-http-client/hackage-security-http-client.cabal b/hackage-security/hackage-security-http-client/hackage-security-http-client.cabal
index f354bf5..8445e9a 100644
--- a/hackage-security/hackage-security-http-client/hackage-security-http-client.cabal
+++ b/hackage-security/hackage-security-http-client/hackage-security-http-client.cabal
@@ -34,6 +34,6 @@ library
-- see comments in hackage-security.cabal
if flag(use-network-uri)
build-depends: network-uri >= 2.6 && < 2.7,
- network >= 2.6 && < 2.7
+ network >= 2.6 && < 2.8
else
build-depends: network >= 2.5 && < 2.6
diff --git a/hackage-security/hackage-security/ChangeLog.md b/hackage-security/hackage-security/ChangeLog.md
index 00f835d..aee8155 100644
--- a/hackage-security/hackage-security/ChangeLog.md
+++ b/hackage-security/hackage-security/ChangeLog.md
@@ -1,3 +1,18 @@
+unreleased
+----------
+
+* Allow `network-2.7.0.0`
+* Allow `aeson-1.4.0.0`
+
+0.5.3.0
+-------
+
+* Use `flock(2)`-based locking where available
+ (compat-shim taken from `cabal-install`'s code-base) (#207)
+* Improve handling of async exceptions (#187)
+* Detect & recover from local corruption of uncompressed index tarball (#196)
+* Support `base-4.11`
+
0.5.2.2
-------
diff --git a/hackage-security/hackage-security/hackage-security.cabal b/hackage-security/hackage-security/hackage-security.cabal
index d63f5b8..148daa4 100644
--- a/hackage-security/hackage-security/hackage-security.cabal
+++ b/hackage-security/hackage-security/hackage-security.cabal
@@ -1,5 +1,7 @@
+cabal-version: 1.12
name: hackage-security
-version: 0.5.2.2
+version: 0.5.3.0
+
synopsis: Hackage security library
description: The hackage security library provides both server and
client utilities for securing the Hackage package server
@@ -21,13 +23,12 @@ description: The hackage security library provides both server and
license: BSD3
license-file: LICENSE
author: Edsko de Vries
-maintainer: edsko@well-typed.com
+maintainer: cabal-devel@haskell.org
copyright: Copyright 2015-2016 Well-Typed LLP
category: Distribution
homepage: https://github.com/haskell/hackage-security
bug-reports: https://github.com/haskell/hackage-security/issues
build-type: Simple
-cabal-version: >=1.10
extra-source-files:
ChangeLog.md
@@ -37,15 +38,15 @@ source-repository head
location: https://github.com/haskell/hackage-security.git
flag base48
- description: Are we using base 4.8 or later?
+ description: Are we using @base@ 4.8 or later?
manual: False
flag use-network-uri
- description: Are we using network-uri?
+ description: Are we using @network-uri@?
manual: False
-Flag old-directory
- description: Use directory < 1.2 and old-time
+flag old-directory
+ description: Use @directory@ < 1.2 and @old-time@
manual: False
default: False
@@ -90,17 +91,19 @@ library
Hackage.Security.TUF.Targets
Hackage.Security.TUF.Timestamp
Hackage.Security.Util.Base64
+ Hackage.Security.Util.Exit
+ Hackage.Security.Util.FileLock
Hackage.Security.Util.JSON
Hackage.Security.Util.Stack
Hackage.Security.Util.TypedEmbedded
Prelude
-- We support ghc 7.4 (bundled with Cabal 1.14) and up
- build-depends: base >= 4.5 && < 5,
+ build-depends: base >= 4.5 && < 4.13,
base16-bytestring >= 0.1.1 && < 0.2,
base64-bytestring >= 1.0 && < 1.1,
bytestring >= 0.9 && < 0.11,
- Cabal >= 1.14 && < 2.2,
- containers >= 0.4 && < 0.6,
+ Cabal >= 1.14 && < 2.6,
+ containers >= 0.4 && < 0.7,
ed25519 >= 0.0 && < 0.1,
filepath >= 1.2 && < 1.5,
mtl >= 2.2 && < 2.3,
@@ -121,6 +124,8 @@ library
old-time >= 1 && < 1.2
else
build-depends: directory >= 1.2 && < 1.4
+ build-tool-depends: hsc2hs:hsc2hs >= 0.67 && <0.69
+
hs-source-dirs: src
default-language: Haskell2010
default-extensions: DefaultSignatures
@@ -195,7 +200,7 @@ library
-- dependency in network is not redundant.)
if flag(use-network-uri)
build-depends: network-uri >= 2.6 && < 2.7,
- network >= 2.6 && < 2.7
+ network >= 2.6 && < 2.9
else
build-depends: network >= 2.5 && < 2.6
@@ -204,9 +209,7 @@ library
if impl(ghc >= 7.10)
other-extensions: AllowAmbiguousTypes
--- StaticPointers
--- ^^^ Temporarily disabled because Hackage doesn't know yet about this
--- extension and will therefore reject this package.
+ StaticPointers
test-suite TestSuite
type: exitcode-stdio-1.0
@@ -218,21 +221,28 @@ test-suite TestSuite
TestSuite.JSON
TestSuite.PrivateKeys
TestSuite.Util.StrictMVar
- build-depends: base,
+
+ -- inherited constraints from lib:hackage-security component
+ build-depends: hackage-security,
+ base,
Cabal,
containers,
- HUnit,
bytestring,
- hackage-security,
network-uri,
tar,
- tasty,
- tasty-hunit,
- tasty-quickcheck,
- QuickCheck,
- temporary,
time,
zlib
+
+ -- dependencies exclusive to test-suite
+ build-depends: tasty == 1.0.*,
+ tasty-hunit == 0.10.*,
+ tasty-quickcheck == 0.10.*,
+ QuickCheck == 2.11.*,
+ aeson == 1.4.*,
+ vector == 0.12.*,
+ unordered-containers >=0.2.8.0 && <0.3,
+ temporary == 1.2.*
+
hs-source-dirs: tests
default-language: Haskell2010
default-extensions: FlexibleContexts
diff --git a/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Cache.hs b/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Cache.hs
index 28395e6..949b651 100644
--- a/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Cache.hs
+++ b/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Cache.hs
@@ -16,6 +16,7 @@ module Hackage.Security.Client.Repository.Cache (
import Control.Exception
import Control.Monad
+import Control.Monad.IO.Class
import Data.Maybe
import Codec.Archive.Tar (Entries(..))
import Codec.Archive.Tar.Index (TarIndex, IndexBuilder, TarEntryOffset)
@@ -29,6 +30,7 @@ import Hackage.Security.Client.Repository
import Hackage.Security.Client.Formats
import Hackage.Security.TUF
import Hackage.Security.Util.Checked
+import Hackage.Security.Util.Exit
import Hackage.Security.Util.IO
import Hackage.Security.Util.Path
@@ -65,21 +67,47 @@ cacheRemoteFile cache downloaded f isCached = do
unzipIndex :: IO ()
unzipIndex = do
createDirectoryIfMissing True (takeDirectory indexUn)
- shouldTryIncremenal <- cachedIndexProbablyValid
- if shouldTryIncremenal
- then unzipIncremenal
- else unzipNonIncremenal
+ shouldTryIncremental <- cachedIndexProbablyValid
+ if shouldTryIncremental
+ then do
+ success <- unzipIncremental
+ unless success unzipNonIncremental
+ else unzipNonIncremental
where
- unzipIncremenal = do
+ unzipIncremental = do
compressed <- readLazyByteString indexGz
let uncompressed = GZip.decompress compressed
- withFile indexUn ReadWriteMode $ \h -> do
- currentSize <- hFileSize h
+
+ -- compare prefix of old index with prefix of new index to
+ -- ensure that it's safe to incrementally append
+ (seekTo',newTail') <- withFile indexUn ReadMode $ \h ->
+ multipleExitPoints $ do
+ currentSize <- liftIO $ hFileSize h
let seekTo = 0 `max` (currentSize - tarTrailer)
- hSeek h AbsoluteSeek seekTo
- BS.L.hPut h $ BS.L.drop (fromInteger seekTo) uncompressed
+ (newPrefix,newTail) = BS.L.splitAt (fromInteger seekTo)
+ uncompressed
+
+ (oldPrefix,oldTrailer) <- BS.L.splitAt (fromInteger seekTo) <$>
+ liftIO (BS.L.hGetContents h)
+
+ unless (oldPrefix == newPrefix) $
+ exit (0,mempty) -- corrupted index.tar prefix
+
+ -- sanity check: verify there's a 1KiB zero-filled trailer
+ unless (oldTrailer == tarTrailerBs) $
+ exit (0,mempty) -- corrupted .tar trailer
- unzipNonIncremenal = do
+ return (seekTo,newTail)
+
+ if seekTo' <= 0
+ then return False -- fallback to non-incremental update
+ else withFile indexUn ReadWriteMode $ \h -> do
+ -- everything seems fine; append the new data
+ liftIO $ hSeek h AbsoluteSeek seekTo'
+ liftIO $ BS.L.hPut h newTail'
+ return True
+
+ unzipNonIncremental = do
compressed <- readLazyByteString indexGz
let uncompressed = GZip.decompress compressed
withFile indexUn WriteMode $ \h ->
@@ -108,6 +136,8 @@ cacheRemoteFile cache downloaded f isCached = do
tarTrailer :: Integer
tarTrailer = 1024
+ tarTrailerBs = BS.L.replicate (fromInteger tarTrailer) 0x00
+
-- | Rebuild the tarball index
--
-- Attempts to add to the existing index, if one exists.
diff --git a/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Remote.hs b/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Remote.hs
index cee4b0c..948bd9d 100644
--- a/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Remote.hs
+++ b/hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Remote.hs
@@ -30,7 +30,6 @@ module Hackage.Security.Client.Repository.Remote (
import Control.Concurrent
import Control.Exception
import Control.Monad.Cont
-import Control.Monad.Except
import Data.List (nub, intercalate)
import Data.Typeable
import Network.URI hiding (uriPath, path)
@@ -50,6 +49,7 @@ import Hackage.Security.Util.IO
import Hackage.Security.Util.Path
import Hackage.Security.Util.Pretty
import Hackage.Security.Util.Some
+import Hackage.Security.Util.Exit
import qualified Hackage.Security.Client.Repository.Cache as Cache
{-------------------------------------------------------------------------------
@@ -686,39 +686,3 @@ verifyRemoteFile remoteTemp trustedInfo = do
, temp
]
-{-------------------------------------------------------------------------------
- Auxiliary: multiple exit points
--------------------------------------------------------------------------------}
-
--- | Multiple exit points
---
--- We can simulate the imperative code
---
--- > if (cond1)
--- > return exp1;
--- > if (cond2)
--- > return exp2;
--- > if (cond3)
--- > return exp3;
--- > return exp4;
---
--- as
---
--- > multipleExitPoints $ do
--- > when (cond1) $
--- > exit exp1
--- > when (cond) $
--- > exit exp2
--- > when (cond)
--- > exit exp3
--- > return exp4
-multipleExitPoints :: Monad m => ExceptT a m a -> m a
-multipleExitPoints = liftM aux . runExceptT
- where
- aux :: Either a a -> a
- aux (Left a) = a
- aux (Right a) = a
-
--- | Function exit point (see 'multipleExitPoints')
-exit :: Monad m => e -> ExceptT e m a
-exit = throwError
diff --git a/hackage-security/hackage-security/src/Hackage/Security/Util/Checked.hs b/hackage-security/hackage-security/src/Hackage/Security/Util/Checked.hs
index 62392b9..e876791 100644
--- a/hackage-security/hackage-security/src/Hackage/Security/Util/Checked.hs
+++ b/hackage-security/hackage-security/src/Hackage/Security/Util/Checked.hs
@@ -9,6 +9,8 @@
{-# LANGUAGE IncoherentInstances #-}
#endif
+{-# LANGUAGE DeriveDataTypeable#-}
+
-- | Checked exceptions
module Hackage.Security.Util.Checked (
Throws
@@ -25,6 +27,7 @@ module Hackage.Security.Util.Checked (
import Control.Exception (Exception, IOException)
import qualified Control.Exception as Base
+import Data.Typeable (Typeable)
#if __GLASGOW_HASKELL__ >= 708
import GHC.Prim (coerce)
@@ -50,14 +53,48 @@ unthrow _ x = unWrap (coerceWrap (Wrap x :: Wrap e a))
Base exceptions
-------------------------------------------------------------------------------}
+-- | Determine if an exception is asynchronous, based on its type.
+isAsync :: Exception e => e -> Bool
+#if MIN_VERSION_base(4, 7, 0)
+isAsync e =
+ case Base.fromException $ Base.toException e of
+ Just Base.SomeAsyncException{} -> True
+ Nothing -> False
+#else
+-- Earlier versions of GHC had no SomeAsyncException. We have to
+-- instead make up a list of async exceptions.
+isAsync e =
+ let se = Base.toException e
+ in case () of
+ ()
+ | Just (_ :: Base.AsyncException) <- Base.fromException se -> True
+ | show e == "<<timeout>>" -> True
+ | otherwise -> False
+#endif
+
+-- | 'Base.catch', but immediately rethrows asynchronous exceptions
+-- (as determined by 'isAsync').
+catchSync :: Exception e => IO a -> (e -> IO a) -> IO a
+catchSync act onErr = act `Base.catch` \e ->
+ if isAsync e
+ then Base.throwIO e
+ else onErr e
+
+-- | Wraps up an async exception as a synchronous exception.
+newtype SyncException = SyncException Base.SomeException
+ deriving (Show, Typeable)
+instance Exception SyncException
+
-- | Throw a checked exception
throwChecked :: (Exception e, Throws e) => e -> IO a
-throwChecked = Base.throwIO
+throwChecked e
+ | isAsync e = Base.throwIO $ SyncException $ Base.toException e
+ | otherwise = Base.throwIO e
-- | Catch a checked exception
catchChecked :: forall a e. Exception e
=> (Throws e => IO a) -> (e -> IO a) -> IO a
-catchChecked act = Base.catch (unthrow (Proxy :: Proxy e) act)
+catchChecked act = catchSync (unthrow (Proxy :: Proxy e) act)
-- | 'catchChecked' with the arguments reversed
handleChecked :: Exception e => (e -> IO a) -> (Throws e => IO a) -> IO a
diff --git a/hackage-security/hackage-security/src/Hackage/Security/Util/Exit.hs b/hackage-security/hackage-security/src/Hackage/Security/Util/Exit.hs
new file mode 100644
index 0000000..0138591
--- /dev/null
+++ b/hackage-security/hackage-security/src/Hackage/Security/Util/Exit.hs
@@ -0,0 +1,40 @@
+module Hackage.Security.Util.Exit where
+
+import Control.Monad.Except
+
+{-------------------------------------------------------------------------------
+ Auxiliary: multiple exit points
+-------------------------------------------------------------------------------}
+
+-- | Multiple exit points
+--
+-- We can simulate the imperative code
+--
+-- > if (cond1)
+-- > return exp1;
+-- > if (cond2)
+-- > return exp2;
+-- > if (cond3)
+-- > return exp3;
+-- > return exp4;
+--
+-- as
+--
+-- > multipleExitPoints $ do
+-- > when (cond1) $
+-- > exit exp1
+-- > when (cond2) $
+-- > exit exp2
+-- > when (cond3) $
+-- > exit exp3
+-- > return exp4
+multipleExitPoints :: Monad m => ExceptT a m a -> m a
+multipleExitPoints = liftM aux . runExceptT
+ where
+ aux :: Either a a -> a
+ aux (Left a) = a
+ aux (Right a) = a
+
+-- | Function exit point (see 'multipleExitPoints')
+exit :: Monad m => e -> ExceptT e m a
+exit = throwError
diff --git a/hackage-security/hackage-security/src/Hackage/Security/Util/FileLock.hsc b/hackage-security/hackage-security/src/Hackage/Security/Util/FileLock.hsc
new file mode 100644
index 0000000..65bee01
--- /dev/null
+++ b/hackage-security/hackage-security/src/Hackage/Security/Util/FileLock.hsc
@@ -0,0 +1,202 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE InterruptibleFFI #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+-- | This compat module can be removed once base-4.10 (ghc-8.2) is the minimum
+-- required version. Though note that the locking functionality is not in
+-- public modules in base-4.10, just in the "GHC.IO.Handle.Lock" module.
+--
+-- Copied from @cabal-install@ codebase "Distribution.Client.Compat.FileLock".
+module Hackage.Security.Util.FileLock (
+ FileLockingNotSupported(..)
+ , LockMode(..)
+ , hLock
+ , hTryLock
+ ) where
+
+#if MIN_VERSION_base(4,10,0)
+
+import GHC.IO.Handle.Lock
+
+#else
+
+-- The remainder of this file is a modified copy
+-- of GHC.IO.Handle.Lock from ghc-8.2.x
+--
+-- The modifications were just to the imports and the CPP, since we do not have
+-- access to the HAVE_FLOCK from the ./configure script. We approximate the
+-- lack of HAVE_FLOCK with @defined(solaris2_HOST_OS) || defined(aix_HOST_OS)@
+-- instead since those are known major Unix platforms lacking @flock()@ or
+-- having broken one.
+
+import Control.Exception (Exception)
+import Data.Typeable
+
+#if defined(solaris2_HOST_OS) || defined(aix_HOST_OS)
+
+import Control.Exception (throwIO)
+import System.IO (Handle)
+
+#else
+
+import Data.Bits
+import Data.Function
+import Control.Concurrent.MVar
+
+import Foreign.C.Error
+import Foreign.C.Types
+
+import GHC.IO.Handle.Types
+import GHC.IO.FD
+import GHC.IO.Exception
+
+#if defined(mingw32_HOST_OS)
+
+#if defined(i386_HOST_ARCH)
+## define WINDOWS_CCONV stdcall
+#elif defined(x86_64_HOST_ARCH)
+## define WINDOWS_CCONV ccall
+#else
+# error Unknown mingw32 arch
+#endif
+
+#include <windows.h>
+
+import Foreign.Marshal.Alloc
+import Foreign.Marshal.Utils
+import Foreign.Ptr
+import GHC.Windows
+
+#else /* !defined(mingw32_HOST_OS), so assume unix with flock() */
+
+#include <sys/file.h>
+
+#endif /* !defined(mingw32_HOST_OS) */
+
+#endif /* !(defined(solaris2_HOST_OS) || defined(aix_HOST_OS)) */
+
+
+-- | Exception thrown by 'hLock' on non-Windows platforms that don't support
+-- 'flock'.
+data FileLockingNotSupported = FileLockingNotSupported
+ deriving (Typeable, Show)
+
+instance Exception FileLockingNotSupported
+
+
+-- | Indicates a mode in which a file should be locked.
+data LockMode = SharedLock | ExclusiveLock
+
+-- | If a 'Handle' references a file descriptor, attempt to lock contents of the
+-- underlying file in appropriate mode. If the file is already locked in
+-- incompatible mode, this function blocks until the lock is established. The
+-- lock is automatically released upon closing a 'Handle'.
+--
+-- Things to be aware of:
+--
+-- 1) This function may block inside a C call. If it does, in order to be able
+-- to interrupt it with asynchronous exceptions and/or for other threads to
+-- continue working, you MUST use threaded version of the runtime system.
+--
+-- 2) The implementation uses 'LockFileEx' on Windows and 'flock' otherwise,
+-- hence all of their caveats also apply here.
+--
+-- 3) On non-Windows plaftorms that don't support 'flock' (e.g. Solaris) this
+-- function throws 'FileLockingNotImplemented'. We deliberately choose to not
+-- provide fcntl based locking instead because of its broken semantics.
+--
+-- @since 4.10.0.0
+hLock :: Handle -> LockMode -> IO ()
+hLock h mode = lockImpl h "hLock" mode True >> return ()
+
+-- | Non-blocking version of 'hLock'.
+--
+-- @since 4.10.0.0
+hTryLock :: Handle -> LockMode -> IO Bool
+hTryLock h mode = lockImpl h "hTryLock" mode False
+
+----------------------------------------
+
+#if defined(solaris2_HOST_OS) || defined(aix_HOST_OS)
+
+-- | No-op implementation.
+lockImpl :: Handle -> String -> LockMode -> Bool -> IO Bool
+lockImpl _ _ _ _ = throwIO FileLockingNotSupported
+
+#else /* !(defined(solaris2_HOST_OS) || defined(aix_HOST_OS)) */
+
+#if defined(mingw32_HOST_OS)
+
+lockImpl :: Handle -> String -> LockMode -> Bool -> IO Bool
+lockImpl h ctx mode block = do
+ FD{fdFD = fd} <- handleToFd h
+ wh <- throwErrnoIf (== iNVALID_HANDLE_VALUE) ctx $ c_get_osfhandle fd
+ allocaBytes sizeof_OVERLAPPED $ \ovrlpd -> do
+ fillBytes ovrlpd (fromIntegral sizeof_OVERLAPPED) 0
+ let flags = cmode .|. (if block then 0 else #{const LOCKFILE_FAIL_IMMEDIATELY})
+ -- We want to lock the whole file without looking up its size to be
+ -- consistent with what flock does. According to documentation of LockFileEx
+ -- "locking a region that goes beyond the current end-of-file position is
+ -- not an error", however e.g. Windows 10 doesn't accept maximum possible
+ -- value (a pair of MAXDWORDs) for mysterious reasons. Work around that by
+ -- trying 2^32-1.
+ fix $ \retry -> c_LockFileEx wh flags 0 0xffffffff 0x0 ovrlpd >>= \b -> case b of
+ True -> return True
+ False -> getLastError >>= \err -> case () of
+ () | not block && err == #{const ERROR_LOCK_VIOLATION} -> return False
+ | err == #{const ERROR_OPERATION_ABORTED} -> retry
+ | otherwise -> failWith ctx err
+ where
+ sizeof_OVERLAPPED = #{size OVERLAPPED}
+
+ cmode = case mode of
+ SharedLock -> 0
+ ExclusiveLock -> #{const LOCKFILE_EXCLUSIVE_LOCK}
+
+-- https://msdn.microsoft.com/en-us/library/aa297958.aspx
+foreign import ccall unsafe "_get_osfhandle"
+ c_get_osfhandle :: CInt -> IO HANDLE
+
+-- https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203.aspx
+foreign import WINDOWS_CCONV interruptible "LockFileEx"
+ c_LockFileEx :: HANDLE -> DWORD -> DWORD -> DWORD -> DWORD -> Ptr () -> IO BOOL
+
+#else /* !defined(mingw32_HOST_OS), so assume unix with flock() */
+
+lockImpl :: Handle -> String -> LockMode -> Bool -> IO Bool
+lockImpl h ctx mode block = do
+ FD{fdFD = fd} <- handleToFd h
+ let flags = cmode .|. (if block then 0 else #{const LOCK_NB})
+ fix $ \retry -> c_flock fd flags >>= \n -> case n of
+ 0 -> return True
+ _ -> getErrno >>= \errno -> case () of
+ () | not block && errno == eWOULDBLOCK -> return False
+ | errno == eINTR -> retry
+ | otherwise -> ioException $ errnoToIOError ctx errno (Just h) Nothing
+ where
+ cmode = case mode of
+ SharedLock -> #{const LOCK_SH}
+ ExclusiveLock -> #{const LOCK_EX}
+
+foreign import ccall interruptible "flock"
+ c_flock :: CInt -> CInt -> IO CInt
+
+#endif /* !defined(mingw32_HOST_OS) */
+
+-- | Turn an existing Handle into a file descriptor. This function throws an
+-- IOError if the Handle does not reference a file descriptor.
+handleToFd :: Handle -> IO FD
+handleToFd h = case h of
+ FileHandle _ mv -> do
+ Handle__{haDevice = dev} <- readMVar mv
+ case cast dev of
+ Just fd -> return fd
+ Nothing -> throwErr "not a file descriptor"
+ DuplexHandle{} -> throwErr "not a file handle"
+ where
+ throwErr msg = ioException $ IOError (Just h)
+ InappropriateType "handleToFd" msg Nothing Nothing
+
+#endif /* defined(solaris2_HOST_OS) || defined(aix_HOST_OS) */
+
+#endif /* MIN_VERSION_base */
diff --git a/hackage-security/hackage-security/src/Hackage/Security/Util/IO.hs b/hackage-security/hackage-security/src/Hackage/Security/Util/IO.hs
index 1601b7b..3e9f8d5 100644
--- a/hackage-security/hackage-security/src/Hackage/Security/Util/IO.hs
+++ b/hackage-security/hackage-security/src/Hackage/Security/Util/IO.hs
@@ -7,12 +7,14 @@ module Hackage.Security.Util.IO (
, timedIO
) where
+import Control.Monad (unless)
import Control.Exception
import Data.Time
import System.IO hiding (openTempFile, withFile)
import System.IO.Error
import Hackage.Security.Util.Path
+import Hackage.Security.Util.FileLock (hTryLock, LockMode(ExclusiveLock), FileLockingNotSupported)
{-------------------------------------------------------------------------------
Miscelleneous
@@ -30,22 +32,51 @@ handleDoesNotExist act =
then return Nothing
else throwIO e
--- | Attempt to create a filesystem lock in the specified directory
+-- | Attempt to create a filesystem lock in the specified directory.
--
+-- This will use OS-specific file locking primitives: "GHC.IO.Handle.Lock" with
+-- @base-4.10" and later or a shim for @base@ versions.
+--
+-- Throws an exception if the lock is already present.
+--
+-- May fallback to locking via creating a directory:
-- Given a file @/path/to@, we do this by attempting to create the directory
-- @//path/to/hackage-security-lock@, and deleting the directory again
-- afterwards. Creating a directory that already exists will throw an exception
-- on most OSs (certainly Linux, OSX and Windows) and is a reasonably common way
-- to implement a lock file.
withDirLock :: Path Absolute -> IO a -> IO a
-withDirLock dir = bracket_ takeLock releaseLock
+withDirLock dir = bracket takeLock releaseLock . const
where
lock :: Path Absolute
lock = dir </> fragment "hackage-security-lock"
- takeLock, releaseLock :: IO ()
- takeLock = createDirectory lock
- releaseLock = removeDirectory lock
+ lock' :: FilePath
+ lock' = toFilePath lock
+
+ takeLock = do
+ h <- openFile lock' ReadWriteMode
+ handle (takeDirLock h) $ do
+ gotlock <- hTryLock h ExclusiveLock
+ unless gotlock $
+ fail $ "hTryLock: lock already exists: " ++ lock'
+ return (Just h)
+
+ takeDirLock :: Handle -> FileLockingNotSupported -> IO (Maybe Handle)
+ takeDirLock h _ = do
+ -- We fallback to directory locking
+ -- so we need to cleanup lock file first: close and remove
+ hClose h
+ handle onIOError (removeFile lock)
+ createDirectory lock
+ return Nothing
+
+ onIOError :: IOError -> IO ()
+ onIOError _ = hPutStrLn stderr
+ "withDirLock: cannot remove lock file before directory lock fallback"
+
+ releaseLock (Just h) = hClose h
+ releaseLock Nothing = removeDirectory lock
{-------------------------------------------------------------------------------
Debugging
diff --git a/hackage-security/hackage-security/tests/TestSuite.hs b/hackage-security/hackage-security/tests/TestSuite.hs
index e615644..3284ed7 100644
--- a/hackage-security/hackage-security/tests/TestSuite.hs
+++ b/hackage-security/hackage-security/tests/TestSuite.hs
@@ -70,6 +70,7 @@ tests = testGroup "hackage-security" [
testProperty "prop_roundtrip_canonical" JSON.prop_roundtrip_canonical
, testProperty "prop_roundtrip_pretty" JSON.prop_roundtrip_pretty
, testProperty "prop_canonical_pretty" JSON.prop_canonical_pretty
+ , testProperty "prop_aeson_canonical" JSON.prop_aeson_canonical
]
]
diff --git a/hackage-security/hackage-security/tests/TestSuite/JSON.hs b/hackage-security/hackage-security/tests/TestSuite/JSON.hs
index 0696586..654ad4e 100644
--- a/hackage-security/hackage-security/tests/TestSuite/JSON.hs
+++ b/hackage-security/hackage-security/tests/TestSuite/JSON.hs
@@ -3,6 +3,7 @@ module TestSuite.JSON (
prop_roundtrip_canonical,
prop_roundtrip_pretty,
prop_canonical_pretty,
+ prop_aeson_canonical,
) where
-- stdlib
@@ -16,6 +17,11 @@ import Test.QuickCheck
-- hackage-security
import Text.JSON.Canonical
+-- aeson
+import Data.Aeson (Value (..), eitherDecode, FromJSON (..))
+import Data.String (fromString)
+import qualified Data.Vector as V
+import qualified Data.HashMap.Strict as HM
prop_roundtrip_canonical, prop_roundtrip_pretty, prop_canonical_pretty
:: JSValue -> Bool
@@ -30,6 +36,9 @@ prop_canonical_pretty jsval =
parseCanonicalJSON (renderCanonicalJSON jsval) ==
fmap canonicalise (parseCanonicalJSON (BS.pack (prettyCanonicalJSON jsval)))
+prop_aeson_canonical jsval =
+ eitherDecode (renderCanonicalJSON jsval) == Right (toAeson (canonicalise jsval))
+
canonicalise :: JSValue -> JSValue
canonicalise v@JSNull = v
canonicalise v@(JSBool _) = v
@@ -46,12 +55,13 @@ instance Arbitrary JSValue where
[ (1, pure JSNull)
, (1, JSBool <$> arbitrary)
, (2, JSNum <$> arbitrary)
- , (2, JSString <$> arbitrary)
+ , (2, JSString . getASCIIString <$> arbitrary)
, (3, JSArray <$> resize (sz `div` 2) arbitrary)
- , (3, JSObject . noDupFields <$> resize (sz `div` 2) arbitrary)
+ , (3, JSObject . mapFirst getASCIIString . noDupFields <$> resize (sz `div` 2) arbitrary)
]
where
noDupFields = nubBy (\(x,_) (y,_) -> x==y)
+ mapFirst f = map (\(x, y) -> (f x, y))
shrink JSNull = []
shrink (JSBool _) = []
@@ -62,6 +72,13 @@ instance Arbitrary JSValue where
where
shrinkSnd (a,b) = [ (a,b') | b' <- shrink b ]
+toAeson :: JSValue -> Value
+toAeson JSNull = Null
+toAeson (JSBool b) = Bool b
+toAeson (JSNum n) = Number (fromIntegral n)
+toAeson (JSString s) = String (fromString s)
+toAeson (JSArray xs) = Array $ V.fromList [ toAeson x | x <- xs ]
+toAeson (JSObject xs) = Object $ HM.fromList [ (fromString k, toAeson v) | (k, v) <- xs ]
instance Arbitrary Int54 where
arbitrary = fromIntegral <$>
diff --git a/hackport.cabal b/hackport.cabal
index e4b557a..0ab195f 100644
--- a/hackport.cabal
+++ b/hackport.cabal
@@ -1,5 +1,5 @@
Name: hackport
-Version: 0.5.6
+Version: 0.6
License: GPL
License-file: LICENSE
Author: Henning G√ľnther, Duncan Coutts, Lennart Kolmodin
@@ -66,6 +66,7 @@ Executable hackport
-- hackage-security
DefaultSignatures,
DeriveDataTypeable,
+ DoAndIfThenElse,
EmptyDataDecls,
ExistentialQuantification,
FlexibleContexts,
@@ -126,6 +127,8 @@ Executable hackport
Status
Merge
Util
+ -- hsc files
+ Hackage.Security.Util.FileLock
Test-Suite test-resolve-category
ghc-options: -Wall