From b1c02f1fd7a2939f9e5c1b0b2fe1df1207e096dc Mon Sep 17 00:00:00 2001 From: Fendor Date: Sat, 31 Jul 2021 15:02:29 +0200 Subject: [PATCH 01/10] Enhance show-build-info API Adds missing file information for test and benchmark stanzas. --- Cabal/src/Distribution/Simple/Build.hs | 3 +- .../src/Distribution/Simple/ShowBuildInfo.hs | 138 +++++++++++------- 2 files changed, 86 insertions(+), 55 deletions(-) diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 5b0842fb9e6..596a885b401 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -141,8 +141,9 @@ showBuildInfo :: PackageDescription -- ^ Mostly information from the .cabal fil showBuildInfo pkg_descr lbi flags = do let verbosity = fromFlag (buildVerbosity flags) targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs flags) + pwd <- getCurrentDirectory let targetsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) - doc = mkBuildInfo pkg_descr lbi flags targetsToBuild + doc = mkBuildInfo pwd pkg_descr lbi flags targetsToBuild return $ renderJson doc diff --git a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs index 5dfe8e3a107..fbbebbe9e0d 100644 --- a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs +++ b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs @@ -54,7 +54,10 @@ -- Note: At the moment this is only supported when using the GHC compiler. -- -module Distribution.Simple.ShowBuildInfo (mkBuildInfo) where +{-# LANGUAGE OverloadedStrings #-} + +module Distribution.Simple.ShowBuildInfo + ( mkBuildInfo, mkBuildInfo', mkCompilerInfo, mkComponentInfo ) where import Distribution.Compat.Prelude import Prelude () @@ -74,71 +77,98 @@ import Distribution.Utils.Json import Distribution.Types.TargetInfo import Distribution.Text import Distribution.Pretty -import Distribution.Utils.Path -- | Construct a JSON document describing the build information for a -- package. mkBuildInfo - :: PackageDescription -- ^ Mostly information from the .cabal file + :: FilePath -- ^ The source directory of the package + -> PackageDescription -- ^ Mostly information from the .cabal file -> LocalBuildInfo -- ^ Configuration information -> BuildFlags -- ^ Flags that the user passed to build -> [TargetInfo] -> Json -mkBuildInfo pkg_descr lbi _flags targetsToBuild = info - where - targetToNameAndLBI target = - (componentLocalName $ targetCLBI target, targetCLBI target) - componentsToBuild = map targetToNameAndLBI targetsToBuild +mkBuildInfo wdir pkg_descr lbi _flags targetsToBuild = + JsonObject $ + mkBuildInfo' (mkCompilerInfo (withPrograms lbi) (compiler lbi)) + (map (mkComponentInfo wdir pkg_descr lbi . targetCLBI) targetsToBuild) - info = JsonObject - [ "cabal-version" .= JsonString (display cabalVersion) - , "compiler" .= mkCompilerInfo - , "components" .= JsonArray (map mkComponentInfo componentsToBuild) - ] +-- | A variant of 'mkBuildInfo' if you need to call 'mkCompilerInfo' and +-- 'mkComponentInfo' yourself. +mkBuildInfo' + :: Json -- ^ The 'Json' from 'mkCompilerInfo' + -> [Json] -- ^ The 'Json' from 'mkComponentInfo' + -> [(String, Json)] +mkBuildInfo' cmplrInfo componentInfos = + [ "cabal-version" .= JsonString (display cabalVersion) + , "compiler" .= cmplrInfo + , "components" .= JsonArray componentInfos + ] - mkCompilerInfo = JsonObject - [ "flavour" .= JsonString (prettyShow $ compilerFlavor $ compiler lbi) - , "compiler-id" .= JsonString (showCompilerId $ compiler lbi) - , "path" .= path - ] - where - path = maybe JsonNull (JsonString . programPath) - $ (flavorToProgram . compilerFlavor $ compiler lbi) - >>= flip lookupProgram (withPrograms lbi) +mkCompilerInfo :: ProgramDb -> Compiler -> Json +mkCompilerInfo programDb cmplr = JsonObject + [ "flavour" .= JsonString (prettyShow $ compilerFlavor cmplr) + , "compiler-id" .= JsonString (showCompilerId cmplr) + , "path" .= path + ] + where + path = maybe JsonNull (JsonString . programPath) + $ (flavorToProgram . compilerFlavor $ cmplr) + >>= flip lookupProgram programDb - flavorToProgram :: CompilerFlavor -> Maybe Program - flavorToProgram GHC = Just ghcProgram - flavorToProgram GHCJS = Just ghcjsProgram - flavorToProgram UHC = Just uhcProgram - flavorToProgram JHC = Just jhcProgram - flavorToProgram _ = Nothing + flavorToProgram :: CompilerFlavor -> Maybe Program + flavorToProgram GHC = Just ghcProgram + flavorToProgram GHCJS = Just ghcjsProgram + flavorToProgram UHC = Just uhcProgram + flavorToProgram JHC = Just jhcProgram + flavorToProgram _ = Nothing - mkComponentInfo (name, clbi) = JsonObject - [ "type" .= JsonString compType - , "name" .= JsonString (prettyShow name) - , "unit-id" .= JsonString (prettyShow $ componentUnitId clbi) - , "compiler-args" .= JsonArray (map JsonString $ getCompilerArgs bi lbi clbi) - , "modules" .= JsonArray (map (JsonString . display) modules) - , "src-files" .= JsonArray (map JsonString sourceFiles) - , "src-dirs" .= JsonArray (map JsonString $ map getSymbolicPath $ hsSourceDirs bi) - ] - where - bi = componentBuildInfo comp - comp = fromMaybe (error $ "mkBuildInfo: no component " ++ prettyShow name) $ lookupComponent pkg_descr name - compType = case comp of - CLib _ -> "lib" - CExe _ -> "exe" - CTest _ -> "test" - CBench _ -> "bench" - CFLib _ -> "flib" - modules = case comp of - CLib lib -> explicitLibModules lib - CExe exe -> exeModules exe - _ -> [] - sourceFiles = case comp of - CLib _ -> [] - CExe exe -> [modulePath exe] - _ -> [] +mkComponentInfo :: FilePath -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> Json +mkComponentInfo wdir pkg_descr lbi clbi = JsonObject $ + [ "type" .= JsonString compType + , "name" .= JsonString (prettyShow name) + , "unit-id" .= JsonString (prettyShow $ componentUnitId clbi) + , "compiler-args" .= JsonArray (map JsonString $ getCompilerArgs bi lbi clbi) + , "modules" .= JsonArray (map (JsonString . display) modules) + , "src-files" .= JsonArray (map JsonString sourceFiles) + , "hs-src-dirs" .= JsonArray (map (JsonString . prettyShow) $ hsSourceDirs bi) + , "src-dir" .= JsonString wdir + ] <> cabalFile + where + name = componentLocalName clbi + bi = componentBuildInfo comp + comp = fromMaybe (error $ "mkBuildInfo: no component " ++ prettyShow name) $ lookupComponent pkg_descr name + compType = case comp of + CLib _ -> "lib" + CExe _ -> "exe" + CTest _ -> "test" + CBench _ -> "bench" + CFLib _ -> "flib" + modules = case comp of + CLib lib -> explicitLibModules lib + CExe exe -> exeModules exe + CTest test -> + case testInterface test of + TestSuiteExeV10 _ _ -> [] + TestSuiteLibV09 _ modName -> [modName] + TestSuiteUnsupported _ -> [] + CBench bench -> benchmarkModules bench + CFLib flib -> foreignLibModules flib + sourceFiles = case comp of + CLib _ -> [] + CExe exe -> [modulePath exe] + CTest test -> + case testInterface test of + TestSuiteExeV10 _ fp -> [fp] + TestSuiteLibV09 _ _ -> [] + TestSuiteUnsupported _ -> [] + CBench bench -> case benchmarkInterface bench of + BenchmarkExeV10 _ fp -> [fp] + BenchmarkUnsupported _ -> [] + + CFLib _ -> [] + cabalFile + | Just fp <- pkgDescrFile lbi = [("cabal-file", JsonString fp)] + | otherwise = [] -- | Get the command-line arguments that would be passed -- to the compiler to build the given component. From fb53623fdb3fed6838cd819fbc7b25b8c7def78f Mon Sep 17 00:00:00 2001 From: Fendor Date: Sat, 31 Jul 2021 15:07:23 +0200 Subject: [PATCH 02/10] Enhance show-build-info command API --- Cabal/src/Distribution/Simple.hs | 31 ++++++++++++++------------ Cabal/src/Distribution/Simple/Build.hs | 21 ++++++++++++----- Cabal/src/Distribution/Simple/Setup.hs | 17 +++++++++----- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/Cabal/src/Distribution/Simple.hs b/Cabal/src/Distribution/Simple.hs index fc915322968..1eb958a0350 100644 --- a/Cabal/src/Distribution/Simple.hs +++ b/Cabal/src/Distribution/Simple.hs @@ -265,31 +265,34 @@ buildAction hooks flags args = do hooks flags' { buildArgs = args } args showBuildInfoAction :: UserHooks -> ShowBuildInfoFlags -> Args -> IO () -showBuildInfoAction hooks (ShowBuildInfoFlags flags fileOutput) args = do - distPref <- findDistPrefOrDefault (buildDistPref flags) - let verbosity = fromFlag $ buildVerbosity flags +showBuildInfoAction hooks flags args = do + let buildFlags = buildInfoBuildFlags flags + distPref <- findDistPrefOrDefault (buildDistPref buildFlags) + let verbosity = fromFlag $ buildVerbosity buildFlags lbi <- getBuildConfig hooks verbosity distPref - let flags' = flags { buildDistPref = toFlag distPref - , buildCabalFilePath = maybeToFlag (cabalFilePath lbi) - } + let buildFlags' = + buildFlags { buildDistPref = toFlag distPref + , buildCabalFilePath = maybeToFlag (cabalFilePath lbi) + } progs <- reconfigurePrograms verbosity - (buildProgramPaths flags') - (buildProgramArgs flags') + (buildProgramPaths buildFlags') + (buildProgramArgs buildFlags') (withPrograms lbi) - pbi <- preBuild hooks args flags' + pbi <- preBuild hooks args buildFlags' let lbi' = lbi { withPrograms = progs } pkg_descr0 = localPkgDescr lbi' pkg_descr = updatePackageDescription pbi pkg_descr0 -- TODO: Somehow don't ignore build hook? - buildInfoString <- showBuildInfo pkg_descr lbi' flags - case fileOutput of - Nothing -> B.putStr buildInfoString - Just fp -> B.writeFile fp buildInfoString + buildInfoByteString <- showBuildInfo pkg_descr lbi' flags + + case buildInfoOutputFile flags of + Nothing -> B.putStr buildInfoByteString + Just fp -> B.writeFile fp buildInfoByteString - postBuild hooks args flags' pkg_descr lbi' + postBuild hooks args buildFlags' pkg_descr lbi' replAction :: UserHooks -> ReplFlags -> Args -> IO () replAction hooks flags args = do diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 596a885b401..7c5bd280d89 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -88,6 +88,7 @@ import Distribution.Compat.Graph (IsNode(..)) import Control.Monad import Data.ByteString.Lazy (ByteString) +import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.Set as Set import System.FilePath ( (), (<.>), takeDirectory ) import System.Directory ( getCurrentDirectory ) @@ -135,16 +136,24 @@ build pkg_descr lbi flags suffixes = do showBuildInfo :: PackageDescription -- ^ Mostly information from the .cabal file - -> LocalBuildInfo -- ^ Configuration information - -> BuildFlags -- ^ Flags that the user passed to build + -> LocalBuildInfo -- ^ Configuration information + -> ShowBuildInfoFlags -- ^ Flags that the user passed to build -> IO ByteString showBuildInfo pkg_descr lbi flags = do - let verbosity = fromFlag (buildVerbosity flags) - targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs flags) + let buildFlags = buildInfoBuildFlags flags + verbosity = fromFlag (buildVerbosity buildFlags) + targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs buildFlags) pwd <- getCurrentDirectory let targetsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) - doc = mkBuildInfo pwd pkg_descr lbi flags targetsToBuild - return $ renderJson doc + result + | fromFlag (buildInfoComponentsOnly flags) = + let components = map (mkComponentInfo pwd pkg_descr lbi . targetCLBI) + targetsToBuild + in LBS.unlines $ map renderJson components + | otherwise = + let json = mkBuildInfo pwd pkg_descr lbi buildFlags targetsToBuild + in renderJson json + return result repl :: PackageDescription -- ^ Mostly information from the .cabal file diff --git a/Cabal/src/Distribution/Simple/Setup.hs b/Cabal/src/Distribution/Simple/Setup.hs index 195ef3e1eb7..8f0ad64de1f 100644 --- a/Cabal/src/Distribution/Simple/Setup.hs +++ b/Cabal/src/Distribution/Simple/Setup.hs @@ -2189,15 +2189,18 @@ optionNumJobs get set = -- ------------------------------------------------------------ data ShowBuildInfoFlags = ShowBuildInfoFlags - { buildInfoBuildFlags :: BuildFlags - , buildInfoOutputFile :: Maybe FilePath + { buildInfoBuildFlags :: BuildFlags + , buildInfoOutputFile :: Maybe FilePath + , buildInfoComponentsOnly :: Flag Bool + -- ^ If 'True' then only print components, each separated by a newline } deriving (Show, Typeable) defaultShowBuildFlags :: ShowBuildInfoFlags defaultShowBuildFlags = ShowBuildInfoFlags - { buildInfoBuildFlags = defaultBuildFlags - , buildInfoOutputFile = Nothing + { buildInfoBuildFlags = defaultBuildFlags + , buildInfoOutputFile = Nothing + , buildInfoComponentsOnly = Flag False } showBuildInfoCommand :: ProgramDb -> CommandUI ShowBuildInfoFlags @@ -2234,8 +2237,12 @@ showBuildInfoCommand progDb = CommandUI ++ [ option [] ["buildinfo-json-output"] "Write the result to the given file instead of stdout" - buildInfoOutputFile (\pf flags -> flags { buildInfoOutputFile = pf }) + buildInfoOutputFile (\v flags -> flags { buildInfoOutputFile = v }) (reqArg' "FILE" Just (maybe [] pure)) + , option [] ["buildinfo-components-only"] + "Print out only the component info, each separated by a newline" + buildInfoComponentsOnly (\v flags -> flags { buildInfoComponentsOnly = v}) + trueArg ] } From a6e227a84c8fbeb380569a1e4ab94a3f9b06f64a Mon Sep 17 00:00:00 2001 From: Fendor Date: Mon, 16 Aug 2021 11:35:39 +0200 Subject: [PATCH 03/10] Remove show-build-info command and generate build-info on build Removes 'show-build-info' command from 'lib:Cabal' and replaces it by generating build-info whenever a build happens. Add flag '--dump-buildinfo' to signal the build step to dump build information for the package/component we are currently building. --- .../src/Test/QuickCheck/Instances/Cabal.hs | 8 +- .../Distribution/Utils/Structured.hs | 2 +- .../src/Data/TreeDiff/Instances/Cabal.hs | 2 + Cabal/Cabal.cabal | 1 + Cabal/src/Distribution/Simple.hs | 32 ------ Cabal/src/Distribution/Simple/Build.hs | 52 ++++----- Cabal/src/Distribution/Simple/BuildPaths.hs | 6 +- Cabal/src/Distribution/Simple/Setup.hs | 103 ++++-------------- .../src/Distribution/Simple/ShowBuildInfo.hs | 76 +++++++------ Cabal/src/Distribution/Types/DumpBuildInfo.hs | 15 +++ .../src/Distribution/Client/Config.hs | 1 + .../Client/ProjectConfig/Legacy.hs | 42 +++++-- .../Client/ProjectConfig/Types.hs | 3 +- .../Distribution/Client/ProjectPlanning.hs | 4 + .../Client/ProjectPlanning/Types.hs | 4 +- .../src/Distribution/Client/Setup.hs | 7 +- .../Distribution/Client/ProjectConfig.hs | 8 +- 17 files changed, 173 insertions(+), 193 deletions(-) create mode 100644 Cabal/src/Distribution/Types/DumpBuildInfo.hs diff --git a/Cabal-QuickCheck/src/Test/QuickCheck/Instances/Cabal.hs b/Cabal-QuickCheck/src/Test/QuickCheck/Instances/Cabal.hs index 3d471ea6283..b2b6853c604 100644 --- a/Cabal-QuickCheck/src/Test/QuickCheck/Instances/Cabal.hs +++ b/Cabal-QuickCheck/src/Test/QuickCheck/Instances/Cabal.hs @@ -25,7 +25,7 @@ import Distribution.ModuleName import Distribution.Simple.Compiler (DebugInfoLevel (..), OptimisationLevel (..), PackageDB (..), ProfDetailLevel (..), knownProfDetailLevels) import Distribution.Simple.Flag (Flag (..)) import Distribution.Simple.InstallDirs -import Distribution.Simple.Setup (HaddockTarget (..), TestShowDetails (..)) +import Distribution.Simple.Setup (HaddockTarget (..), TestShowDetails (..), DumpBuildInfo) import Distribution.SPDX import Distribution.System import Distribution.Types.Dependency @@ -486,6 +486,12 @@ instance Arbitrary PackageDB where , SpecificPackageDB <$> arbitraryShortToken ] +------------------------------------------------------------------------------- +-- DumpBuildInfo +------------------------------------------------------------------------------- + +instance Arbitrary DumpBuildInfo where + arbitrary = arbitraryBoundedEnum ------------------------------------------------------------------------------- -- Helpers diff --git a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs index 1d715af86e9..9ad401b4b17 100644 --- a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs +++ b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs @@ -29,7 +29,7 @@ tests = testGroup "Distribution.Utils.Structured" , testCase "GenericPackageDescription" $ md5Check (Proxy :: Proxy GenericPackageDescription) 0xa164cbe5092a1cd31da1f15358d1537a , testCase "LocalBuildInfo" $ - md5Check (Proxy :: Proxy LocalBuildInfo) 0xac70971ea59d30aab7e4b6dafc9113d4 + md5Check (Proxy :: Proxy LocalBuildInfo) 0x9ce83e4aec3b2fa6d7f999dbc32c2a33 #endif ] diff --git a/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs b/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs index 40765a6764b..98bb586d507 100644 --- a/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs +++ b/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs @@ -29,6 +29,7 @@ import Distribution.Simple.Setup (HaddockTarget, TestShowDetai import Distribution.System import Distribution.Types.AbiHash (AbiHash) import Distribution.Types.ComponentId (ComponentId) +import Distribution.Types.DumpBuildInfo (DumpBuildInfo) import Distribution.Types.PackageVersionConstraint import Distribution.Types.UnitId (DefUnitId, UnitId) import Distribution.Utils.NubList (NubList) @@ -74,6 +75,7 @@ instance ToExpr CompilerId instance ToExpr ComponentId instance ToExpr DebugInfoLevel instance ToExpr DefUnitId +instance ToExpr DumpBuildInfo instance ToExpr ExeDependency instance ToExpr Executable instance ToExpr ExecutableScope diff --git a/Cabal/Cabal.cabal b/Cabal/Cabal.cabal index 8158d4640ed..3991c48321e 100644 --- a/Cabal/Cabal.cabal +++ b/Cabal/Cabal.cabal @@ -201,6 +201,7 @@ library Distribution.Types.ComponentInclude Distribution.Types.ConfVar Distribution.Types.Dependency + Distribution.Types.DumpBuildInfo Distribution.Types.ExeDependency Distribution.Types.LegacyExeDependency Distribution.Types.PkgconfigDependency diff --git a/Cabal/src/Distribution/Simple.hs b/Cabal/src/Distribution/Simple.hs index 1eb958a0350..fe19558db1a 100644 --- a/Cabal/src/Distribution/Simple.hs +++ b/Cabal/src/Distribution/Simple.hs @@ -104,7 +104,6 @@ import Distribution.Compat.Directory (makeAbsolute) import Distribution.Compat.Environment (getEnvironment) import Distribution.Compat.GetShortPathName (getShortPathName) -import qualified Data.ByteString.Lazy as B import Data.List (unionBy, (\\)) import Distribution.PackageDescription.Parsec @@ -179,7 +178,6 @@ defaultMainHelper hooks args = topHandler $ do [configureCommand progs `commandAddAction` \fs as -> configureAction hooks fs as >> return () ,buildCommand progs `commandAddAction` buildAction hooks - ,showBuildInfoCommand progs `commandAddAction` showBuildInfoAction hooks ,replCommand progs `commandAddAction` replAction hooks ,installCommand `commandAddAction` installAction hooks ,copyCommand `commandAddAction` copyAction hooks @@ -264,36 +262,6 @@ buildAction hooks flags args = do (return lbi { withPrograms = progs }) hooks flags' { buildArgs = args } args -showBuildInfoAction :: UserHooks -> ShowBuildInfoFlags -> Args -> IO () -showBuildInfoAction hooks flags args = do - let buildFlags = buildInfoBuildFlags flags - distPref <- findDistPrefOrDefault (buildDistPref buildFlags) - let verbosity = fromFlag $ buildVerbosity buildFlags - lbi <- getBuildConfig hooks verbosity distPref - let buildFlags' = - buildFlags { buildDistPref = toFlag distPref - , buildCabalFilePath = maybeToFlag (cabalFilePath lbi) - } - - progs <- reconfigurePrograms verbosity - (buildProgramPaths buildFlags') - (buildProgramArgs buildFlags') - (withPrograms lbi) - - pbi <- preBuild hooks args buildFlags' - let lbi' = lbi { withPrograms = progs } - pkg_descr0 = localPkgDescr lbi' - pkg_descr = updatePackageDescription pbi pkg_descr0 - -- TODO: Somehow don't ignore build hook? - - buildInfoByteString <- showBuildInfo pkg_descr lbi' flags - - case buildInfoOutputFile flags of - Nothing -> B.putStr buildInfoByteString - Just fp -> B.writeFile fp buildInfoByteString - - postBuild hooks args buildFlags' pkg_descr lbi' - replAction :: UserHooks -> ReplFlags -> Args -> IO () replAction hooks flags args = do distPref <- findDistPrefOrDefault (replDistPref flags) diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 7c5bd280d89..78bf79feaef 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -19,7 +19,7 @@ -- module Distribution.Simple.Build ( - build, showBuildInfo, repl, + build, repl, startInterpreter, initialBuildSteps, @@ -87,11 +87,10 @@ import Distribution.Version (thisVersion) import Distribution.Compat.Graph (IsNode(..)) import Control.Monad -import Data.ByteString.Lazy (ByteString) -import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.Set as Set +import qualified Data.ByteString.Lazy as LBS import System.FilePath ( (), (<.>), takeDirectory ) -import System.Directory ( getCurrentDirectory ) +import System.Directory ( getCurrentDirectory, removeFile, doesFileExist ) -- ----------------------------------------------------------------------------- -- |Build the libraries and executables in this package. @@ -129,32 +128,33 @@ build pkg_descr lbi flags suffixes = do mb_ipi <- buildComponent verbosity (buildNumJobs flags) pkg_descr lbi' suffixes comp clbi distPref return (maybe index (Index.insert `flip` index) mb_ipi) + + when shouldDumpBuildInfo $ do + -- Changing this line might break consumers of the dumped build info. + -- Announce changes on mailing lists! + let activeTargets = allTargetsInBuildOrder' pkg_descr lbi + info verbosity $ "Dump build information for: " + ++ intercalate ", " + (map (showComponentName . componentLocalName . targetCLBI) + activeTargets) + pwd <- getCurrentDirectory + let (warns, json) = mkBuildInfo pwd pkg_descr lbi flags activeTargets + buildInfoText = renderJson json + unless (null warns) $ + warn verbosity $ "Encountered warnings while dumping build-info:\n" + ++ unlines warns + LBS.writeFile (buildInfoPref distPref) buildInfoText + + when (not shouldDumpBuildInfo) $ do + -- Remove existing build-info.json as it might be outdated now. + exists <- doesFileExist (buildInfoPref distPref) + when exists $ removeFile (buildInfoPref distPref) + return () where distPref = fromFlag (buildDistPref flags) verbosity = fromFlag (buildVerbosity flags) - - -showBuildInfo :: PackageDescription -- ^ Mostly information from the .cabal file - -> LocalBuildInfo -- ^ Configuration information - -> ShowBuildInfoFlags -- ^ Flags that the user passed to build - -> IO ByteString -showBuildInfo pkg_descr lbi flags = do - let buildFlags = buildInfoBuildFlags flags - verbosity = fromFlag (buildVerbosity buildFlags) - targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs buildFlags) - pwd <- getCurrentDirectory - let targetsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) - result - | fromFlag (buildInfoComponentsOnly flags) = - let components = map (mkComponentInfo pwd pkg_descr lbi . targetCLBI) - targetsToBuild - in LBS.unlines $ map renderJson components - | otherwise = - let json = mkBuildInfo pwd pkg_descr lbi buildFlags targetsToBuild - in renderJson json - return result - + shouldDumpBuildInfo = fromFlagOrDefault NoDumpBuildInfo (configDumpBuildInfo (configFlags lbi)) == DumpBuildInfo repl :: PackageDescription -- ^ Mostly information from the .cabal file -> LocalBuildInfo -- ^ Configuration information diff --git a/Cabal/src/Distribution/Simple/BuildPaths.hs b/Cabal/src/Distribution/Simple/BuildPaths.hs index 86df023663d..733953747c8 100644 --- a/Cabal/src/Distribution/Simple/BuildPaths.hs +++ b/Cabal/src/Distribution/Simple/BuildPaths.hs @@ -15,7 +15,7 @@ module Distribution.Simple.BuildPaths ( defaultDistPref, srcPref, - haddockDirName, hscolourPref, haddockPref, + buildInfoPref, haddockDirName, hscolourPref, haddockPref, autogenPackageModulesDir, autogenComponentModulesDir, @@ -67,6 +67,10 @@ srcPref distPref = distPref "src" hscolourPref :: HaddockTarget -> FilePath -> PackageDescription -> FilePath hscolourPref = haddockPref +-- | Build info json file, generated in every build +buildInfoPref :: FilePath -> FilePath +buildInfoPref distPref = distPref "build-info.json" + -- | This is the name of the directory in which the generated haddocks -- should be stored. It does not include the @/doc/html@ prefix. haddockDirName :: HaddockTarget -> PackageDescription -> FilePath diff --git a/Cabal/src/Distribution/Simple/Setup.hs b/Cabal/src/Distribution/Simple/Setup.hs index 8f0ad64de1f..6ed08e7ed33 100644 --- a/Cabal/src/Distribution/Simple/Setup.hs +++ b/Cabal/src/Distribution/Simple/Setup.hs @@ -45,7 +45,7 @@ module Distribution.Simple.Setup ( HaddockFlags(..), emptyHaddockFlags, defaultHaddockFlags, haddockCommand, HscolourFlags(..), emptyHscolourFlags, defaultHscolourFlags, hscolourCommand, BuildFlags(..), emptyBuildFlags, defaultBuildFlags, buildCommand, - ShowBuildInfoFlags(..), defaultShowBuildFlags, showBuildInfoCommand, + DumpBuildInfo(..), ReplFlags(..), defaultReplFlags, replCommand, ReplOptions(..), CleanFlags(..), emptyCleanFlags, defaultCleanFlags, cleanCommand, @@ -99,6 +99,7 @@ import Distribution.Simple.InstallDirs import Distribution.Verbosity import Distribution.Utils.NubList import Distribution.Types.ComponentId +import Distribution.Types.DumpBuildInfo import Distribution.Types.GivenComponent import Distribution.Types.Module import Distribution.Types.PackageVersionConstraint @@ -274,6 +275,11 @@ data ConfigFlags = ConfigFlags { -- ^Halt and show an error message indicating an error in flag assignment configRelocatable :: Flag Bool, -- ^ Enable relocatable package built configDebugInfo :: Flag DebugInfoLevel, -- ^ Emit debug info. + configDumpBuildInfo :: Flag DumpBuildInfo, + -- ^ Should we dump available build information on build? + -- Dump build information to disk before attempting to build, + -- tooling can parse these files and use them to compile the + -- source files themselves. configUseResponseFiles :: Flag Bool, -- ^ Whether to use response files at all. They're used for such tools -- as haddock, or ld. @@ -343,6 +349,7 @@ instance Eq ConfigFlags where && equal configFlagError && equal configRelocatable && equal configDebugInfo + && equal configDumpBuildInfo && equal configUseResponseFiles where equal f = on (==) f a b @@ -393,6 +400,7 @@ defaultConfigFlags progDb = emptyConfigFlags { configFlagError = NoFlag, configRelocatable = Flag False, configDebugInfo = Flag NoDebugInfo, + configDumpBuildInfo = NoFlag, configUseResponseFiles = NoFlag } @@ -561,6 +569,17 @@ configureOptions showOrParseArgs = "Don't emit debug info" ] + , multiOption "build-info" + configDumpBuildInfo + (\v flags -> flags { configDumpBuildInfo = v }) + [noArg (Flag DumpBuildInfo) [] + ["enable-build-info"] + "Enable build information generation during project building", + noArg (Flag NoDumpBuildInfo) [] + ["disable-build-info"] + "Disable build information generation during project building" + ] + ,option "" ["library-for-ghci"] "compile library for use with GHCi" configGHCiLib (\v flags -> flags { configGHCiLib = v }) @@ -2183,88 +2202,6 @@ optionNumJobs get set = | otherwise -> Right (Just n) _ -> Left "The jobs value should be a number or '$ncpus'" - --- ------------------------------------------------------------ --- * show-build-info command flags --- ------------------------------------------------------------ - -data ShowBuildInfoFlags = ShowBuildInfoFlags - { buildInfoBuildFlags :: BuildFlags - , buildInfoOutputFile :: Maybe FilePath - , buildInfoComponentsOnly :: Flag Bool - -- ^ If 'True' then only print components, each separated by a newline - } deriving (Show, Typeable) - -defaultShowBuildFlags :: ShowBuildInfoFlags -defaultShowBuildFlags = - ShowBuildInfoFlags - { buildInfoBuildFlags = defaultBuildFlags - , buildInfoOutputFile = Nothing - , buildInfoComponentsOnly = Flag False - } - -showBuildInfoCommand :: ProgramDb -> CommandUI ShowBuildInfoFlags -showBuildInfoCommand progDb = CommandUI - { commandName = "show-build-info" - , commandSynopsis = "Emit details about how a package would be built." - , commandDescription = Just $ \_ -> wrapText $ - "Components encompass executables, tests, and benchmarks.\n" - ++ "\n" - ++ "Affected by configuration options, see `configure`.\n" - , commandNotes = Just $ \pname -> - "Examples:\n" - ++ " " ++ pname ++ " show-build-info " - ++ " All the components in the package\n" - ++ " " ++ pname ++ " show-build-info foo " - ++ " A component (i.e. lib, exe, test suite)\n\n" - ++ programFlagsDescription progDb ---TODO: re-enable once we have support for module/file targets --- ++ " " ++ pname ++ " show-build-info Foo.Bar " --- ++ " A module\n" --- ++ " " ++ pname ++ " show-build-info Foo/Bar.hs" --- ++ " A file\n\n" --- ++ "If a target is ambiguous it can be qualified with the component " --- ++ "name, e.g.\n" --- ++ " " ++ pname ++ " show-build-info foo:Foo.Bar\n" --- ++ " " ++ pname ++ " show-build-info testsuite1:Foo/Bar.hs\n" - , commandUsage = usageAlternatives "show-build-info" $ - [ "[FLAGS]" - , "COMPONENTS [FLAGS]" - ] - , commandDefaultFlags = defaultShowBuildFlags - , commandOptions = \showOrParseArgs -> - parseBuildFlagsForShowBuildInfoFlags showOrParseArgs progDb - ++ - [ option [] ["buildinfo-json-output"] - "Write the result to the given file instead of stdout" - buildInfoOutputFile (\v flags -> flags { buildInfoOutputFile = v }) - (reqArg' "FILE" Just (maybe [] pure)) - , option [] ["buildinfo-components-only"] - "Print out only the component info, each separated by a newline" - buildInfoComponentsOnly (\v flags -> flags { buildInfoComponentsOnly = v}) - trueArg - ] - - } - -parseBuildFlagsForShowBuildInfoFlags :: ShowOrParseArgs -> ProgramDb -> [OptionField ShowBuildInfoFlags] -parseBuildFlagsForShowBuildInfoFlags showOrParseArgs progDb = - map - (liftOption - buildInfoBuildFlags - (\bf flags -> flags { buildInfoBuildFlags = bf } ) - ) - buildFlags - where - buildFlags = buildOptions progDb showOrParseArgs - ++ - [ optionVerbosity - buildVerbosity (\v flags -> flags { buildVerbosity = v }) - - , optionDistPref - buildDistPref (\d flags -> flags { buildDistPref = d }) showOrParseArgs - ] - -- ------------------------------------------------------------ -- * Other Utils -- ------------------------------------------------------------ diff --git a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs index fbbebbe9e0d..9bfda3eadd5 100644 --- a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs +++ b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs @@ -2,7 +2,7 @@ -- This module defines a simple JSON-based format for exporting basic -- information about a Cabal package and the compiler configuration Cabal -- would use to build it. This can be produced with the --- @cabal new-show-build-info@ command. +-- @cabal build --enable-build-info@ command. -- -- -- This format is intended for consumption by external tooling and should @@ -14,7 +14,7 @@ -- Below is an example of the output this module produces, -- -- @ --- { "cabal-version": "1.23.0.0", +-- { "cabal-lib-version": "1.23.0.0", -- "compiler": { -- "flavour": "GHC", -- "compiler-id": "ghc-7.10.2", @@ -34,7 +34,10 @@ -- } -- @ -- --- The @cabal-version@ property provides the version of the Cabal library +-- The output format needs to be validated against 'doc/json-schemas/build-info.schema.json'. +-- If the format changes, update the schema as well! +-- +-- The @cabal-lib-version@ property provides the version of the Cabal library -- which generated the output. The @compiler@ property gives some basic -- information about the compiler Cabal would use to compile the package. -- @@ -56,8 +59,11 @@ {-# LANGUAGE OverloadedStrings #-} -module Distribution.Simple.ShowBuildInfo - ( mkBuildInfo, mkBuildInfo', mkCompilerInfo, mkComponentInfo ) where +module Distribution.Simple.ShowBuildInfo ( + mkBuildInfo, mkBuildInfo', mkCompilerInfo, mkComponentInfo + ) where + +import System.FilePath import Distribution.Compat.Prelude import Prelude () @@ -68,12 +74,14 @@ import qualified Distribution.Simple.Program.GHC as GHC import Distribution.PackageDescription import Distribution.Compiler import Distribution.Verbosity -import Distribution.Simple.Compiler -import Distribution.Simple.LocalBuildInfo +import Distribution.Simple.Compiler (Compiler, showCompilerId, compilerFlavor) import Distribution.Simple.Program import Distribution.Simple.Setup import Distribution.Simple.Utils (cabalVersion) import Distribution.Utils.Json +import Distribution.Types.Component +import Distribution.Types.ComponentLocalBuildInfo +import Distribution.Types.LocalBuildInfo import Distribution.Types.TargetInfo import Distribution.Text import Distribution.Pretty @@ -86,11 +94,13 @@ mkBuildInfo -> LocalBuildInfo -- ^ Configuration information -> BuildFlags -- ^ Flags that the user passed to build -> [TargetInfo] - -> Json -mkBuildInfo wdir pkg_descr lbi _flags targetsToBuild = - JsonObject $ - mkBuildInfo' (mkCompilerInfo (withPrograms lbi) (compiler lbi)) - (map (mkComponentInfo wdir pkg_descr lbi . targetCLBI) targetsToBuild) + -> ([String], Json) -- ^ Json representation of buildinfo alongside generated warnings +mkBuildInfo wdir pkg_descr lbi _flags targetsToBuild = (warnings, JsonObject buildInfoFields) + where + buildInfoFields = mkBuildInfo' (mkCompilerInfo (withPrograms lbi) (compiler lbi)) componentInfos + componentInfosWithWarnings = map (mkComponentInfo wdir pkg_descr lbi . targetCLBI) targetsToBuild + componentInfos = map snd componentInfosWithWarnings + warnings = concatMap fst componentInfosWithWarnings -- | A variant of 'mkBuildInfo' if you need to call 'mkCompilerInfo' and -- 'mkComponentInfo' yourself. @@ -98,21 +108,21 @@ mkBuildInfo' :: Json -- ^ The 'Json' from 'mkCompilerInfo' -> [Json] -- ^ The 'Json' from 'mkComponentInfo' -> [(String, Json)] -mkBuildInfo' cmplrInfo componentInfos = - [ "cabal-version" .= JsonString (display cabalVersion) - , "compiler" .= cmplrInfo - , "components" .= JsonArray componentInfos - ] +mkBuildInfo' compilerInfo componentInfos = + [ "cabal-lib-version" .= JsonString (display cabalVersion) + , "compiler" .= compilerInfo + , "components" .= JsonArray componentInfos + ] mkCompilerInfo :: ProgramDb -> Compiler -> Json -mkCompilerInfo programDb cmplr = JsonObject - [ "flavour" .= JsonString (prettyShow $ compilerFlavor cmplr) - , "compiler-id" .= JsonString (showCompilerId cmplr) +mkCompilerInfo programDb compilerInfo = JsonObject + [ "flavour" .= JsonString (prettyShow $ compilerFlavor compilerInfo) + , "compiler-id" .= JsonString (showCompilerId compilerInfo) , "path" .= path ] where path = maybe JsonNull (JsonString . programPath) - $ (flavorToProgram . compilerFlavor $ cmplr) + $ (flavorToProgram . compilerFlavor $ compilerInfo) >>= flip lookupProgram programDb flavorToProgram :: CompilerFlavor -> Maybe Program @@ -122,20 +132,22 @@ mkCompilerInfo programDb cmplr = JsonObject flavorToProgram JHC = Just jhcProgram flavorToProgram _ = Nothing -mkComponentInfo :: FilePath -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> Json -mkComponentInfo wdir pkg_descr lbi clbi = JsonObject $ +mkComponentInfo :: FilePath -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> ([String], Json) +mkComponentInfo wdir pkg_descr lbi clbi = (warnings, JsonObject $ [ "type" .= JsonString compType , "name" .= JsonString (prettyShow name) , "unit-id" .= JsonString (prettyShow $ componentUnitId clbi) - , "compiler-args" .= JsonArray (map JsonString $ getCompilerArgs bi lbi clbi) + , "compiler-args" .= JsonArray (map JsonString compilerArgs) , "modules" .= JsonArray (map (JsonString . display) modules) , "src-files" .= JsonArray (map JsonString sourceFiles) , "hs-src-dirs" .= JsonArray (map (JsonString . prettyShow) $ hsSourceDirs bi) - , "src-dir" .= JsonString wdir - ] <> cabalFile + , "src-dir" .= JsonString (addTrailingPathSeparator wdir) + ] <> cabalFile) where + (warnings, compilerArgs) = getCompilerArgs bi lbi clbi name = componentLocalName clbi bi = componentBuildInfo comp + -- If this error happens, a cabal invariant has been violated comp = fromMaybe (error $ "mkBuildInfo: no component " ++ prettyShow name) $ lookupComponent pkg_descr name compType = case comp of CLib _ -> "lib" @@ -176,13 +188,15 @@ getCompilerArgs :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo - -> [String] + -> ([String], [String]) getCompilerArgs bi lbi clbi = case compilerFlavor $ compiler lbi of - GHC -> ghc - GHCJS -> ghc - c -> error $ "ShowBuildInfo.getCompilerArgs: Don't know how to get "++ - "build arguments for compiler "++show c + GHC -> ([], ghc) + GHCJS -> ([], ghc) + c -> + ( ["ShowBuildInfo.getCompilerArgs: Don't know how to get build " + ++ " arguments for compiler " ++ show c] + , []) where -- This is absolutely awful ghc = GHC.renderGhcOptions (compiler lbi) (hostPlatform lbi) baseOpts diff --git a/Cabal/src/Distribution/Types/DumpBuildInfo.hs b/Cabal/src/Distribution/Types/DumpBuildInfo.hs new file mode 100644 index 00000000000..5657a65aa71 --- /dev/null +++ b/Cabal/src/Distribution/Types/DumpBuildInfo.hs @@ -0,0 +1,15 @@ +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +module Distribution.Types.DumpBuildInfo + ( DumpBuildInfo(..) + ) where + +import Distribution.Compat.Prelude + +data DumpBuildInfo + = NoDumpBuildInfo + | DumpBuildInfo + deriving (Read, Show, Eq, Ord, Enum, Bounded, Generic, Typeable) + +instance Binary DumpBuildInfo +instance Structured DumpBuildInfo diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index 1c2df3dc713..c5ca5f18114 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -409,6 +409,7 @@ instance Semigroup SavedConfig where configFlagError = combine configFlagError, configRelocatable = combine configRelocatable, configUseResponseFiles = combine configUseResponseFiles, + configDumpBuildInfo = combine configDumpBuildInfo, configAllowDependingOnPrivateLibs = combine configAllowDependingOnPrivateLibs } diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 0aca7ac2a4a..38852d0fd00 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -54,7 +54,7 @@ import Distribution.Simple.Setup , HaddockFlags(..), haddockOptions, defaultHaddockFlags , TestFlags(..), testOptions', defaultTestFlags , BenchmarkFlags(..), benchmarkOptions', defaultBenchmarkFlags - , programDbPaths', splitArgs + , programDbPaths', splitArgs, DumpBuildInfo (NoDumpBuildInfo, DumpBuildInfo) ) import Distribution.Client.NixStyleOptions (NixStyleFlags (..)) import Distribution.Client.ProjectFlags (ProjectFlags (..), projectFlagsOptions, defaultProjectFlags) @@ -437,6 +437,7 @@ convertLegacyPerPackageFlags configFlags installFlags configCoverage = coverage, configLibCoverage = libcoverage, --deprecated configDebugInfo = packageConfigDebugInfo, + configDumpBuildInfo = packageConfigDumpBuildInfo, configRelocatable = packageConfigRelocatable } = configFlags packageConfigProgramPaths = MapLast (Map.fromList configProgramPaths) @@ -724,6 +725,7 @@ convertToLegacyAllPackageConfig configRelocatable = mempty, configDebugInfo = mempty, configUseResponseFiles = mempty, + configDumpBuildInfo = mempty, configAllowDependingOnPrivateLibs = mempty } @@ -797,6 +799,7 @@ convertToLegacyPerPackageConfig PackageConfig {..} = configRelocatable = packageConfigRelocatable, configDebugInfo = packageConfigDebugInfo, configUseResponseFiles = mempty, + configDumpBuildInfo = packageConfigDumpBuildInfo, configAllowDependingOnPrivateLibs = mempty } @@ -1083,6 +1086,7 @@ legacyPackageConfigFieldDescrs = dispFlagAssignment parsecFlagAssignment configConfigurationsFlags (\v conf -> conf { configConfigurationsFlags = v }) + , overrideDumpBuildInfo ] . filterFields [ "with-compiler", "with-hc-pkg" @@ -1180,6 +1184,23 @@ legacyPackageConfigFieldDescrs = (toFlag <$> parsec <|> pure mempty) configHcFlavor (\v flags -> flags { configHcFlavor = v }) + overrideDumpBuildInfo = + liftField configDumpBuildInfo + (\v flags -> flags { configDumpBuildInfo = v }) $ + let name = "build-info" in + FieldDescr name + (\f -> case f of + Flag NoDumpBuildInfo -> Disp.text "False" + Flag DumpBuildInfo -> Disp.text "True" + _ -> Disp.empty) + (\line str _ -> case () of + _ | str == "False" -> ParseOk [] (Flag NoDumpBuildInfo) + | str == "True" -> ParseOk [] (Flag DumpBuildInfo) + | lstr == "false" -> ParseOk [caseWarning name] (Flag NoDumpBuildInfo) + | lstr == "true" -> ParseOk [caseWarning name] (Flag DumpBuildInfo) + | otherwise -> ParseFailed (NoParse name line) + where + lstr = lowercase str) -- TODO: [code cleanup] The following is a hack. The "optimization" and -- "debug-info" fields are OptArg, and viewAsFieldDescr fails on that. @@ -1202,13 +1223,11 @@ legacyPackageConfigFieldDescrs = | str == "0" -> ParseOk [] (Flag NoOptimisation) | str == "1" -> ParseOk [] (Flag NormalOptimisation) | str == "2" -> ParseOk [] (Flag MaximumOptimisation) - | lstr == "false" -> ParseOk [caseWarning] (Flag NoOptimisation) - | lstr == "true" -> ParseOk [caseWarning] (Flag NormalOptimisation) + | lstr == "false" -> ParseOk [caseWarning name] (Flag NoOptimisation) + | lstr == "true" -> ParseOk [caseWarning name] (Flag NormalOptimisation) | otherwise -> ParseFailed (NoParse name line) where - lstr = lowercase str - caseWarning = PWarning $ - "The '" ++ name ++ "' field is case sensitive, use 'True' or 'False'.") + lstr = lowercase str) overrideFieldDebugInfo = liftField configDebugInfo (\v flags -> flags { configDebugInfo = v }) $ @@ -1227,13 +1246,14 @@ legacyPackageConfigFieldDescrs = | str == "1" -> ParseOk [] (Flag MinimalDebugInfo) | str == "2" -> ParseOk [] (Flag NormalDebugInfo) | str == "3" -> ParseOk [] (Flag MaximalDebugInfo) - | lstr == "false" -> ParseOk [caseWarning] (Flag NoDebugInfo) - | lstr == "true" -> ParseOk [caseWarning] (Flag NormalDebugInfo) + | lstr == "false" -> ParseOk [caseWarning name] (Flag NoDebugInfo) + | lstr == "true" -> ParseOk [caseWarning name] (Flag NormalDebugInfo) | otherwise -> ParseFailed (NoParse name line) where - lstr = lowercase str - caseWarning = PWarning $ - "The '" ++ name ++ "' field is case sensitive, use 'True' or 'False'.") + lstr = lowercase str) + + caseWarning name = PWarning $ + "The '" ++ name ++ "' field is case sensitive, use 'True' or 'False'." prefixTest name | "test-" `isPrefixOf` name = name | otherwise = "test-" ++ name diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs index 92850b27925..eb32ce27410 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs @@ -59,7 +59,7 @@ import Distribution.Simple.Compiler ( Compiler, CompilerFlavor , OptimisationLevel(..), ProfDetailLevel, DebugInfoLevel(..) ) import Distribution.Simple.Setup - ( Flag, HaddockTarget(..), TestShowDetails(..) ) + ( Flag, HaddockTarget(..), TestShowDetails(..), DumpBuildInfo (..) ) import Distribution.Simple.InstallDirs ( PathTemplate ) import Distribution.Utils.NubList @@ -271,6 +271,7 @@ data PackageConfig packageConfigCoverage :: Flag Bool, packageConfigRelocatable :: Flag Bool, packageConfigDebugInfo :: Flag DebugInfoLevel, + packageConfigDumpBuildInfo :: Flag DumpBuildInfo, packageConfigRunTests :: Flag Bool, --TODO: [required eventually] use this packageConfigDocumentation :: Flag Bool, --TODO: [required eventually] use this -- Haddock options diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 4113f5f1fb1..1745f25632e 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -115,6 +115,8 @@ import Distribution.ModuleName import Distribution.Package import Distribution.Types.AnnotatedId import Distribution.Types.ComponentName +import Distribution.Types.DumpBuildInfo + ( DumpBuildInfo (..) ) import Distribution.Types.LibraryName import Distribution.Types.GivenComponent (GivenComponent(..)) @@ -1838,6 +1840,7 @@ elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB elabStripLibs = perPkgOptionFlag pkgid False packageConfigStripLibs elabStripExes = perPkgOptionFlag pkgid False packageConfigStripExes elabDebugInfo = perPkgOptionFlag pkgid NoDebugInfo packageConfigDebugInfo + elabDumpBuildInfo = perPkgOptionFlag pkgid NoDumpBuildInfo packageConfigDumpBuildInfo -- Combine the configured compiler prog settings with the user-supplied -- config. For the compiler progs any user-supplied config was taken @@ -3468,6 +3471,7 @@ setupHsConfigureFlags (ReadyPackage elab@ElaboratedConfiguredPackage{..}) configStripExes = toFlag elabStripExes configStripLibs = toFlag elabStripLibs configDebugInfo = toFlag elabDebugInfo + configDumpBuildInfo = toFlag elabDumpBuildInfo configConfigurationsFlags = elabFlagAssignment configConfigureArgs = elabConfigureScriptArgs diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs index 2d5eecdd353..48d3fd6a620 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs @@ -95,7 +95,8 @@ import Distribution.Simple.LocalBuildInfo ( ComponentName(..), LibraryName(..) ) import qualified Distribution.Simple.InstallDirs as InstallDirs import Distribution.Simple.InstallDirs (PathTemplate) -import Distribution.Simple.Setup (HaddockTarget, TestShowDetails, ReplOptions) +import Distribution.Simple.Setup + ( HaddockTarget, TestShowDetails, DumpBuildInfo (..), ReplOptions ) import Distribution.Version import qualified Distribution.Solver.Types.ComponentDeps as CD @@ -261,6 +262,7 @@ data ElaboratedConfiguredPackage elabStripLibs :: Bool, elabStripExes :: Bool, elabDebugInfo :: DebugInfoLevel, + elabDumpBuildInfo :: DumpBuildInfo, elabProgramPaths :: Map String FilePath, elabProgramArgs :: Map String [String], diff --git a/cabal-install/src/Distribution/Client/Setup.hs b/cabal-install/src/Distribution/Client/Setup.hs index 13cd1001fbc..033932a0e7e 100644 --- a/cabal-install/src/Distribution/Client/Setup.hs +++ b/cabal-install/src/Distribution/Client/Setup.hs @@ -450,7 +450,7 @@ filterConfigureFlags :: ConfigFlags -> Version -> ConfigFlags filterConfigureFlags flags cabalLibVersion -- NB: we expect the latest version to be the most common case, -- so test it first. - | cabalLibVersion >= mkVersion [2,5,0] = flags_latest + | cabalLibVersion >= mkVersion [3,7,0] = flags_latest -- The naming convention is that flags_version gives flags with -- all flags *introduced* in version eliminated. -- It is NOT the latest version of Cabal library that @@ -483,7 +483,10 @@ filterConfigureFlags flags cabalLibVersion flags_3_7_0 = flags_latest { -- Cabal < 3.7 does not know about --extra-lib-dirs-static - configExtraLibDirsStatic = [] + configExtraLibDirsStatic = [], + + -- Cabal < 3.7 does not understand '--enable-build-info' or '--disable-build-info' + configDumpBuildInfo = NoFlag } flags_2_5_0 = flags_3_7_0 { diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs index 95b7e1ccb79..fc9d0c36f77 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs @@ -559,7 +559,7 @@ instance Arbitrary PackageConfig where <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary - <*> arbitrary <*> arbitrary + <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitraryFlag arbitraryShortToken <*> arbitrary @@ -618,6 +618,7 @@ instance Arbitrary PackageConfig where , packageConfigCoverage = x25 , packageConfigRelocatable = x26 , packageConfigDebugInfo = x27 + , packageConfigDumpBuildInfo = x27_1 , packageConfigRunTests = x28 , packageConfigDocumentation = x29 , packageConfigHaddockHoogle = x30 @@ -674,6 +675,7 @@ instance Arbitrary PackageConfig where , packageConfigCoverage = x25' , packageConfigRelocatable = x26' , packageConfigDebugInfo = x27' + , packageConfigDumpBuildInfo = x27_1' , packageConfigRunTests = x28' , packageConfigDocumentation = x29' , packageConfigHaddockHoogle = x30' @@ -703,7 +705,7 @@ instance Arbitrary PackageConfig where (x10', x11', x12', x13', x14'), (x15', x16', x53', x17', x18', x19')), ((x20', x20_1', x21', x22', x23', x24'), - (x25', x26', x27', x28', x29'), + (x25', x26', x27', x27_1', x28', x29'), (x30', x31', x32', (x33', x33_1'), x34'), (x35', x36', x37', x38', x43', x39'), (x40', x41'), @@ -717,7 +719,7 @@ instance Arbitrary PackageConfig where map NonEmpty x18, x19)), ((x20, x20_1, x21, x22, x23, x24), - (x25, x26, x27, x28, x29), + (x25, x26, x27, x27_1, x28, x29), (x30, x31, x32, (x33, x33_1), x34), (x35, x36, fmap NonEmpty x37, x38, x43, fmap NonEmpty x39), (x40, x41), From a2413e5670c2d5ad06103df45fa3f03ca0e6aba0 Mon Sep 17 00:00:00 2001 From: Fendor Date: Fri, 30 Jul 2021 12:35:25 +0200 Subject: [PATCH 04/10] Add build-info location to plan.json --- .../Distribution/Client/ProjectPlanOutput.hs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs index bf001178a24..ec0d5a676b2 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs @@ -45,11 +45,13 @@ import Distribution.Simple.GHC , GhcEnvironmentFileEntry(..), simpleGhcEnvironmentFile , writeGhcEnvironmentFile ) import Distribution.Simple.BuildPaths - ( dllExtension, exeExtension ) + ( dllExtension, exeExtension, buildInfoPref ) import qualified Distribution.Compat.Graph as Graph import Distribution.Compat.Graph (Graph, Node) import qualified Distribution.Compat.Binary as Binary import Distribution.Simple.Utils +import Distribution.Types.Version + ( mkVersion ) import Distribution.Verbosity import Prelude () @@ -150,7 +152,7 @@ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig = | Just hash <- [elabPkgSourceHash elab] ] ++ (case elabBuildStyle elab of BuildInplaceOnly -> - ["dist-dir" J..= J.String dist_dir] + ["dist-dir" J..= J.String dist_dir] ++ [buildInfoFileLocation] BuildAndInstall -> -- TODO: install dirs? [] @@ -175,6 +177,20 @@ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig = ] ++ bin_file (compSolverName comp) where + -- | Only add build-info file location if the Setup.hs CLI + -- is recent enough to be able to generate build info files. + -- Otherwise, do not add the expected file location. + -- + -- Consumers of `plan.json` can use the absence of this file location + -- to indicate that the given component uses `build-type: Custom` + -- with an old lib:Cabal version. + buildInfoFileLocation :: J.Pair + buildInfoFileLocation + | elabSetupScriptCliVersion elab < mkVersion [3, 7, 0, 0] + = ("build-info" J..= J.Null) + | otherwise + = ("build-info" J..= J.String (buildInfoPref dist_dir)) + packageLocationToJ :: PackageLocation (Maybe FilePath) -> J.Value packageLocationToJ pkgloc = case pkgloc of @@ -262,7 +278,6 @@ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig = jdisplay :: Pretty a => a -> J.Value jdisplay = J.String . prettyShow - ----------------------------------------------------------------------------- -- Project status -- From 705228e7300f959494ae6eb7d1e85b3c8812687d Mon Sep 17 00:00:00 2001 From: Fendor Date: Thu, 8 Jul 2021 11:04:39 +0200 Subject: [PATCH 05/10] Re-design test-cases for show-build-info --- .../PackageTests/ShowBuildInfo/A/A.cabal | 23 +++ .../PackageTests/ShowBuildInfo/A/B/B.cabal | 10 + .../PackageTests/ShowBuildInfo/A/B/lib/B.hs | 4 + .../ShowBuildInfo/A/build-info-all.out | 20 ++ .../ShowBuildInfo/A/build-info-all.test.hs | 28 +++ .../ShowBuildInfo/A/build-info-exe.out | 8 + .../ShowBuildInfo/A/build-info-exe.test.hs | 14 ++ .../ShowBuildInfo/A/cabal.project | 1 + .../ShowBuildInfo/A/remove-outdated.out | 15 ++ .../ShowBuildInfo/A/remove-outdated.test.hs | 23 +++ .../PackageTests/ShowBuildInfo/A/src/A.hs | 3 + .../PackageTests/ShowBuildInfo/A/src/Main.hs | 3 + .../PackageTests/ShowBuildInfo/A/src/Test.hs | 4 + .../ShowBuildInfo/Complex/Complex.cabal | 72 +++++++ .../ShowBuildInfo/Complex/app/Main.lhs | 8 + .../ShowBuildInfo/Complex/app/Other.hs | 3 + .../ShowBuildInfo/Complex/benchmark/Main.hs | 3 + .../ShowBuildInfo/Complex/cabal.project | 4 + .../another-framework.cabal | 8 + .../repo/criterion-1.1.4.0/criterion.cabal | 8 + .../test-framework.cabal | 8 + .../ShowBuildInfo/Complex/single.out | 67 +++++++ .../ShowBuildInfo/Complex/single.test.hs | 41 ++++ .../ShowBuildInfo/Complex/src/A.hs | 5 + .../ShowBuildInfo/Complex/src/B.hs | 3 + .../ShowBuildInfo/Complex/src/C.hs | 5 + .../ShowBuildInfo/Complex/src/D.hs | 5 + .../ShowBuildInfo/Complex/test/FuncMain.hs | 1 + .../ShowBuildInfo/Complex/test/UnitMain.hs | 1 + .../ShowBuildInfo/Custom/Custom.cabal | 21 ++ .../ShowBuildInfo/Custom/Setup.hs | 12 ++ .../ShowBuildInfo/Custom/app/Main.hs | 5 + .../ShowBuildInfo/Custom/cabal.project | 2 + .../ShowBuildInfo/Custom/custom.out | 7 + .../ShowBuildInfo/Custom/custom.test.hs | 28 +++ .../ShowBuildInfo/Custom/src/MyLib.hs | 4 + cabal-testsuite/cabal-testsuite.cabal | 1 + .../src/Test/Cabal/DecodeShowBuildInfo.hs | 188 ++++++++++++++++++ cabal-testsuite/src/Test/Cabal/Monad.hs | 13 +- .../src/Test/Cabal/OutputNormalizer.hs | 26 +++ cabal-testsuite/src/Test/Cabal/Plan.hs | 27 ++- 41 files changed, 730 insertions(+), 2 deletions(-) create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/A.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/B/B.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/B/lib/B.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.out create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.test.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.out create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.test.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/cabal.project create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.out create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/src/A.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Main.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Test.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/Complex.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Main.lhs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Other.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/benchmark/Main.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/cabal.project create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/another-framework-0.8.1.1/another-framework.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/criterion-1.1.4.0/criterion.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/test-framework-0.8.1.1/test-framework.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.out create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.test.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/A.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/B.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/C.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/D.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/FuncMain.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/UnitMain.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Custom.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Setup.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Custom/app/Main.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Custom/cabal.project create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.out create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.test.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/Custom/src/MyLib.hs create mode 100644 cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/A.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/A/A.cabal new file mode 100644 index 00000000000..a1420d9676e --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/A.cabal @@ -0,0 +1,23 @@ +cabal-version: 2.4 +name: A +version: 0.1.0.0 +license: BSD-3-Clause + +library + exposed-modules: A + build-depends: base >=4 + hs-source-dirs: src + default-language: Haskell2010 + +executable A + main-is: Main.hs + build-depends: base >=4 + hs-source-dirs: src + default-language: Haskell2010 + +test-suite A-tests + type: exitcode-stdio-1.0 + main-is: Test.hs + build-depends: base >=4, A + hs-source-dirs: src + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/B/B.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/A/B/B.cabal new file mode 100644 index 00000000000..e81b9eb3a1d --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/B/B.cabal @@ -0,0 +1,10 @@ +cabal-version: 2.4 +name: B +version: 0.1.0.0 +license: BSD-3-Clause + +library + exposed-modules: B + build-depends: base >=4.0.0.0, A + hs-source-dirs: lib + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/B/lib/B.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/B/lib/B.hs new file mode 100644 index 00000000000..8b74dfe6b43 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/B/lib/B.hs @@ -0,0 +1,4 @@ +module B where + +foo :: Int -> Int +foo = id diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.out b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.out new file mode 100644 index 00000000000..4ea2b2ee427 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.out @@ -0,0 +1,20 @@ +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - A-0.1.0.0 (lib) (first run) + - A-0.1.0.0 (exe:A) (first run) + - B-0.1.0.0 (lib) (first run) + - A-0.1.0.0 (test:A-tests) (first run) +Configuring library for A-0.1.0.0.. +Preprocessing library for A-0.1.0.0.. +Building library for A-0.1.0.0.. +Configuring executable 'A' for A-0.1.0.0.. +Preprocessing executable 'A' for A-0.1.0.0.. +Building executable 'A' for A-0.1.0.0.. +Configuring library for B-0.1.0.0.. +Preprocessing library for B-0.1.0.0.. +Building library for B-0.1.0.0.. +Configuring test suite 'A-tests' for A-0.1.0.0.. +Preprocessing test suite 'A-tests' for A-0.1.0.0.. +Building test suite 'A-tests' for A-0.1.0.0.. diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.test.hs new file mode 100644 index 00000000000..8fdc04e7a2a --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-all.test.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE OverloadedStrings #-} +import Test.Cabal.Prelude +import Test.Cabal.DecodeShowBuildInfo + +main = cabalTest $ do + runShowBuildInfo ["all", "--enable-tests"] + withPlan $ do + assertComponent "A" (exe "A") + defCompAssertion + { sourceFiles = ["Main.hs"] + , sourceDirs = ["src"] + } + assertComponent "A" mainLib + defCompAssertion + { modules = ["A"] + , sourceDirs = ["src"] + } + + assertComponent "B" mainLib + defCompAssertion + { modules = ["B"] + , sourceDirs = ["lib"] + } + assertComponent "A" (test "A-tests") + defCompAssertion + { sourceFiles = ["Test.hs"] + , sourceDirs = ["src"] + } diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.out b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.out new file mode 100644 index 00000000000..9a6b8886d87 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.out @@ -0,0 +1,8 @@ +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - A-0.1.0.0 (exe:A) (first run) +Configuring executable 'A' for A-0.1.0.0.. +Preprocessing executable 'A' for A-0.1.0.0.. +Building executable 'A' for A-0.1.0.0.. diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.test.hs new file mode 100644 index 00000000000..a27bf2d8167 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/build-info-exe.test.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE OverloadedStrings #-} +import Test.Cabal.Prelude +import Test.Cabal.DecodeShowBuildInfo + +main = cabalTest $ do + runShowBuildInfo ["exe:A"] + withPlan $ do + assertComponent "A" (exe "A") + defCompAssertion + { sourceFiles = ["Main.hs"] + , sourceDirs = ["src"] + -- does not list lib as a target + , compilerArgsPred = all (/= "A-0.1.0.0-inplace") + } diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/cabal.project b/cabal-testsuite/PackageTests/ShowBuildInfo/A/cabal.project new file mode 100644 index 00000000000..9a091f69b3b --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/cabal.project @@ -0,0 +1 @@ +packages: . ./B/ diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.out b/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.out new file mode 100644 index 00000000000..7bb32b8b674 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.out @@ -0,0 +1,15 @@ +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - A-0.1.0.0 (exe:A) (first run) +Configuring executable 'A' for A-0.1.0.0.. +Preprocessing executable 'A' for A-0.1.0.0.. +Building executable 'A' for A-0.1.0.0.. +# cabal v2-build +Build profile: -w ghc- -O1 +In order, the following will be built: + - A-0.1.0.0 (exe:A) (configuration changed) +Configuring executable 'A' for A-0.1.0.0.. +Preprocessing executable 'A' for A-0.1.0.0.. +Building executable 'A' for A-0.1.0.0.. diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs new file mode 100644 index 00000000000..f6bcdf047fe --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE OverloadedStrings #-} +import Test.Cabal.Prelude +import Test.Cabal.DecodeShowBuildInfo +import Test.Cabal.Plan +import Control.Monad.Trans.Reader +import System.Directory + +main = cabalTest $ do + runShowBuildInfo ["exe:A"] + withPlan $ do + assertComponent "A" (exe "A") + defCompAssertion + { sourceFiles = ["Main.hs"] + , sourceDirs = ["src"] + -- does not list lib as a target + , compilerArgsPred = all (/= "A-0.1.0.0-inplace") + } + + cabal' "v2-build" ["exe:A", "--disable-build-info"] + withPlan $ do + Just plan <- fmap testPlan ask + let fp = buildInfoFile plan "A" (exe "A") + shouldNotExist fp diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/A.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/A.hs new file mode 100644 index 00000000000..2ec6a4f2188 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/A.hs @@ -0,0 +1,3 @@ +module A where + +foo = 2 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Main.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Main.hs new file mode 100644 index 00000000000..de106fe48f9 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Main.hs @@ -0,0 +1,3 @@ +module Main where + +main = return () diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Test.hs new file mode 100644 index 00000000000..d82a4bd93b7 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/src/Test.hs @@ -0,0 +1,4 @@ +module Main where + +main :: IO () +main = return () diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/Complex.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/Complex.cabal new file mode 100644 index 00000000000..d8ea0a46eca --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/Complex.cabal @@ -0,0 +1,72 @@ +cabal-version: 2.4 +name: Complex +version: 0.1.0.0 +license: MIT + +library + build-depends: base + hs-source-dirs: src doesnt-exist + default-language: Haskell2010 + exposed-modules: + A + B + + autogen-modules: Paths_Complex + other-modules: + C + D + Paths_Complex + + ghc-options: -Wall + +executable Complex + main-is: Main.lhs + build-depends: + , base + , Complex + + hs-source-dirs: app + autogen-modules: Paths_Complex + other-modules: + Other + Paths_Complex + + ghc-options: + -threaded -rtsopts "-with-rtsopts=-N -T" -Wredundant-constraints + + default-language: Haskell2010 + +test-suite unit-test + type: exitcode-stdio-1.0 + hs-source-dirs: test + build-depends: + , another-framework + , base + + main-is: UnitMain.hs + default-language: Haskell2010 + +test-suite func-test + type: exitcode-stdio-1.0 + hs-source-dirs: test + build-depends: + , base + , Complex + , test-framework + + main-is: FuncMain.hs + default-language: Haskell2010 + +benchmark complex-benchmarks + type: exitcode-stdio-1.0 + main-is: Main.hs + other-modules: Paths_Complex + autogen-modules: Paths_Complex + hs-source-dirs: benchmark + ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N + build-depends: + , base + , Complex + , criterion ^>=1.1.4 + + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Main.lhs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Main.lhs new file mode 100644 index 00000000000..6f482ce0695 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Main.lhs @@ -0,0 +1,8 @@ +> module Main where +> +> import A +> import Other +> +> main = do +> print foo +> print bar diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Other.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Other.hs new file mode 100644 index 00000000000..5d0685b1815 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/app/Other.hs @@ -0,0 +1,3 @@ +module Other where + +bar = () diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/benchmark/Main.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/benchmark/Main.hs new file mode 100644 index 00000000000..7753bcff18c --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/benchmark/Main.hs @@ -0,0 +1,3 @@ +module Main where + +main = pure () diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/cabal.project b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/cabal.project new file mode 100644 index 00000000000..a40dbcbdd1e --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/cabal.project @@ -0,0 +1,4 @@ +packages: . + +tests: True +benchmarks: True diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/another-framework-0.8.1.1/another-framework.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/another-framework-0.8.1.1/another-framework.cabal new file mode 100644 index 00000000000..173443e1906 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/another-framework-0.8.1.1/another-framework.cabal @@ -0,0 +1,8 @@ +name: another-framework +version: 0.8.1.1 +build-type: Simple +cabal-version: >= 1.10 + +library + build-depends: base + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/criterion-1.1.4.0/criterion.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/criterion-1.1.4.0/criterion.cabal new file mode 100644 index 00000000000..e7cdc916530 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/criterion-1.1.4.0/criterion.cabal @@ -0,0 +1,8 @@ +name: criterion +version: 1.1.4.0 +build-type: Simple +cabal-version: >= 1.10 + +library + build-depends: base, ghc-prim + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/test-framework-0.8.1.1/test-framework.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/test-framework-0.8.1.1/test-framework.cabal new file mode 100644 index 00000000000..2235e2eeb39 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/repo/test-framework-0.8.1.1/test-framework.cabal @@ -0,0 +1,8 @@ +name: test-framework +version: 0.8.1.1 +build-type: Simple +cabal-version: >= 1.10 + +library + build-depends: base + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.out b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.out new file mode 100644 index 00000000000..a0e8e3cde86 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.out @@ -0,0 +1,67 @@ +# cabal v2-update +Downloading the latest package list from test-local-repo +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - Complex-0.1.0.0 (lib) (first run) + - Complex-0.1.0.0 (exe:Complex) (first run) +Configuring library for Complex-0.1.0.0.. +Warning: 'hs-source-dirs: doesnt-exist' specifies a directory which does not exist. +Preprocessing library for Complex-0.1.0.0.. +Building library for Complex-0.1.0.0.. +Configuring executable 'Complex' for Complex-0.1.0.0.. +Warning: 'hs-source-dirs: doesnt-exist' specifies a directory which does not exist. +Preprocessing executable 'Complex' for Complex-0.1.0.0.. +Building executable 'Complex' for Complex-0.1.0.0.. +# show-build-info Complex exe:Complex +{"cabal-lib-version":"3.7.0.0","compiler":{"flavour":"ghc","compiler-id":"ghc-","path":""},"components":[{"type":"exe","name":"exe:Complex","unit-id":"Complex-0.1.0.0-inplace-Complex","compiler-args":["-fbuilding-cabal-package","-O","-outputdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build","-odir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build","-hidir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build","-stubdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build","-i","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build","-iapp","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build/Complex/autogen","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build/Complex/autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build","-optP-include","-optP/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/x/Complex/build/Complex/autogen/cabal_macros.h","-hide-all-packages","-Wmissing-home-modules","-no-user-package-db","-package-db","/single.dist/home/.cabal/store/ghc-/package.db","-package-db","/single.dist/work/./dist/packagedb/ghc-","-package-id","","-package-id","","-XHaskell2010","-threaded","-rtsopts","-with-rtsopts=-N -T","-Wredundant-constraints"],"modules":["Other","Paths_Complex"],"src-files":["Main.lhs"],"hs-src-dirs":["app"],"src-dir":"/","cabal-file":"./Complex.cabal"}]} +# cabal build +Up to date +# show-build-info Complex lib +{"cabal-lib-version":"3.7.0.0","compiler":{"flavour":"ghc","compiler-id":"ghc-","path":""},"components":[{"type":"lib","name":"lib","unit-id":"Complex-0.1.0.0-inplace","compiler-args":["-fbuilding-cabal-package","-O","-outputdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build","-odir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build","-hidir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build","-stubdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build","-i","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build","-isrc","-idoesnt-exist","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build/autogen","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build/autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build","-optP-include","-optP/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/build/autogen/cabal_macros.h","-this-unit-id","Complex-0.1.0.0-inplace","-hide-all-packages","-Wmissing-home-modules","-no-user-package-db","-package-db","/single.dist/home/.cabal/store/ghc-/package.db","-package-db","/single.dist/work/./dist/packagedb/ghc-","-package-id","","-XHaskell2010","-Wall"],"modules":["A","B","C","D","Paths_Complex"],"src-files":[],"hs-src-dirs":["src","doesnt-exist"],"src-dir":"/","cabal-file":"./Complex.cabal"}]} +# cabal build +Build profile: -w ghc- -O1 +In order, the following will be built: + - criterion-1.1.4.0 (lib) (requires build) + - Complex-0.1.0.0 (bench:complex-benchmarks) (first run) +Configuring library for criterion-1.1.4.0.. +Preprocessing library for criterion-1.1.4.0.. +Building library for criterion-1.1.4.0.. +Installing library in +Configuring benchmark 'complex-benchmarks' for Complex-0.1.0.0.. +Warning: 'hs-source-dirs: doesnt-exist' specifies a directory which does not exist. +Preprocessing benchmark 'complex-benchmarks' for Complex-0.1.0.0.. +Building benchmark 'complex-benchmarks' for Complex-0.1.0.0.. +# show-build-info Complex bench:complex-benchmarks +{"cabal-lib-version":"3.7.0.0","compiler":{"flavour":"ghc","compiler-id":"ghc-","path":""},"components":[{"type":"bench","name":"bench:complex-benchmarks","unit-id":"Complex-0.1.0.0-inplace-complex-benchmarks","compiler-args":["-fbuilding-cabal-package","-O","-outputdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build","-odir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build","-hidir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build","-stubdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build","-i","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build","-ibenchmark","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build/complex-benchmarks/autogen","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build/complex-benchmarks/autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build","-optP-include","-optP/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/b/complex-benchmarks/build/complex-benchmarks/autogen/cabal_macros.h","-hide-all-packages","-Wmissing-home-modules","-no-user-package-db","-package-db","/single.dist/home/.cabal/store/ghc-/package.db","-package-db","/single.dist/work/./dist/packagedb/ghc-","-package-id","","-package-id","","-package-id","","-XHaskell2010","-Wall","-rtsopts","-threaded","-with-rtsopts=-N"],"modules":["Paths_Complex"],"src-files":["Main.hs"],"hs-src-dirs":["benchmark"],"src-dir":"/","cabal-file":"./Complex.cabal"}]} +# cabal build +Build profile: -w ghc- -O1 +In order, the following will be built: + - test-framework-0.8.1.1 (lib) (requires build) + - Complex-0.1.0.0 (test:func-test) (first run) +Configuring library for test-framework-0.8.1.1.. +Preprocessing library for test-framework-0.8.1.1.. +Building library for test-framework-0.8.1.1.. +Installing library in +Configuring test suite 'func-test' for Complex-0.1.0.0.. +Warning: 'hs-source-dirs: doesnt-exist' specifies a directory which does not exist. +Preprocessing test suite 'func-test' for Complex-0.1.0.0.. +Building test suite 'func-test' for Complex-0.1.0.0.. +# show-build-info Complex test:func-test +{"cabal-lib-version":"3.7.0.0","compiler":{"flavour":"ghc","compiler-id":"ghc-","path":""},"components":[{"type":"test","name":"test:func-test","unit-id":"Complex-0.1.0.0-inplace-func-test","compiler-args":["-fbuilding-cabal-package","-O","-outputdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build","-odir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build","-hidir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build","-stubdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build","-i","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build","-itest","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build/func-test/autogen","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build/func-test/autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build","-optP-include","-optP/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/func-test/build/func-test/autogen/cabal_macros.h","-hide-all-packages","-Wmissing-home-modules","-no-user-package-db","-package-db","/single.dist/home/.cabal/store/ghc-/package.db","-package-db","/single.dist/work/./dist/packagedb/ghc-","-package-id","","-package-id","","-package-id","","-XHaskell2010"],"modules":[],"src-files":["FuncMain.hs"],"hs-src-dirs":["test"],"src-dir":"/","cabal-file":"./Complex.cabal"}]} +# cabal build +Build profile: -w ghc- -O1 +In order, the following will be built: + - another-framework-0.8.1.1 (lib) (requires build) + - Complex-0.1.0.0 (test:unit-test) (first run) +Configuring library for another-framework-0.8.1.1.. +Preprocessing library for another-framework-0.8.1.1.. +Building library for another-framework-0.8.1.1.. +Installing library in +Configuring test suite 'unit-test' for Complex-0.1.0.0.. +Warning: 'hs-source-dirs: doesnt-exist' specifies a directory which does not exist. +Preprocessing test suite 'unit-test' for Complex-0.1.0.0.. +Building test suite 'unit-test' for Complex-0.1.0.0.. +# show-build-info Complex test:unit-test +{"cabal-lib-version":"3.7.0.0","compiler":{"flavour":"ghc","compiler-id":"ghc-","path":""},"components":[{"type":"test","name":"test:unit-test","unit-id":"Complex-0.1.0.0-inplace-unit-test","compiler-args":["-fbuilding-cabal-package","-O","-outputdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build","-odir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build","-hidir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build","-stubdir","/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build","-i","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build","-itest","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build/unit-test/autogen","-i/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build/unit-test/autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build/global-autogen","-I/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build","-optP-include","-optP/single.dist/work/./dist/build//ghc-/Complex-0.1.0.0/t/unit-test/build/unit-test/autogen/cabal_macros.h","-hide-all-packages","-Wmissing-home-modules","-no-user-package-db","-package-db","/single.dist/home/.cabal/store/ghc-/package.db","-package-db","/single.dist/work/./dist/packagedb/ghc-","-package-id","","-package-id","","-XHaskell2010"],"modules":[],"src-files":["UnitMain.hs"],"hs-src-dirs":["test"],"src-dir":"/","cabal-file":"./Complex.cabal"}]} diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.test.hs new file mode 100644 index 00000000000..5b083d69c16 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/single.test.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE OverloadedStrings #-} +import Test.Cabal.Prelude +import Test.Cabal.DecodeShowBuildInfo + +main = cabalTest $ withRepo "repo" $ do + runShowBuildInfo ["exe:Complex"] >> withPlan (do + recordBuildInfo "Complex" (exe "Complex") + assertComponent "Complex" (exe "Complex") defCompAssertion + { modules = ["Other", "Paths_Complex"] + , sourceFiles = ["Main.lhs"] + , sourceDirs = ["app"] + }) + + runShowBuildInfo ["lib:Complex"] >> withPlan (do + recordBuildInfo "Complex" mainLib + assertComponent "Complex" mainLib defCompAssertion + { modules = ["A", "B", "C", "D", "Paths_Complex"] + , sourceDirs = ["src", "doesnt-exist"] + }) + + runShowBuildInfo ["benchmark:complex-benchmarks"] >> withPlan (do + recordBuildInfo "Complex" (bench "complex-benchmarks") + assertComponent "Complex" (bench "complex-benchmarks") defCompAssertion + { modules = ["Paths_Complex"] + , sourceFiles = ["Main.hs"] + , sourceDirs = ["benchmark"] + }) + + runShowBuildInfo ["test:func-test"] >> withPlan (do + recordBuildInfo "Complex" (test "func-test") + assertComponent "Complex" (test "func-test") defCompAssertion + { sourceFiles = ["FuncMain.hs"] + , sourceDirs = ["test"] + }) + + runShowBuildInfo ["test:unit-test"] >> withPlan (do + recordBuildInfo "Complex" (test "unit-test") + assertComponent "Complex" (test "unit-test") defCompAssertion + { sourceFiles = ["UnitMain.hs"] + , sourceDirs = ["test"] + }) diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/A.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/A.hs new file mode 100644 index 00000000000..18032f68988 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/A.hs @@ -0,0 +1,5 @@ +module A where + +import D + +foo = d diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/B.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/B.hs new file mode 100644 index 00000000000..93b0222a65d --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/B.hs @@ -0,0 +1,3 @@ +module B where + +b = 1 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/C.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/C.hs new file mode 100644 index 00000000000..419eb7eca64 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/C.hs @@ -0,0 +1,5 @@ +module C where + +import B + +c = b diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/D.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/D.hs new file mode 100644 index 00000000000..d9be40b5ba2 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/src/D.hs @@ -0,0 +1,5 @@ +module D where + +import C + +d = c diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/FuncMain.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/FuncMain.hs new file mode 100644 index 00000000000..b3549c2fe3d --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/FuncMain.hs @@ -0,0 +1 @@ +main = return () diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/UnitMain.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/UnitMain.hs new file mode 100644 index 00000000000..b3549c2fe3d --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Complex/test/UnitMain.hs @@ -0,0 +1 @@ +main = return () diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Custom.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Custom.cabal new file mode 100644 index 00000000000..26e2d7f00fb --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Custom.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: Custom +version: 0.1.0.0 + +build-type: Custom + +custom-setup + setup-depends: Cabal >= 3.7, base + +library + exposed-modules: MyLib + build-depends: base + hs-source-dirs: src + default-language: Haskell2010 + +executable great-exe + main-is: Main.hs + build-depends: base, Custom + hs-source-dirs: app + default-language: Haskell2010 + diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Setup.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Setup.hs new file mode 100644 index 00000000000..a1825767ed7 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/Setup.hs @@ -0,0 +1,12 @@ +-- Setup.hs taken from 'cabal-testsuite/Setup.hs' +{-# LANGUAGE Haskell2010 #-} +module Main (main) where + +import Distribution.Simple + +main :: IO () +main = defaultMainWithHooks simpleUserHooks + { buildHook = \pkg lbi hooks flags -> do + putStrLn "Custom Setup.hs has been invoked!" + buildHook simpleUserHooks pkg lbi hooks flags + } diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/app/Main.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/app/Main.hs new file mode 100644 index 00000000000..83b7ee473d7 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/app/Main.hs @@ -0,0 +1,5 @@ +module Main where + +import MyLib + +main = pure () diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/cabal.project b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/cabal.project new file mode 100644 index 00000000000..1b8a75ee74b --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/cabal.project @@ -0,0 +1,2 @@ +packages: + . diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.out b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.out new file mode 100644 index 00000000000..3cd432d6bb2 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.out @@ -0,0 +1,7 @@ +# Setup configure +Configuring Custom-0.1.0.0... +# Setup build +Preprocessing library for Custom-0.1.0.0.. +Building library for Custom-0.1.0.0.. +Preprocessing executable 'great-exe' for Custom-0.1.0.0.. +Building executable 'great-exe' for Custom-0.1.0.0.. diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.test.hs new file mode 100644 index 00000000000..a76e888c2b7 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/custom.test.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE OverloadedStrings #-} +import Test.Cabal.Prelude +import Test.Cabal.DecodeShowBuildInfo +import Control.Monad.Trans.Reader + +main = setupTest $ do + -- No cabal test because per-component is broken with it + skipUnlessGhcVersion ">= 8.1" + withPackageDb $ do + setup_build ["--enable-build-info"] + env <- ask + let buildInfoFp = testDistDir env "build-info.json" + buildInfo <- decodeBuildInfoFile buildInfoFp + assertCommonBuildInfo buildInfo + let [libBI, exeBI] = components buildInfo + + assertComponentPure libBI defCompAssertion + { modules = ["MyLib"] + , compType = "lib" + , sourceDirs = ["src"] + } + + assertComponentPure exeBI defCompAssertion + { sourceFiles = ["Main.hs"] + , compType = "exe" + , sourceDirs = ["app"] + } + diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/src/MyLib.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/src/MyLib.hs new file mode 100644 index 00000000000..e657c4403f6 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/Custom/src/MyLib.hs @@ -0,0 +1,4 @@ +module MyLib (someFunc) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/cabal-testsuite/cabal-testsuite.cabal b/cabal-testsuite/cabal-testsuite.cabal index a3a9ad46f27..5285bffb846 100644 --- a/cabal-testsuite/cabal-testsuite.cabal +++ b/cabal-testsuite/cabal-testsuite.cabal @@ -39,6 +39,7 @@ library hs-source-dirs: src exposed-modules: Test.Cabal.CheckArMetadata + Test.Cabal.DecodeShowBuildInfo Test.Cabal.Monad Test.Cabal.OutputNormalizer Test.Cabal.Plan diff --git a/cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs b/cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs new file mode 100644 index 00000000000..47fbcc83795 --- /dev/null +++ b/cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs @@ -0,0 +1,188 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE FlexibleContexts #-} +module Test.Cabal.DecodeShowBuildInfo where + +import Test.Cabal.Prelude +import Test.Cabal.Plan +import Distribution.Compat.Stack +import Distribution.Text (display) +import Distribution.Types.ComponentName +import Distribution.Types.LibraryName +import Distribution.Types.UnqualComponentName +import Distribution.Package +import Distribution.Pretty (prettyShow) +import Control.Monad.Trans.Reader +import Data.Aeson +import GHC.Generics +import System.Exit + +-- | Execute 'cabal build --enable-build-info'. +-- +-- Results can be read via 'withPlan', 'buildInfoFile' and 'decodeBuildInfoFile'. +runShowBuildInfo :: [String] -> TestM () +runShowBuildInfo args = cabal "build" ("--enable-build-info":args) + +-- | Read 'build-info.json' for a given package and component +-- from disk and record the content. Helpful for defining test-cases +-- where the build info matters. +recordBuildInfo :: PackageName -> ComponentName -> TestM () +recordBuildInfo pkgName cname = do + Just plan <- fmap testPlan ask + let fp = buildInfoFile plan pkgName cname + recordMode RecordAll $ do + recordHeader ["show-build-info", prettyShow pkgName, prettyShow cname] + buildInfo <- liftIO $ readFile fp + recordLog $ Result ExitSuccess "build --enable-build-info" buildInfo + +-- | Decode the given filepath into a 'BuildInfo'. +-- +-- If the filepath doesn't exist or its contents are not a valid 'BuildInfo' +-- json file, then an error is raised. +decodeBuildInfoFile :: FilePath -> TestM BuildInfo +decodeBuildInfoFile fp = do + shouldExist fp + res <- liftIO $ eitherDecodeFileStrict fp + case res of + Left err -> fail $ "Could not parse show-build-info file: " ++ err + Right buildInfos -> return buildInfos + +data BuildInfo = BuildInfo + { cabalLibVersion :: String + , compiler :: CompilerInfo + , components :: [ComponentInfo] + } deriving (Generic, Show) + +data CompilerInfo = CompilerInfo + { flavour :: String + , compilerId :: String + , path :: String + } deriving (Generic, Show) + +data ComponentInfo = ComponentInfo + { componentType :: String + , componentName :: String + , componentUnitId :: String + , componentCompilerArgs :: [String] + , componentModules :: [String] + , componentSrcFiles :: [FilePath] + , componentHsSrcDirs :: [FilePath] + , componentSrcDir :: FilePath + } deriving (Generic, Show) + +instance ToJSON BuildInfo where + toEncoding = genericToEncoding defaultOptions +instance FromJSON BuildInfo where + parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelTo2 '-' } + +instance ToJSON CompilerInfo where + toEncoding = genericToEncoding defaultOptions +instance FromJSON CompilerInfo where + parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelTo2 '-' } + +instance ToJSON ComponentInfo where + toEncoding = genericToEncoding defaultOptions +instance FromJSON ComponentInfo where + parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = drop 10 . camelTo2 '-' } + +-- ----------------------------------------------------------- +-- Assertion Helpers to define succinct test cases +-- ----------------------------------------------------------- + +data ComponentAssertion = ComponentAssertion + { unitIdPred :: (String -> Bool) + -- ^ Predicate to apply to a component's unit id. + , compilerArgsPred :: ([String] -> Bool) + -- ^ Predicate the compilation arguments must satisfy. + , modules :: [String] + -- ^ Which modules should a component contain. + , sourceFiles :: [FilePath] + -- ^ Which source files are part of a component. + , sourceDirs :: [FilePath] + -- ^ Expected source directories for a component. + , compType :: String + -- ^ Type of the component, usually one of 'bench', 'exe', 'test', 'lib', 'flib' + } + +defCompAssertion :: ComponentAssertion +defCompAssertion = ComponentAssertion + { unitIdPred = not . null + , compilerArgsPred = not . null + , modules = [] + , sourceFiles = [] + , sourceDirs = [] + , compType = mempty + } + +-- | Assert common build information, such as compiler location, compiler version +-- and cabal library version. +assertCommonBuildInfo :: WithCallStack (BuildInfo -> TestM ()) +assertCommonBuildInfo buildInfo = do + assertEqual "Cabal Version" (display cabalVersionLibrary) (cabalLibVersion buildInfo) + assertEqual "Compiler flavour" "ghc" (flavour $ compiler buildInfo) + assertBool "Compiler id" (and $ zipWith (==) "ghc" (compilerId $ compiler buildInfo)) + assertBool "Compiler path non-empty" (not . null . path $ compiler buildInfo) + +-- | Pure assertion helper. Check whether the given 'ComponentInfo' satisfy +-- the 'ComponentAssertion'. +assertComponentPure :: WithCallStack (ComponentInfo -> ComponentAssertion -> TestM ()) +assertComponentPure component ComponentAssertion{..} = do + assertEqual "Component type" compType (componentType component) + assertBool "Component Unit Id" (unitIdPred $ componentUnitId component) + assertBool "Component compiler args" (compilerArgsPred $ componentCompilerArgs component) + assertEqual "Component modules" modules (componentModules component) + assertEqual "Component source files" sourceFiles (componentSrcFiles component) + assertEqual "Component source directories" sourceDirs (componentHsSrcDirs component) + +-- | @'assertComponent' pkgName cname assertion@ +-- +-- Assert that a component identified by 'pkgName' and 'cname', generated +-- a 'build-info.json' and its contents satisfy the assertions specified in 'assertion'. +-- +-- This assertion must be wrapped in 'withPlan'. +assertComponent :: WithCallStack (PackageName -> ComponentName -> ComponentAssertion -> TestM ()) +assertComponent pkgName cname assert = do + Just plan <- fmap testPlan ask + let fp = buildInfoFile plan pkgName cname + buildInfo <- decodeBuildInfoFile fp + assertCommonBuildInfo buildInfo + + let component = findComponentInfo buildInfo + let assertWithCompType = assert { compType = compTypeStr cname } + assertComponentPure component assertWithCompType + where + compTypeStr :: ComponentName -> String + compTypeStr (CLibName _) = "lib" + compTypeStr (CFLibName _) = "flib" + compTypeStr (CExeName _) = "exe" + compTypeStr (CTestName _) = "test" + compTypeStr (CBenchName _) = "bench" + + findComponentInfo :: BuildInfo -> ComponentInfo + findComponentInfo buildInfo = + case filter (\c -> prettyShow cname == componentName c) (components buildInfo) of + [x] -> x + [] -> error $ "findComponentInfo: component " ++ prettyShow cname ++ " does not" + ++ " exist in build info-file" + _ -> error $ "findComponentInfo: found multiple copies of component " ++ prettyShow cname + ++ " in build info plan" + +-- | Helper function to create an executable component name. +exe :: String -> ComponentName +exe = CExeName . mkUnqualComponentName + +-- | Helper function to create a named sub-library component name. +lib :: String -> ComponentName +lib = CLibName . LSubLibName . mkUnqualComponentName + +-- | Helper function to create an test component name. +test :: String -> ComponentName +test = CTestName . mkUnqualComponentName + +-- | Helper function to create an benchmark component name. +bench :: String -> ComponentName +bench = CBenchName . mkUnqualComponentName + +-- | Helper function to create a main library component name. +mainLib :: ComponentName +mainLib = CLibName LMainLibName diff --git a/cabal-testsuite/src/Test/Cabal/Monad.hs b/cabal-testsuite/src/Test/Cabal/Monad.hs index fa1407eae03..376d144e606 100644 --- a/cabal-testsuite/src/Test/Cabal/Monad.hs +++ b/cabal-testsuite/src/Test/Cabal/Monad.hs @@ -48,6 +48,8 @@ module Test.Cabal.Monad ( CommonArgs(..), renderCommonArgs, commonArgParser, + -- * Version Constants + cabalVersionLibrary, ) where import Test.Cabal.Script @@ -63,9 +65,11 @@ import Distribution.Simple.Program.Db import Distribution.Simple.Program import Distribution.Simple.Configure ( configCompilerEx ) +import qualified Distribution.Simple.Utils as U (cabalVersion) import Distribution.Text import Distribution.Verbosity +import Distribution.Version import Data.Monoid ((<>), mempty) import qualified Control.Exception as E @@ -399,6 +403,7 @@ mkNormalizerEnv = do list_out <- liftIO $ readProcess (programPath ghc_pkg_program) ["list", "--global", "--simple-output"] "" tmpDir <- liftIO $ getTemporaryDirectory + return NormalizerEnv { normalizerRoot = addTrailingPathSeparator (testSourceDir env), @@ -411,8 +416,14 @@ mkNormalizerEnv = do normalizerKnownPackages = mapMaybe simpleParse (words list_out), normalizerPlatform - = testPlatform env + = testPlatform env, + normalizerCabalVersion + = cabalVersionLibrary } + where + +cabalVersionLibrary :: Version +cabalVersionLibrary = U.cabalVersion requireProgramM :: Program -> TestM ConfiguredProgram requireProgramM program = do diff --git a/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs b/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs index 4e6aec19890..e67c63b0dc6 100644 --- a/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs +++ b/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs @@ -49,6 +49,7 @@ normalizeOutput nenv = "/incoming/new-" -- Normalize architecture . resub (posixRegexEscape (display (normalizerPlatform nenv))) "" + . normalizeBuildInfoJson -- Some GHC versions are chattier than others . resub "^ignoring \\(possibly broken\\) abi-depends field for packages" "" -- Normalize the current GHC version. Apply this BEFORE packageIdRegex, @@ -67,6 +68,30 @@ normalizeOutput nenv = resub (posixRegexEscape (display pid) ++ "(-[A-Za-z0-9.-]+)?") (prettyShow (packageName pid) ++ "-") + -- 'build-info.json' contains a plethora of host system specific information. + -- + -- This must happen before the root-dir normalisation. + normalizeBuildInfoJson = + -- Remove ghc path from show-build-info output + resub ("\"path\":\"[^\"]*\"}") + "\"path\":\"\"}" + -- Remove cabal version output from show-build-info output + . resub ("{\"cabal-version\":\"" ++ posixRegexEscape (display (normalizerCabalVersion nenv)) ++ "\"") + "{\"cabal-version\":\"\"" + -- Remove the package id for stuff such as: + -- > "-package-id","base-4.14.0.0-" + -- and replace it with: + -- > "-package-id","" + -- + -- Otherwise, output can not be properly normalized as on MacOs we remove + -- vowels from packages to make the names shorter. + -- E.g. "another-framework-0.8.1.1" -> "nthr-frmwrk-0.8.1.1" + -- + -- This makes it impossible to have a stable package id, thus remove it completely. + -- Check manually in your test-cases if the package-id needs to be verified. + . resub ("\"-package-id\",\"([^\"]*)\"") + "\"-package-id\",\"\"" + data NormalizerEnv = NormalizerEnv { normalizerRoot :: FilePath , normalizerTmpDir :: FilePath @@ -74,6 +99,7 @@ data NormalizerEnv = NormalizerEnv , normalizerGhcVersion :: Version , normalizerKnownPackages :: [PackageId] , normalizerPlatform :: Platform + , normalizerCabalVersion :: Version } posixSpecialChars :: [Char] diff --git a/cabal-testsuite/src/Test/Cabal/Plan.hs b/cabal-testsuite/src/Test/Cabal/Plan.hs index 223832edc19..0665de9a9b9 100644 --- a/cabal-testsuite/src/Test/Cabal/Plan.hs +++ b/cabal-testsuite/src/Test/Cabal/Plan.hs @@ -5,6 +5,7 @@ module Test.Cabal.Plan ( Plan, DistDirOrBinFile(..), planDistDir, + buildInfoFile, ) where import Distribution.Parsec (simpleParsec) @@ -27,6 +28,7 @@ data InstallItem -- local or inplace package data ConfiguredInplace = ConfiguredInplace { configuredInplaceDistDir :: FilePath + , configuredInplaceBuildInfo :: Maybe FilePath , configuredInplacePackageName :: PackageName , configuredInplaceComponentName :: Maybe ComponentName } @@ -57,9 +59,10 @@ instance FromJSON InstallItem where instance FromJSON ConfiguredInplace where parseJSON (Object v) = do dist_dir <- v .: "dist-dir" + build_info <- v .:? "build-info" pkg_name <- v .: "pkg-name" component_name <- v .:? "component-name" - return (ConfiguredInplace dist_dir pkg_name component_name) + return (ConfiguredInplace dist_dir build_info pkg_name component_name) parseJSON invalid = typeMismatch "ConfiguredInplace" invalid instance FromJSON ConfiguredGlobal where @@ -109,3 +112,25 @@ planDistDir plan pkg_name cname = Nothing -> True Just cname' -> cname == cname' return $ DistDir $ configuredInplaceDistDir conf + +buildInfoFile :: Plan -> PackageName -> ComponentName -> FilePath +buildInfoFile plan pkg_name cname = + case concatMap p (planInstallPlan plan) of + [Just x] -> x + [Nothing] -> error $ "buildInfoFile: component " ++ prettyShow cname + ++ " of package " ++ prettyShow pkg_name ++ " does not" + ++ " have a build info-file" + [] -> error $ "buildInfoFile: component " ++ prettyShow cname + ++ " of package " ++ prettyShow pkg_name ++ " either does not" + ++ " exist in the install plan or build info-file" + _ -> error $ "buildInfoFile: found multiple copies of component " ++ prettyShow cname + ++ " of package " ++ prettyShow pkg_name ++ " in install plan" + where + p APreExisting = [] + p (AConfiguredGlobal _) = [] + p (AConfiguredInplace conf) = do + guard (configuredInplacePackageName conf == pkg_name) + guard $ case configuredInplaceComponentName conf of + Nothing -> True + Just cname' -> cname == cname' + return $ configuredInplaceBuildInfo conf From 6b954b0ca15eeaa0cc10f8debca1e29886280c0f Mon Sep 17 00:00:00 2001 From: fendor Date: Sun, 8 Aug 2021 18:34:40 +0200 Subject: [PATCH 06/10] Add user-guide documentation for 'build-info' --- .../src/Distribution/Simple/ShowBuildInfo.hs | 4 + doc/cabal-project.rst | 26 ++++++ doc/json-schemas/build-info.schema.json | 84 +++++++++++++++++++ doc/requirements.txt | 1 + doc/setup-commands.rst | 61 +++++++++++++- 5 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 doc/json-schemas/build-info.schema.json diff --git a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs index 9bfda3eadd5..44bb55916f3 100644 --- a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs +++ b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs @@ -104,6 +104,10 @@ mkBuildInfo wdir pkg_descr lbi _flags targetsToBuild = (warnings, JsonObject bui -- | A variant of 'mkBuildInfo' if you need to call 'mkCompilerInfo' and -- 'mkComponentInfo' yourself. +-- +-- If you change the format or any name in the output json, don't forget to update +-- the schema at @\/doc\/json-schemas\/build-info.schema.json@ and the docs of +-- @--enable-build-info@\/@--disable-build-info@. mkBuildInfo' :: Json -- ^ The 'Json' from 'mkCompilerInfo' -> [Json] -- ^ The 'Json' from 'mkComponentInfo' diff --git a/doc/cabal-project.rst b/doc/cabal-project.rst index e66825957a0..6ac3b6b8e16 100644 --- a/doc/cabal-project.rst +++ b/doc/cabal-project.rst @@ -1432,6 +1432,32 @@ Advanced global configuration options the ``-package-env -`` option that allows ignoring the package environment files). +.. cfg-field:: build-info: True, False + --enable-build-info + --disable-build-info + :synopsis: Whether build information for each individual component should be + written in a machine readable format. + + :default: ``False`` + + Enable generation of build information for Cabal components. Contains very + detailed information on how to build an individual component, such as + compiler version, modules of a component and how to compile the component. + + The output format is in json, and the exact location can be discovered from + ``plan.json``, where it is identified by ``build-info`` within the items in + the ``install-plan``. + Note, that this field in ``plan.json`` can be ``null``, if and only if + ``build-type: Custom`` is set, and the ``Cabal`` version is too + old (i.e. ``< 3.7``). + If the field is missing entirely, the component is not a local one, thus, + no ``build-info`` exists for that particular component within the + ``install-plan``. + + .. note:: + The format and fields of the generated build information is currently experimental, + in the future we might add or remove fields, depending on the needs of other tooling. + .. cfg-field:: http-transport: curl, wget, powershell, or plain-http --http-transport=transport diff --git a/doc/json-schemas/build-info.schema.json b/doc/json-schemas/build-info.schema.json new file mode 100644 index 00000000000..ae3be7e5310 --- /dev/null +++ b/doc/json-schemas/build-info.schema.json @@ -0,0 +1,84 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$comment": "When you change this, make sure to update the code in 'ShowBuildInfo.hs'", + "type": "object", + "properties": { + "cabal-lib-version": { + "type": "string" + }, + "compiler": { + "type": "object", + "properties": { + "flavour": { + "type": "string" + }, + "compiler-id": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": ["flavour", "compiler-id", "path"] + }, + "components": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "unit-id": { + "type": "string" + }, + "compiler-args": { + "type": "array", + "items": { + "type": "string" + } + }, + "modules": { + "type": "array", + "items": { + "type": "string" + } + }, + "src-files": { + "type": "array", + "items": { + "type": "string" + } + }, + "hs-src-dirs": { + "type": "array", + "items": { + "type": "string" + } + }, + "src-dir": { + "type": "string" + }, + "cabal-file": { + "type": "string" + } + }, + "required": [ + "type", + "name", + "unit-id", + "compiler-args", + "modules", + "src-files", + "hs-src-dirs", + "src-dir", + "cabal-file" + ] + } + } + }, + "required": ["cabal-lib-version", "compiler", "components"] +} diff --git a/doc/requirements.txt b/doc/requirements.txt index 38333cb225d..3ec19d5512a 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,2 +1,3 @@ sphinx == 3.1.* sphinx_rtd_theme +sphinx-jsonschema == 1.16.* diff --git a/doc/setup-commands.rst b/doc/setup-commands.rst index d2162aa6b95..12c4f32f89b 100644 --- a/doc/setup-commands.rst +++ b/doc/setup-commands.rst @@ -32,7 +32,7 @@ performs the actual building, while the last both copies the build results to some permanent place and registers the package with GHC. .. note :: - + Global installing of packages is not recommended. The :ref:`nix-style-builds` is the preferred way of building and installing packages. @@ -1034,6 +1034,61 @@ Miscellaneous options Specify a soft constraint on versions of a package. The solver will attempt to satisfy these preferences on a "best-effort" basis. +.. option:: --enable-build-info + + Generate accurate build information for build components. + + Information contains meta information, such as component type, compiler type, and + Cabal library version used during the build, but also fine grained information, + such as dependencies, what modules are part of the component, etc... + + On build, a file ``build-info.json`` (in the ``json`` format) will be written to + the root of the build directory. + + .. note:: + The format and fields of the generated build information is currently + experimental. In the future we might add or remove fields, depending + on the needs of other tooling. + + :: example + { + "cabal-lib-version": "", + "compiler": { + "flavour": "", + "compiler-id": "", + "path": "" + }, + "components": [ + { + "type": "", + "name": "", + "unit-id": "", + "compiler-args": [ + "" + ], + "modules": [ + "" + ], + "src-files": [ + "" + ], + "hs-src-dirs": [ + "" + ], + "src-dir": "", + "cabal-file": "" + } + ] + } + + .. jsonschema:: ./json-schemas/build-info.schema.json + +.. option:: --disable-build-info + + (default) Do not generate detailed build information for built components. + + Already generated `build-info.json` files will be removed since they would be stale otherwise. + .. option:: --disable-response-files Enable workaround for older versions of programs such as ``ar`` or @@ -1132,7 +1187,7 @@ This command takes the following options: .. option:: --hscolour-css=path The argument *path* denotes a CSS file, which is passed to HsColour_ as in - + :: $ runhaskell Setup.hs hscolour --css=*path* @@ -1358,7 +1413,7 @@ the package. results in real time). .. option:: --test-options=options - + Give extra options to the test executables. .. option:: --test-option=option From c444ad8e4650ff148ba2df1db7b78a513db668e4 Mon Sep 17 00:00:00 2001 From: Fendor Date: Mon, 30 Aug 2021 13:25:47 +0200 Subject: [PATCH 07/10] Always write build-info, even before building --- Cabal/src/Distribution/Simple/Build.hs | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 78bf79feaef..6d7b2115d64 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -114,21 +114,9 @@ build pkg_descr lbi flags suffixes = do internalPackageDB <- createInternalPackageDB verbosity lbi distPref - (\f -> foldM_ f (installedPkgs lbi) componentsToBuild) $ \index target -> do - let comp = targetComponent target - clbi = targetCLBI target - componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity - let bi = componentBuildInfo comp - progs' = addInternalBuildTools pkg_descr lbi bi (withPrograms lbi) - lbi' = lbi { - withPrograms = progs', - withPackageDB = withPackageDB lbi ++ [internalPackageDB], - installedPkgs = index - } - mb_ipi <- buildComponent verbosity (buildNumJobs flags) pkg_descr - lbi' suffixes comp clbi distPref - return (maybe index (Index.insert `flip` index) mb_ipi) - + -- Before the actual building, dump out build-information. + -- This way, if the actual compilation failed, the options have still been + -- dumped. when shouldDumpBuildInfo $ do -- Changing this line might break consumers of the dumped build info. -- Announce changes on mailing lists! @@ -150,6 +138,22 @@ build pkg_descr lbi flags suffixes = do exists <- doesFileExist (buildInfoPref distPref) when exists $ removeFile (buildInfoPref distPref) + -- Now do the actual building + (\f -> foldM_ f (installedPkgs lbi) componentsToBuild) $ \index target -> do + let comp = targetComponent target + clbi = targetCLBI target + componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity + let bi = componentBuildInfo comp + progs' = addInternalBuildTools pkg_descr lbi bi (withPrograms lbi) + lbi' = lbi { + withPrograms = progs', + withPackageDB = withPackageDB lbi ++ [internalPackageDB], + installedPkgs = index + } + mb_ipi <- buildComponent verbosity (buildNumJobs flags) pkg_descr + lbi' suffixes comp clbi distPref + return (maybe index (Index.insert `flip` index) mb_ipi) + return () where distPref = fromFlag (buildDistPref flags) From b68243c986156ffcfc9d353f7c47574e0fe65e43 Mon Sep 17 00:00:00 2001 From: Fendor Date: Mon, 30 Aug 2021 13:26:26 +0200 Subject: [PATCH 08/10] Add test-cases for extracting build-info, even if a component failed to build --- .../ShowBuildInfo/A/remove-outdated.test.hs | 1 - .../CompileFail/CompileFail.cabal | 29 ++++++++++++++ .../ShowBuildInfo/CompileFail/app/Main.hs | 6 +++ .../ShowBuildInfo/CompileFail/cabal.project | 1 + .../CompileFail/compile-fail.out | 22 +++++++++++ .../CompileFail/compile-fail.test.hs | 38 +++++++++++++++++++ .../ShowBuildInfo/CompileFail/src/MyLib.hs | 4 ++ .../ShowBuildInfo/CompileFail/src/MyLib2.hs | 6 +++ .../ShowBuildInfo/CompileFail/test/Main.hs | 6 +++ .../src/Test/Cabal/DecodeShowBuildInfo.hs | 2 +- 10 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/CompileFail.cabal create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/app/Main.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/cabal.project create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.out create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.test.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib2.hs create mode 100644 cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/test/Main.hs diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs index f6bcdf047fe..f2f0c4302fc 100644 --- a/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/A/remove-outdated.test.hs @@ -3,7 +3,6 @@ import Test.Cabal.Prelude import Test.Cabal.DecodeShowBuildInfo import Test.Cabal.Plan import Control.Monad.Trans.Reader -import System.Directory main = cabalTest $ do runShowBuildInfo ["exe:A"] diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/CompileFail.cabal b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/CompileFail.cabal new file mode 100644 index 00000000000..0fd965fb985 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/CompileFail.cabal @@ -0,0 +1,29 @@ +cabal-version: 3.0 +name: CompileFail +version: 0.1.0.0 +build-type: Simple + +library + exposed-modules: MyLib + build-depends: base + hs-source-dirs: src + default-language: Haskell2010 + +library failing + exposed-modules: MyLib2 + build-depends: base + hs-source-dirs: src + default-language: Haskell2010 + +test-suite CompileFail-test + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Main.hs + build-depends: base, CompileFail + +executable CompileFail-exe + default-language: Haskell2010 + hs-source-dirs: app + main-is: Main.hs + build-depends: base, failing diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/app/Main.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/app/Main.hs new file mode 100644 index 00000000000..1a3121450a4 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import MyLib2 (someFunc2) + +main :: IO () +main = someFunc2 diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/cabal.project b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/cabal.project new file mode 100644 index 00000000000..6f920794c80 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/cabal.project @@ -0,0 +1 @@ +packages: ./ diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.out b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.out new file mode 100644 index 00000000000..3c13bf35ea9 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.out @@ -0,0 +1,22 @@ +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - CompileFail-0.1.0.0 (lib) (first run) + - CompileFail-0.1.0.0 (test:CompileFail-test) (first run) +Configuring library for CompileFail-0.1.0.0.. +Preprocessing library for CompileFail-0.1.0.0.. +Building library for CompileFail-0.1.0.0.. +Configuring test suite 'CompileFail-test' for CompileFail-0.1.0.0.. +Preprocessing test suite 'CompileFail-test' for CompileFail-0.1.0.0.. +Building test suite 'CompileFail-test' for CompileFail-0.1.0.0.. +# cabal build +Build profile: -w ghc- -O1 +In order, the following will be built: + - CompileFail-0.1.0.0 (lib:failing) (first run) + - CompileFail-0.1.0.0 (exe:CompileFail-exe) (first run) +Configuring library 'failing' for CompileFail-0.1.0.0.. +Preprocessing library 'failing' for CompileFail-0.1.0.0.. +Building library 'failing' for CompileFail-0.1.0.0.. +cabal: Failed to build CompileFail-0.1.0.0 because it depends on CompileFail-0.1.0.0 which itself failed to build. +Failed to build CompileFail-0.1.0.0-inplace-failing. diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.test.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.test.hs new file mode 100644 index 00000000000..fc1fba234d5 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/compile-fail.test.hs @@ -0,0 +1,38 @@ +{-# LANGUAGE OverloadedStrings #-} +import Test.Cabal.Prelude +import Test.Cabal.DecodeShowBuildInfo +import Test.Cabal.Plan +import Control.Monad.Trans.Reader + +main = cabalTest $ do + -- Leaf component fails to compile, should still dump + -- build info for both components. + fails $ runShowBuildInfo ["test:CompileFail-test"] + withPlan $ do + -- Lib has to be built, thus info is dumped + assertComponent "CompileFail" mainLib + defCompAssertion + { modules = ["MyLib"] + , sourceDirs = ["src"] + } + + -- Build Info is still dumped, although compilation failed + assertComponent "CompileFail" (test "CompileFail-test") + defCompAssertion + { sourceFiles = ["Main.hs"] + , sourceDirs = ["test"] + } + + fails $ runShowBuildInfo ["exe:CompileFail-exe"] + withPlan $ do + -- Internal Lib has to be built, thus info is dumped + assertComponent "CompileFail" (lib "failing") + defCompAssertion + { modules = ["MyLib2"] + , sourceDirs = ["src"] + } + -- However, since the internal lib failed to compile + -- we can not have executable build information. + Just plan <- fmap testPlan ask + let fp = buildInfoFile plan "CompileFail" (exe "CompileFail-exe") + shouldNotExist fp diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib.hs new file mode 100644 index 00000000000..e657c4403f6 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib.hs @@ -0,0 +1,4 @@ +module MyLib (someFunc) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib2.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib2.hs new file mode 100644 index 00000000000..9b620c62fcd --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/src/MyLib2.hs @@ -0,0 +1,6 @@ +module MyLib2 (someFunc2) where + +someFunc2 :: IO () +-- Intentional typo, should fail to compile +someFunc2 = putStrn "someFunc" +-- ^^------- missing 'L' diff --git a/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/test/Main.hs b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/test/Main.hs new file mode 100644 index 00000000000..bae4112fa97 --- /dev/null +++ b/cabal-testsuite/PackageTests/ShowBuildInfo/CompileFail/test/Main.hs @@ -0,0 +1,6 @@ +module Main (main) where + +main :: IO () +-- Intentional typo, should fail to compile +main = putStrn "Test suite not yet implemented." +-- ^^------- missing 'L' diff --git a/cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs b/cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs index 47fbcc83795..02c1cb7e733 100644 --- a/cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs +++ b/cabal-testsuite/src/Test/Cabal/DecodeShowBuildInfo.hs @@ -111,7 +111,7 @@ defCompAssertion = ComponentAssertion , modules = [] , sourceFiles = [] , sourceDirs = [] - , compType = mempty + , compType = "" } -- | Assert common build information, such as compiler location, compiler version From 66669b1131e3823f1cf89d975899ae6d9618bda7 Mon Sep 17 00:00:00 2001 From: Fendor Date: Sat, 4 Sep 2021 12:30:27 +0200 Subject: [PATCH 09/10] Extract 'dumpBuildInfo' function --- Cabal/src/Distribution/Simple/Build.hs | 61 +++++++++++++++++--------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 6d7b2115d64..8891490a197 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -117,26 +117,7 @@ build pkg_descr lbi flags suffixes = do -- Before the actual building, dump out build-information. -- This way, if the actual compilation failed, the options have still been -- dumped. - when shouldDumpBuildInfo $ do - -- Changing this line might break consumers of the dumped build info. - -- Announce changes on mailing lists! - let activeTargets = allTargetsInBuildOrder' pkg_descr lbi - info verbosity $ "Dump build information for: " - ++ intercalate ", " - (map (showComponentName . componentLocalName . targetCLBI) - activeTargets) - pwd <- getCurrentDirectory - let (warns, json) = mkBuildInfo pwd pkg_descr lbi flags activeTargets - buildInfoText = renderJson json - unless (null warns) $ - warn verbosity $ "Encountered warnings while dumping build-info:\n" - ++ unlines warns - LBS.writeFile (buildInfoPref distPref) buildInfoText - - when (not shouldDumpBuildInfo) $ do - -- Remove existing build-info.json as it might be outdated now. - exists <- doesFileExist (buildInfoPref distPref) - when exists $ removeFile (buildInfoPref distPref) + dumpBuildInfo verbosity distPref (configDumpBuildInfo (configFlags lbi)) pkg_descr lbi flags -- Now do the actual building (\f -> foldM_ f (installedPkgs lbi) componentsToBuild) $ \index target -> do @@ -158,7 +139,45 @@ build pkg_descr lbi flags suffixes = do where distPref = fromFlag (buildDistPref flags) verbosity = fromFlag (buildVerbosity flags) - shouldDumpBuildInfo = fromFlagOrDefault NoDumpBuildInfo (configDumpBuildInfo (configFlags lbi)) == DumpBuildInfo + + +-- | Write available build information for 'LocalBuildInfo' to disk. +-- +-- Dumps detailed build information 'build-info.json' to the given directory. +-- Build information contains basics such as compiler details, but also +-- lists what modules a component contains and how to compile the component, assuming +-- lib:Cabal made sure that dependencies are up-to-date. +dumpBuildInfo :: Verbosity + -> FilePath -- ^ To which directory should the build-info be dumped? + -> Flag DumpBuildInfo -- ^ Should we dump detailed build information for this component? + -> PackageDescription -- ^ Mostly information from the .cabal file + -> LocalBuildInfo -- ^ Configuration information + -> BuildFlags -- ^ Flags that the user passed to build + -> IO () +dumpBuildInfo verbosity distPref dumpBuildInfoFlag pkg_descr lbi flags = do + when shouldDumpBuildInfo $ do + -- Changing this line might break consumers of the dumped build info. + -- Announce changes on mailing lists! + let activeTargets = allTargetsInBuildOrder' pkg_descr lbi + info verbosity $ "Dump build information for: " + ++ intercalate ", " + (map (showComponentName . componentLocalName . targetCLBI) + activeTargets) + pwd <- getCurrentDirectory + let (warns, json) = mkBuildInfo pwd pkg_descr lbi flags activeTargets + buildInfoText = renderJson json + unless (null warns) $ + warn verbosity $ "Encountered warnings while dumping build-info:\n" + ++ unlines warns + LBS.writeFile (buildInfoPref distPref) buildInfoText + + when (not shouldDumpBuildInfo) $ do + -- Remove existing build-info.json as it might be outdated now. + exists <- doesFileExist (buildInfoPref distPref) + when exists $ removeFile (buildInfoPref distPref) + where + shouldDumpBuildInfo = fromFlagOrDefault NoDumpBuildInfo dumpBuildInfoFlag == DumpBuildInfo + repl :: PackageDescription -- ^ Mostly information from the .cabal file -> LocalBuildInfo -- ^ Configuration information From 0702193d93bb08fbbf5f1980bb2e661ae5342a68 Mon Sep 17 00:00:00 2001 From: Fendor Date: Sat, 4 Sep 2021 12:49:42 +0200 Subject: [PATCH 10/10] Make compiler path not nullable in dumped build-info Refactor the API slightly s.t. a ConfiguredProgram for the Compiler is passed to build-info generation directly. --- Cabal/src/Distribution/Simple/Build.hs | 19 +++++++++++++- .../src/Distribution/Simple/ShowBuildInfo.hs | 25 +++++++------------ .../Distribution/Client/ProjectPlanOutput.hs | 8 +++--- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 8891490a197..8a4beecfce4 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -69,6 +69,7 @@ import Distribution.Simple.BuildTarget import Distribution.Simple.BuildToolDepends import Distribution.Simple.PreProcess import Distribution.Simple.LocalBuildInfo +import Distribution.Simple.Program.Builtin (ghcProgram, ghcjsProgram, uhcProgram, jhcProgram, haskellSuiteProgram) import Distribution.Simple.Program.Types import Distribution.Simple.Program.Db import Distribution.Simple.ShowBuildInfo @@ -164,7 +165,13 @@ dumpBuildInfo verbosity distPref dumpBuildInfoFlag pkg_descr lbi flags = do (map (showComponentName . componentLocalName . targetCLBI) activeTargets) pwd <- getCurrentDirectory - let (warns, json) = mkBuildInfo pwd pkg_descr lbi flags activeTargets + + (compilerProg, _) <- case flavorToProgram (compilerFlavor (compiler lbi)) of + Nothing -> die' verbosity $ "dumpBuildInfo: Unknown compiler flavor: " + ++ show (compilerFlavor (compiler lbi)) + Just program -> requireProgram verbosity program (withPrograms lbi) + + let (warns, json) = mkBuildInfo pwd pkg_descr lbi flags (compilerProg, compiler lbi) activeTargets buildInfoText = renderJson json unless (null warns) $ warn verbosity $ "Encountered warnings while dumping build-info:\n" @@ -178,6 +185,16 @@ dumpBuildInfo verbosity distPref dumpBuildInfoFlag pkg_descr lbi flags = do where shouldDumpBuildInfo = fromFlagOrDefault NoDumpBuildInfo dumpBuildInfoFlag == DumpBuildInfo + -- | Given the flavor of the compiler, try to find out + -- which program we need. + flavorToProgram :: CompilerFlavor -> Maybe Program + flavorToProgram GHC = Just ghcProgram + flavorToProgram GHCJS = Just ghcjsProgram + flavorToProgram UHC = Just uhcProgram + flavorToProgram JHC = Just jhcProgram + flavorToProgram HaskellSuite {} = Just haskellSuiteProgram + flavorToProgram _ = Nothing + repl :: PackageDescription -- ^ Mostly information from the .cabal file -> LocalBuildInfo -- ^ Configuration information diff --git a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs index 44bb55916f3..729fb689926 100644 --- a/Cabal/src/Distribution/Simple/ShowBuildInfo.hs +++ b/Cabal/src/Distribution/Simple/ShowBuildInfo.hs @@ -93,11 +93,15 @@ mkBuildInfo -> PackageDescription -- ^ Mostly information from the .cabal file -> LocalBuildInfo -- ^ Configuration information -> BuildFlags -- ^ Flags that the user passed to build + -> (ConfiguredProgram, Compiler) + -- ^ Compiler information. + -- Needs to be passed explicitly, as we can't extract that information here + -- without some partial function. -> [TargetInfo] -> ([String], Json) -- ^ Json representation of buildinfo alongside generated warnings -mkBuildInfo wdir pkg_descr lbi _flags targetsToBuild = (warnings, JsonObject buildInfoFields) +mkBuildInfo wdir pkg_descr lbi _flags compilerInfo targetsToBuild = (warnings, JsonObject buildInfoFields) where - buildInfoFields = mkBuildInfo' (mkCompilerInfo (withPrograms lbi) (compiler lbi)) componentInfos + buildInfoFields = mkBuildInfo' (uncurry mkCompilerInfo compilerInfo) componentInfos componentInfosWithWarnings = map (mkComponentInfo wdir pkg_descr lbi . targetCLBI) targetsToBuild componentInfos = map snd componentInfosWithWarnings warnings = concatMap fst componentInfosWithWarnings @@ -118,23 +122,12 @@ mkBuildInfo' compilerInfo componentInfos = , "components" .= JsonArray componentInfos ] -mkCompilerInfo :: ProgramDb -> Compiler -> Json -mkCompilerInfo programDb compilerInfo = JsonObject +mkCompilerInfo :: ConfiguredProgram -> Compiler -> Json +mkCompilerInfo compilerProgram compilerInfo = JsonObject [ "flavour" .= JsonString (prettyShow $ compilerFlavor compilerInfo) , "compiler-id" .= JsonString (showCompilerId compilerInfo) - , "path" .= path + , "path" .= JsonString (programPath compilerProgram) ] - where - path = maybe JsonNull (JsonString . programPath) - $ (flavorToProgram . compilerFlavor $ compilerInfo) - >>= flip lookupProgram programDb - - flavorToProgram :: CompilerFlavor -> Maybe Program - flavorToProgram GHC = Just ghcProgram - flavorToProgram GHCJS = Just ghcjsProgram - flavorToProgram UHC = Just uhcProgram - flavorToProgram JHC = Just jhcProgram - flavorToProgram _ = Nothing mkComponentInfo :: FilePath -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> ([String], Json) mkComponentInfo wdir pkg_descr lbi clbi = (warnings, JsonObject $ diff --git a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs index ec0d5a676b2..876982a9570 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs @@ -179,17 +179,17 @@ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig = where -- | Only add build-info file location if the Setup.hs CLI -- is recent enough to be able to generate build info files. - -- Otherwise, do not add the expected file location. + -- Otherwise, write 'null'. -- - -- Consumers of `plan.json` can use the absence of this file location + -- Consumers of `plan.json` can use the nullability of this file location -- to indicate that the given component uses `build-type: Custom` -- with an old lib:Cabal version. buildInfoFileLocation :: J.Pair buildInfoFileLocation | elabSetupScriptCliVersion elab < mkVersion [3, 7, 0, 0] - = ("build-info" J..= J.Null) + = "build-info" J..= J.Null | otherwise - = ("build-info" J..= J.String (buildInfoPref dist_dir)) + = "build-info" J..= J.String (buildInfoPref dist_dir) packageLocationToJ :: PackageLocation (Maybe FilePath) -> J.Value packageLocationToJ pkgloc =