cabal-flatpak 0.1.0.4 → 0.1.1
raw patch · 6 files changed
+150/−43 lines, 6 filesdep +yamldep ~aesondep ~containersdep ~optparse-applicative
Dependencies added: yaml
Dependency ranges changed: aeson, containers, optparse-applicative, tar, text
Files
- Changes.md +5/−0
- ReadMe.md +8/−1
- cabal-flatpak.cabal +9/−8
- src/Flatpak.hs +71/−8
- src/Generate.hs +34/−22
- src/Main.hs +23/−4
Changes.md view
@@ -1,5 +1,10 @@ # Change log for the `cabal-flatpak` package +## 0.1.1++ * reduce file size of manifests using inlined script `revise.sh`.++ ## 0.1 * No need to specify `main-sources` field anymore.
ReadMe.md view
@@ -10,7 +10,7 @@ However, for maintaining your own Flatpak repository or for distributing Flatpak bundles it is not strictly necessary to have such a verbose manifest.-You may get results quicker by writing you own manifest+You may get results quicker by writing your own manifest which simply runs `cabal-install` or `stack`. `stack` should give you reproducible builds but its package database contains only a subset of Hackage.@@ -101,6 +101,13 @@ ### Build the Flatpak package++First you need to install a Flatpak runtime environment and its SDK:++~~~~+$ sudo flatpak install org.freedesktop.Platform org.freedesktop.Sdk org.freedesktop.Platform.Compat.i386 org.freedesktop.Sdk.Compat.i386+~~~~+ You may refer to the `Makefile` that is shipped with `cabal-flatpak` for how to eventually build the Flatpak package.
cabal-flatpak.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: 2.2 Name: cabal-flatpak-Version: 0.1.0.4+Version: 0.1.1 License: BSD-3-Clause License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -25,7 +25,7 @@ flatpak.cabal.json Source-Repository this- Tag: 0.1.0.4+ Tag: 0.1.1 Type: darcs Location: https://hub.darcs.net/thielema/cabal-flatpak/ @@ -38,18 +38,19 @@ cabal-plan >=0.5 && <0.8, cryptohash-sha256 >=0.11 && <0.12, zlib >=0.5.3 && <0.7,- tar >=0.5.0.3 && <0.6,+ tar >=0.5.0.3 && <0.7, http-client-tls >=0.3.5.3 && <0.4, http-client >=0.5.12 && <0.8, http-types >=0.10 && <0.13, aeson-pretty >=0.8.7 && <0.9,- aeson >=2.0 && <2.2,+ aeson >=2.0 && <2.3,+ yaml >=0.11 && <0.12, shell-utility >=0.1 && <0.2,- optparse-applicative >=0.11 && <0.18,+ optparse-applicative >=0.11 && <0.19, pathtype >=0.8 && <0.9,- text >=1.2 && <2.1,- bytestring >=0.9 && <0.12,- containers >=0.5 && <0.7,+ text >=1.2 && <2.2,+ bytestring >=0.9 && <0.13,+ containers >=0.5 && <0.8, utility-ht >=0.0.12 && <0.1, base >=4.5 && <5
src/Flatpak.hs view
@@ -2,6 +2,7 @@ SourceType(..), archive, Source(..), Module(..), ModuleItem(ModuleEmbed), Arch(..), archString, T(..),+ encodeJson, encodeYaml, PackageHook(..), Cabal(..), ) where @@ -9,13 +10,21 @@ import qualified Cabal.Plan as Plan +import qualified Data.Yaml.Pretty as YamlPretty+import qualified Data.Aeson.Encode.Pretty as JsonPretty import qualified Data.Aeson.Key as Key import qualified Data.Aeson as Json+import qualified Data.Map as Map+import qualified Data.ByteString.Lazy.Char8 as BL+import qualified Data.ByteString.Char8 as B import qualified Data.Text as Text import qualified Data.Monoid.HT as Mn import Data.Aeson.Types (Parser, Pair) import Data.Aeson (FromJSON, ToJSON, parseJSON, toJSON, withObject)+import Data.Map (Map)+import Data.Text (Text) import Data.Tuple.HT (mapFst)+import Data.Ord.HT (comparing) import Data.Monoid (mempty, (<>)) import Text.Printf (printf)@@ -64,7 +73,8 @@ data SourceType = Archive {stripComponents :: Int} |- Git | Bzr | File | Dir | Script | Shell | Patch | ExtraData+ Git | Bzr | File | Dir |+ Script {commands :: [String]} | Shell | Patch | ExtraData deriving (Show) archive :: SourceType@@ -78,11 +88,13 @@ Archive {stripComponents = strip} -> simple "archive" ++ Mn.when (strip/=1) [("strip-components", Json.toJSON strip)]+ Script {commands = cmds} ->+ simple "script" +++ [("commands", Json.toJSON cmds)] Git -> simple "git" Bzr -> simple "bzr" File -> simple "file" Dir -> simple "dir"- Script -> simple "script" Shell -> simple "shell" Patch -> simple "patch" ExtraData -> simple "extra-data"@@ -95,7 +107,7 @@ "bzr" -> return Bzr "file" -> return File "dir" -> return Dir- "script" -> return Script+ "script" -> fmap Script $ obj .: "commands" "shell" -> return Shell "patch" -> return Patch "extra-data" -> return ExtraData@@ -104,7 +116,7 @@ data Source = Source { typ :: SourceType,- url :: String,+ url :: Maybe String, destFilename :: Maybe String, sha256 :: Maybe Plan.Sha256 } deriving (Show)@@ -113,16 +125,16 @@ parseJSON = withObject "Source" $ \o -> pure Source <*> (parseSourceType o =<< o .: "type")- <*> o .: "url"+ <*> o .:? ("url", Nothing) <*> o .:? ("dest-filename", Nothing)- <*> o .: "sha256"+ <*> o .:? ("sha256", Nothing) instance ToJSON Source where toJSON = object $ (pairsFromSourceType . typ) <>- ("url" .= url) <>+ ("url" .=? (url, Nothing)) <> ("dest-filename" .=? (destFilename, Nothing)) <>- ("sha256" .= sha256) <>+ ("sha256" .=? (sha256, Nothing)) <> mempty @@ -238,6 +250,57 @@ ("cleanup-commands" .=* cleanupCommands) <> ("modules" .= modules) <> mempty+++{- |+A global ordering of all fields (across all substructures)+appearing in the manifest JSON.+This is an ugly hack, but is needed for get pretty JSON and also field ordering.+See <https://github.com/informatikr/aeson-pretty/issues/28>.+-}+fieldOrder :: [Text]+fieldOrder =+ map Text.pack $+ "app-id" :+ "name" :+ "runtime" :+ "runtime-version" :+ "sdk" :+ "only-arches" :+ "command" :+ "finish-args" :+ "cleanup" :+ "cleanup-commands" :+ "main-package" :+ "main-sources" :+ "package-hooks" :+ "modules" :+ "buildsystem" :+ "builddir" :+ "config-opts" :+ "build-commands" :+ "post-install" :+ "sources" :+ []++encodeJson :: T -> BL.ByteString+encodeJson =+ JsonPretty.encodePretty'+ JsonPretty.defConfig {+ JsonPretty.confCompare = JsonPretty.keyOrder fieldOrder+ }+++fieldOrderMap :: Map Text Int+fieldOrderMap =+ Map.fromListWith (error "duplicate key") $ zip fieldOrder [0..]++encodeYaml :: T -> B.ByteString+encodeYaml =+ YamlPretty.encodePretty $+ YamlPretty.setConfCompare+ (comparing $ \k -> maybe (Right k) Left $ Map.lookup k fieldOrderMap) $+ YamlPretty.defConfig
src/Generate.hs view
@@ -105,7 +105,9 @@ Flatpak.sources = [Flatpak.Source { Flatpak.typ = Flatpak.archive,- Flatpak.url = ghcDirUrl compiler ++ ghcArchive compiler arch,+ Flatpak.url =+ Just $+ ghcDirUrl compiler ++ ghcArchive compiler arch, Flatpak.destFilename = Nothing, Flatpak.sha256 = Just hash }]@@ -132,13 +134,13 @@ in if' (matchName mainPkg pkg && not (null mainSrcs)) mainSrcs $ Flatpak.Source { Flatpak.typ = typ,- Flatpak.url = pkgUrl,+ Flatpak.url = Just pkgUrl, Flatpak.destFilename = Nothing, Flatpak.sha256 = Plan.uSha256 pkg } : Flatpak.Source { Flatpak.typ = Flatpak.File,- Flatpak.url = cabalUrl,+ Flatpak.url = Just cabalUrl, Flatpak.destFilename = Just cabalPath, Flatpak.sha256 = Plan.uCabalSha256 pkg } :@@ -233,47 +235,57 @@ [Flatpak.Source { Flatpak.typ = Flatpak.Archive {Flatpak.stripComponents = 0},- Flatpak.url = url,+ Flatpak.url = Just url, Flatpak.destFilename = Nothing, Flatpak.sha256 = Plan.parseSha256 . Text.pack =<< cabalHash arch }] } +reviseCabalScript :: [String]+reviseCabalScript =+ "tarball=\"$1\"" :+ "pkgid=\"$(basename $tarball .tar.gz)\"" :+ "name=\"$(echo $pkgid | sed -r 's:^(.*)-(.*)$:\\1:')\"" :+ "ver=\"$(echo $pkgid | sed -r 's:^(.*)-(.*)$:\\2:')\"" :+ "echo Revising .cabal file for \"$pkgid\"" :+ "gunzip \"$tarball\"" :+ "mkdir \"$pkgid\"" :+ "mv \"$name.cabal\" \"$pkgid\"" :+ "tar rf \"$pkgid.tar\" \"$pkgid/$name.cabal\"" :+ "gzip \"$pkgid.tar\"" :+ []+ allInOneModule :: (String, [Flatpak.Source]) -> Options -> [String] -> [RevisedUnit] -> Flatpak.Module allInOneModule main (cabalCfgOptions, ghcOptions) postInstall pkgs =- let dispPkgId = Text.unpack . Plan.dispPkgId- in Flatpak.Module {+ Flatpak.Module { Flatpak.name = "haskell-parts", Flatpak.onlyArches = [], Flatpak.buildsystem = "simple", Flatpak.builddir = False, Flatpak.configOpts = [], Flatpak.buildCommands =- (flip concatMap pkgs $ \(pkg,_) ->- let pkgId@(Plan.PkgId (Plan.PkgName name) _version) =- Plan.uPId pkg- pkgName = Text.unpack name- pkgVerName = dispPkgId pkgId- in printf "gunzip %s.tar.gz" pkgVerName :- printf "mkdir %s" pkgVerName :- printf "mv %s.cabal %s/" pkgName pkgVerName :- printf "tar rf %s.tar %s/%s.cabal"- pkgVerName pkgVerName pkgName :- printf "gzip %s.tar" pkgVerName :- []) +++ "find . -name '*.tar.gz' | while read pkg; do bash revise.sh \"$pkg\"; done" : "mkdir .cabal" : "touch .cabal/config" : unwords ("cabal --config-file=.cabal/config install" :- "--offline --prefix=/app" :- cabalCfgOptions ++ map ghcOption ghcOptions ++- map (printf "%s.tar.gz" . dispPkgId . Plan.uPId . fst) pkgs) :+ "-j$FLATPAK_BUILDER_N_JOBS" :+ "--offline --prefix=/app" :+ cabalCfgOptions ++ map ghcOption ghcOptions +++ ["*.tar.gz"]) : [], Flatpak.postInstall = postInstall, Flatpak.cleanupModule = [],- Flatpak.sources = concatMap (packageSources main Flatpak.File) pkgs+ Flatpak.sources =+ Flatpak.Source {+ Flatpak.typ = Flatpak.Script reviseCabalScript,+ Flatpak.url = Nothing,+ Flatpak.destFilename = Just "revise.sh",+ Flatpak.sha256 = Nothing+ } :+ concatMap (packageSources main Flatpak.File) pkgs } manifestCabalInstall ::
src/Main.hs view
@@ -21,10 +21,12 @@ import Control.Monad (when) import Control.Applicative ((<$>)) -import qualified Data.Aeson.Encode.Pretty as JsonPretty import qualified Data.Aeson as Json+import qualified Data.Yaml as Yaml import qualified Data.ByteString.Lazy.Char8 as BL+import qualified Data.ByteString.Char8 as B import qualified Data.Text as Text+import qualified Data.Char as Char import qualified Data.Traversable as Trav import qualified Data.Graph as Graph import qualified Data.Map as Map@@ -46,6 +48,14 @@ Map.mapWithKey (\k v -> ((), k, Set.toList v)) m +isYamlFileName :: Path.File absRel -> Bool+isYamlFileName path =+ case map Char.toLower $ Path.takeExtension path of+ ".yaml" -> True+ ".yml" -> True+ _ -> False++ main :: IO () main = do opt <- OP.execParser Option.info@@ -56,8 +66,11 @@ Plan.findAndDecodePlanJson $ Plan.ProjectRelativeToDir $ Path.toString dir projectJson <-- either fail return . Json.eitherDecode- =<< (BL.readFile $ Path.toString $ Option.input opt)+ if isYamlFileName $ Option.input opt+ then Yaml.decodeFileThrow (Path.toString $ Option.input opt)+ else+ either fail return =<<+ (Json.eitherDecodeFileStrict' $ Path.toString $ Option.input opt) {- Only cabal >= 2.4.1.0 provides the SHA 256 hashes.@@ -142,7 +155,13 @@ "units without a SHA256 hash for the Cabal file:" : map (Text.unpack . Plan.dispPkgId . Plan.uPId) unrevisedUnits - BL.writeFile (Path.toString $ Option.output opt) $ JsonPretty.encodePretty $+ (if isYamlFileName $ Option.output opt+ then+ B.writeFile (Path.toString $ Option.output opt) .+ Flatpak.encodeYaml+ else+ BL.writeFile (Path.toString $ Option.output opt) .+ Flatpak.encodeJson) $ if Option.cabalInstall opt then Generate.manifestCabalInstall plan archHashes revisedUnits projectJson