commit ghc-cabal-helper for openSUSE:Factory
Hello community, here is the log from the commit of package ghc-cabal-helper for openSUSE:Factory checked in at 2015-09-30 05:53:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-cabal-helper (Old) and /work/SRC/openSUSE:Factory/.ghc-cabal-helper.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "ghc-cabal-helper" Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-cabal-helper/ghc-cabal-helper.changes 2015-09-08 17:47:23.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.ghc-cabal-helper.new/ghc-cabal-helper.changes 2015-09-30 05:53:13.000000000 +0200 @@ -1,0 +2,5 @@ +Mon Sep 28 13:52:02 UTC 2015 - mimi.vx@gmail.com + +- update to 0.6.0.0 + +------------------------------------------------------------------- Old: ---- cabal-helper-0.5.2.0.tar.gz New: ---- cabal-helper-0.6.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-cabal-helper.spec ++++++ --- /var/tmp/diff_new_pack.27h2vp/_old 2015-09-30 05:53:13.000000000 +0200 +++ /var/tmp/diff_new_pack.27h2vp/_new 2015-09-30 05:53:13.000000000 +0200 @@ -19,7 +19,7 @@ %global pkg_name cabal-helper %bcond_with tests Name: ghc-cabal-helper -Version: 0.5.2.0 +Version: 0.6.0.0 Release: 0 Summary: Simple interface to some of Cabal's configuration state used by ghc-mod License: AGPL-3.0+ ++++++ cabal-helper-0.5.2.0.tar.gz -> cabal-helper-0.6.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Common.hs new/cabal-helper-0.6.0.0/CabalHelper/Common.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Common.hs 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/CabalHelper/Common.hs 2015-09-07 08:02:26.000000000 +0200 @@ -30,6 +30,8 @@ import System.Environment import System.IO import System.Exit +import System.Directory +import System.FilePath import Text.ParserCombinators.ReadP import Prelude @@ -48,20 +50,6 @@ prog <- getProgName hPutStrLn stderr $ prog ++ ": " ++ str -align :: String -> String -> String -> String -align n an str = let - h:rest = lines str - [hm] = match n h - rest' = [ move (hm - rm) r | r <- rest, rm <- match an r] - in - unlines (h:rest') - where - match p str' = maybeToList $ - fst <$> find ((p `isPrefixOf`) . snd) ([0..] `zip` tails str') - move i str' | i > 0 = replicate i ' ' ++ str' - move i str' = drop i str' - - -- | @getCabalConfigHeader "dist/setup-config"@ returns the cabal version and -- compiler version getCabalConfigHeader :: FilePath -> IO (Maybe (Version, (ByteString, Version))) @@ -95,3 +83,6 @@ runReadP p i = case filter ((=="") . snd) $ readP_to_S p i of (a,""):[] -> a _ -> error $ "Error parsing: " ++ show i + +appDataDir :: IO FilePath +appDataDir = (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Compile.hs new/cabal-helper-0.6.0.0/CabalHelper/Compile.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Compile.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/cabal-helper-0.6.0.0/CabalHelper/Compile.hs 2015-09-07 08:02:26.000000000 +0200 @@ -0,0 +1,375 @@ +-- cabal-helper: Simple interface to Cabal's configuration state +-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org> +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +{-# LANGUAGE RecordWildCards, FlexibleContexts #-} +module CabalHelper.Compile where + +import Control.Applicative +import Control.Arrow +import Control.Exception as E +import Control.Monad +import Control.Monad.Trans.Maybe +import Control.Monad.IO.Class +import Data.Traversable +import Data.Char +import Data.List +import Data.Maybe +import Data.String +import Data.Version +import Text.Printf +import System.Directory +import System.FilePath +import System.Process +import System.Exit +import System.IO +import Prelude + +import Distribution.System (buildPlatform) +import Distribution.Text (display) + +import Paths_cabal_helper (version) +import CabalHelper.Data +import CabalHelper.Common +import CabalHelper.Sandbox (getSandboxPkgDb) +import CabalHelper.Types +import CabalHelper.Log + +data Compile = Compile { + compCabalHelperSourceDir :: FilePath, + compCabalSourceDir :: Maybe FilePath, + compPackageDb :: Maybe FilePath, + compCabalVersion :: Version, + compPackageDeps :: [String] + } + +compileHelper :: Options -> Version -> FilePath -> FilePath -> IO (Either ExitCode FilePath) +compileHelper opts cabalVer projdir distdir = withHelperSources $ \chdir -> do + case cabalPkgDb opts of + Nothing -> + run [ + -- TODO: here ghc's caching fails and it always recompiles, probably + -- because we write the sources to a tempdir and they always look + -- newer than the Cabal sources, not sure if we can fix this + compileCabalSource chdir + , Right <$> MaybeT (cachedExe cabalVer) + , compileSandbox chdir + , compileGlobal chdir + , cachedCabalPkg chdir + , MaybeT (Just <$> compilePrivatePkgDb chdir) + ] + mdb -> + run [ Right <$> MaybeT (cachedExe cabalVer) + , liftIO $ compileWithPkg chdir mdb cabalVer + ] + + where + run actions = fromJust <$> runMaybeT (msum actions) + + logMsg = "compiling helper with Cabal from " + + +-- for relaxed deps: find (sameMajorVersionAs cabalVer) . reverse . sort + + -- | Check if this version is globally available + compileGlobal :: FilePath -> MaybeT IO (Either ExitCode FilePath) + compileGlobal chdir = do + -- TODO: add option to let user specify custom package-db, relevant when + -- using a Cabal compiled from git! + + ver <- MaybeT $ find (== cabalVer) <$> listCabalVersions opts + vLog opts $ logMsg ++ "user/global package-db" + liftIO $ compileWithPkg chdir Nothing ver + + -- | Check if this version is available in the project sandbox + compileSandbox :: FilePath -> MaybeT IO (Either ExitCode FilePath) + compileSandbox chdir = do + sandbox <- MaybeT $ getSandboxPkgDb projdir (display buildPlatform) =<< ghcVersion opts + ver <- MaybeT $ find (== cabalVer) <$> listCabalVersions' opts (Just sandbox) + vLog opts $ logMsg ++ "sandbox package-db" + liftIO $ compileWithPkg chdir (Just sandbox) ver + + + -- | Check if we already compiled this version of cabal into a private + -- package-db + cachedCabalPkg :: FilePath -> MaybeT IO (Either ExitCode FilePath) + cachedCabalPkg chdir = do + db_exists <- liftIO $ cabalPkgDbExists opts cabalVer + case db_exists of + False -> mzero + True -> do + db <- liftIO $ getPrivateCabalPkgDb opts cabalVer + vLog opts $ logMsg ++ "private package-db in " ++ db + liftIO $ compileWithPkg chdir (Just db) cabalVer + + -- | See if we're in a cabal source tree + compileCabalSource :: FilePath -> MaybeT IO (Either ExitCode FilePath) + compileCabalSource chdir = do + let cabalFile = projdir </> "Cabal.cabal" + isCabalMagicVer = cabalVer == Version [1,9999] [] + cabalSrc <- liftIO $ doesFileExist cabalFile + + when isCabalMagicVer $ + vLog opts $ "cabal magic version (1.9999) found" + + when cabalSrc $ + vLog opts $ "directory above distdir looks like cabal source tree (Cabal.cabal exists)" + + case isCabalMagicVer || cabalSrc of + False -> mzero + True -> liftIO $ do + ver <- cabalFileVersion <$> readFile cabalFile + vLog opts $ "compiling helper with local Cabal source tree" + compileWithCabalTree chdir ver projdir + + -- | Compile the requested cabal version into an isolated package-db + compilePrivatePkgDb :: FilePath -> IO (Either ExitCode FilePath) + compilePrivatePkgDb chdir = do + db <- installCabal opts cabalVer `E.catch` + \(SomeException _) -> errorInstallCabal cabalVer distdir + compileWithPkg chdir (Just db) cabalVer + + compileWithCabalTree chdir ver srcDir = + compile distdir opts $ Compile chdir (Just srcDir) Nothing ver [] + + compileWithPkg chdir mdb ver = + compile distdir opts $ Compile chdir Nothing mdb ver [cabalPkgId ver] + + cabalPkgId v = "Cabal-" ++ showVersion v + +compile :: FilePath -> Options -> Compile -> IO (Either ExitCode FilePath) +compile distdir opts@Options {..} Compile {..} = do + cCabalSourceDir <- canonicalizePath `traverse` compCabalSourceDir + appdir <- appDataDir + + let outdir' = maybe appdir (const $ distdir </> "cabal-helper") cCabalSourceDir + createDirectoryIfMissing True outdir' + outdir <- canonicalizePath outdir' + + let exedir' = maybe outdir (const distdir) cCabalSourceDir + createDirectoryIfMissing True exedir' + exedir <- canonicalizePath exedir' + exe <- exePath' compCabalVersion <$> canonicalizePath exedir + + vLog opts $ "outdir: " ++ outdir + vLog opts $ "exedir: " ++ exedir + + let Version (mj:mi:_) _ = compCabalVersion + let ghc_opts = + concat [ + [ "-outputdir", outdir + , "-o", exe + , "-optP-DCABAL_HELPER=1" + , "-optP-DCABAL_MAJOR=" ++ show mj + , "-optP-DCABAL_MINOR=" ++ show mi + ], + maybeToList $ ("-package-conf="++) <$> compPackageDb, + map ("-i"++) $ nub $ ".":maybeToList cCabalSourceDir, + + if isNothing cCabalSourceDir + then [ "-hide-all-packages" + , "-package", "base" + , "-package", "containers" + , "-package", "directory" + , "-package", "filepath" + , "-package", "process" + , "-package", "bytestring" + , "-package", "ghc-prim" + ] + else [], + + concatMap (\p -> ["-package", p]) compPackageDeps, + [ "--make", "CabalHelper/Main.hs" ] + ] + + vLog opts $ intercalate " " $ map (("\""++) . (++"\"")) $ ghcProgram:ghc_opts + + -- TODO: touch exe after, ghc doesn't do that if the input files didn't + -- actually change + rv <- callProcessStderr' (Just compCabalHelperSourceDir) ghcProgram ghc_opts + return $ case rv of + ExitSuccess -> Right exe + e@(ExitFailure _) -> Left e + +exePath :: Version -> IO FilePath +exePath compCabalVersion = do + exePath' compCabalVersion <$> appDataDir + +exePath' :: Version-> FilePath -> FilePath +exePath' compCabalVersion outdir = + outdir </> "cabal-helper-" ++ showVersion version -- our ver + ++ "-Cabal-" ++ showVersion compCabalVersion + +callProcessStderr' :: Maybe FilePath -> FilePath -> [String] -> IO ExitCode +callProcessStderr' mwd exe args = do + (_, _, _, h) <- createProcess (proc exe args) { std_out = UseHandle stderr + , cwd = mwd } + waitForProcess h + +callProcessStderr :: Maybe FilePath -> FilePath -> [String] -> IO () +callProcessStderr mwd exe args = do + rv <- callProcessStderr' mwd exe args + case rv of + ExitSuccess -> return () + ExitFailure v -> processFailedException "callProcessStderr" exe args v + +processFailedException :: String -> String -> [String] -> Int -> IO a +processFailedException fn exe args rv = + panic $ concat [fn, ": ", exe, " " + , intercalate " " (map show args) + , " (exit " ++ show rv ++ ")"] + +installCabal :: Options -> Version -> IO FilePath +installCabal opts ver = do + appdir <- appDataDir + let sver = showVersion ver + hPutStr stderr $ printf "\ +\cabal-helper-wrapper: Installing a private copy of Cabal because we couldn't\n\ +\find the right version in your global/user package-db, this might take a\n\ +\while but will only happen once per Cabal version you're using.\n\ +\\n\ +\If anything goes horribly wrong just delete this directory and try again:\n\ +\ %s\n\ +\\n\ +\If you want to avoid this automatic installation altogether install\n\ +\version %s of Cabal manually (into your user or global package-db):\n\ +\ $ cabal install Cabal --constraint \"Cabal == %s\"\n\ +\\n\ +\Installing Cabal %s ...\n" appdir sver sver sver + + db <- createPkgDb opts ver + cabalInstallVer <- cabalInstallVersion opts + cabal_opts <- return $ concat + [ + [ "--package-db=clear" + , "--package-db=global" + , "--package-db=" ++ db + , "--prefix=" ++ db </> "prefix" + , "--with-ghc=" ++ ghcProgram opts + ] + , if cabalInstallVer >= Version [1,20,0,0] [] + then ["--no-require-sandbox"] + else [] + , if ghcPkgProgram opts /= ghcPkgProgram defaultOptions + then [ "--with-ghc-pkg=" ++ ghcPkgProgram opts ] + else [] + , [ "install", "Cabal", "--constraint" + , "Cabal == " ++ showVersion ver ] + ] + + vLog opts $ intercalate " " $ map (("\""++) . (++"\"")) $ cabalProgram opts:cabal_opts + + callProcessStderr (Just "/") (cabalProgram opts) cabal_opts + hPutStrLn stderr "done" + return db + +errorInstallCabal :: Version -> FilePath -> a +errorInstallCabal cabalVer _distdir = panic $ printf "\ +\Installing Cabal version %s failed.\n\ +\\n\ +\You have the following choices to fix this:\n\ +\\n\ +\- The easiest way to try and fix this is just reconfigure the project and try\n\ +\ again:\n\ +\ $ cabal clean && cabal configure\n\ +\\n\ +\- If that fails you can try to install the version of Cabal mentioned above\n\ +\ into your global/user package-db somehow, you'll probably have to fix\n\ +\ something otherwise it wouldn't have failed above:\n\ +\ $ cabal install Cabal --constraint 'Cabal == %s'\n\ +\\n\ +\- If you're using `Build-Type: Simple`:\n\ +\ - You can see if you can reinstall your cabal-install executable while\n\ +\ having it linked to a version of Cabal that's available in you\n\ +\ package-dbs or can be built automatically:\n\ +\ $ ghc-pkg list | grep Cabal # find an available Cabal version\n\ +\ Cabal-W.X.Y.Z\n\ +\ $ cabal install cabal-install --constraint 'Cabal == W.X.*'\n\ +\ Afterwards you'll have to reconfigure your project:\n\ +\ $ cabal clean && cabal configure\n\ +\\n\ +\- If you're using `Build-Type: Custom`:\n\ +\ - Have cabal-install rebuild your Setup.hs executable with a version of the\n\ +\ Cabal library that you have available in your global/user package-db:\n\ +\ $ cabal clean && cabal configure\n\ +\ You might also have to install some version of the Cabal to do this:\n\ +\ $ cabal install Cabal\n\ +\\n" sver sver + where + sver = showVersion cabalVer + +cachedExe :: Version -> IO (Maybe FilePath) +cachedExe compCabalVersion = do + exe <- exePath compCabalVersion + exists <- doesFileExist exe + return $ if exists then Just exe else Nothing + +listCabalVersions :: Options -> IO [Version] +listCabalVersions opts = listCabalVersions' opts Nothing + +-- TODO: Include sandbox? Probably only relevant for build-type:custom projects. +listCabalVersions' :: Options -> Maybe FilePath -> IO [Version] +listCabalVersions' Options {..} mdb = do + let mdbopt = ("--package-conf="++) <$> mdb + opts = ["list", "--simple-output", "Cabal"] ++ maybeToList mdbopt + + catMaybes . map (fmap snd . parsePkgId . fromString) . words + <$> readProcess ghcPkgProgram opts "" + +cabalPkgDbExists :: Options -> Version -> IO Bool +cabalPkgDbExists opts ver = do + db <- getPrivateCabalPkgDb opts ver + dexists <- doesDirectoryExist db + case dexists of + False -> return False + True -> do + vers <- listCabalVersions' opts (Just db) + return $ ver `elem` vers + + +ghcVersion :: Options -> IO Version +ghcVersion Options {..} = do + parseVer . trim <$> readProcess ghcProgram ["--numeric-version"] "" + +ghcPkgVersion :: Options -> IO Version +ghcPkgVersion Options {..} = do + parseVer . trim . dropWhile (not . isDigit) <$> readProcess ghcPkgProgram ["--version"] "" + +cabalInstallVersion :: Options -> IO Version +cabalInstallVersion Options {..} = do + parseVer . trim <$> readProcess cabalProgram ["--numeric-version"] "" + +trim :: String -> String +trim = dropWhileEnd isSpace + +createPkgDb :: Options -> Version -> IO FilePath +createPkgDb opts@Options {..} ver = do + db <- getPrivateCabalPkgDb opts ver + exists <- doesDirectoryExist db + when (not exists) $ callProcessStderr Nothing ghcPkgProgram ["init", db] + return db + +getPrivateCabalPkgDb :: Options -> Version -> IO FilePath +getPrivateCabalPkgDb opts ver = do + appdir <- appDataDir + ghcVer <- ghcVersion opts + return $ appdir </> "Cabal-" ++ showVersion ver ++ "-db-" ++ showVersion ghcVer + +-- | Find @version: XXX@ delcaration in a cabal file +cabalFileVersion :: String -> Version +cabalFileVersion cabalFile = + fromJust $ parseVer . extract <$> find ("version:" `isPrefixOf`) ls + where + ls = map (map toLower) $ lines cabalFile + extract = dropWhile (/=':') >>> drop 1 >>> dropWhile isSpace >>> takeWhile (not . isSpace) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Data.hs new/cabal-helper-0.6.0.0/CabalHelper/Data.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Data.hs 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/CabalHelper/Data.hs 2015-09-07 08:02:26.000000000 +0200 @@ -41,5 +41,6 @@ [ ("Main.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Main.hs"))) , ("Common.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Common.hs"))) , ("Sandbox.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Sandbox.hs"))) + , ("Licenses.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Licenses.hs"))) , ("Types.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Types.hs"))) ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Licenses.hs new/cabal-helper-0.6.0.0/CabalHelper/Licenses.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Licenses.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/cabal-helper-0.6.0.0/CabalHelper/Licenses.hs 2015-09-07 08:02:26.000000000 +0200 @@ -0,0 +1,102 @@ +{-# LANGUAGE CPP #-} +module CabalHelper.Licenses where + +-- Copyright (c) 2014, Jasper Van der Jeugt <m@jaspervdj.be> + +-------------------------------------------------------------------------------- +import Control.Arrow ((***), (&&&)) +import Control.Monad (forM_, unless) +import Data.List (foldl', sort) +import Data.Maybe (catMaybes) +import Data.Version (Version) +import Data.Set (Set) +import qualified Data.Set as Set +import Distribution.InstalledPackageInfo (InstalledPackageInfo) +import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo +import qualified Distribution.License as Cabal +import qualified Distribution.Package as Cabal +import qualified Distribution.Simple.Configure as Cabal +import qualified Distribution.Simple.LocalBuildInfo as Cabal +import qualified Distribution.Simple.PackageIndex as Cabal +import qualified Distribution.Text as Cabal +import System.Directory (getDirectoryContents) +import System.Exit (exitFailure) +import System.FilePath (takeExtension) +import System.IO (hPutStrLn, stderr) + +-------------------------------------------------------------------------------- + +#if CABAL_MAJOR == 1 && CABAL_MINOR > 22 +type PackageIndex a = Cabal.PackageIndex (InstalledPackageInfo.InstalledPackageInfo) +#elif CABAL_MAJOR == 1 && CABAL_MINOR >= 22 +type PackageIndex a = Cabal.PackageIndex (InstalledPackageInfo.InstalledPackageInfo_ a) +#else +type PackageIndex a = Cabal.PackageIndex +#endif + +findTransitiveDependencies + :: PackageIndex a + -> Set Cabal.InstalledPackageId + -> Set Cabal.InstalledPackageId +findTransitiveDependencies pkgIdx set0 = go Set.empty (Set.toList set0) + where + go set [] = set + go set (q : queue) + | q `Set.member` set = go set queue + | otherwise = + case Cabal.lookupInstalledPackageId pkgIdx q of + Nothing -> + -- Not found can mean that the package still needs to be + -- installed (e.g. a component of the target cabal package). + -- We can ignore those. + go set queue + Just ipi -> + go (Set.insert q set) + (InstalledPackageInfo.depends ipi ++ queue) + + +-------------------------------------------------------------------------------- +getDependencyInstalledPackageIds + :: Cabal.LocalBuildInfo -> Set Cabal.InstalledPackageId +getDependencyInstalledPackageIds lbi = + findTransitiveDependencies (Cabal.installedPkgs lbi) $ + Set.fromList $ map fst $ Cabal.externalPackageDeps lbi + +-------------------------------------------------------------------------------- +getDependencyInstalledPackageInfos + :: Cabal.LocalBuildInfo -> [InstalledPackageInfo] +getDependencyInstalledPackageInfos lbi = catMaybes $ + map (Cabal.lookupInstalledPackageId pkgIdx) $ + Set.toList (getDependencyInstalledPackageIds lbi) + where + pkgIdx = Cabal.installedPkgs lbi + + +-------------------------------------------------------------------------------- +groupByLicense + :: [InstalledPackageInfo] + -> [(Cabal.License, [InstalledPackageInfo])] +groupByLicense = foldl' + (\assoc ipi -> insert (InstalledPackageInfo.license ipi) ipi assoc) [] + where + -- 'Cabal.License' doesn't have an 'Ord' instance so we need to use an + -- association list instead of 'Map'. The number of licenses probably won't + -- exceed 100 so I think we're alright. + insert :: Eq k => k -> v -> [(k, [v])] -> [(k, [v])] + insert k v [] = [(k, [v])] + insert k v ((k', vs) : kvs) + | k == k' = (k, v : vs) : kvs + | otherwise = (k', vs) : insert k v kvs + + +-------------------------------------------------------------------------------- +displayDependencyLicenseList + :: [(Cabal.License, [InstalledPackageInfo])] + -> [(String, [(String, Version)])] +displayDependencyLicenseList = + map (Cabal.display *** map (getName &&& getVersion)) + where + getName = + Cabal.display . Cabal.pkgName . InstalledPackageInfo.sourcePackageId + getVersion = + Cabal.pkgVersion . InstalledPackageInfo.sourcePackageId diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Log.hs new/cabal-helper-0.6.0.0/CabalHelper/Log.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Log.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/cabal-helper-0.6.0.0/CabalHelper/Log.hs 2015-09-07 08:02:26.000000000 +0200 @@ -0,0 +1,14 @@ +module CabalHelper.Log where + +import Control.Monad +import Control.Monad.IO.Class +import Data.String +import System.IO +import Prelude + +import CabalHelper.Types + +vLog :: MonadIO m => Options -> String -> m () +vLog Options { verbose = True } msg = + liftIO $ hPutStrLn stderr msg +vLog _ _ = return () diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Main.hs new/cabal-helper-0.6.0.0/CabalHelper/Main.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Main.hs 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/CabalHelper/Main.hs 2015-09-07 08:02:26.000000000 +0200 @@ -79,28 +79,31 @@ import System.IO.Unsafe (unsafeInterleaveIO, unsafePerformIO) import Text.Printf +import CabalHelper.Licenses import CabalHelper.Sandbox import CabalHelper.Common -import CabalHelper.Types +import CabalHelper.Types hiding (Options(..)) usage = do prog <- getProgName - hPutStr stderr $ align "(" "|" ("Usage: " ++ prog ++ " " ++ usageMsg) + hPutStr stderr $ "Usage: " ++ prog ++ " " ++ usageMsg where usageMsg = "" - ++"DIST_DIR ( version\n" - ++" | print-lbi [--human]\n" - ++" | write-autogen-files\n" - ++" | compiler-version\n" - ++" | ghc-options [--with-inplace]\n" - ++" | ghc-src-options [--with-inplace]\n" - ++" | ghc-pkg-options [--with-inplace]\n" - ++" | ghc-merged-pkg-options [--with-inplace]\n" - ++" | ghc-lang-options [--with-inplace]\n" - ++" | package-db-stack\n" - ++" | entrypoints\n" - ++" | source-dirs\n" - ++" ) ...\n" + ++"PROJ_DIR DIST_DIR [--with-* ...] (\n" + ++" version\n" + ++" | print-lbi [--human]\n" + ++" | write-autogen-files\n" + ++" | compiler-version\n" + ++" | ghc-options [--with-inplace]\n" + ++" | ghc-src-options [--with-inplace]\n" + ++" | ghc-pkg-options [--with-inplace]\n" + ++" | ghc-merged-pkg-options [--with-inplace]\n" + ++" | ghc-lang-options [--with-inplace]\n" + ++" | package-db-stack\n" + ++" | entrypoints\n" + ++" | source-dirs\n" + ++" | licenses\n" + ++" ) ...\n" commands :: [String] commands = [ "print-bli" @@ -112,7 +115,8 @@ , "ghc-lang-options" , "package-db-stack" , "entrypoints" - , "source-dirs"] + , "source-dirs" + , "licenses"] main :: IO () main = do @@ -201,10 +205,9 @@ ghcOptPackages = ghcOptPackages opts }) - let res' = res { ghcOptPackageDBs = withPackageDB lbi - , ghcOptHideAllPackages = Flag True - , ghcOptPackages = nub $ ghcOptPackages res - } + let res' = nubPackageFlags $ res { ghcOptPackageDBs = withPackageDB lbi + , ghcOptHideAllPackages = Flag True + } Just . ChResponseList <$> renderGhcOptions' lbi v res' @@ -239,6 +242,10 @@ res <- componentsMap lbi v distdir $$ \_ _ bi -> return $ hsSourceDirs bi return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])]) + "licenses":[] -> do + return $ Just $ ChResponseLicenses $ + displayDependencyLicenseList $ groupByLicense $ getDependencyInstalledPackageInfos lbi + "print-lbi":flags -> case flags of ["--human"] -> print lbi >> return Nothing @@ -375,7 +382,12 @@ isInplaceDep :: (InstalledPackageId, PackageId) -> Bool isInplaceDep (ipid, pid) = inplacePackageId pid == ipid +#if CABAL_MAJOR == 1 && CABAL_MINOR >= 22 +-- >= 1.22 uses NubListR +nubPackageFlags opts = opts +#else nubPackageFlags opts = opts { ghcOptPackages = nub $ ghcOptPackages opts } +#endif renderGhcOptions' lbi v opts = do #if CABAL_MAJOR == 1 && CABAL_MINOR < 20 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Types.hs new/cabal-helper-0.6.0.0/CabalHelper/Types.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Types.hs 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/CabalHelper/Types.hs 2015-09-07 08:02:26.000000000 +0200 @@ -37,6 +37,7 @@ | ChResponsePkgDbs [ChPkgDb] | ChResponseLbi String | ChResponseVersion String Version + | ChResponseLicenses [(String, [(String, Version)])] deriving (Eq, Ord, Read, Show, Generic) data ChEntrypoint = ChSetupEntrypoint -- ^ Almost like 'ChExeEntrypoint' but @@ -55,3 +56,15 @@ | ChPkgUser | ChPkgSpecific FilePath deriving (Eq, Ord, Read, Show, Generic) + +data Options = Options { + verbose :: Bool + , ghcProgram :: FilePath + , ghcPkgProgram :: FilePath + , cabalProgram :: FilePath + , cabalVersion :: Maybe Version + , cabalPkgDb :: Maybe FilePath +} + +defaultOptions :: Options +defaultOptions = Options False "ghc" "ghc-pkg" "cabal" Nothing Nothing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/CabalHelper/Wrapper.hs new/cabal-helper-0.6.0.0/CabalHelper/Wrapper.hs --- old/cabal-helper-0.5.2.0/CabalHelper/Wrapper.hs 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/CabalHelper/Wrapper.hs 2015-09-07 08:02:26.000000000 +0200 @@ -13,17 +13,11 @@ -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. - {-# LANGUAGE RecordWildCards, FlexibleContexts #-} module Main where import Control.Applicative -import Control.Arrow -import Control.Exception as E import Control.Monad -import Control.Monad.Trans.Maybe -import Control.Monad.IO.Class -import Data.Traversable import Data.Char import Data.List import Data.Maybe @@ -43,15 +37,15 @@ import Distribution.Text (display) import Paths_cabal_helper (version) -import CabalHelper.Data import CabalHelper.Common import CabalHelper.GuessGhc -import CabalHelper.Sandbox (getSandboxPkgDb) +import CabalHelper.Compile +import CabalHelper.Types usage :: IO () usage = do prog <- getProgName - hPutStr stderr $ align "(" "|" ("Usage: " ++ prog ++ " " ++ usageMsg) + hPutStr stderr $ "Usage: " ++ prog ++ " " ++ usageMsg where usageMsg = "\ \( print-appdatadir\n\ @@ -60,18 +54,10 @@ \ [--with-ghc=GHC_PATH]\n\ \ [--with-ghc-pkg=GHC_PKG_PATH]\n\ \ [--with-cabal=CABAL_PATH]\n\ +\ [--with-cabal-version=VERSION]\n\ +\ [--with-cabal-pkg-db=PKG_DB]\n\ \ PROJ_DIR DIST_DIR ( print-exe | [CABAL_HELPER_ARGS...] ) )\n" -data Options = Options { - verbose :: Bool - , ghcProgram :: FilePath - , ghcPkgProgram :: FilePath - , cabalProgram :: FilePath -} - -defaultOptions :: Options -defaultOptions = Options False "ghc" "ghc-pkg" "cabal" - globalArgSpec :: [OptDescr (Options -> Options)] globalArgSpec = [ option "" ["verbose"] "Be more verbose" $ @@ -85,6 +71,13 @@ , option "" ["with-cabal"] "cabal-install executable to use" $ reqArg "PROG" $ \p o -> o { cabalProgram = p } + + , option "" ["with-cabal-version"] "Cabal library version to use" $ + reqArg "VERSION" $ \p o -> o { cabalVersion = Just $ parseVer p } + + , option "" ["with-cabal-pkg-db"] "package database to look for Cabal library in" $ + reqArg "PKG_DB" $ \p o -> o { cabalPkgDb = Just p } + ] where option :: [Char] -> [String] -> String -> ArgDescr a -> OptDescr a @@ -132,342 +125,18 @@ \- Check first line of: %s\n\ \- Maybe try: $ cabal configure" cfgf Just (hdrCabalVersion, _) -> do - eexe <- compileHelper opts hdrCabalVersion projdir distdir - case eexe of - Left e -> exitWith e - Right exe -> - case args' of - "print-exe":_ -> putStrLn exe - _ -> do - (_,_,_,h) <- createProcess $ proc exe args - exitWith =<< waitForProcess h + case cabalVersion opts of + Just ver | hdrCabalVersion /= ver -> panic $ printf "\ +\Cabal version %s was requested setup configuration was\n\ +\written by version %s" (showVersion ver) (showVersion hdrCabalVersion) + _ -> do + eexe <- compileHelper opts hdrCabalVersion projdir distdir + case eexe of + Left e -> exitWith e + Right exe -> + case args' of + "print-exe":_ -> putStrLn exe + _ -> do + (_,_,_,h) <- createProcess $ proc exe args + exitWith =<< waitForProcess h _ -> error "invalid command line" - -appDataDir :: IO FilePath -appDataDir = (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod" - -compileHelper :: Options -> Version -> FilePath -> FilePath -> IO (Either ExitCode FilePath) -compileHelper opts cabalVer projdir distdir = withHelperSources $ \chdir -> do - run [ compileCabalSource chdir -- TODO: here ghc's caching fails and it always - -- recompiles, probably because we write the - -- sources to a tempdir and they always look - -- newer than the Cabal sources, not sure if we - -- can fix this - , Right <$> MaybeT (cachedExe cabalVer) - , compileSandbox chdir - , compileGlobal chdir - , cachedCabalPkg chdir - , MaybeT (Just <$> compilePrivatePkgDb chdir) - ] - - where - run actions = fromJust <$> runMaybeT (msum actions) - - logMsg = "compiling helper with Cabal from " - - --- for relaxed deps: find (sameMajorVersionAs cabalVer) . reverse . sort - - -- | Check if this version is globally available - compileGlobal :: FilePath -> MaybeT IO (Either ExitCode FilePath) - compileGlobal chdir = do - -- TODO: add option to let user specify custom package-db, relevant when - -- using a Cabal compiled from git! - - ver <- MaybeT $ find (== cabalVer) <$> listCabalVersions opts - vLog opts $ logMsg ++ "user/global package-db" - liftIO $ compileWithPkg chdir Nothing ver - - -- | Check if this version is available in the project sandbox - compileSandbox :: FilePath -> MaybeT IO (Either ExitCode FilePath) - compileSandbox chdir = do - sandbox <- MaybeT $ getSandboxPkgDb projdir (display buildPlatform) =<< ghcVersion opts - ver <- MaybeT $ find (== cabalVer) <$> listCabalVersions' opts (Just sandbox) - vLog opts $ logMsg ++ "sandbox package-db" - liftIO $ compileWithPkg chdir (Just sandbox) ver - - - -- | Check if we already compiled this version of cabal into a private - -- package-db - cachedCabalPkg :: FilePath -> MaybeT IO (Either ExitCode FilePath) - cachedCabalPkg chdir = do - db_exists <- liftIO $ cabalPkgDbExists opts cabalVer - case db_exists of - False -> mzero - True -> do - db <- liftIO $ cabalPkgDb opts cabalVer - vLog opts $ logMsg ++ "private package-db in " ++ db - liftIO $ compileWithPkg chdir (Just db) cabalVer - - -- | See if we're in a cabal source tree - compileCabalSource :: FilePath -> MaybeT IO (Either ExitCode FilePath) - compileCabalSource chdir = do - let cabalFile = projdir </> "Cabal.cabal" - isCabalMagicVer = cabalVer == Version [1,9999] [] - cabalSrc <- liftIO $ doesFileExist cabalFile - - when isCabalMagicVer $ - vLog opts $ "cabal magic version (1.9999) found" - - when cabalSrc $ - vLog opts $ "directory above distdir looks like cabal source tree (Cabal.cabal exists)" - - case isCabalMagicVer || cabalSrc of - False -> mzero - True -> liftIO $ do - ver <- cabalFileVersion <$> readFile cabalFile - vLog opts $ "compiling helper with local Cabal source tree" - compileWithCabalTree chdir ver projdir - - -- | Compile the requested cabal version into an isolated package-db - compilePrivatePkgDb :: FilePath -> IO (Either ExitCode FilePath) - compilePrivatePkgDb chdir = do - db <- installCabal opts cabalVer `E.catch` - \(SomeException _) -> errorInstallCabal cabalVer distdir - compileWithPkg chdir (Just db) cabalVer - - compileWithCabalTree chdir ver srcDir = - compile distdir opts $ Compile chdir (Just srcDir) Nothing ver [] - - compileWithPkg chdir mdb ver = - compile distdir opts $ Compile chdir Nothing mdb ver [cabalPkgId ver] - - cabalPkgId v = "Cabal-" ++ showVersion v - -errorInstallCabal :: Version -> FilePath -> a -errorInstallCabal cabalVer _distdir = panic $ printf "\ -\Installing Cabal version %s failed.\n\ -\\n\ -\You have the following choices to fix this:\n\ -\\n\ -\- The easiest way to try and fix this is just reconfigure the project and try\n\ -\ again:\n\ -\ $ cabal clean && cabal configure\n\ -\\n\ -\- If that fails you can try to install the version of Cabal mentioned above\n\ -\ into your global/user package-db somehow, you'll probably have to fix\n\ -\ something otherwise it wouldn't have failed above:\n\ -\ $ cabal install Cabal --constraint 'Cabal == %s'\n\ -\\n\ -\- If you're using `Build-Type: Simple`:\n\ -\ - You can see if you can reinstall your cabal-install executable while\n\ -\ having it linked to a version of Cabal that's available in you\n\ -\ package-dbs or can be built automatically:\n\ -\ $ ghc-pkg list | grep Cabal # find an available Cabal version\n\ -\ Cabal-W.X.Y.Z\n\ -\ $ cabal install cabal-install --constraint 'Cabal == W.X.*'\n\ -\ Afterwards you'll have to reconfigure your project:\n\ -\ $ cabal clean && cabal configure\n\ -\\n\ -\- If you're using `Build-Type: Custom`:\n\ -\ - Have cabal-install rebuild your Setup.hs executable with a version of the\n\ -\ Cabal library that you have available in your global/user package-db:\n\ -\ $ cabal clean && cabal configure\n\ -\ You might also have to install some version of the Cabal to do this:\n\ -\ $ cabal install Cabal\n\ -\\n" sver sver - where - sver = showVersion cabalVer - - -data Compile = Compile { - cabalHelperSourceDir :: FilePath, - cabalSourceDir :: Maybe FilePath, - packageDb :: Maybe FilePath, - cabalVersion :: Version, - packageDeps :: [String] - } - -compile :: FilePath -> Options -> Compile -> IO (Either ExitCode FilePath) -compile distdir opts@Options {..} Compile {..} = do - cCabalSourceDir <- canonicalizePath `traverse` cabalSourceDir - appdir <- appDataDir - - let outdir' = maybe appdir (const $ distdir </> "cabal-helper") cCabalSourceDir - createDirectoryIfMissing True outdir' - outdir <- canonicalizePath outdir' - - let exedir' = maybe outdir (const distdir) cCabalSourceDir - createDirectoryIfMissing True exedir' - exedir <- canonicalizePath exedir' - exe <- exePath' cabalVersion <$> canonicalizePath exedir - - vLog opts $ "outdir: " ++ outdir - vLog opts $ "exedir: " ++ exedir - - let Version (mj:mi:_) _ = cabalVersion - let ghc_opts = - concat [ - [ "-outputdir", outdir - , "-o", exe - , "-optP-DCABAL_HELPER=1" - , "-optP-DCABAL_MAJOR=" ++ show mj - , "-optP-DCABAL_MINOR=" ++ show mi - ], - maybeToList $ ("-package-conf="++) <$> packageDb, - map ("-i"++) $ nub $ ".":maybeToList cCabalSourceDir, - - if isNothing cCabalSourceDir - then [ "-hide-all-packages" - , "-package", "base" - , "-package", "directory" - , "-package", "filepath" - , "-package", "process" - , "-package", "bytestring" - , "-package", "ghc-prim" - ] - else [], - - concatMap (\p -> ["-package", p]) packageDeps, - [ "--make", "CabalHelper/Main.hs" ] - ] - - vLog opts $ intercalate " " $ map (("\""++) . (++"\"")) $ ghcProgram:ghc_opts - - -- TODO: touch exe after, ghc doesn't do that if the input files didn't - -- actually change - rv <- callProcessStderr' (Just cabalHelperSourceDir) ghcProgram ghc_opts - return $ case rv of - ExitSuccess -> Right exe - e@(ExitFailure _) -> Left e - -exePath :: Version -> IO FilePath -exePath cabalVersion = do - exePath' cabalVersion <$> appDataDir - -exePath' :: Version-> FilePath -> FilePath -exePath' cabalVersion outdir = - outdir </> "cabal-helper-" ++ showVersion version -- our ver - ++ "-Cabal-" ++ showVersion cabalVersion - -cachedExe :: Version -> IO (Maybe FilePath) -cachedExe cabalVersion = do - exe <- exePath cabalVersion - exists <- doesFileExist exe - return $ if exists then Just exe else Nothing - -callProcessStderr' :: Maybe FilePath -> FilePath -> [String] -> IO ExitCode -callProcessStderr' mwd exe args = do - (_, _, _, h) <- createProcess (proc exe args) { std_out = UseHandle stderr - , cwd = mwd } - waitForProcess h - -callProcessStderr :: Maybe FilePath -> FilePath -> [String] -> IO () -callProcessStderr mwd exe args = do - rv <- callProcessStderr' mwd exe args - case rv of - ExitSuccess -> return () - ExitFailure v -> processFailedException "callProcessStderr" exe args v - -processFailedException :: String -> String -> [String] -> Int -> IO a -processFailedException fn exe args rv = - panic $ concat [fn, ": ", exe, " " - , intercalate " " (map show args) - , " (exit " ++ show rv ++ ")"] - -installCabal :: Options -> Version -> IO FilePath -installCabal opts ver = do - appdir <- appDataDir - let sver = showVersion ver - hPutStr stderr $ printf "\ -\cabal-helper-wrapper: Installing a private copy of Cabal because we couldn't\n\ -\find the right version in your global/user package-db, this might take a\n\ -\while but will only happen once per Cabal version you're using.\n\ -\\n\ -\If anything goes horribly wrong just delete this directory and try again:\n\ -\ %s\n\ -\\n\ -\If you want to avoid this automatic installation altogether install\n\ -\version %s of Cabal manually (into your user or global package-db):\n\ -\ $ cabal install Cabal --constraint \"Cabal == %s\"\n\ -\\n\ -\Installing Cabal %s ...\n" appdir sver sver sver - - db <- createPkgDb opts ver - cabalInstallVer <- cabalInstallVersion opts - cabal_opts <- return $ concat - [ - [ "--package-db=clear" - , "--package-db=global" - , "--package-db=" ++ db - , "--prefix=" ++ db </> "prefix" - , "--with-ghc=" ++ ghcProgram opts - ] - , if cabalInstallVer >= Version [1,20,0,0] [] - then ["--no-require-sandbox"] - else [] - , if ghcPkgProgram opts /= ghcPkgProgram defaultOptions - then [ "--with-ghc-pkg=" ++ ghcPkgProgram opts ] - else [] - , [ "install", "Cabal", "--constraint" - , "Cabal == " ++ showVersion ver ] - ] - - vLog opts $ intercalate " " $ map (("\""++) . (++"\"")) $ cabalProgram opts:cabal_opts - - callProcessStderr (Just "/") (cabalProgram opts) cabal_opts - hPutStrLn stderr "done" - return db - -ghcVersion :: Options -> IO Version -ghcVersion Options {..} = do - parseVer . trim <$> readProcess ghcProgram ["--numeric-version"] "" - -ghcPkgVersion :: Options -> IO Version -ghcPkgVersion Options {..} = do - parseVer . trim . dropWhile (not . isDigit) <$> readProcess ghcPkgProgram ["--version"] "" - -cabalInstallVersion :: Options -> IO Version -cabalInstallVersion Options {..} = do - parseVer . trim <$> readProcess cabalProgram ["--numeric-version"] "" - -trim :: String -> String -trim = dropWhileEnd isSpace - -createPkgDb :: Options -> Version -> IO FilePath -createPkgDb opts@Options {..} ver = do - db <- cabalPkgDb opts ver - exists <- doesDirectoryExist db - when (not exists) $ callProcessStderr Nothing ghcPkgProgram ["init", db] - return db - -cabalPkgDb :: Options -> Version -> IO FilePath -cabalPkgDb opts ver = do - appdir <- appDataDir - ghcVer <- ghcVersion opts - return $ appdir </> "Cabal-" ++ showVersion ver ++ "-db-" ++ showVersion ghcVer - -cabalPkgDbExists :: Options -> Version -> IO Bool -cabalPkgDbExists opts ver = do - db <- cabalPkgDb opts ver - dexists <- doesDirectoryExist db - case dexists of - False -> return False - True -> do - vers <- listCabalVersions' opts (Just db) - return $ ver `elem` vers - -listCabalVersions :: Options -> IO [Version] -listCabalVersions opts = listCabalVersions' opts Nothing - --- TODO: Include sandbox? Probably only relevant for build-type:custom projects. -listCabalVersions' :: Options -> Maybe FilePath -> IO [Version] -listCabalVersions' Options {..} mdb = do - let mdbopt = ("--package-conf="++) <$> mdb - opts = ["list", "--simple-output", "Cabal"] ++ maybeToList mdbopt - - catMaybes . map (fmap snd . parsePkgId . fromString) . words - <$> readProcess ghcPkgProgram opts "" - --- | Find @version: XXX@ delcaration in a cabal file -cabalFileVersion :: String -> Version -cabalFileVersion cabalFile = - fromJust $ parseVer . extract <$> find ("version:" `isPrefixOf`) ls - where - ls = map (map toLower) $ lines cabalFile - extract = dropWhile (/=':') >>> drop 1 >>> dropWhile isSpace >>> takeWhile (not . isSpace) - -vLog :: MonadIO m => Options -> String -> m () -vLog Options { verbose = True } msg = - liftIO $ hPutStrLn stderr msg -vLog _ _ = return () diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/Distribution/Helper.hs new/cabal-helper-0.6.0.0/Distribution/Helper.hs --- old/cabal-helper-0.5.2.0/Distribution/Helper.hs 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/Distribution/Helper.hs 2015-09-07 08:02:26.000000000 +0200 @@ -14,17 +14,24 @@ -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. -{-# LANGUAGE CPP, FlexibleContexts, ConstraintKinds #-} +{-# LANGUAGE CPP, RecordWildCards, FlexibleContexts, ConstraintKinds #-} {-# LANGUAGE GeneralizedNewtypeDeriving, DeriveDataTypeable, DeriveGeneric #-} module Distribution.Helper ( Programs(..) + , defaultPrograms + , QueryEnv + , qeReadProcess + , qePrograms + , qeProjectDir + , qeDistDir + , qeCabalPkgDb + , qeCabalVer + , defaultQueryEnv -- * Running Queries , Query , runQuery - , runQuery' - , runQuery'' -- * Queries against Cabal\'s on disk state @@ -36,6 +43,7 @@ , ghcPkgOptions , ghcMergedPkgOptions , ghcLangOptions + , pkgLicenses -- * Result types , ChModuleName(..) @@ -67,7 +75,6 @@ import Control.Exception as E import Data.Char import Data.List -import Data.Default import Data.Version import Data.Typeable import Distribution.Simple.BuildPaths (exeExtension) @@ -81,7 +88,7 @@ import Prelude import Paths_cabal_helper (getLibexecDir) -import CabalHelper.Types +import CabalHelper.Types hiding (Options(..)) import CabalHelper.Sandbox -- | Paths or names of various programs we need. @@ -91,8 +98,45 @@ ghcPkgProgram :: FilePath } deriving (Eq, Ord, Show, Read, Generic, Typeable) -instance Default Programs where - def = Programs "cabal" "ghc" "ghc-pkg" +defaultPrograms :: Programs +defaultPrograms = Programs "cabal" "ghc" "ghc-pkg" + +data QueryEnv = QueryEnv { + -- | How to start the cabal-helper process. Useful if you need to + -- capture stderr output from the helper. + qeReadProcess :: FilePath -> [String] -> String -> IO String, + + qePrograms :: Programs, + + -- | Path to project directory, i.e. the one containing the + -- @project.cabal@ file + qeProjectDir :: FilePath, + + -- | Path to the @dist/@ directory + qeDistDir :: FilePath, + + -- | Where to look for the Cabal library when linking the helper + qeCabalPkgDb :: Maybe FilePath, + + -- | If @dist/setup-config@ wasn\'t written by this version of Cabal throw + -- an error + qeCabalVer :: Maybe Version + } + +defaultQueryEnv :: FilePath + -- ^ Path to project directory, i.e. the one containing the + -- @project.cabal@ file + -> FilePath + -- ^ Path to the @dist/@ directory + -> QueryEnv +defaultQueryEnv projdir distdir = QueryEnv { + qeReadProcess = readProcess + , qePrograms = defaultPrograms + , qeProjectDir = projdir + , qeDistDir = distdir + , qeCabalPkgDb = Nothing + , qeCabalVer = Nothing + } data SomeLocalBuildInfo = SomeLocalBuildInfo { slbiPackageDbStack :: [ChPkgDb], @@ -102,7 +146,8 @@ slbiGhcSrcOptions :: [(ChComponentName, [String])], slbiGhcPkgOptions :: [(ChComponentName, [String])], slbiGhcMergedPkgOptions :: [String], - slbiGhcLangOptions :: [(ChComponentName, [String])] + slbiGhcLangOptions :: [(ChComponentName, [String])], + slbiPkgLicenses :: [(String, [(String, Version)])] } deriving (Eq, Ord, Read, Show) -- | Caches helper executable result so it doesn't have to be run more than once @@ -112,54 +157,20 @@ (ReaderT QueryEnv m) a } deriving (Functor, Applicative, Monad, MonadIO) -data QueryEnv = QueryEnv { - _qeReadProcess :: FilePath -> [String] -> String -> IO String, - _qeProgs :: Programs, - _qeProjectDir :: FilePath, - _qeDistDir :: FilePath - } - type MonadQuery m = ( MonadIO m , MonadState (Maybe SomeLocalBuildInfo) m , MonadReader QueryEnv m) -run :: Monad m - => QueryEnv -> Maybe SomeLocalBuildInfo -> Query m a -> m a +run :: Monad m => QueryEnv -> Maybe SomeLocalBuildInfo -> Query m a -> m a run e s action = flip runReaderT e (flip evalStateT s (unQuery action)) -- | @runQuery query distdir@. Run a 'Query'. @distdir@ is where Cabal's -- @setup-config@ file is located. runQuery :: Monad m - => FilePath -- ^ Path to project directory, i.e. the one containing the - -- @project.cabal@ file - -> FilePath -- ^ Path to @dist/@ - -> Query m a - -> m a -runQuery pd dd action = run (QueryEnv readProcess def pd dd) Nothing action - -runQuery' :: Monad m - => Programs - -> FilePath -- ^ Path to project directory, i.e. the one containing the - -- @project.cabal@ file - -> FilePath -- ^ Path to @dist/@ - -> Query m a - -> m a -runQuery' progs pd dd action = - run (QueryEnv readProcess progs pd dd) Nothing action - -runQuery'' :: Monad m - => (FilePath -> [String] -> String -> IO String) - -- ^ How to start the cabal-helper process. Useful if you need to - -- capture stderr output from the helper. - -> Programs - -> FilePath -- ^ Path to project directory, i.e. the one containing the - -- @project.cabal@ file - -> FilePath -- ^ Path to @dist/@ + => QueryEnv -> Query m a -> m a -runQuery'' readProc progs pd dd action = - run (QueryEnv readProc progs pd dd) Nothing action - +runQuery qe action = run qe Nothing action getSlbi :: MonadQuery m => m SomeLocalBuildInfo getSlbi = do @@ -198,6 +209,9 @@ -- | Only language related options, i.e. @-XSomeExtension@ ghcLangOptions :: MonadIO m => Query m [(ChComponentName, [String])] +-- | Get the licenses of the packages the current project is linking against. +pkgLicenses :: MonadIO m => Query m [(String, [(String, Version)])] + packageDbStack = Query $ slbiPackageDbStack `liftM` getSlbi entrypoints = Query $ slbiEntrypoints `liftM` getSlbi sourceDirs = Query $ slbiSourceDirs `liftM` getSlbi @@ -206,6 +220,7 @@ ghcPkgOptions = Query $ slbiGhcPkgOptions `liftM` getSlbi ghcMergedPkgOptions = Query $ slbiGhcMergedPkgOptions `liftM` getSlbi ghcLangOptions = Query $ slbiGhcLangOptions `liftM` getSlbi +pkgLicenses = Query $ slbiPkgLicenses `liftM` getSlbi -- | Run @cabal configure@ reconfigure :: MonadIO m @@ -218,7 +233,7 @@ [ "--with-ghc=" ++ ghcProgram progs ] -- Only pass ghc-pkg if it was actually set otherwise we -- might break cabal's guessing logic - ++ if ghcPkgProgram progs /= ghcPkgProgram def + ++ if ghcPkgProgram progs /= "ghc-pkg" then [ "--with-ghc-pkg=" ++ ghcPkgProgram progs ] else [] ++ cabalOpts @@ -226,13 +241,17 @@ return () getSomeConfigState :: MonadQuery m => m SomeLocalBuildInfo -getSomeConfigState = ask >>= \(QueryEnv readProc progs projdir distdir) -> do - let progArgs = [ "--with-ghc=" ++ ghcProgram progs +getSomeConfigState = ask >>= \QueryEnv {..} -> do + let progs = qePrograms + projdir = qeProjectDir + distdir = qeDistDir + + progArgs = [ "--with-ghc=" ++ ghcProgram progs , "--with-ghc-pkg=" ++ ghcPkgProgram progs , "--with-cabal=" ++ cabalProgram progs ] - let args = [ "package-db-stack" + args = [ "package-db-stack" , "entrypoints" , "source-dirs" , "ghc-options" @@ -240,14 +259,15 @@ , "ghc-pkg-options" , "ghc-merged-pkg-options" , "ghc-lang-options" - ] ++ progArgs + , "licenses" + ] res <- liftIO $ do exe <- findLibexecExe "cabal-helper-wrapper" - out <- readProc exe (projdir:distdir:args) "" + out <- qeReadProcess exe (progArgs ++ projdir:distdir:args) "" evaluate (read out) `E.catch` \(SomeException _) -> error $ concat ["getSomeConfigState", ": ", exe, " " - , intercalate " " (map show $ distdir:args) + , intercalate " " (map show $ progArgs ++ projdir:distdir:args) , " (read failed)"] let [ Just (ChResponsePkgDbs pkgDbs), @@ -257,10 +277,12 @@ Just (ChResponseCompList ghcSrcOpts), Just (ChResponseCompList ghcPkgOpts), Just (ChResponseList ghcMergedPkgOpts), - Just (ChResponseCompList ghcLangOpts) ] = res + Just (ChResponseCompList ghcLangOpts), + Just (ChResponseLicenses pkgLics) + ] = res return $ SomeLocalBuildInfo - pkgDbs eps srcDirs ghcOpts ghcSrcOpts ghcPkgOpts ghcMergedPkgOpts ghcLangOpts + pkgDbs eps srcDirs ghcOpts ghcSrcOpts ghcPkgOpts ghcMergedPkgOpts ghcLangOpts pkgLics -- | Make sure the appropriate helper executable for the given project is -- installed and ready to run queries. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/cabal-helper.cabal new/cabal-helper-0.6.0.0/cabal-helper.cabal --- old/cabal-helper-0.5.2.0/cabal-helper.cabal 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/cabal-helper.cabal 2015-09-07 08:02:26.000000000 +0200 @@ -1,5 +1,5 @@ name: cabal-helper -version: 0.5.2.0 +version: 0.6.0.0 synopsis: Simple interface to some of Cabal's configuration state used by ghc-mod description: @cabal-helper@ provides a library which wraps the internal use of @@ -35,6 +35,7 @@ build-type: Custom cabal-version: >=1.10 extra-source-files: CabalHelper/Main.hs + CabalHelper/Licenses.hs source-repository head type: git @@ -49,7 +50,6 @@ GHC-Options: -Wall Build-Depends: base >= 4.5 && < 5 , Cabal >= 1.14 && < 1.23 - , data-default , directory , filepath , transformers @@ -66,6 +66,8 @@ CabalHelper.Common CabalHelper.GuessGhc CabalHelper.Data + CabalHelper.Compile + CabalHelper.Log GHC-Options: -Wall X-Install-Target: $libexecdir Build-Depends: base >= 4.5 && < 5 @@ -78,16 +80,26 @@ , template-haskell , temporary , utf8-string - + , ghc-prim Test-Suite spec Default-Language: Haskell2010 Type: exitcode-stdio-1.0 Main-Is: Spec.hs - Hs-Source-Dirs: tests + Hs-Source-Dirs: tests, . GHC-Options: -Wall Build-Depends: base >= 4.5 && < 5 , cabal-helper , extra , unix + , Cabal >= 1.14 && < 1.23 + , directory + , filepath + , transformers + , mtl , process + , ghc-prim + , bytestring + , utf8-string + , template-haskell + , temporary \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cabal-helper-0.5.2.0/tests/Spec.hs new/cabal-helper-0.6.0.0/tests/Spec.hs --- old/cabal-helper-0.5.2.0/tests/Spec.hs 2015-08-20 04:02:22.000000000 +0200 +++ new/cabal-helper-0.6.0.0/tests/Spec.hs 2015-09-07 08:02:26.000000000 +0200 @@ -2,10 +2,83 @@ import System.Environment.Extra (lookupEnv) import System.Posix.Env (setEnv) import System.Process +import System.Exit import Data.Maybe +import Data.Version import Data.Functor +import Control.Exception as E +import Control.Arrow + +import CabalHelper.Common +import CabalHelper.Compile +import CabalHelper.Types main :: IO () main = do flip (setEnv "HOME") True =<< fromMaybe "/tmp" <$> lookupEnv "TMPDIR" writeAutogenFiles readProcess "." "./dist" + + _ <- system "cabal update" + + let vers :: [(Version, [Version])] + vers = map (parseVer *** map parseVer) [ + ("7.4", [ "1.14.0" + ]), + + ("7.6", [ "1.16.0" + , "1.16.0.1" + , "1.16.0.2" + , "1.16.0.3" + ]), + + ("7.8", [ +-- "1.18.0" +-- , "1.18.1" + "1.18.1.1" +-- , "1.18.1.2" + , "1.18.1.3" + , "1.18.1.4" + , "1.18.1.5" + , "1.18.1.6" + + , "1.20.0.0" + , "1.20.0.1" + , "1.20.0.2" + , "1.20.0.3" + , "1.22.0.0" + , "1.22.1.0" + , "1.22.1.1" + ]), + + ("7.10", [ + "1.22.2.0" + , "1.22.3.0" + , "1.22.4.0" + ]) + ] + + ghcVer <- ghcVersion defaultOptions + + let cabalVers = concat $ map snd $ dropWhile ((<ghcVer) . fst) vers + + rvs <- mapM compilePrivatePkgDb cabalVers + + if any isLeft' rvs + then exitFailure + else exitSuccess + where + isLeft' (Left _) = True + isLeft' (Right _) = False + +compilePrivatePkgDb :: Version -> IO (Either ExitCode FilePath) +compilePrivatePkgDb cabalVer = do + db <- installCabal defaultOptions cabalVer `E.catch` + \(SomeException _) -> errorInstallCabal cabalVer "dist" + compileWithPkg "." (Just db) cabalVer + +compileWithPkg :: FilePath -> Maybe FilePath -> Version -> IO (Either ExitCode FilePath) +compileWithPkg chdir mdb ver = + compile "dist" defaultOptions $ Compile chdir Nothing mdb ver [cabalPkgId ver] + +cabalPkgId :: Version -> String +cabalPkgId v = "Cabal-" ++ showVersion v
participants (1)
-
root@hilbert.suse.de