diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b13473e..59f67b32 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## Changes in 0.30.0
+ - Transparently add `build-tool-depends` on `hspec-discover`/`markdown-unlit`
+ when a packages depends on `hspec`/`markdown-unlit` for compatibility of
+ existing package specifications with `cabal new-build` (see #254)
+
## Changes in 0.29.5
- Fix a regression related to indentation sniffing (close #310)
diff --git a/README.md b/README.md
index 7230007d..b0ec7c14 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+TODO: ConCon: A convention over configuration extension for Hpack
+
# hpack: A modern format for Haskell packages
Hpack is a format for Haskell packages. It is a modern alternative to the
@@ -35,6 +37,30 @@ at the Singapore Haskell meetup: http://typeful.net/talks/hpack
## Documentation
+### Build tools and compatibility with cabal new-build
+
+When using sandboxes, `cabal` makes all executables of all transitive
+dependencies available during the build so that they can be used as build
+tools.
+
+However, with `cabal new-build` build tools have to be specified explicitly via
+`build-tool-depends`.
+
+For compatibility of existing package specifications with `cabal new-build` we
+transparently add `build-tool-depends` when a package directly depends on
+certain packages.
+
+- When a package depends on `hspec` then `hpack` adds `hspec-discover` to `build-tool-depends`
+- When a package depends on `markdown-unlit` then `hpack` adds `markdown-unlit` to `build-tool-depends`
+
+You can opt out of this behavior by adding
+
+```yaml
+verbatim:
+ build-tool-depends: null
+```
+to `package.yaml`.
+
### Quick-reference
#### Top-level fields
@@ -149,7 +175,7 @@ this reason it is recommended to only use tags as Git references.
| --- | --- | --- | --- | --- |
| `dependencies` | `setup-depends` | | Implies `build-type: Custom` | |
-#### Common fields
+#### Common fields
These fields can be specified top-level or on a per section basis; top-level
values are merged with per section values.
@@ -178,8 +204,48 @@ values are merged with per section values.
| `ld-options` | · | | |
| `dependencies` | `build-depends` | | |
| `pkg-config-dependencies` | `pkgconfig-depends` | | |
-| `build-tools` | · | | |
| `when` | | | Accepts a list of conditionals (see [Conditionals](#conditionals)) |
+| `haskell-build-tools` | `build-tools` and/or `build-tool-depends` | | |
+| `system-build-tools` | `build-tools` | | | -- FIXME: separate PR
+
+**`build-tools`: A set of Haskell executables that are needed to build this component**
+
+Each entry consists of a *name* and an optional *version constraint*.
+
+The name can be specified in two ways:
+
+1. Qualified: `:`
+1. Unqualified: ``
+
+A qualified name refers to an executable named `` in a
+package named ``.
+
+An unqualified name refers to one of two things:
+
+1. Another executable from the same package named ``
+2. An executable from another package where the executable name and the package name are identical
+
+The first takes precedence over the second; if this is undesired then
+qualified names must be used instead.
+
+Examples:
+```yaml
+build-tools:
+ - alex
+ - happy:happy
+
+build-tools:
+ alex:alex: 0.1.0
+ alex: 0.1.0
+```
+
+
+`build-tools` field subsumes Cabal's `build-tools` and
+`build-tool-depends` fields.
+
+alex
+
+
#### Library fields
diff --git a/hpack.cabal b/hpack.cabal
index ae31eb54..4ee8203f 100644
--- a/hpack.cabal
+++ b/hpack.cabal
@@ -1,13 +1,13 @@
cabal-version: >= 1.10
--- This file has been generated from package.yaml by hpack version 0.29.5.
+-- This file has been generated from package.yaml by hpack version 0.30.0.
--
-- see: https://github.com/sol/hpack
--
--- hash: 0ff0fec4e3939cb7b4ceee3340f6b006edeac6c8114cb165b6495e61a76c79c0
+-- hash: d081afc89b25afda429f687327eccfed9c8212f1f01f3ed42966c1706e518152
name: hpack
-version: 0.29.5
+version: 0.30.0
synopsis: A modern format for Haskell packages
description: See README at
category: Development
@@ -148,6 +148,8 @@ test-suite spec
, unordered-containers
, vector
, yaml >=0.8.28
+ build-tool-depends:
+ hspec-discover:hspec-discover ==2.*
other-modules:
Data.Aeson.Config.FromValueSpec
Data.Aeson.Config.TypesSpec
diff --git a/package.yaml b/package.yaml
index b5bfaaec..b0767a23 100644
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
name: hpack
-version: 0.29.5
+version: 0.30.0
synopsis: A modern format for Haskell packages
description: See README at
maintainer: Simon Hengel
diff --git a/src/Hpack/Config.hs b/src/Hpack/Config.hs
index 6757da6c..a15efb28 100644
--- a/src/Hpack/Config.hs
+++ b/src/Hpack/Config.hs
@@ -39,6 +39,8 @@ module Hpack.Config (
, Dependencies(..)
, DependencyVersion(..)
, SourceDependency(..)
+, BuildTools
+, BuildTool(..)
, GitRef
, GitUrl
, GhcOption
@@ -170,7 +172,7 @@ packageDependencies Package{..} = nub . sortBy (comparing (lexicographically . f
deps xs = [(name, version) | (name, version) <- (Map.toList . unDependencies . sectionDependencies) xs]
section :: a -> Section a
-section a = Section a [] mempty [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] Nothing [] mempty []
+section a = Section a [] mempty [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] Nothing [] mempty mempty []
packageConfig :: FilePath
packageConfig = "package.yaml"
@@ -644,7 +646,7 @@ determineCabalVersion inferredLicense pkg@Package{..} = (
| version >= makeVersion [2,1] = showVersion version
| otherwise = (">= " ++) . showVersion $ version
- version = fromMaybe (makeVersion [1,10]) $ maximum [
+ version = fromMaybe (makeVersion [1,12]) $ maximum [
packageCabalVersion
, packageLibrary >>= libraryCabalVersion
, internalLibsCabalVersion packageInternalLibraries
@@ -788,9 +790,17 @@ data Section a = Section {
, sectionBuildable :: Maybe Bool
, sectionConditionals :: [Conditional (Section a)]
, sectionBuildTools :: Dependencies
+, sectionBuildToolDepends :: BuildTools
, sectionVerbatim :: [Verbatim]
} deriving (Eq, Show, Functor, Foldable, Traversable)
+type BuildTools = Map BuildTool DependencyVersion
+
+data BuildTool = BuildTool {
+ buildToolPackage :: String
+, buildToolExecutable :: String
+} deriving (Eq, Ord, Show)
+
data Conditional a = Conditional {
conditionalCondition :: String
, conditionalThen :: a
@@ -1228,13 +1238,15 @@ toSection_ (Product CommonOptions{..} a) = Section {
, sectionInstallIncludes = fromMaybeList commonOptionsInstallIncludes
, sectionLdOptions = fromMaybeList commonOptionsLdOptions
, sectionBuildable = commonOptionsBuildable
- , sectionDependencies = fromMaybe mempty commonOptionsDependencies
+ , sectionDependencies = dependencies
, sectionPkgConfigDependencies = fromMaybeList commonOptionsPkgConfigDependencies
, sectionConditionals = conditionals
, sectionBuildTools = fromMaybe mempty commonOptionsBuildTools
+ , sectionBuildToolDepends = hardCodedBuildToolsFromDependencies dependencies
, sectionVerbatim = fromMaybeList commonOptionsVerbatim
}
where
+ dependencies = fromMaybe mempty commonOptionsDependencies
conditionals = map toConditional (fromMaybeList commonOptionsWhen)
toConditional :: ConditionalSection CSources CxxSources JsSources a -> Conditional (Section a)
@@ -1244,6 +1256,15 @@ toSection_ (Product CommonOptions{..} a) = Section {
where
conditional (Condition (Cond c)) = Conditional c
+hardCodedBuildToolsFromDependencies :: Dependencies -> BuildTools
+hardCodedBuildToolsFromDependencies = Map.fromList . mapMaybe f . Map.toList . unDependencies
+ where
+ f (pkg, version) = (,) <$> lookup pkg known <*> pure version
+ known = [
+ ("hspec", BuildTool "hspec-discover" "hspec-discover")
+ , ("markdown-unlit", BuildTool "markdown-unlit" "markdown-unlit")
+ ]
+
pathsModuleFromPackageName :: String -> String
pathsModuleFromPackageName name = "Paths_" ++ map f name
where
diff --git a/src/Hpack/Render.hs b/src/Hpack/Render.hs
index 9fd6ab3e..1575fbb3 100644
--- a/src/Hpack/Render.hs
+++ b/src/Hpack/Render.hs
@@ -242,6 +242,7 @@ renderSection renderSectionData extraFieldsStart extraFieldsEnd Section{..} = ad
, renderDependencies "build-depends" sectionDependencies
, Field "pkgconfig-depends" (CommaSeparatedList sectionPkgConfigDependencies)
, renderDependencies "build-tools" sectionBuildTools
+ , renderBuildToolDepends sectionBuildToolDepends
]
++ maybe [] (return . renderBuildable) sectionBuildable
++ map (renderConditional renderSectionData) sectionConditionals
@@ -312,13 +313,20 @@ renderSignatures = Field "signatures" . CommaSeparatedList
renderDependencies :: String -> Dependencies -> Element
renderDependencies name = Field name . CommaSeparatedList . map renderDependency . Map.toList . unDependencies
+renderBuildToolDepends :: BuildTools -> Element
+renderBuildToolDepends = Field "build-tool-depends" . CommaSeparatedList . map renderBuildTool . Map.toList
+
+renderBuildTool :: (BuildTool, DependencyVersion) -> String
+renderBuildTool (BuildTool pkg executable, version) = pkg ++ ":" ++ executable ++ renderVersion version
+
renderDependency :: (String, DependencyVersion) -> String
-renderDependency (name, version) = name ++ v
- where
- v = case version of
- AnyVersion -> ""
- VersionRange x -> " " ++ x
- SourceDependency _ -> ""
+renderDependency (name, version) = name ++ renderVersion version
+
+renderVersion :: DependencyVersion -> String
+renderVersion version = case version of
+ AnyVersion -> ""
+ VersionRange x -> " " ++ x
+ SourceDependency _ -> ""
renderGhcOptions :: [GhcOption] -> Element
renderGhcOptions = Field "ghc-options" . WordList
diff --git a/test/EndToEndSpec.hs b/test/EndToEndSpec.hs
index 86bf448e..828b08ab 100644
--- a/test/EndToEndSpec.hs
+++ b/test/EndToEndSpec.hs
@@ -435,6 +435,17 @@ spec = around_ (inTempDirectoryNamed "foo") $ do
- "*.markdown"
|] `shouldWarn` ["Specified pattern \"*.markdown\" for extra-doc-files does not match any files"]
+ describe "build-tools" $ do
+ it "accepts known build tool" $ do
+ [i|
+ executable:
+ build-tools:
+ alex == 0.1.0
+ |] `shouldRenderTo` executable_ "foo" [i|
+ build-tools:
+ alex ==0.1.0
+ |]
+
describe "dependencies" $ do
it "accepts single dependency" $ do
[i|
@@ -457,17 +468,42 @@ spec = around_ (inTempDirectoryNamed "foo") $ do
, transformers
|]
+ context "when package depends on hspec" $ do
+ it "adds build-tool-depends on hspec-discover:hspec-discover" $ do
+ [i|
+ executable:
+ dependencies:
+ - hspec == 2.*
+ |] `shouldRenderTo` executable_ "foo" [i|
+ build-depends:
+ hspec ==2.*
+ build-tool-depends:
+ hspec-discover:hspec-discover ==2.*
+ |]
+
+ it "gives verbatim precedence" $ do
+ [i|
+ executable:
+ dependencies:
+ - hspec == 2.*
+ verbatim:
+ build-tool-depends: null
+ |] `shouldRenderTo` executable_ "foo" [i|
+ build-depends:
+ hspec ==2.*
+ |]
+
context "with both global and section specific dependencies" $ do
it "combines dependencies" $ do
[i|
dependencies:
- base
executable:
- dependencies: hspec
+ dependencies: transformers
|] `shouldRenderTo` executable_ "foo" [i|
build-depends:
base
- , hspec
+ , transformers
|]
it "gives section specific dependencies precedence" $ do
@@ -1413,7 +1449,7 @@ executable #{name}
|]
package :: String -> Package
-package = Package "foo" "0.0.0" "Simple" ">= 1.10"
+package = Package "foo" "0.0.0" "Simple" ">= 1.12"
data Package = Package {
packageName :: String