summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Thompson <will@willthompson.co.uk>2015-08-18 12:18:05 +0100
committerWill Thompson <will@willthompson.co.uk>2015-08-18 12:18:05 +0100
commit62bacb9fa9ce65c33c0bf14d45a2d619d2e108b9 (patch)
tree6e9222dd924d00cb622c7ac8cb306419d032d5c3
parent95ac81a922c47ef3bdee61a9d989d6648ce90e5c (diff)
parent0328a9024234c8fab001726786c09b5f9b36192e (diff)
Merge branch 'no-cabal-hgettext'
https://github.com/fpco/stackage/issues/746
-rw-r--r--.travis.yml103
-rw-r--r--GetText.hs220
-rw-r--r--Makefile3
-rw-r--r--Setup.hs31
-rw-r--r--bustle.cabal15
-rwxr-xr-xmake_travis_yml.hs228
6 files changed, 534 insertions, 66 deletions
diff --git a/.travis.yml b/.travis.yml
index aa8be2f..8b3e518 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,58 +1,79 @@
+# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
+language: c
sudo: false
-env:
-# TODO
-# # Fedora 22:
-# - CABALVER=1.18 GHCVER=7.8.4
-# 7.10.2 is not in the magic whitelist at https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
- - CABALVER=1.22 GHCVER=7.10.1
- - CABALVER=head GHCVER=head
+cache:
+ directories:
+ - $HOME/.cabsnap
+ - $HOME/.cabal/packages
+
+before_cache:
+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
matrix:
- allow_failures:
- - env: CABALVER=head GHCVER=head
-
-addons:
- apt:
- sources:
- # https://github.com/hvr/multi-ghc-travis
- - hvr-ghc
- packages:
- - libpcap-dev
- - libgtk-3-dev
- - libcairo2-dev
- # GAH: I guess env does not have any effect here
- - cabal-install-1.18
- - cabal-install-1.22
- - cabal-install-head
- - ghc-7.8.4
- - ghc-7.10.1
- - ghc-head
- # /GAH
- - happy-1.19.4
- - alex-3.1.3
+ include:
+ - env: CABALVER=1.18 GHCVER=7.8.4
+ compiler: ": #GHC 7.8.4"
+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4,libpcap-dev,libgtk-3-dev,libcairo2-dev,happy-1.19.4,alex-3.1.3], sources: [hvr-ghc]}}
+ - env: CABALVER=1.22 GHCVER=7.10.1
+ compiler: ": #GHC 7.10.1"
+ addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.1,libpcap-dev,libgtk-3-dev,libcairo2-dev,happy-1.19.4,alex-3.1.3], sources: [hvr-ghc]}}
+
+before_install:
+ - unset CC
+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:/opt/happy/1.19.4/bin:/opt/alex/3.1.3/bin:$PATH
install:
-# Is it just not installing those packages?
- - dpkg -l
- - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:/opt/alex/3.1.3/bin:/opt/happy/1.19.4/bin:$PATH
- cabal --version
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
- - ghc-pkg list Cabal
- - travis_retry cabal update
-# TODO: shouldn't gtk3 et al build-depend on this? Should we do so?
- - cabal install gtk2hs-buildtools
- - cabal install --only-dependencies --enable-tests --enable-benchmarks
+ - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
+ then
+ zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
+ $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
+ fi
+ - travis_retry cabal update -v
+ - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
+ - cabal install --only-dependencies --enable-tests --enable-benchmarks --flags='WithGtk2HsBuildTools' --dry -v > installplan.txt
+ - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
+
+# check whether current requested install-plan matches cached package-db snapshot
+ - if diff -u installplan.txt $HOME/.cabsnap/installplan.txt;
+ then
+ echo "cabal build-cache HIT";
+ rm -rfv .ghc;
+ cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
+ cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
+ else
+ echo "cabal build-cache MISS";
+ rm -rf $HOME/.cabsnap;
+ mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
+ travis_retry cabal install --only-dependencies --enable-tests --enable-benchmarks --flags='WithGtk2HsBuildTools';
+ fi
+
+# snapshot package-db on cache miss
+ - if [ ! -d $HOME/.cabsnap ];
+ then
+ echo "snapshotting package-db to build-cache";
+ mkdir $HOME/.cabsnap;
+ cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
+ cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
+ fi
+# Here starts the actual work to be performed for the package under test;
+# any command which exits with a non-zero exit code causes the build to fail.
script:
- if [ -f configure.ac ]; then autoreconf -i; fi
- - cabal configure --enable-tests --enable-benchmarks -v2
- - cabal build
- - cabal test
+ - cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging
+ - cabal build # this builds all libraries and executables (including tests/benchmarks)
+ - ./dist/setup/setup test
- cabal check
- - cabal sdist
+ - cabal sdist # tests that a source-distribution can be generated
+
# Check that the resulting source distribution can be built & installed.
# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
# `cabal install --force-reinstalls dist/*-*.tar.gz`
- SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
(cd dist && cabal install --force-reinstalls "$SRC_TGZ")
+
+# EOF
diff --git a/GetText.hs b/GetText.hs
new file mode 100644
index 0000000..1c27146
--- /dev/null
+++ b/GetText.hs
@@ -0,0 +1,220 @@
+-- | This library extends the Distribution with internationalization support.
+--
+-- It performs two functions:
+--
+-- * compiles and installs PO files to the specified directory
+--
+-- * tells the application where files were installed to make it able
+-- to bind them to the code
+--
+-- Each PO file will be placed to the
+-- @{datadir}\/locale\/{loc}\/LC_MESSAGES\/{domain}.mo@ where:
+--
+-- [@datadir@] Usually @prefix/share@ but could be different, depends
+-- on system.
+--
+-- [@loc@] Locale name (language code, two characters). This module
+-- supposes, that each PO file has a base name set to the proper
+-- locale, e.g. @de.po@ is the German translation of the program, so
+-- this file will be placed under @{datadir}\/locale\/de@ directory
+--
+-- [@domain@] Program domain. A unique identifier of single
+-- translational unit (program). By default domain will be set to the
+-- package name, but its name could be configured in the @.cabal@ file.
+--
+-- The module defines following @.cabal@ fields:
+--
+-- [@x-gettext-domain-name@] Name of the domain. One ofmore
+-- alphanumeric characters separated by hyphens or underlines. When
+-- not set, package name will be used.
+--
+-- [@x-gettext-po-files@] List of files with translations. Could be
+-- used a limited form of wildcards, e.g.: @x-gettext-po-files:
+-- po/*.po@
+--
+-- [@x-gettext-domain-def@] Name of the macro, in which domain name
+-- will be passed to the program. Default value is
+-- @__MESSAGE_CATALOG_DOMAIN__@
+--
+-- [@x-gettext-msg-cat-def@] Name of the macro, in which path to the
+-- message catalog will be passed to the program. Default value is
+-- @__MESSAGE_CATALOG_DIR__@
+--
+-- The last two parameters are used to send configuration data to the
+-- code during its compilation. The most common usage example is:
+--
+--
+-- > ...
+-- > prepareI18N = do
+-- > setLocale LC_ALL (Just "")
+-- > bindTextDomain __MESSAGE_CATALOG_DOMAIN__ (Just __MESSAGE_CATALOG_DIR__)
+-- > textDomain __MESSAGE_CATALOG_DOMAIN__
+-- >
+-- > main = do
+-- > prepareI18N
+-- > ...
+-- >
+-- > ...
+--
+--
+-- /NOTE:/ files, passed in the @x-gettext-po-files@ are not
+-- automatically added to the source distribution, so they should be
+-- also added to the @extra-source-files@ parameter, along with
+-- translation template file (usually @message.pot@)
+--
+-- /WARNING:/ sometimes, when only configuration targets changes, code
+-- will not recompile, thus you should execute @cabal clean@ to
+-- cleanup the build and restart it again from the configuration. This
+-- is temporary bug, it will be fixed in next releases.
+--
+-- /TODO:/ this is lifted verbatim (modulo other /TODO/s) from hgettext's
+-- Distribution.Simple.I18N.GetText partly to expose individual hooks and
+-- partly to avoid the /cabal configure/-time dependency. For the latter,
+-- see https://github.com/fpco/stackage/issues/746
+--
+
+module GetText
+ (
+ -- | /TODO:/ upstream exporting the individual hooks?
+ installPOFiles,
+
+ -- | /TODO:/ upstream generating GetText_foo.hs rather than exporting these?
+ getDomainNameDefault,
+ getPackageName,
+ targetDataDir,
+
+ installGetTextHooks,
+ gettextDefaultMain
+ ) where
+
+import Distribution.Simple
+import Distribution.Simple.Setup as S
+import Distribution.Simple.LocalBuildInfo
+import Distribution.PackageDescription
+import Distribution.Simple.Configure
+import Distribution.Simple.InstallDirs as I
+import Distribution.Simple.Utils
+
+import Language.Haskell.Extension
+
+import Control.Monad
+import Control.Arrow (second)
+import Data.Maybe (listToMaybe, maybeToList, fromMaybe)
+import Data.List (unfoldr,nub,null)
+import System.FilePath
+import System.Directory
+import System.Process
+
+-- | Default main function, same as
+--
+-- > defaultMainWithHooks $ installGetTextHooks simpleUserHooks
+--
+gettextDefaultMain :: IO ()
+gettextDefaultMain = defaultMainWithHooks $ installGetTextHooks simpleUserHooks
+
+-- | Installs hooks, used by GetText module to install
+-- PO files to the system. Previous won't be disabled
+--
+installGetTextHooks :: UserHooks -- ^ initial user hooks
+ -> UserHooks -- ^ patched user hooks
+installGetTextHooks uh = uh{
+ confHook = \a b ->
+ (confHook uh) a b >>=
+ return . updateLocalBuildInfo,
+
+ postInst = \a b c d ->
+ (postInst uh) a b c d >>
+ installPOFiles a b c d
+ }
+
+
+updateLocalBuildInfo :: LocalBuildInfo -> LocalBuildInfo
+updateLocalBuildInfo l =
+ let sMap = getCustomFields l
+ [domDef, catDef] = map ($ sMap) [getDomainDefine, getMsgCatalogDefine]
+ dom = getDomainNameDefault sMap (getPackageName l)
+ tar = targetDataDir l
+ [catMS, domMS] = map (uncurry formatMacro) [(domDef, dom), (catDef, tar)]
+ in (appendCPPOptions [domMS,catMS] . appendExtension [EnableExtension CPP]) l
+
+installPOFiles :: Args -> InstallFlags -> PackageDescription -> LocalBuildInfo -> IO ()
+installPOFiles _ _ _ l =
+ let sMap = getCustomFields l
+ destDir = targetDataDir l
+ dom = getDomainNameDefault sMap (getPackageName l)
+ installFile file = do
+ let fname = takeFileName file
+ let bname = takeBaseName fname
+ let targetDir = destDir </> bname </> "LC_MESSAGES"
+ -- ensure we have directory destDir/{loc}/LC_MESSAGES
+ createDirectoryIfMissing True targetDir
+ system $ "msgfmt --output-file=" ++
+ (targetDir </> dom <.> "mo") ++
+ " " ++ file
+ in do
+ filelist <- getPoFilesDefault sMap
+ -- copy all whose name is in the form of dir/{loc}.po to the
+ -- destDir/{loc}/LC_MESSAGES/dom.mo
+ -- with the 'msgfmt' tool
+ mapM_ installFile filelist
+
+forBuildInfo :: LocalBuildInfo -> (BuildInfo -> BuildInfo) -> LocalBuildInfo
+forBuildInfo l f =
+ let a = l{localPkgDescr = updPkgDescr (localPkgDescr l)}
+ updPkgDescr x = x{library = updLibrary (library x),
+ executables = updExecs (executables x)}
+ updLibrary Nothing = Nothing
+ updLibrary (Just x) = Just $ x{libBuildInfo = f (libBuildInfo x)}
+ updExecs x = map updExec x
+ updExec x = x{buildInfo = f (buildInfo x)}
+ in a
+
+appendExtension :: [Extension] -> LocalBuildInfo -> LocalBuildInfo
+appendExtension exts l =
+ forBuildInfo l updBuildInfo
+ where updBuildInfo x = x{defaultExtensions = updExts (defaultExtensions x)}
+ updExts s = nub (s ++ exts)
+
+appendCPPOptions :: [String] -> LocalBuildInfo -> LocalBuildInfo
+appendCPPOptions opts l =
+ forBuildInfo l updBuildInfo
+ where updBuildInfo x = x{cppOptions = updOpts (cppOptions x)}
+ updOpts s = nub (s ++ opts)
+
+formatMacro name value = "-D" ++ name ++ "=" ++ (show value)
+
+targetDataDir :: LocalBuildInfo -> FilePath
+targetDataDir l =
+ let dirTmpls = installDirTemplates l
+ prefix' = prefix dirTmpls
+ data' = datadir dirTmpls
+ dataEx = I.fromPathTemplate $ I.substPathTemplate [(PrefixVar, prefix')] data'
+ in dataEx ++ "/locale"
+
+getPackageName :: LocalBuildInfo -> String
+getPackageName = fromPackageName . packageName . localPkgDescr
+ where fromPackageName (PackageName s) = s
+
+getCustomFields :: LocalBuildInfo -> [(String, String)]
+getCustomFields = customFieldsPD . localPkgDescr
+
+findInParametersDefault :: [(String, String)] -> String -> String -> String
+findInParametersDefault al name def = (fromMaybe def . lookup name) al
+
+getDomainNameDefault :: [(String, String)] -> String -> String
+getDomainNameDefault al d = findInParametersDefault al "x-gettext-domain-name" d
+
+getDomainDefine :: [(String, String)] -> String
+getDomainDefine al = findInParametersDefault al "x-gettext-domain-def" "__MESSAGE_CATALOG_DOMAIN__"
+
+getMsgCatalogDefine :: [(String, String)] -> String
+getMsgCatalogDefine al = findInParametersDefault al "x-gettext-msg-cat-def" "__MESSAGE_CATALOG_DIR__"
+
+getPoFilesDefault :: [(String, String)] -> IO [String]
+getPoFilesDefault al = toFileList $ findInParametersDefault al "x-gettext-po-files" ""
+ where toFileList "" = return []
+ toFileList x = liftM concat $ mapM matchFileGlob $ split' x
+ -- from Blow your mind (HaskellWiki)
+ -- splits string by newline, space and comma
+ split' x = concatMap lines $ concatMap words $ unfoldr (\b -> fmap (const . (second $ drop 1) . break (==',') $ b) . listToMaybe $ b) x
+
diff --git a/Makefile b/Makefile
index 12ba32a..459b2f2 100644
--- a/Makefile
+++ b/Makefile
@@ -123,3 +123,6 @@ maintainer-make-release: bustle.cabal dist/build/autogen/version.txt
git tag -s -m 'Bustle '`cat dist/build/autogen/version.txt` \
bustle-`cat dist/build/autogen/version.txt`
make maintainer-binary-tarball
+
+.travis.yml: bustle.cabal make_travis_yml.hs
+ ./make_travis_yml.hs $< libpcap-dev libgtk-3-dev libcairo2-dev happy-1.19.4 alex-3.1.3 > $@
diff --git a/Setup.hs b/Setup.hs
index 9ab758c..d067f7e 100644
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,11 +1,9 @@
{-# OPTIONS_GHC -Wall #-}
-import Data.Maybe (fromMaybe)
import System.FilePath ( (</>), (<.>) )
import Distribution.PackageDescription
import Distribution.Simple
import Distribution.Simple.BuildPaths ( autogenModulesDir )
-import Distribution.Simple.InstallDirs as I
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Setup as S
import Distribution.Simple.Utils
@@ -14,7 +12,7 @@ import Distribution.Text ( display )
import Distribution.ModuleName (ModuleName)
import qualified Distribution.ModuleName as ModuleName
-import qualified Distribution.Simple.I18N.GetText as GetText
+import qualified GetText as GetText
main :: IO ()
main = defaultMainWithHooks $ installBustleHooks simpleUserHooks
@@ -31,13 +29,13 @@ main = defaultMainWithHooks $ installBustleHooks simpleUserHooks
installBustleHooks :: UserHooks
-> UserHooks
installBustleHooks uh = uh
- { postInst = postInst gtuh
+ { postInst = \a b c d -> do
+ postInst uh a b c d
+ GetText.installPOFiles a b c d
, buildHook = \pkg lbi hooks flags -> do
writeGetTextConstantsFile pkg lbi flags
buildHook uh pkg lbi hooks flags
}
- where
- gtuh = GetText.installGetTextHooks uh
writeGetTextConstantsFile :: PackageDescription -> LocalBuildInfo -> BuildFlags -> IO ()
@@ -90,24 +88,7 @@ generateModule pkg lbi =
"getMessageCatalogDir = catchIO (getEnv \"" ++ fixedPackageName pkg ++ "_localedir\") (\\_ -> return messageCatalogDir)\n"
sMap = customFieldsPD (localPkgDescr lbi)
- dom = getDomainNameDefault sMap (getPackageName lbi)
- tar = targetDataDir lbi
+ dom = GetText.getDomainNameDefault sMap (GetText.getPackageName lbi)
+ tar = GetText.targetDataDir lbi
-- Cargo-culted from hgettext
-findInParametersDefault :: [(String, String)] -> String -> String -> String
-findInParametersDefault al name def = (fromMaybe def . lookup name) al
-
-getPackageName :: LocalBuildInfo -> String
-getPackageName = fromPackageName . packageName . localPkgDescr
- where fromPackageName (PackageName s) = s
-
-getDomainNameDefault :: [(String, String)] -> String -> String
-getDomainNameDefault al d = findInParametersDefault al "x-gettext-domain-name" d
-
-targetDataDir :: LocalBuildInfo -> FilePath
-targetDataDir l =
- let dirTmpls = installDirTemplates l
- prefix' = prefix dirTmpls
- data' = datadir dirTmpls
- dataEx = I.fromPathTemplate $ I.substPathTemplate [(PrefixVar, prefix')] data'
- in dataEx ++ "/locale"
diff --git a/bustle.cabal b/bustle.cabal
index 05386d8..f9edb84 100644
--- a/bustle.cabal
+++ b/bustle.cabal
@@ -2,6 +2,7 @@ Name: bustle
Category: Network, Desktop
Version: 0.5.1
Cabal-Version: >= 1.18
+Tested-With: GHC >= 7.8.4 && < 7.11
Synopsis: Draw sequence diagrams of D-Bus traffic
Description: Draw sequence diagrams of D-Bus traffic
License: OtherLicense
@@ -35,6 +36,10 @@ Extra-source-files:
, ldd-me-up.sh
, LICENSE.bundled-libraries
+ -- inlined copy of the Cabal hooks from hgettext;
+ -- see https://github.com/fpco/stackage/issues/746
+ , GetText.hs
+
-- wow many translate
, po/*.po
, po/*.pot
@@ -67,6 +72,13 @@ Flag threaded
Description: Build with the multi-threaded runtime
Default: True
+Flag WithGtk2HsBuildTools
+ Description: Build-depend on gtk2hs-buildtools. They aren't (currently)
+ used to build Bustle itself but are needed to build
+ dependencies like gtk3, which for whatever reason do not
+ Build-Depend on the tools.
+ Default: False
+
Executable bustle
Main-is: Bustle.hs
Other-modules: Bustle.Application.Monad
@@ -122,6 +134,9 @@ Executable bustle
, text
, time
+ if flag(WithGtk2HsBuildTools)
+ Build-Depends: gtk2hs-buildtools
+
Executable test-monitor
if flag(InteractiveTests)
buildable: True
diff --git a/make_travis_yml.hs b/make_travis_yml.hs
new file mode 100755
index 0000000..551cbd5
--- /dev/null
+++ b/make_travis_yml.hs
@@ -0,0 +1,228 @@
+#!/usr/bin/env runghc
+
+-- NB: This code deliberately avoids relying on non-standard packages
+
+import Control.Monad
+import Data.Maybe
+import Data.List
+import System.Environment
+import System.Exit
+import System.IO
+
+import Distribution.PackageDescription.Parse (readPackageDescription)
+import Distribution.PackageDescription (packageDescription, testedWith)
+import Distribution.Compiler (CompilerFlavor(..))
+import Distribution.Version
+import Distribution.Text
+
+putStrLnErr :: String -> IO ()
+putStrLnErr m = hPutStrLn stderr ("*ERROR* " ++ m) >> exitFailure
+
+putStrLnWarn :: String -> IO ()
+putStrLnWarn m = hPutStrLn stderr ("*WARNING* " ++ m)
+
+putStrLnInfo :: String -> IO ()
+putStrLnInfo m = hPutStrLn stderr ("*INFO* " ++ m)
+
+main :: IO ()
+main = do
+ args <- getArgs
+ case args of
+ (cabfn:xpkgs) -> do genTravisFromCabalFile cabfn xpkgs
+ _ -> putStrLnErr (unlines $ [ "expected .cabal file as command-line argument"
+ , "Usage: make_travis_yml.hs <cabal-file> <extra-apt-packages...>"
+ , ""
+ , "Example: make_travis_yml.hs someProject.cabal alex-3.1.4 liblzma-dev > .travis.yml"
+ ])
+
+genTravisFromCabalFile :: FilePath -> [String] -> IO ()
+genTravisFromCabalFile fn xpkgs = do
+ gpd <- readPackageDescription maxBound fn
+
+ let compilers = testedWith $ packageDescription $ gpd
+
+ let unknownComps = nub [ c | (c,_) <- compilers, c /= GHC ]
+ ghcVerConstrs = [ vc | (GHC,vc) <- compilers ]
+ ghcVerConstrs' = simplifyVersionRange $ foldr unionVersionRanges noVersion ghcVerConstrs
+
+ when (null compilers) $ do
+ putStrLnErr "empty or missing 'tested-with:' definition in .cabal file"
+
+ unless (null unknownComps) $ do
+ putStrLnWarn $ "ignoring unsupported compilers mentioned in tested-with: " ++ show unknownComps
+
+ when (null ghcVerConstrs) $ do
+ putStrLnErr "'tested-with:' doesn't mention any 'GHC' version"
+
+ when (isNoVersion ghcVerConstrs') $ do
+ putStrLnErr "'tested-with:' describes an empty version range for 'GHC'"
+
+ when (isAnyVersion ghcVerConstrs') $ do
+ putStrLnErr "'tested-with:' allows /any/ 'GHC' version"
+
+ let testedGhcVersions = filter (`withinRange` ghcVerConstrs') knownGhcVersions
+
+ when (null testedGhcVersions) $ do
+ putStrLnErr "no known GHC version is allowed by the 'tested-with' specification"
+
+ let pathBits = concat [ [ "/opt/ghc/$GHCVER/bin", "/opt/cabal/$CABALVER/bin" ]
+ , catMaybes (map pathForPackage xpkgs)
+ , ["$PATH"]
+ ]
+
+ -- FIXME
+ let flags = "WithGtk2HsBuildTools"
+
+ putStrLnInfo $ "Generating Travis-CI config for testing for GHC versions: " ++ (unwords $ map disp' $ testedGhcVersions)
+
+ ----------------------------------------------------------------------------
+ -- travis.yml generation starts here
+
+ putStrLn "# This file has been generated -- see https://github.com/hvr/multi-ghc-travis"
+ putStrLn "language: c"
+ putStrLn "sudo: false"
+ putStrLn ""
+ putStrLn "cache:"
+ putStrLn " directories:"
+ putStrLn " - $HOME/.cabsnap"
+ putStrLn " - $HOME/.cabal/packages"
+ putStrLn ""
+ putStrLn "before_cache:"
+ putStrLn " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log"
+ putStrLn " - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar"
+ putStrLn ""
+ putStrLn "matrix:"
+ putStrLn " include:"
+
+ forM_ testedGhcVersions $ \gv -> do
+ let cvs = disp' (lookupCabVer gv)
+ gvs = disp' gv
+
+ xpkgs' = concatMap (',':) xpkgs
+
+ putStrLn $ concat [ " - env: CABALVER=", cvs, " GHCVER=", gvs ]
+ putStrLn $ concat [ " compiler: \": #GHC ", gvs, "\"" ]
+ putStrLn $ concat [ " addons: {apt: {packages: [cabal-install-", cvs, ",ghc-", gvs, xpkgs'
+ , "], sources: [hvr-ghc]}}" ]
+ return ()
+
+ let headGhcVers = filter isHead testedGhcVersions
+
+ unless (null headGhcVers) $ do
+ putStrLn ""
+ putStrLn " allow_failures:"
+
+ forM_ headGhcVers $ \gv -> do
+ let cvs = disp' (lookupCabVer gv)
+ gvs = disp' gv
+ putStrLn $ concat [ " - env: CABALVER=", cvs, " GHCVER=", gvs ]
+
+ putStrLn ""
+ putStrLn "before_install:"
+ putStrLn " - unset CC"
+ putStrLn $ " - export PATH=" ++ intercalate ":" pathBits
+
+ putStrLn ""
+
+ putStr $ unlines
+ [ "install:"
+ , " - cabal --version"
+ , " - echo \"$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]\""
+ , " - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];"
+ , " then"
+ , " zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >"
+ , " $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;"
+ , " fi"
+ , " - travis_retry cabal update -v"
+ , " - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
+ , " - cabal install --only-dependencies --enable-tests --enable-benchmarks --flags='" ++ flags ++ "' --dry -v > installplan.txt"
+ , " - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt"
+ , ""
+ , "# check whether current requested install-plan matches cached package-db snapshot"
+ , " - if diff -u installplan.txt $HOME/.cabsnap/installplan.txt;"
+ , " then"
+ , " echo \"cabal build-cache HIT\";"
+ , " rm -rfv .ghc;"
+ , " cp -a $HOME/.cabsnap/ghc $HOME/.ghc;"
+ , " cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;"
+ , " else"
+ , " echo \"cabal build-cache MISS\";"
+ , " rm -rf $HOME/.cabsnap;"
+ , " mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;"
+ -- TODO: shouldn't gtk3 et al build-depend on gtk2hs-buildtools? We have to do
+ -- this incantation twice because gtk2hs-buildtools may not be installed before
+ -- the things that secretly depend on its presence. Silly.
+ , " travis_retry cabal install --only-dependencies --enable-tests --enable-benchmarks --flags='" ++ flags ++ "';"
+ , " fi"
+ , " "
+ , "# snapshot package-db on cache miss"
+ , " - if [ ! -d $HOME/.cabsnap ];"
+ , " then"
+ , " echo \"snapshotting package-db to build-cache\";"
+ , " mkdir $HOME/.cabsnap;"
+ , " cp -a $HOME/.ghc $HOME/.cabsnap/ghc;"
+ , " cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;"
+ , " fi"
+ , ""
+ , "# Here starts the actual work to be performed for the package under test;"
+ , "# any command which exits with a non-zero exit code causes the build to fail."
+ , "script:"
+ , " - if [ -f configure.ac ]; then autoreconf -i; fi"
+ , " - cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging"
+ , " - cabal build # this builds all libraries and executables (including tests/benchmarks)"
+ -- FIXME
+ -- , " - cabal test"
+ , " - ./dist/setup/setup test"
+ , " - cabal check"
+ , " - cabal sdist # tests that a source-distribution can be generated"
+ , ""
+ , "# Check that the resulting source distribution can be built & installed."
+ , "# If there are no other `.tar.gz` files in `dist`, this can be even simpler:"
+ , "# `cabal install --force-reinstalls dist/*-*.tar.gz`"
+ , " - SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&"
+ , " (cd dist && cabal install --force-reinstalls \"$SRC_TGZ\")"
+ , ""
+ , "# EOF"
+ ]
+
+ return ()
+ where
+ knownGhcVersions :: [Version]
+ knownGhcVersions = fmap (`Version` [])
+ [ [7,0,1], [7,0,2], [7,0,3], [7,0,4]
+ , [7,2,1], [7,2,2]
+ , [7,4,1], [7,4,2]
+ , [7,6,1], [7,6,2], [7,6,3]
+ , [7,8,1], [7,8,2], [7,8,3], [7,8,4]
+ , [7,10,1]
+ -- 7.10.2 is not in the magic whitelist at
+ -- https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
+ --, [7,10,2]
+ , [7,11] -- HEAD
+ ]
+
+ lookupCabVer :: Version -> Version
+ lookupCabVer (Version (x:y:_) _) = maybe (error "internal error") id $ lookup (x,y) cabalVerMap
+ where
+ cabalVerMap = fmap (fmap (`Version` []))
+ [ ((7, 0), [1,16])
+ , ((7, 2), [1,16])
+ , ((7, 4), [1,16])
+ , ((7, 6), [1,16])
+ , ((7, 8), [1,18])
+ , ((7,10), [1,22])
+ , ((7,11), [1,23]) -- HEAD
+ ]
+
+ isHead (Version (_:y:_) _) = odd (y :: Int)
+
+ disp' v | isHead v = "head"
+ | otherwise = display v
+
+ pathForPackage :: String -> Maybe String
+ pathForPackage xpkg = do
+ i <- findIndex (== '-') xpkg
+ let (p, _:ver) = splitAt i xpkg
+ guard $ p `elem` ["happy", "alex"]
+ return $ "/opt/" ++ p ++ "/" ++ ver ++ "/bin"
+