This package provides a robust JSON representation of cabal files. While it is not the first or the only package to do so, I believe it has some interesting features that make it worth:
- Simple. Zero non-boot dependency, you only need GHC.
- Robust. It uses Cabal-syntax own code and makes no assumptions on the fields.
- Helpful. It transforms if conditions in a format easier to handle.
- Flexible. It can simplify those conditionals based on flags passed on the cli.
This tool is aimed to build-system developers who need to extract information about a package to drive the build process.
Usage: cabal2json [OPTION...] files...
-h --help Print this help message
--out=FILE Path for output
-f FLAG --flag=FLAG Specialise to flag assignment FLAG
-c COMPILER --compiler=COMPILER Specialise to the compiler COMPILER
-o OS --os=OS Specialise to the operating system OS
-a ARCH --arch=ARCH Specialise to the architecture ARCH
-p PLATFORM --platform=PLATFORM Specialise to the platform PLATFORM
--debug Output an intermediate format useful for debugging
--pristine Preserve the conditional structure
Using template-haskell-2.19.0.0, cabal2json
produces:
{
"cabal-version": ">=1.10",
"name": "template-haskell",
"version": "2.19.0.0",
"license": "BSD3",
"license-file": [
"LICENSE"
],
"license-files": [
"LICENSE"
],
"maintainer": "libraries@haskell.org",
"bug-reports": "https://gitlab.haskell.org/ghc/ghc/issues/new",
"synopsis": "Support library for Template Haskell",
"description": "This package provides modules containing facilities for manipulating\nHaskell source code using Template Haskell.\n\nSee <http://www.haskell.org/haskellwiki/Template_Haskell> for more\ninformation.",
"category": "Template Haskell",
"build-type": "Simple",
"extra-source-files": [
"changelog.md"
],
"source-repositories": {
"head": {
"type": "git",
"location": "https://gitlab.haskell.org/ghc/ghc.git",
"subdir": "libraries/template-haskell"
}
},
"components": {
"lib": {
"exposed-modules": [
"Language.Haskell.TH",
"Language.Haskell.TH.Lib",
"Language.Haskell.TH.Ppr",
"Language.Haskell.TH.PprLib",
"Language.Haskell.TH.Quote",
"Language.Haskell.TH.Syntax",
"Language.Haskell.TH.LanguageExtensions",
"Language.Haskell.TH.CodeDo",
"Language.Haskell.TH.Lib.Internal"
],
"other-modules": [
"Language.Haskell.TH.Lib.Map",
{
"_if": {
"flag": "vendor-filepath"
},
"_then": [
"System.FilePath",
"System.FilePath.Posix",
"System.FilePath.Windows"
]
}
],
"default-language": "Haskell2010",
"default-extensions": [
"NoImplicitPrelude",
{
"_if": {
"flag": "vendor-filepath"
},
"_then": [
"ImplicitPrelude"
]
}
],
"other-extensions": [
"BangPatterns",
"CPP",
"DefaultSignatures",
"DeriveDataTypeable",
"DeriveGeneric",
"FlexibleInstances",
"RankNTypes",
"RoleAnnotations",
"ScopedTypeVariables"
],
"ghc-options": [
"-Wall",
"-this-unit-id",
"template-haskell"
],
"build-depends": [
{
"package": "base",
"version": ">=4.11 && <4.18",
"libs": [
"base"
]
},
{
"package": "ghc-boot-th",
"version": "==9.4.1",
"libs": [
"ghc-boot-th"
]
},
{
"package": "ghc-prim",
"version": ">=0",
"libs": [
"ghc-prim"
]
},
{
"package": "pretty",
"version": ">=1.1 && <1.2",
"libs": [
"pretty"
]
},
{
"_if": {
"not": {
"flag": "vendor-filepath"
}
},
"_then": [
{
"package": "filepath",
"version": ">=0",
"libs": [
"filepath"
]
}
]
}
],
"hs-source-dirs": [
{
"_if": {
"flag": "vendor-filepath"
},
"_then": [
"./vendored-filepath",
"."
]
},
{
"_if": {
"not": {
"flag": "vendor-filepath"
}
},
"_then": [
"."
]
}
]
}
}
}
Activating the flag vendor-filepath
with cabal2json --flag +vendor-filepath
produces:
{
"cabal-version": ">=1.10",
"name": "template-haskell",
"version": "2.19.0.0",
"license": "BSD3",
"license-file": [
"LICENSE"
],
"license-files": [
"LICENSE"
],
"maintainer": "libraries@haskell.org",
"bug-reports": "https://gitlab.haskell.org/ghc/ghc/issues/new",
"synopsis": "Support library for Template Haskell",
"description": "This package provides modules containing facilities for manipulating\nHaskell source code using Template Haskell.\n\nSee <http://www.haskell.org/haskellwiki/Template_Haskell> for more\ninformation.",
"category": "Template Haskell",
"build-type": "Simple",
"extra-source-files": [
"changelog.md"
],
"source-repositories": {
"head": {
"type": "git",
"location": "https://gitlab.haskell.org/ghc/ghc.git",
"subdir": "libraries/template-haskell"
}
},
"flags": {
"vendor-filepath": {
"description": "Vendor the dependency on filepath",
"default": false,
"manual": true
}
},
"components": {
"lib": {
"exposed-modules": [
"Language.Haskell.TH",
"Language.Haskell.TH.Lib",
"Language.Haskell.TH.Ppr",
"Language.Haskell.TH.PprLib",
"Language.Haskell.TH.Quote",
"Language.Haskell.TH.Syntax",
"Language.Haskell.TH.LanguageExtensions",
"Language.Haskell.TH.CodeDo",
"Language.Haskell.TH.Lib.Internal"
],
"hs-source-dirs": [
"./vendored-filepath",
"."
],
"other-modules": [
"Language.Haskell.TH.Lib.Map",
"System.FilePath",
"System.FilePath.Posix",
"System.FilePath.Windows"
],
"default-language": "Haskell2010",
"default-extensions": [
"NoImplicitPrelude",
"ImplicitPrelude"
],
"other-extensions": [
"BangPatterns",
"CPP",
"DefaultSignatures",
"DeriveDataTypeable",
"DeriveGeneric",
"FlexibleInstances",
"RankNTypes",
"RoleAnnotations",
"ScopedTypeVariables"
],
"ghc-options": [
"-Wall",
"-this-unit-id",
"template-haskell"
],
"build-depends": [
{
"package": "base",
"version": ">=4.11 && <4.18",
"libs": [
"base"
]
},
{
"package": "ghc-boot-th",
"version": "==9.4.1",
"libs": [
"ghc-boot-th"
]
},
{
"package": "ghc-prim",
"version": ">=0",
"libs": [
"ghc-prim"
]
},
{
"package": "pretty",
"version": ">=1.1 && <1.2",
"libs": [
"pretty"
]
}
]
}
}
}
The JSON representation is obtained through the following steps:
-
A cabal file is parsed using
readGenericPackageDescription
. -
The cli flags are used to simplify the conditionals.
-
The conditionals are pushed down into the field values. E.g.
if flag(vendor-filepath) other-modules: System.FilePath System.FilePath.Posix System.FilePath.Windows hs-source-dirs: ./vendored-filepath . default-extensions: ImplicitPrelude else build-depends: filepath hs-source-dirs: .
is transformed into:
other-modules: if flag(vendor-filepath) System.FilePath System.FilePath.Posix System.FilePath.Windows hs-source-dirs: if flag(vendor-filepath) ./vendored-filepath . default-extensions: if flag(vendor-filepath) ImplicitPrelude build-depends: if !flag(vendor-filepath) filepath hs-source-dirs: if !flag(vendor-filepath) .
In other words, a tree of fields is transformed into fields of trees.
-
Lastly the tree is flattened into a list where every field value is guarded by its cumulative conditionals.
if impl(ghc >= 8.0) if flag(os-string) build-depends: filepath >= 1.5.0.0, os-string >= 2.0.0 else build-depends: filepath >= 1.4.100.0 && < 1.5.0.0
becomes
build-depends: if impl(ghc >= 8.0) && flag(os-string) filepath >= 1.5.0.0 os-string >= 2.0.0 if impl(ghc >= 8.0) && !flag(os-string) filepath >= 1.4.100.0 && < 1.5.0.0
Andrea Bedini (andrea@andreabedini.com)