Ten Cache Misses

Crushing Haskell like a Tin Can

Converting .cabal files to .json

Someone on #haskell last night was looking for a tool to convert a cabal-install package description to JSON. I hacked something together a while back to do exactly this for the cabal-waf project. While it’s not worthy of a standalone release on hackage it might be useful for someone else out there.

The source is also available for your cloning pleasure on GitHub. Enjoy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{-# OPTIONS_GHC -fno-warn-deprecations #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE TemplateHaskell #-}

module Main (main) where

import Control.Monad (when)
import qualified Data.ByteString.Lazy as BL
import Data.List
import Data.Aeson
import Data.Aeson.TH
import Distribution.ModuleName (ModuleName)
import Distribution.Package
import Distribution.PackageDescription
import Distribution.PackageDescription.Configuration
import Distribution.PackageDescription.Parse (readPackageDescription)
import Distribution.Verbosity
import Distribution.Version
import Distribution.License
import Distribution.Compiler
import Language.Haskell.Extension
import System.Environment (getArgs)

deriveJSON id ''PackageDescription
deriveJSON id ''PackageIdentifier
deriveJSON id ''PackageName
deriveJSON id ''Version
deriveJSON id ''SourceRepo
deriveJSON id ''RepoKind
deriveJSON id ''RepoType
deriveJSON id ''BuildType
deriveJSON id ''Library
deriveJSON id ''Executable
deriveJSON id ''TestSuite
deriveJSON id ''License
deriveJSON id ''CompilerFlavor
deriveJSON id ''TestSuiteInterface
deriveJSON id ''BuildInfo
deriveJSON id ''ModuleName
deriveJSON id ''Dependency
deriveJSON id ''Language
deriveJSON id ''Extension
deriveJSON id ''KnownExtension
deriveJSON id ''TestType
deriveJSON id ''VersionRange

main :: IO ()
main = do
  args <- getArgs
  when (length args == 0) $ fail "missing .cabal file"

  let (source:_) = args
  gdesc <- readPackageDescription normal source
  let desc = flattenPackageDescription gdesc
      bs = encode . toJSON $ desc
  BL.putStrLn bs

Running it on a small-ish .cabal file…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
name:           ghc-prim
version:        0.2.0.0
license:        BSD3
license-file:   LICENSE
maintainer:     libraries@haskell.org
bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29
synopsis:       GHC primitives
description:
    GHC primitives.
cabal-version:  >=1.6
build-type: Custom

source-repository head
    type:     git
    location: http://darcs.haskell.org/packages/ghc-prim.git/

flag include-ghc-prim {
    Description: Include GHC.Prim in exposed-modules
    default: False
}

Library {
    build-depends: rts
    exposed-modules:
        GHC.Classes
        GHC.CString
        GHC.Debug
        GHC.Generics
        GHC.Magic
        GHC.PrimopWrappers
        GHC.IntWord64
        GHC.Tuple
        GHC.Types

    if flag(include-ghc-prim) {
        exposed-modules: GHC.Prim
    }

    c-sources:
        cbits/debug.c
        cbits/longlong.c
        cbits/popcnt.c
    extensions: CPP, MagicHash, ForeignFunctionInterface, UnliftedFFITypes,
                UnboxedTuples, EmptyDataDecls, NoImplicitPrelude
    -- We need to set the package name to ghc-prim (without a version number)
    -- as it's magic.
    ghc-options: -package-name ghc-prim
}

Produces output that is easier to consume outside of Haskell. It’s worth noting that conditional statements in the .cabal script are resolved, so the if flag statement above results in GHC.Prim being listed as an exposedModule.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
{"author":""
,"bugReports":"http://hackage.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29"
,"buildDepends":[["rts",{"AnyVersion":[]}]]
,"buildType":{"Custom":[]}
,"category":""
,"copyright":""
,"customFieldsPD":[]
,"dataDir":""
,"dataFiles":[]
,"description":"GHC primitives."
,"executables":[]
,"extraSrcFiles":[]
,"extraTmpFiles":[]
,"homepage":""
,"library":
  {"exposedModules":
    [["GHC","Classes"]
    ,["GHC","CString"]
    ,["GHC","Debug"]
    ,["GHC","Generics"]
    ,["GHC","Magic"]
    ,["GHC","PrimopWrappers"]
    ,["GHC","IntWord64"]
    ,["GHC","Tuple"]
    ,["GHC","Types"]
    ,["GHC","Prim"]]
  ,"libBuildInfo":
    {"buildTools":[]
    ,"buildable":true
    ,"cSources":["cbits/debug.c","cbits/longlong.c","cbits/popcnt.c"]
    ,"ccOptions":[]
    ,"cppOptions":[]
    ,"customFieldsBI":[]
    ,"defaultExtensions":[]
    ,"defaultLanguage":null
    ,"extraLibDirs":[]
    ,"extraLibs":[]
    ,"frameworks":[]
    ,"ghcProfOptions":[]
    ,"ghcSharedOptions":[]
    ,"hsSourceDirs":["."]
    ,"includeDirs":[]
    ,"includes":[]
    ,"installIncludes":[]
    ,"ldOptions":[]
    ,"oldExtensions":
      [{"EnableExtension":{"CPP":[]}}
      ,{"EnableExtension":{"MagicHash":[]}}
      ,{"EnableExtension":{"ForeignFunctionInterface":[]}}
      ,{"EnableExtension":{"UnliftedFFITypes":[]}}
      ,{"EnableExtension":{"UnboxedTuples":[]}}
      ,{"EnableExtension":{"EmptyDataDecls":[]}}
      ,{"DisableExtension":{"ImplicitPrelude":[]}}]
    ,"options":[[{"GHC":[]},["-package-name","ghc-prim"]]]
    ,"otherExtensions":[]
    ,"otherLanguages":[]
    ,"otherModules":[]
    ,"pkgconfigDepends":[]
    ,"targetBuildDepends":[]}
  ,"libExposed":true}
,"license":{"BSD3":[]}
,"licenseFile":"LICENSE"
,"maintainer":"libraries@haskell.org"
,"package":{"pkgName":"ghc-prim","pkgVersion":{"versionBranch":[0,2,0,0],"versionTags":[]}}
,"pkgUrl":""
,"sourceRepos":
  [{"repoBranch":null
   ,"repoKind":{"RepoHead":[]}
   ,"repoLocation":"http://darcs.haskell.org/packages/ghc-prim.git/"
   ,"repoModule":null
   ,"repoSubdir":null
   ,"repoTag":null
   ,"repoType":{"Git":[]}}]
,"specVersionRaw":
  {"Right":
    {"UnionVersionRanges":
      [{"ThisVersion":{"versionBranch":[1,6] ,"versionTags":[]}},
       {"LaterVersion":{"versionBranch":[1,6],"versionTags":[]}}]}}
,"stability":""
,"synopsis":"GHC primitives"
,"testSuites":[]
,"testedWith":[]}

Comments