Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -149,7 +175,7 @@ this reason it is recommended to only use tags as Git references.
| --- | --- | --- | --- | --- |
| `dependencies` | `setup-depends` | | Implies `build-type: Custom` | |

#### <a name="common-fields">Common fields
#### <a name="common-fields"></a>Common fields

These fields can be specified top-level or on a per section basis; top-level
values are merged with per section values.
Expand Down Expand Up @@ -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: `<package>:<executable>`
1. Unqualified: `<executable>`

A qualified name refers to an executable named `<executable>` in a
package named `<package>`.

An unqualified name refers to one of two things:

1. Another executable from the same package named `<executable>`
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

<package-name>

#### <a name="library-fields"></a>Library fields

Expand Down
8 changes: 5 additions & 3 deletions hpack.cabal
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/sol/hpack#readme>
category: Development
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/sol/hpack#readme>
maintainer: Simon Hengel <sol@typeful.net>
Expand Down
27 changes: 24 additions & 3 deletions src/Hpack/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ module Hpack.Config (
, Dependencies(..)
, DependencyVersion(..)
, SourceDependency(..)
, BuildTools
, BuildTool(..)
, GitRef
, GitUrl
, GhcOption
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
20 changes: 14 additions & 6 deletions src/Hpack/Render.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
42 changes: 39 additions & 3 deletions test/EndToEndSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down