packages feed

cabal-fix 0.0.0.2 → 0.1.0.0

raw patch · 7 files changed

+873/−919 lines, 7 filesdep +doctest-paralleldep ~Cabal-syntaxPVP ok

version bump matches the API change (PVP)

Dependencies added: doctest-parallel

Dependency ranges changed: Cabal-syntax

API changes (from Hackage documentation)

Files

ChangeLog.md view
@@ -1,3 +1,8 @@++# 0.1++* Added doctests+ # 0.0.0.1  * conception
cabal-fix.cabal view
@@ -1,31 +1,29 @@ cabal-version: 3.0 name: cabal-fix-version: 0.0.0.2+version: 0.1.0.0 license: BSD-3-Clause license-file: LICENSE category: distribution author: Tony Day maintainer: tonyday567@gmail.com-homepage: https://github.com/tonyday567/cabal-fixes#readme-bug-reports: https://github.com/tonyday567/cabal-fixes/issues+homepage: https://github.com/tonyday567/cabal-fix#readme+bug-reports: https://github.com/tonyday567/cabal-fix/issues synopsis: Fix for cabal files. description:     An executable and library to help fix cabal files and explore cabal. build-type: Simple tested-with:-    GHC == 8.10.7-    GHC == 9.2.8-    GHC == 9.4.8-    GHC == 9.6.4-    GHC == 9.8.1+    , GHC == 9.10.1+    , GHC == 9.6.5+    , GHC == 9.8.2 extra-doc-files:     ChangeLog.md     other/textdeps.svg-    readme.org+    readme.md  source-repository head     type: git-    location: https://github.com/tonyday567/cabal-fixes+    location: https://github.com/tonyday567/cabal-fix  common ghc-options-exe-stanza     ghc-options:@@ -44,67 +42,14 @@         -Wredundant-constraints  common ghc2021-stanza-    if impl ( ghc >= 9.2 )-        default-language: GHC2021--    if impl ( ghc < 9.2 )-        default-language: Haskell2010-        default-extensions:-            BangPatterns-            BinaryLiterals-            ConstrainedClassMethods-            ConstraintKinds-            DeriveDataTypeable-            DeriveFoldable-            DeriveFunctor-            DeriveGeneric-            DeriveLift-            DeriveTraversable-            DoAndIfThenElse-            EmptyCase-            EmptyDataDecls-            EmptyDataDeriving-            ExistentialQuantification-            ExplicitForAll-            FlexibleContexts-            FlexibleInstances-            ForeignFunctionInterface-            GADTSyntax-            GeneralisedNewtypeDeriving-            HexFloatLiterals-            ImplicitPrelude-            InstanceSigs-            KindSignatures-            MonomorphismRestriction-            MultiParamTypeClasses-            NamedFieldPuns-            NamedWildCards-            NumericUnderscores-            PatternGuards-            PolyKinds-            PostfixOperators-            RankNTypes-            RelaxedPolyRec-            ScopedTypeVariables-            StandaloneDeriving-            StarIsType-            TraditionalRecordSyntax-            TupleSections-            TypeApplications-            TypeOperators-            TypeSynonymInstances--    if impl ( ghc < 9.2 ) && impl ( ghc >= 8.10 )-        default-extensions:-            ImportQualifiedPost-            StandaloneKindSignatures+    default-language: GHC2021  library     import: ghc-options-stanza     import: ghc2021-stanza     hs-source-dirs: src     build-depends:-        , Cabal-syntax       >=3.10 && <3.11+        , Cabal-syntax       >=3.10 && <3.15         , algebraic-graphs   >=0.7 && <0.8         , base               >=4.14 && <5         , bytestring         >=0.11 && <0.13@@ -142,3 +87,13 @@         , pretty-simple        >=4.1 && <4.2         , text                 >=2.0 && <2.2         , tree-diff            >=0.3 && <0.4++test-suite doctests+    import: ghc2021-stanza+    main-is: doctests.hs+    hs-source-dirs: test+    build-depends:+        , base             >=4.14 && <5+        , doctest-parallel >=0.3 && <0.4+    ghc-options: -threaded+    type: exitcode-stdio-1.0
+ readme.md view
@@ -0,0 +1,828 @@++# Table of Contents++1.  [cabal-fix](#orgbfbcd8c)+2.  [App Usage](#org2f4aafb)+3.  [App Configuration](#orga921360)+4.  [Library Usage](#orgbb0e777)+5.  [cabal init](#orge6fca8d)+    1.  [minimal](#orgc14dafe)+    2.  [code example](#orgd8c294c)+    3.  [simple](#org2493565)+6.  [Archive Exploration](#org733a366)+    1.  [imports](#org69e0aa6)+    2.  [tar file to list of cabal files](#org241dfdb)+        1.  [entries](#orgdc43fe9)+        2.  [Maximum file size:](#org39c1923)+        3.  [zero size](#orgcf21c34)+        4.  [preferred-versions](#orgb39b7ed)+        5.  [package.json](#org9b4f148)+        6.  [cabal files](#orge4acb82)+    3.  [latestCabals to CabalFields map](#org9ce30f8)+    4.  [CabalFields map to dependency graph](#orgb4f4cbb)+    5.  [algebraic-graphs](#org6f1757a)+    6.  [sections](#orgd2876cc)+        1.  [section count](#org39f7aed)+        2.  [section types](#orge952475)+        3.  [section in section](#org74c0b33)+        4.  [zero-section cfs](#orgd5e491e)+    7.  [Dependency counts](#org9a284e6)+    8.  [version ranges](#org198f27d)+        1.  [all versions are unique?](#org0b08b16)+        2.  [Version counts](#org39af952)+7.  [Field re-ordering](#org9771b7b)+8.  [references](#org15314af)+++<a id="orgbfbcd8c"></a>++# cabal-fix++[![img](https://img.shields.io/hackage/v/cabal-fix.svg)](https://hackage.haskell.org/package/cabal-fix)+[![img](https://github.com/tonyday567/cabal-fix/workflows/haskell-ci/badge.svg)](https://github.com/tonyday567/cabal-fix/actions?query=workflow%3Ahaskell-ci)++`cabal-fix` helps fix your cabal files. This package:++-   Contains an app which parses your existing cabal file, re-renders it according to some config, then either displays a diff or alters your cabal file, in-place.+-   Is an idempotent parser of a cabal file or ByteString, into the `Field` type used in Cabal.+-   Is an inexact printer.+-   Contains code to explore the cabal index archive at the base of your cabal installation.+++<a id="org2f4aafb"></a>++# App Usage++    +    #+end_export+    cabal-fix --help++    fixes your cabal file+    +    Usage: cabal-fix COMMAND [-d|--directory ARG] [-c|--config ARG]+    +      cabal fixer+    +    Available options:+      -d,--directory ARG       project directory+      -c,--config ARG          config file+      -h,--help                Show this help text+    +    Available commands:+      inplace                  fix cabal file inplace+      check                    check cabal file+      genConfig                generate config file+++<a id="orga921360"></a>++# App Configuration++The configuration of cabal-fix is encapsulated in the Config type. The configuration file generated by the app (via `cabal-fix genConfig`) just (pretty) prints defaultConfig.++    import Text.Pretty.Simple+    pPrint defaultConfig++    Config+        { freeTexts = [ "description" ]+        , fieldRemovals = []+        , preferredDeps =+            [+                ( "base"+                , ">=4.7 && <5"+                )+            ]+        , addFields = []+        , fixCommas =+            [+                ( "extra-doc-files"+                , NoCommas+                )+            ,+                ( "build-depends"+                , PrefixCommas+                )+            ]+        , sortFieldLines =+            [ "build-depends"+            , "exposed-modules"+            , "default-extensions"+            , "ghc-options"+            , "extra-doc-files"+            , "tested-with"+            ]+        , sortFields = True+        , fieldOrdering =+            [+                ( "cabal-version"+                , 0.0+                )+            ,+                ( "import"+                , 1.0+                )+            ,+                ( "main-is"+                , 2.0+                )+            ,+                ( "default-language"+                , 3.0+                )+            ,+                ( "name"+                , 4.0+                )+            ,+                ( "hs-source-dirs"+                , 5.0+                )+            ,+                ( "version"+                , 6.0+                )+            ,+                ( "build-depends"+                , 7.0+                )+            ,+                ( "exposed-modules"+                , 8.0+                )+            ,+                ( "license"+                , 9.0+                )+            ,+                ( "license-file"+                , 10.0+                )+            ,+                ( "other-modules"+                , 11.0+                )+            ,+                ( "copyright"+                , 12.0+                )+            ,+                ( "category"+                , 13.0+                )+            ,+                ( "author"+                , 14.0+                )+            ,+                ( "default-extensions"+                , 15.0+                )+            ,+                ( "ghc-options"+                , 16.0+                )+            ,+                ( "maintainer"+                , 17.0+                )+            ,+                ( "homepage"+                , 18.0+                )+            ,+                ( "bug-reports"+                , 19.0+                )+            ,+                ( "synopsis"+                , 20.0+                )+            ,+                ( "description"+                , 21.0+                )+            ,+                ( "build-type"+                , 22.0+                )+            ,+                ( "tested-with"+                , 23.0+                )+            ,+                ( "extra-doc-files"+                , 24.0+                )+            ,+                ( "source-repository"+                , 25.0+                )+            ,+                ( "type"+                , 26.0+                )+            ,+                ( "common"+                , 27.0+                )+            ,+                ( "location"+                , 28.0+                )+            ,+                ( "library"+                , 29.0+                )+            ,+                ( "executable"+                , 30.0+                )+            ,+                ( "test-suite"+                , 31.0+                )+            ]+        , fixBuildDeps = True+        , depAlignment = DepAligned+        , removeBlankFields = True+        , valueAligned = ValueUnaligned+        , sectionMargin = Margin+        , commentMargin = NoMargin+        , narrowN = 60+        , indentN = 4+        }+++<a id="orgbb0e777"></a>++# Library Usage++    :set -XOverloadedStrings+    :set -XOverloadedLabels+    :set -Wno-incomplete-uni-patterns+    :set -Wno-name-shadowing+    import CabalFix+    import Optics.Extra+    import Data.ByteString.Char8 qualified as C+    bs = minimalExampleBS+    cfg = defaultConfig+    (Just cf) = preview (cabalFields' cfg) bs+    fs = cf & view (#fields % fieldList')++    Build profile: -w ghc-9.4.8 -O1+    In order, the following will be built (use -v for more details):+     - cabal-fix-0.0.0.1 (lib) (ephemeral targets)+    Preprocessing library for cabal-fix-0.0.0.1..+    GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help+    [1 of 4] Compiling CabalFix.FlatParse ( src/CabalFix/FlatParse.hs, interpreted )+    [2 of 4] Compiling CabalFix         ( src/CabalFix.hs, interpreted )+    [3 of 4] Compiling CabalFix.Archive ( src/CabalFix/Archive.hs, interpreted )+    [4 of 4] Compiling CabalFix.Patch   ( src/CabalFix/Patch.hs, interpreted )+    Ok, four modules loaded.++    cf & review (cabalFields' cfg) & C.putStr++    cabal-version: 3.0+    name: minimal+    version: 0.1.0.0+    license: BSD-2-Clause+    license-file: LICENSE+    build-type: Simple+    extra-doc-files: CHANGELOG.md+    +    common warnings+        ghc-options: -Wall+    +    library+        import: warnings+        exposed-modules: MyLib+        build-depends: base ^>=4.17.2.1+        hs-source-dirs: src+        default-language: GHC2021+    +    test-suite minimal-test+        import: warnings+        default-language: GHC2021+        type: exitcode-stdio-1.0+        hs-source-dirs: test+        main-is: Main.hs+        build-depends:+            base ^>=4.17.2.1,+            minimal+++<a id="orge6fca8d"></a>++# cabal init+++<a id="orgc14dafe"></a>++## minimal++A minimal `cabal init`++    mkdir minimal && cd minimal && cabal init --minimal --simple --overwrite --lib --tests --language=GHC2021 --license=BSD-2-Clause  -p minimal++    [Log] Using cabal specification: 3.0+    [Log] Creating fresh file LICENSE...+    [Log] Creating fresh file CHANGELOG.md...+    [Log] Creating fresh directory ./src...+    [Log] Creating fresh file src/MyLib.hs...+    [Log] Creating fresh directory ./test...+    [Log] Creating fresh file test/Main.hs...+    [Log] Creating fresh file minimal.cabal...+    [Warning] No synopsis given. You should edit the .cabal file and add one.+    [Info] You may want to edit the .cabal file and add a Description field.++Compared with the original `cabal init` contents, `cabal-fix`:++-   widens the `base` range, in line with standard practice.+-   reorders the `test-suite` section fields, in line with the ordering of the `library` section ones.++    cabal-fix check -d "minimal" -c "other/minimal.config"++    Right (Just [+      -"    build-depends:    base ^>=4.17.2.1",+      +"    build-depends:    base >=4.14 && <5",+      -"    default-language: GHC2021",+      +"    main-is:          Main.hs",+      -"    type:             exitcode-stdio-1.0",+      +"    build-depends:",+      -"    hs-source-dirs:   test",+      +"        base    >=4.14 && <5,",+      -"    main-is:          Main.hs",+      +"        minimal",+      -"    build-depends:",+      +"    hs-source-dirs:   test",+      -"        base ^>=4.17.2.1,",+      +"    default-language: GHC2021",+      -"        minimal",+      +"    type:             exitcode-stdio-1.0"])+++<a id="orgd8c294c"></a>++## code example++For reference, the code below should produce the same results as the app run above:++    :set -XOverloadedStrings+    :set -XOverloadedLabels+    :set -Wno-incomplete-uni-patterns+    :set -Wno-name-shadowing+    :set -Wno-type-defaults+    import CabalFix+    import Text.Pretty.Simple+    import CabalFix.Patch+    import Data.TreeDiff+    bs = minimalExampleBS+    cfg = minimalConfig+    (Just cf) = preview (cabalFields' cfg) bs+    bs' = review (cabalFields' cfg) cf+    (Just cf') = preview (cabalFields' cfg) bs'+    cfFixed = fixCabalFields cfg cf+    bsFixed = review (cabalFields' cfg) cfFixed+    fmap ansiWlBgEditExpr $ patch (C.lines bs) (C.lines bsFixed)++    Just [+      -"    build-depends:    base ^>=4.17.2.1",+      +"    build-depends:    base >=4.14 && <5",+      -"    default-language: GHC2021",+      +"    main-is:          Main.hs",+      -"    type:             exitcode-stdio-1.0",+      +"    build-depends:",+      -"    hs-source-dirs:   test",+      +"        base    >=4.14 && <5,",+      -"    main-is:          Main.hs",+      +"        minimal",+      -"    build-depends:",+      +"    hs-source-dirs:   test",+      -"        base ^>=4.17.2.1,",+      +"    default-language: GHC2021",+      -"        minimal",+      +"    type:             exitcode-stdio-1.0"]+++<a id="org2493565"></a>++## simple++    mkdir simple && cd simple && cabal init --simple --overwrite --lib --tests --language=GHC2021 --license=BSD-2-Clause  -p simple++    [Log] Using cabal specification: 3.0+    [Log] Creating fresh file LICENSE...+    [Log] Creating fresh file CHANGELOG.md...+    [Log] Creating fresh directory ./src...+    [Log] Creating fresh file src/MyLib.hs...+    [Log] Creating fresh directory ./test...+    [Log] Creating fresh file test/Main.hs...+    [Log] Creating fresh file simple.cabal...+    [Warning] No synopsis given. You should edit the .cabal file and add one.+    [Info] You may want to edit the .cabal file and add a Description field.++    cabal-fix check -d "simple" -c "other/minimal.config"++    Right (Just [+      +"cabal-version:   3.0",+      -"cabal-version:      3.0",+      +"",+      -"name:               simple",+      +"name:            simple",+      -"version:            0.1.0.0",+      +"version:         0.1.0.0",+      -"license:            BSD-2-Clause",+      +"license:         BSD-2-Clause",+      -"license-file:       LICENSE",+      +"license-file:    LICENSE",+      -"build-type:         Simple",+      +"build-type:      Simple",+      -"extra-doc-files:    CHANGELOG.md",+      +"extra-doc-files: CHANGELOG.md",+      -"    build-depends:    base ^>=4.17.2.1",+      +"    build-depends:    base >=4.14 && <5",+      -"    -- Base language which the package is written in.",+      +"    -- The entrypoint to the test suite.",+      -"    default-language: GHC2021",+      +"    main-is:          Main.hs",+      -"    -- Modules included in this executable, other than Main.",+      -"    -- other-modules:",+      +"    -- Test dependencies.",+      -"",+      +"    build-depends:",+      -"    -- LANGUAGE extensions used by modules in this package.",+      +"        base   >=4.14 && <5,",+      -"    -- other-extensions:",+      +"        simple",+      -"    -- The interface type and version of the test suite.",+      +"    -- Directories containing source files.",+      -"    type:             exitcode-stdio-1.0",+      +"    hs-source-dirs:   test",+      -"    -- Directories containing source files.",+      +"    -- Base language which the package is written in.",+      -"    hs-source-dirs:   test",+      +"    default-language: GHC2021",+      -"    -- The entrypoint to the test suite.",+      +"    -- Modules included in this executable, other than Main.",+      -"    main-is:          Main.hs",+      +"    -- other-modules:",+      +"    -- LANGUAGE extensions used by modules in this package.",+      -"    -- Test dependencies.",+      +"    -- other-extensions:",+      -"    build-depends:",+      +"",+      -"        base ^>=4.17.2.1,",+      +"    -- The interface type and version of the test suite.",+      -"        simple",+      +"    type:             exitcode-stdio-1.0"])+++<a id="org733a366"></a>++# Archive Exploration++CabalFix.Archive contains functions to extract and explore cabal files listed in your cabal index file.++The sections below are some exploration notes.+++<a id="org69e0aa6"></a>++## imports++    :r+    :set -Wno-type-defaults+    :set -Wno-name-shadowing+    :set -XOverloadedLabels+    :set -XOverloadedStrings+    :set -Wno-incomplete-uni-patterns+    import Algebra.Graph+    import Algebra.Graph.ToGraph qualified as ToGraph+    import CabalFix+    import CabalFix.Archive+    import CabalFix.FlatParse+    import Codec.Archive.Tar qualified as Tar+    import Control.Monad+    import Data.Bifunctor+    import Data.ByteString (ByteString)+    import Data.ByteString qualified as BS+    import Data.ByteString.Char8 qualified as C+    import Data.ByteString.Lazy qualified as BSL+    import Data.Char+    import Data.Either+    import Data.Function+    import Data.List qualified as List+    import Data.Map.Strict qualified as Map+    import Data.Ord+    import Data.Set qualified as Set+    import DotParse+    import FlatParse.Basic qualified as FP+    import System.Directory+    import Text.Pretty.Simple++    Ok, four modules loaded.+++<a id="org241dfdb"></a>++## tar file to list of cabal files+++<a id="orgdc43fe9"></a>++### entries++    es <- cabalEntries+    length es++    317368++    Tar.entryPath <$> take 5 es++    ["iconv/0.2/iconv.cabal","Crypto/3.0.3/Crypto.cabal","HDBC/1.0.1/HDBC.cabal","HDBC-odbc/1.0.1.0/HDBC-odbc.cabal","HDBC-postgresql/1.0.1.0/HDBC-postgresql.cabal"]++They are all normal files++    (length [x | (Tar.NormalFile x _) <- Tar.entryContent <$> es])++    317368+++<a id="org39c1923"></a>++### Maximum file size:++    (\xs -> filter ((maximum (snd <$> xs) ==) . snd) xs) $ [(fp,x) | (fp, Tar.NormalFile _ x) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]++    [("acme-everything/2018.11.18/acme-everything.cabal",261865)]+++<a id="orgcf21c34"></a>++### zero size++    take 4 $ (\xs -> filter ((0 ==) . snd) xs) $ [(fp,x) | (fp, Tar.NormalFile _ x) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]++    [("lzma/preferred-versions",0),("signal/preferred-versions",0),("peyotls-codec/preferred-versions",0),("th-orphans/preferred-versions",0)]+++<a id="orgb39b7ed"></a>++### preferred-versions++[Cabal: preferred and deprecated versions | Hackage](https://hackage.haskell.org/package/Cabal/preferred)++    take 3 $ (\xs -> filter ((List.isSuffixOf "preferred-versions") . fst) xs) $ [(fp,bs) | (fp, Tar.NormalFile bs _) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]++    [("ADPfusion/preferred-versions","ADPfusion <0.4.0.0 || >0.4.0.0"),("AesonBson/preferred-versions","AesonBson <0.2.0 || >0.2.0 && <0.2.1 || >0.2.1"),("BiobaseXNA/preferred-versions","BiobaseXNA <0.9.1.0 || >0.9.1.0")]++    length $ (\xs -> filter ((List.isSuffixOf "preferred-versions") . fst) xs) $ [(fp,bs) | (fp, Tar.NormalFile bs _) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]++    3376+++<a id="org9b4f148"></a>++### package.json++`package-json` content is a security/signing feature you can read about in [hackage-security](https://github.com/haskell-ng/hackage-security/blob/master/README.md).++    length $ filter ((== "package.json") . filenameFN . runParser_ filenameP . FP.strToUtf8 . fst) $ filter (not . (List.isSuffixOf "preferred-versions") . fst) $ [(fp,bs) | (fp, Tar.NormalFile bs _) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]++    137524+++<a id="orge4acb82"></a>++### cabal files++Unique package/version combinations.++There are multiple versions of package/versions because of revisions. See [revisions-information.md](https://github.com/haskell-infra/hackage-trustees/blob/master/revisions-information.md)++Unique `*/*.cabal/version` entries++    cs <- cabals+    length cs++    137524++Unique cabal packages++    lcs <- latestCabals+    Map.size lcs++    17631++Average number of versions per package++    (fromIntegral (length cs)) / fromIntegral (Map.size lcs)++    7.800124780216664+++<a id="org9ce30f8"></a>++## latestCabals to CabalFields map++    lcs <- latestCabals defaultConfig+    Map.size lcs+    cfg = defaultConfig+    lcs' = fmap (second (parseCabalFields cfg)) lcs+    Map.size $ Map.filter (snd >>> isLeft) lcs'+    :t lcs'+    badParse = Map.filter (isLeft . parseCabalFields cfg . snd) lcs+    Map.size badParse++    17631+    6+    lcs' :: Map.Map ByteString (Version, Either ByteString CabalFields)+    6+++<a id="orgb4f4cbb"></a>++## CabalFields map to dependency graph++    lcfs <- latestCabalFields+    vlds = validLibDeps $ fmap snd lcfs+    Map.size vlds+    depG = allDepGraph $ fmap snd lcfs+    vertexCount depG+    edgeCount depG++    15547+    15621+    107566+++<a id="org6f1757a"></a>++## algebraic-graphs++An (algebraic) graph of dependencies:++`text` package dependency example++    supers = upstreams "text" depG <> Set.singleton "text"+    superG = induce (`elem` (Data.Foldable.toList supers)) depG++    supers++    fromList ["array","binary","bytestring","deepseq","ghc-prim","template-haskell","text"]++    baseGraph = defaultGraph & attL GraphType (ID "size") .~ Just (IDQuoted "5!") & attL NodeType (ID "shape") .~ Just (ID "box") & attL NodeType (ID "height") .~ Just (ID 2) & gattL (ID "rankdir") .~ Just (IDQuoted "TB")+    g = toDotGraphWith Directed baseGraph superG+    processDotWith Directed ["-Tsvg", "-oother/textdeps.svg"] (dotPrint defaultDotConfig g)+    BS.writeFile "other/textdeps.dot" (dotPrint defaultDotConfig g)++![img](other/textdeps.svg)+++<a id="orgd2876cc"></a>++## sections+++<a id="org39f7aed"></a>++### section count++    cfs = lcfs & Map.toList & fmap (snd . snd)+    cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection >>> length) & count_++    fromList [(0,359),(1,2559),(2,5508),(3,4730),(4,2224),(5,956),(6,479),(7,236),(8,138),(9,98),(10,63),(11,57),(12,31),(13,32),(14,22),(15,16),(16,12),(17,7),(18,11),(19,8),(20,8),(21,8),(22,4),(23,3),(24,7),(25,4),(26,6),(27,1),(28,1),(29,4),(30,2),(32,4),(33,2),(34,4),(36,1),(37,4),(38,1),(39,2),(40,1),(41,1),(43,2),(47,2),(48,2),(50,1),(65,1),(93,1),(97,1),(295,1)]+++<a id="orge952475"></a>++### section types++    cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection) & fmap (fmap (view fieldName')) & mconcat & count_ & Map.toList & List.sortOn (Down . snd)++    [("library",16028),("source-repository",13889),("test-suite",8718),("executable",7292),("flag",4134),("common",2302),("benchmark",1246),("custom-setup",321),("foreign-library",4)]++combinations:++    cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection) & fmap (fmap (view fieldName')) & fmap (filter (not . (flip List.elem) ["source-repository", "custom-setup", "foreign-library", "flag", "common"])) & fmap (count_ >>> Map.toList >>> List.sortOn fst) & count_ & Map.toList & List.sortOn (Down . snd) & take 10++    [([("library",1)],7291),([("library",1),("test-suite",1)],4195),([("executable",1),("library",1)],1148),([("executable",1)],1105),([("executable",1),("library",1),("test-suite",1)],901),([("benchmark",1),("library",1),("test-suite",1)],520),([("library",1),("test-suite",2)],416),([],359),([("executable",2),("library",1)],163),([("executable",2),("library",1),("test-suite",1)],133)]++at least 1 combinations:++    cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection) & fmap (fmap (view fieldName')) & fmap (filter (not . (flip List.elem) ["source-repository", "custom-setup", "foreign-library", "flag", "common"])) & fmap (count_ >>> Map.toList >>> fmap fst >>> List.sortOn id) & count_ & Map.toList & List.sortOn (Down . snd) & take 10++    [(["library"],7297),(["library","test-suite"],4778),(["executable","library"],1490),(["executable","library","test-suite"],1309),(["executable"],1263),(["benchmark","library","test-suite"],739),([],359),(["benchmark","executable","library","test-suite"],182),(["executable","test-suite"],119),(["benchmark","library"],59)]+++<a id="org74c0b33"></a>++### section in section++    sections' = to (filter isSection)+    -- cfs & fmap (foldOf (#fields % fieldList' % sections' % each % secFields' % sections')) & filter (not . null) & fmap (second (fmap (view fieldName'))) & fmap snd & mconcat & count_+    cfs & fmap (foldOf (#fields % fieldList' % sections' % each % secFields' % sections')) & filter (not . null) & fmap ((fmap (view fieldName'))) & mconcat & count_++    fromList [("elif",52),("else",3203),("if",11459),("library",3)]++Embedded libraries are all deprecated.+++<a id="orgd5e491e"></a>++### zero-section cfs++Looks like library fields used to be allowed at the top level&#x2026;++    cfs0 = cfs & toListOf (each % #fields % fieldList') & filter ((==0) . length . (filter isSection))+    length cfs0+    count_ $ cfs0 & fmap (foldOf (field' "build-depends") >>> length)+    cfs00 = cfs0 & filter (foldOf (field' "build-depends") >>> length >>> (==0))+    length cfs00++    359+    fromList [(0,2),(1,349),(2,7),(4,1)]+    2+++<a id="org9a284e6"></a>++## Dependency counts++package dependency count:++    lcfs & fmap (snd >>> libDeps >>> fmap dep >>> List.nub >>> length) & Map.toList & List.sortOn (Down . snd) & take 20++    [("acme-everything",7533),("yesod-platform",132),("planet-mitchell",109),("freckle-app",78),("cachix",76),("btc-lsp",71),("too-many-cells",70),("swarm",68),("ghcide",67),("pandoc",67),("sprinkles",65),("pantry-tmp",64),("taffybar",63),("NGLess",60),("project-m36",59),("stack",59),("espial",58),("hermes",58),("purescript",56),("futhark",55)]++dependency count:++    lcfs & fmap (snd >>> libDeps >>> fmap dep >>> List.nub) & Map.toList & fmap snd & mconcat & count_ & Map.toList & List.sortOn (snd >>> Down) & take 40++    [("base",14883),("bytestring",5384),("text",4972),("containers",4753),("mtl",3468),("transformers",3070),("aeson",2013),("time",1961),("vector",1793),("directory",1597),("filepath",1510),("template-haskell",1472),("unordered-containers",1392),("deepseq",1240),("lens",1173),("hashable",930),("binary",929),("array",892),("exceptions",855),("process",844),("stm",819),("random",811),("http-types",784),("attoparsec",781),("network",756),("parsec",744),("data-default",609),("QuickCheck",597),("conduit",503),("http-client",497),("split",472),("primitive",470),("ghc-prim",456),("async",449),("semigroups",427),("monad-control",424),("scientific",420),("resourcet",401),("unix",398),("utf8-string",392)]+++<a id="org198f27d"></a>++## version ranges++    cs <- cabals+    length cs++    137323++    :t cs+    +    mVersions = Map.fromListWith (<>) $ ((\x -> (nameFN x, (:[]) $ (versionInts $ versionFN x))) . fst) <$> cs+    Map.size mVersions++    cs :: [(FileName, ByteString)]+    17631++    (Just x1) = Map.lookup "chart-svg" mVersions+    x1+    minimum x1+    maximum x1++    [[0,6,0,0],[0,5,2,0],[0,5,1,1],[0,5,1,0],[0,5,0,0],[0,4,1,1],[0,4,1,0],[0,4,0],[0,3,3],[0,3,2],[0,3,1],[0,3,0],[0,2,3],[0,2,2],[0,2,1],[0,2,0],[0,1,3],[0,1,2],[0,1,1],[0,1,0],[0,0,3],[0,0,2],[0,0,1]]+    [0,0,1]+    [0,6,0,0]+++<a id="org0b08b16"></a>++### all versions are unique?++    take 10 $ Map.toList $ Map.filter (\a -> length a /= length (List.nub a)) mVersions++    []+++<a id="org39af952"></a>++### Version counts++    take 10 $ List.sortOn (Down . snd) $ Map.toList $ Map.map length mVersions++    [("haskoin-store",298),("git-annex",282),("hlint",221),("yesod-core",216),("purescript",204),("warp",204),("pandoc",193),("hakyll",192),("egison",190),("persistent",186)]+++<a id="org9771b7b"></a>++# Field re-ordering++    zipWith (\o l -> (fst l, o)) [0..] (List.sortOn snd $ fieldOrdering defaultConfig)++    [("cabal-version",0),("import",1),("main-is",2),("default-language",3),("name",4),("hs-source-dirs",5),("version",6),("build-depends",7),("exposed-modules",8),("license",9),("license-file",10),("other-modules",11),("copyright",12),("category",13),("author",14),("default-extensions",15),("ghc-options",16),("maintainer",17),("homepage",18),("bug-reports",19),("synopsis",20),("description",21),("build-type",22),("tested-with",23),("extra-doc-files",24),("source-repository",25),("type",26),("common",27),("location",28),("library",29),("executable",30),("test-suite",31)]+++<a id="org15314af"></a>++# references++[Distribution.Fields.Field](https://hackage.haskell.org/package/Cabal-syntax-3.10.2.0/docs/Distribution-Fields-Field.html)++[optics-core: Optics as an abstract interface: core definitions](https://hackage.haskell.org/package/optics-core-0.4.1.1)++[6. Package Description — Cabal 3.10.1.0 User&rsquo;s Guide](https://cabal.readthedocs.io/en/stable/cabal-package.html#package-descriptions)+
− readme.org
@@ -1,838 +0,0 @@-* cabal-fix--[[https://hackage.haskell.org/package/cabal-fix][https://img.shields.io/hackage/v/cabal-fix.svg]]-[[https://github.com/tonyday567/cabal-fix/actions?query=workflow%3Ahaskell-ci][https://github.com/tonyday567/cabal-fix/workflows/haskell-ci/badge.svg]]--~cabal-fix~ helps fix your cabal files. This package:--- Contains an app which parses your existing cabal file, re-renders it according to some config, then either displays a diff or alters your cabal file, in-place.-- Is an idempotent parser of a cabal file or ByteString, into the ~Field~ type used in Cabal.-- Is an inexact printer.-- Contains code to explore the cabal index archive at the base of your cabal installation.--* App Usage--#+begin_src sh :results output :exports both--#+end_export-cabal-fix --help-#+end_src--#+RESULTS:-#+begin_example-fixes your cabal file--Usage: cabal-fix COMMAND [-d|--directory ARG] [-c|--config ARG]--  cabal fixer--Available options:-  -d,--directory ARG       project directory-  -c,--config ARG          config file-  -h,--help                Show this help text--Available commands:-  inplace                  fix cabal file inplace-  check                    check cabal file-  genConfig                generate config file-#+end_example--* App Configuration--The configuration of cabal-fix is encapsulated in the Config type. The configuration file generated by the app (via =cabal-fix genConfig=) just (pretty) prints defaultConfig.--#+begin_src haskell-ng :results output :exports both-import Text.Pretty.Simple-pPrint defaultConfig-#+end_src--#+RESULTS:-#+begin_example-Config-    { freeTexts = [ "description" ]-    , fieldRemovals = []-    , preferredDeps =-        [-            ( "base"-            , ">=4.7 && <5"-            )-        ]-    , addFields = []-    , fixCommas =-        [-            ( "extra-doc-files"-            , NoCommas-            )-        ,-            ( "build-depends"-            , PrefixCommas-            )-        ]-    , sortFieldLines =-        [ "build-depends"-        , "exposed-modules"-        , "default-extensions"-        , "ghc-options"-        , "extra-doc-files"-        , "tested-with"-        ]-    , sortFields = True-    , fieldOrdering =-        [-            ( "cabal-version"-            , 0.0-            )-        ,-            ( "import"-            , 1.0-            )-        ,-            ( "main-is"-            , 2.0-            )-        ,-            ( "default-language"-            , 3.0-            )-        ,-            ( "name"-            , 4.0-            )-        ,-            ( "hs-source-dirs"-            , 5.0-            )-        ,-            ( "version"-            , 6.0-            )-        ,-            ( "build-depends"-            , 7.0-            )-        ,-            ( "exposed-modules"-            , 8.0-            )-        ,-            ( "license"-            , 9.0-            )-        ,-            ( "license-file"-            , 10.0-            )-        ,-            ( "other-modules"-            , 11.0-            )-        ,-            ( "copyright"-            , 12.0-            )-        ,-            ( "category"-            , 13.0-            )-        ,-            ( "author"-            , 14.0-            )-        ,-            ( "default-extensions"-            , 15.0-            )-        ,-            ( "ghc-options"-            , 16.0-            )-        ,-            ( "maintainer"-            , 17.0-            )-        ,-            ( "homepage"-            , 18.0-            )-        ,-            ( "bug-reports"-            , 19.0-            )-        ,-            ( "synopsis"-            , 20.0-            )-        ,-            ( "description"-            , 21.0-            )-        ,-            ( "build-type"-            , 22.0-            )-        ,-            ( "tested-with"-            , 23.0-            )-        ,-            ( "extra-doc-files"-            , 24.0-            )-        ,-            ( "source-repository"-            , 25.0-            )-        ,-            ( "type"-            , 26.0-            )-        ,-            ( "common"-            , 27.0-            )-        ,-            ( "location"-            , 28.0-            )-        ,-            ( "library"-            , 29.0-            )-        ,-            ( "executable"-            , 30.0-            )-        ,-            ( "test-suite"-            , 31.0-            )-        ]-    , fixBuildDeps = True-    , depAlignment = DepAligned-    , removeBlankFields = True-    , valueAligned = ValueUnaligned-    , sectionMargin = Margin-    , commentMargin = NoMargin-    , narrowN = 60-    , indentN = 4-    }-#+end_example--* Library Usage--#+begin_src haskell-ng :results output :exports both-:set -XOverloadedStrings-:set -XOverloadedLabels-:set -Wno-incomplete-uni-patterns-:set -Wno-name-shadowing-import CabalFix-import Optics.Extra-import Data.ByteString.Char8 qualified as C-bs = minimalExampleBS-cfg = defaultConfig-(Just cf) = preview (cabalFields' cfg) bs-fs = cf & view (#fields % fieldList')-#+end_src--#+RESULTS:-#+begin_example-Build profile: -w ghc-9.4.8 -O1-In order, the following will be built (use -v for more details):- - cabal-fix-0.0.0.1 (lib) (ephemeral targets)-Preprocessing library for cabal-fix-0.0.0.1..-GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help-[1 of 4] Compiling CabalFix.FlatParse ( src/CabalFix/FlatParse.hs, interpreted )-[2 of 4] Compiling CabalFix         ( src/CabalFix.hs, interpreted )-[3 of 4] Compiling CabalFix.Archive ( src/CabalFix/Archive.hs, interpreted )-[4 of 4] Compiling CabalFix.Patch   ( src/CabalFix/Patch.hs, interpreted )-Ok, four modules loaded.-#+end_example--#+begin_src haskell-ng :results output :exports both-cf & review (cabalFields' cfg) & C.putStr-#+end_src--#+RESULTS:-#+begin_example-cabal-version: 3.0-name: minimal-version: 0.1.0.0-license: BSD-2-Clause-license-file: LICENSE-build-type: Simple-extra-doc-files: CHANGELOG.md--common warnings-    ghc-options: -Wall--library-    import: warnings-    exposed-modules: MyLib-    build-depends: base ^>=4.17.2.1-    hs-source-dirs: src-    default-language: GHC2021--test-suite minimal-test-    import: warnings-    default-language: GHC2021-    type: exitcode-stdio-1.0-    hs-source-dirs: test-    main-is: Main.hs-    build-depends:-        base ^>=4.17.2.1,-        minimal-#+end_example--* cabal init--** minimal--A minimal =cabal init=--#+begin_src sh :results output :exports both-mkdir minimal && cd minimal && cabal init --minimal --simple --overwrite --lib --tests --language=GHC2021 --license=BSD-2-Clause  -p minimal-#+end_src--#+RESULTS:-#+begin_example-[Log] Using cabal specification: 3.0-[Log] Creating fresh file LICENSE...-[Log] Creating fresh file CHANGELOG.md...-[Log] Creating fresh directory ./src...-[Log] Creating fresh file src/MyLib.hs...-[Log] Creating fresh directory ./test...-[Log] Creating fresh file test/Main.hs...-[Log] Creating fresh file minimal.cabal...-[Warning] No synopsis given. You should edit the .cabal file and add one.-[Info] You may want to edit the .cabal file and add a Description field.--#+end_example--Compared with the original =cabal init= contents, =cabal-fix=:--- widens the =base= range, in line with standard practice.-- reorders the =test-suite= section fields, in line with the ordering of the =library= section ones.--#+begin_src sh :results output :exports both-cabal-fix check -d "minimal" -c "other/minimal.config"-#+end_src--#+RESULTS:-#+begin_example-Right (Just [-  -"    build-depends:    base ^>=4.17.2.1",-  +"    build-depends:    base >=4.14 && <5",-  -"    default-language: GHC2021",-  +"    main-is:          Main.hs",-  -"    type:             exitcode-stdio-1.0",-  +"    build-depends:",-  -"    hs-source-dirs:   test",-  +"        base    >=4.14 && <5,",-  -"    main-is:          Main.hs",-  +"        minimal",-  -"    build-depends:",-  +"    hs-source-dirs:   test",-  -"        base ^>=4.17.2.1,",-  +"    default-language: GHC2021",-  -"        minimal",-  +"    type:             exitcode-stdio-1.0"])-#+end_example--** code example--For reference, the code below should produce the same results as the app run above:--#+begin_src haskell-ng :results output :exports both-:set -XOverloadedStrings-:set -XOverloadedLabels-:set -Wno-incomplete-uni-patterns-:set -Wno-name-shadowing-:set -Wno-type-defaults-import CabalFix-import Text.Pretty.Simple-import CabalFix.Patch-import Data.TreeDiff-bs = minimalExampleBS-cfg = minimalConfig-(Just cf) = preview (cabalFields' cfg) bs-bs' = review (cabalFields' cfg) cf-(Just cf') = preview (cabalFields' cfg) bs'-cfFixed = fixCabalFields cfg cf-bsFixed = review (cabalFields' cfg) cfFixed-fmap ansiWlBgEditExpr $ patch (C.lines bs) (C.lines bsFixed)-#+end_src--#+RESULTS:-#+begin_example-Just [-  -"    build-depends:    base ^>=4.17.2.1",-  +"    build-depends:    base >=4.14 && <5",-  -"    default-language: GHC2021",-  +"    main-is:          Main.hs",-  -"    type:             exitcode-stdio-1.0",-  +"    build-depends:",-  -"    hs-source-dirs:   test",-  +"        base    >=4.14 && <5,",-  -"    main-is:          Main.hs",-  +"        minimal",-  -"    build-depends:",-  +"    hs-source-dirs:   test",-  -"        base ^>=4.17.2.1,",-  +"    default-language: GHC2021",-  -"        minimal",-  +"    type:             exitcode-stdio-1.0"]-#+end_example--** simple--#+begin_src sh :results output :exports both-mkdir simple && cd simple && cabal init --simple --overwrite --lib --tests --language=GHC2021 --license=BSD-2-Clause  -p simple-#+end_src--#+RESULTS:-#+begin_example-[Log] Using cabal specification: 3.0-[Log] Creating fresh file LICENSE...-[Log] Creating fresh file CHANGELOG.md...-[Log] Creating fresh directory ./src...-[Log] Creating fresh file src/MyLib.hs...-[Log] Creating fresh directory ./test...-[Log] Creating fresh file test/Main.hs...-[Log] Creating fresh file simple.cabal...-[Warning] No synopsis given. You should edit the .cabal file and add one.-[Info] You may want to edit the .cabal file and add a Description field.--#+end_example--#+begin_src sh :results output :exports both-cabal-fix check -d "simple" -c "other/minimal.config"-#+end_src--#+RESULTS:-#+begin_example-Right (Just [-  +"cabal-version:   3.0",-  -"cabal-version:      3.0",-  +"",-  -"name:               simple",-  +"name:            simple",-  -"version:            0.1.0.0",-  +"version:         0.1.0.0",-  -"license:            BSD-2-Clause",-  +"license:         BSD-2-Clause",-  -"license-file:       LICENSE",-  +"license-file:    LICENSE",-  -"build-type:         Simple",-  +"build-type:      Simple",-  -"extra-doc-files:    CHANGELOG.md",-  +"extra-doc-files: CHANGELOG.md",-  -"    build-depends:    base ^>=4.17.2.1",-  +"    build-depends:    base >=4.14 && <5",-  -"    -- Base language which the package is written in.",-  +"    -- The entrypoint to the test suite.",-  -"    default-language: GHC2021",-  +"    main-is:          Main.hs",-  -"    -- Modules included in this executable, other than Main.",-  -"    -- other-modules:",-  +"    -- Test dependencies.",-  -"",-  +"    build-depends:",-  -"    -- LANGUAGE extensions used by modules in this package.",-  +"        base   >=4.14 && <5,",-  -"    -- other-extensions:",-  +"        simple",-  -"    -- The interface type and version of the test suite.",-  +"    -- Directories containing source files.",-  -"    type:             exitcode-stdio-1.0",-  +"    hs-source-dirs:   test",-  -"    -- Directories containing source files.",-  +"    -- Base language which the package is written in.",-  -"    hs-source-dirs:   test",-  +"    default-language: GHC2021",-  -"    -- The entrypoint to the test suite.",-  +"    -- Modules included in this executable, other than Main.",-  -"    main-is:          Main.hs",-  +"    -- other-modules:",-  +"    -- LANGUAGE extensions used by modules in this package.",-  -"    -- Test dependencies.",-  +"    -- other-extensions:",-  -"    build-depends:",-  +"",-  -"        base ^>=4.17.2.1,",-  +"    -- The interface type and version of the test suite.",-  -"        simple",-  +"    type:             exitcode-stdio-1.0"])-#+end_example--* Archive Exploration--CabalFix.Archive contains functions to extract and explore cabal files listed in your cabal index file.--The sections below are some exploration notes.--** imports-#+begin_src haskell-ng :results output :exports both-:r-:set -Wno-type-defaults-:set -Wno-name-shadowing-:set -XOverloadedLabels-:set -XOverloadedStrings-:set -Wno-incomplete-uni-patterns-import Algebra.Graph-import Algebra.Graph.ToGraph qualified as ToGraph-import CabalFix-import CabalFix.Archive-import CabalFix.FlatParse-import Codec.Archive.Tar qualified as Tar-import Control.Monad-import Data.Bifunctor-import Data.ByteString (ByteString)-import Data.ByteString qualified as BS-import Data.ByteString.Char8 qualified as C-import Data.ByteString.Lazy qualified as BSL-import Data.Char-import Data.Either-import Data.Function-import Data.List qualified as List-import Data.Map.Strict qualified as Map-import Data.Ord-import Data.Set qualified as Set-import DotParse-import FlatParse.Basic qualified as FP-import System.Directory-import Text.Pretty.Simple-#+end_src--#+RESULTS:-: Ok, four modules loaded.--** tar file to list of cabal files-*** entries--#+begin_src haskell-ng :results output :exports both-es <- cabalEntries-length es-#+end_src--#+RESULTS:-: 317368--#+begin_src haskell-ng :results output :exports both-Tar.entryPath <$> take 5 es-#+end_src--#+RESULTS:-: ["iconv/0.2/iconv.cabal","Crypto/3.0.3/Crypto.cabal","HDBC/1.0.1/HDBC.cabal","HDBC-odbc/1.0.1.0/HDBC-odbc.cabal","HDBC-postgresql/1.0.1.0/HDBC-postgresql.cabal"]--They are all normal files--#+begin_src haskell-ng :results output :exports both-(length [x | (Tar.NormalFile x _) <- Tar.entryContent <$> es])-#+end_src--#+RESULTS:-: 317368--*** Maximum file size:--#+begin_src haskell-ng :results output :exports both-(\xs -> filter ((maximum (snd <$> xs) ==) . snd) xs) $ [(fp,x) | (fp, Tar.NormalFile _ x) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]-#+end_src--#+RESULTS:-: [("acme-everything/2018.11.18/acme-everything.cabal",261865)]--*** zero size--#+begin_src haskell-ng :results output :exports both-take 4 $ (\xs -> filter ((0 ==) . snd) xs) $ [(fp,x) | (fp, Tar.NormalFile _ x) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]-#+end_src--#+RESULTS:-: [("lzma/preferred-versions",0),("signal/preferred-versions",0),("peyotls-codec/preferred-versions",0),("th-orphans/preferred-versions",0)]--*** preferred-versions--[[https://hackage.haskell.org/package/Cabal/preferred][Cabal: preferred and deprecated versions | Hackage]]--#+begin_src haskell-ng :results output :exports both-take 3 $ (\xs -> filter ((List.isSuffixOf "preferred-versions") . fst) xs) $ [(fp,bs) | (fp, Tar.NormalFile bs _) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]-#+end_src--#+RESULTS:-: [("ADPfusion/preferred-versions","ADPfusion <0.4.0.0 || >0.4.0.0"),("AesonBson/preferred-versions","AesonBson <0.2.0 || >0.2.0 && <0.2.1 || >0.2.1"),("BiobaseXNA/preferred-versions","BiobaseXNA <0.9.1.0 || >0.9.1.0")]--#+begin_src haskell-ng :results output :exports both-length $ (\xs -> filter ((List.isSuffixOf "preferred-versions") . fst) xs) $ [(fp,bs) | (fp, Tar.NormalFile bs _) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]-#+end_src--#+RESULTS:-: 3376--*** package.json--=package-json= content is a security/signing feature you can read about in [[https://github.com/haskell-ng/hackage-security/blob/master/README.md][hackage-security]].--#+begin_src haskell-ng :results output :exports both-length $ filter ((== "package.json") . filenameFN . runParser_ filenameP . FP.strToUtf8 . fst) $ filter (not . (List.isSuffixOf "preferred-versions") . fst) $ [(fp,bs) | (fp, Tar.NormalFile bs _) <- (\e -> (Tar.entryPath e, Tar.entryContent e)) <$> es]-#+end_src--#+RESULTS:-: 137524--*** cabal files--Unique package/version combinations.--There are multiple versions of package/versions because of revisions. See [[https://github.com/haskell-infra/hackage-trustees/blob/master/revisions-information.md][revisions-information.md]]--Unique =*/*.cabal/version= entries--#+begin_src haskell-ng :results output :exports both-cs <- cabals-length cs-#+end_src--#+RESULTS:-: 137524--Unique cabal packages--#+begin_src haskell-ng :results output :exports both-lcs <- latestCabals-Map.size lcs-#+end_src--#+RESULTS:-: 17631--Average number of versions per package--#+begin_src haskell-ng :results output :exports both-(fromIntegral (length cs)) / fromIntegral (Map.size lcs)-#+end_src--#+RESULTS:-: 7.800124780216664--** latestCabals to CabalFields map--#+begin_src haskell-ng :results output :exports both-lcs <- latestCabals defaultConfig-Map.size lcs-cfg = defaultConfig-lcs' = fmap (second (parseCabalFields cfg)) lcs-Map.size $ Map.filter (snd >>> isLeft) lcs'-:t lcs'-badParse = Map.filter (isLeft . parseCabalFields cfg . snd) lcs-Map.size badParse-#+end_src--#+RESULTS:-: 17631-: 6-: lcs' :: Map.Map ByteString (Version, Either ByteString CabalFields)-: 6-** CabalFields map to dependency graph--#+begin_src haskell-ng :results output :exports both-lcfs <- latestCabalFields-vlds = validLibDeps $ fmap snd lcfs-Map.size vlds-depG = allDepGraph $ fmap snd lcfs-vertexCount depG-edgeCount depG-#+end_src--#+RESULTS:-: 15547-: 15621-: 107566--** algebraic-graphs--An (algebraic) graph of dependencies:--=text= package dependency example--#+begin_src haskell-ng-supers = upstreams "text" depG <> Set.singleton "text"-superG = induce (`elem` (Data.Foldable.toList supers)) depG-#+end_src--#+RESULTS:--#+begin_src haskell-ng :results output :exports both-supers-#+end_src--#+RESULTS:-: fromList ["array","binary","bytestring","deepseq","ghc-prim","template-haskell","text"]---#+begin_src haskell-ng :file other/textdeps.svg :results output graphics file :exports results- baseGraph = defaultGraph & attL GraphType (ID "size") .~ Just (IDQuoted "5!") & attL NodeType (ID "shape") .~ Just (ID "box") & attL NodeType (ID "height") .~ Just (ID 2) & gattL (ID "rankdir") .~ Just (IDQuoted "TB")- g = toDotGraphWith Directed baseGraph superG- processDotWith Directed ["-Tsvg", "-oother/textdeps.svg"] (dotPrint defaultDotConfig g)- BS.writeFile "other/textdeps.dot" (dotPrint defaultDotConfig g)- #+end_src--#+RESULTS:-[[file:other/textdeps.svg]]--** sections-*** section count--#+begin_src haskell-ng :results output :exports both-cfs = lcfs & Map.toList & fmap (snd . snd)-cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection >>> length) & count_-#+end_src--#+RESULTS:-: fromList [(0,359),(1,2559),(2,5508),(3,4730),(4,2224),(5,956),(6,479),(7,236),(8,138),(9,98),(10,63),(11,57),(12,31),(13,32),(14,22),(15,16),(16,12),(17,7),(18,11),(19,8),(20,8),(21,8),(22,4),(23,3),(24,7),(25,4),(26,6),(27,1),(28,1),(29,4),(30,2),(32,4),(33,2),(34,4),(36,1),(37,4),(38,1),(39,2),(40,1),(41,1),(43,2),(47,2),(48,2),(50,1),(65,1),(93,1),(97,1),(295,1)]--*** section types--#+begin_src haskell-ng-cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection) & fmap (fmap (view fieldName')) & mconcat & count_ & Map.toList & List.sortOn (Down . snd)--#+end_src--#+RESULTS:-: [("library",16028),("source-repository",13889),("test-suite",8718),("executable",7292),("flag",4134),("common",2302),("benchmark",1246),("custom-setup",321),("foreign-library",4)]--combinations:--#+begin_src haskell-ng :results output :exports both-cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection) & fmap (fmap (view fieldName')) & fmap (filter (not . (flip List.elem) ["source-repository", "custom-setup", "foreign-library", "flag", "common"])) & fmap (count_ >>> Map.toList >>> List.sortOn fst) & count_ & Map.toList & List.sortOn (Down . snd) & take 10-#+end_src--#+RESULTS:-: [([("library",1)],7291),([("library",1),("test-suite",1)],4195),([("executable",1),("library",1)],1148),([("executable",1)],1105),([("executable",1),("library",1),("test-suite",1)],901),([("benchmark",1),("library",1),("test-suite",1)],520),([("library",1),("test-suite",2)],416),([],359),([("executable",2),("library",1)],163),([("executable",2),("library",1),("test-suite",1)],133)]--at least 1 combinations:--#+begin_src haskell-ng :results output :exports both-cfs & toListOf (each % #fields % fieldList') & fmap (filter isSection) & fmap (fmap (view fieldName')) & fmap (filter (not . (flip List.elem) ["source-repository", "custom-setup", "foreign-library", "flag", "common"])) & fmap (count_ >>> Map.toList >>> fmap fst >>> List.sortOn id) & count_ & Map.toList & List.sortOn (Down . snd) & take 10-#+end_src--#+RESULTS:-: [(["library"],7297),(["library","test-suite"],4778),(["executable","library"],1490),(["executable","library","test-suite"],1309),(["executable"],1263),(["benchmark","library","test-suite"],739),([],359),(["benchmark","executable","library","test-suite"],182),(["executable","test-suite"],119),(["benchmark","library"],59)]--*** section in section--#+begin_src haskell-ng :results output :exports both-sections' = to (filter isSection)--- cfs & fmap (foldOf (#fields % fieldList' % sections' % each % secFields' % sections')) & filter (not . null) & fmap (second (fmap (view fieldName'))) & fmap snd & mconcat & count_-cfs & fmap (foldOf (#fields % fieldList' % sections' % each % secFields' % sections')) & filter (not . null) & fmap ((fmap (view fieldName'))) & mconcat & count_-#+end_src--#+RESULTS:-: fromList [("elif",52),("else",3203),("if",11459),("library",3)]--Embedded libraries are all deprecated.--*** zero-section cfs--Looks like library fields used to be allowed at the top level...--#+begin_src haskell-ng :results output :exports both-cfs0 = cfs & toListOf (each % #fields % fieldList') & filter ((==0) . length . (filter isSection))-length cfs0-count_ $ cfs0 & fmap (foldOf (field' "build-depends") >>> length)-cfs00 = cfs0 & filter (foldOf (field' "build-depends") >>> length >>> (==0))-length cfs00-#+end_src--#+RESULTS:-: 359-: fromList [(0,2),(1,349),(2,7),(4,1)]-: 2--** Dependency counts--package dependency count:--#+begin_src haskell-ng :results output :exports both-lcfs & fmap (snd >>> libDeps >>> fmap dep >>> List.nub >>> length) & Map.toList & List.sortOn (Down . snd) & take 20-#+end_src--#+RESULTS:-: [("acme-everything",7533),("yesod-platform",132),("planet-mitchell",109),("freckle-app",78),("cachix",76),("btc-lsp",71),("too-many-cells",70),("swarm",68),("ghcide",67),("pandoc",67),("sprinkles",65),("pantry-tmp",64),("taffybar",63),("NGLess",60),("project-m36",59),("stack",59),("espial",58),("hermes",58),("purescript",56),("futhark",55)]--dependency count:--#+begin_src haskell-ng :results output :exports both-lcfs & fmap (snd >>> libDeps >>> fmap dep >>> List.nub) & Map.toList & fmap snd & mconcat & count_ & Map.toList & List.sortOn (snd >>> Down) & take 40-#+end_src--#+RESULTS:-: [("base",14883),("bytestring",5384),("text",4972),("containers",4753),("mtl",3468),("transformers",3070),("aeson",2013),("time",1961),("vector",1793),("directory",1597),("filepath",1510),("template-haskell",1472),("unordered-containers",1392),("deepseq",1240),("lens",1173),("hashable",930),("binary",929),("array",892),("exceptions",855),("process",844),("stm",819),("random",811),("http-types",784),("attoparsec",781),("network",756),("parsec",744),("data-default",609),("QuickCheck",597),("conduit",503),("http-client",497),("split",472),("primitive",470),("ghc-prim",456),("async",449),("semigroups",427),("monad-control",424),("scientific",420),("resourcet",401),("unix",398),("utf8-string",392)]--** version ranges--#+begin_src haskell-ng :results output :exports both-cs <- cabals-length cs-#+end_src--#+RESULTS:-: 137323--#+begin_src haskell-ng :results output :exports both-:t cs--mVersions = Map.fromListWith (<>) $ ((\x -> (nameFN x, (:[]) $ (versionInts $ versionFN x))) . fst) <$> cs-Map.size mVersions-#+end_src--#+RESULTS:-: cs :: [(FileName, ByteString)]-: 17631--#+begin_src haskell-ng :results output :exports both-(Just x1) = Map.lookup "chart-svg" mVersions-x1-minimum x1-maximum x1-#+end_src--#+RESULTS:-: [[0,6,0,0],[0,5,2,0],[0,5,1,1],[0,5,1,0],[0,5,0,0],[0,4,1,1],[0,4,1,0],[0,4,0],[0,3,3],[0,3,2],[0,3,1],[0,3,0],[0,2,3],[0,2,2],[0,2,1],[0,2,0],[0,1,3],[0,1,2],[0,1,1],[0,1,0],[0,0,3],[0,0,2],[0,0,1]]-: [0,0,1]-: [0,6,0,0]--*** all versions are unique?--#+begin_src haskell-ng :results output :exports both-take 10 $ Map.toList $ Map.filter (\a -> length a /= length (List.nub a)) mVersions-#+end_src--#+RESULTS:-: []--*** Version counts--#+begin_src haskell-ng :results output :exports both-take 10 $ List.sortOn (Down . snd) $ Map.toList $ Map.map length mVersions-#+end_src--#+RESULTS:-: [("haskoin-store",298),("git-annex",282),("hlint",221),("yesod-core",216),("purescript",204),("warp",204),("pandoc",193),("hakyll",192),("egison",190),("persistent",186)]--* Field re-ordering--#+begin_src haskell-ng :results output :exports both-zipWith (\o l -> (fst l, o)) [0..] (List.sortOn snd $ fieldOrdering defaultConfig)-#+end_src--#+RESULTS:-: [("cabal-version",0),("import",1),("main-is",2),("default-language",3),("name",4),("hs-source-dirs",5),("version",6),("build-depends",7),("exposed-modules",8),("license",9),("license-file",10),("other-modules",11),("copyright",12),("category",13),("author",14),("default-extensions",15),("ghc-options",16),("maintainer",17),("homepage",18),("bug-reports",19),("synopsis",20),("description",21),("build-type",22),("tested-with",23),("extra-doc-files",24),("source-repository",25),("type",26),("common",27),("location",28),("library",29),("executable",30),("test-suite",31)]--* references--[[https://hackage.haskell.org/package/Cabal-syntax-3.10.2.0/docs/Distribution-Fields-Field.html][Distribution.Fields.Field]]--[[https://hackage.haskell.org/package/optics-core-0.4.1.1][optics-core: Optics as an abstract interface: core definitions]]--[[https://cabal.readthedocs.io/en/stable/cabal-package.html#package-descriptions][6. Package Description — Cabal 3.10.1.0 User's Guide]]
src/CabalFix.hs view
@@ -1,15 +1,13 @@ {-# LANGUAGE DataKinds #-}-{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -Wno-orphans #-}-{-# OPTIONS_GHC -Wno-unused-matches #-}+{-# OPTIONS_GHC -Wno-x-partial #-} --- | Tools to print, parse and fix cabal files, 'ByteString' and 'Field' lists.+-- | Tools to print, parse and fix cabal files. module CabalFix   ( -- * Usage     -- $usage@@ -104,6 +102,7 @@ -- -- >>> :set -XOverloadedStrings -- >>> :set -XOverloadedLabels+-- >>> :set -Wno-x-partial -- >>> import CabalFix -- >>> import Optics.Extra -- >>> import Data.ByteString.Char8 qualified as C@@ -374,10 +373,10 @@ fieldSet :: FieldName -> CabalFields -> Maybe (Field Comment) -> CabalFields fieldSet name cf f =   case V.findIndex ((== name) . getName . fieldName) (view #fields cf) of-    Just i -> case f of-      Nothing -> cf & over #fields (\v -> V.take i v <> V.drop (i + 1) v)-      Just f' -> cf & over #fields (\v -> V.take i v <> V.singleton f' <> V.drop (i + 1) v)-    Nothing -> cf & maybe id (over #fields . (\f -> (<> V.singleton f))) f+    Just i' -> case f of+      Nothing -> cf & over #fields (\v -> V.take i' v <> V.drop (i' + 1) v)+      Just f' -> cf & over #fields (\v -> V.take i' v <> V.singleton f' <> V.drop (i' + 1) v)+    Nothing -> cf & maybe id (over #fields . (\x -> (<> V.singleton x))) f  -- | A lens by name into a field (but not a section). --@@ -633,7 +632,7 @@ -- >>> fs & toListOf (section' "test-suite" % each % secFields' % field' "build-depends" % each) & fmap (fixBuildDeps cfg "minimal") -- [Field (Name [] "build-depends") [FieldLine [] ", base    >=4.14 && <5",FieldLine [] ", minimal"]] fixBuildDeps :: Config -> FieldName -> Field ann -> Field ann-fixBuildDeps cfg pname f = overField (bool id (over fieldLines' (fixBDLines cfg pname)) (isName "build-depends" f)) f+fixBuildDeps cfg pn f = overField (bool id (over fieldLines' (fixBDLines cfg pn)) (isName "build-depends" f)) f  fixBDLines :: Config -> ByteString -> [FieldLine ann] -> [FieldLine ann] fixBDLines cfg libdep fls = fls'@@ -725,7 +724,7 @@   PP.vcat $     mconcat $       [ PP.text . fromUTF8BS <$> cs <> [bs]-        | FieldLine cs bs <- fls+      | FieldLine cs bs <- fls       ]  -- | Used in 'fromParsecFields'.@@ -856,7 +855,7 @@ parseCabalFields cfg bs = case readFields bs of   Left err -> Left $ C.pack (show err)   Right fps ->-    (\(fs, ec) -> Right (CabalFields (V.fromList fs) ec)) $+    (\(fs', ec) -> Right (CabalFields (V.fromList fs') ec)) $       foldl' (&) (fmap (fmap (const [])) fs, []) (uncurry addComment <$> cfs)     where       fs = convertFreeTexts (view #freeTexts cfg) fps@@ -925,7 +924,7 @@ makePositionTree :: [Field Position] -> Map.Map Int ([Int], String) makePositionTree fs = foldFss Map.empty [] fs   where-    foldFss m cursor fs = fst $ foldl' stepFss (m, cursor <> [0]) fs+    foldFss m cursor fs' = fst $ foldl' stepFss (m, cursor <> [0]) fs'     stepFss (m, cursor) (Field (Name (Position c _) _) fls) =       (foldFls (Map.insertWith (\_ o -> o) c (cursor, "fieldname") m) cursor fls, inc cursor)     stepFss (m, cursor) (Section (Name (Position c _) _) sas fss) =@@ -944,7 +943,7 @@ addComment (Just (cursor, tag)) cs (fs, extras) = (addc cs cursor tag fs, extras)  addc :: [ByteString] -> [Int] -> String -> [Field [ByteString]] -> [Field [ByteString]]-addc comments [] _ fs = fs+addc _ [] _ fs = fs addc comments [x] "fieldname" fs = take x fs <> [f'] <> drop (x + 1) fs   where     (Field (Name cs n) fls) = (List.!!) fs x
src/CabalFix/Archive.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -Wno-name-shadowing #-}  -- | Archive investigation module CabalFix.Archive where@@ -83,13 +82,13 @@   where     libFields = cf & foldOf (#fields % fieldList' % section' "library" % each % secFields')     libBds = libFields & foldOf (fieldValues' "build-depends")-    libDeps = runParser_ (FP.many depP) libBds+    libDeps' = runParser_ (FP.many depP) libBds     libImports = libFields & toListOf (fieldValues' "import")     cs = cf & foldOf (#fields % fieldList' % section' "common")     libCommons = cs & filter (all (`elem` libImports) . toListOf (secArgs' % each % secArgBS' % _2))     commonsBds = libCommons & foldOf (fieldValues' "build-depends")     commonsDeps = runParser_ (FP.many depP) commonsBds-    deps = fmap (uncurry Dep) (libDeps <> commonsDeps)+    deps = fmap (uncurry Dep) (libDeps' <> commonsDeps)  -- | Map of valid dependencies validLibDeps :: Map.Map ByteString CabalFields -> Map.Map ByteString [ByteString]
+ test/doctests.hs view
@@ -0,0 +1,6 @@+import System.Environment (getArgs)+import Test.DocTest (mainFromCabal)+import Prelude (IO, (=<<))++main :: IO ()+main = mainFromCabal "cabal-fix" =<< getArgs