diff --git a/Cabal/Distribution/Simple.hs b/Cabal/Distribution/Simple.hs index ae9b36a78435595e391fa846202de978ef139ed6..e632acc88e8a188726313f0bb79d521ccc9ec968 100644 --- a/Cabal/Distribution/Simple.hs +++ b/Cabal/Distribution/Simple.hs @@ -72,8 +72,8 @@ import Distribution.Simple.PreProcess import Distribution.Simple.Setup import Distribution.Simple.Command -import Distribution.Simple.Build ( build, showBuildInfo, repl ) -import Distribution.Simple.SrcDist ( sdist ) +import Distribution.Simple.Build +import Distribution.Simple.SrcDist import Distribution.Simple.Register import Distribution.Simple.Configure @@ -265,13 +265,15 @@ buildAction hooks flags args = do (return lbi { withPrograms = progs }) hooks flags' { buildArgs = args } args -showBuildInfoAction :: UserHooks -> BuildFlags -> Args -> IO () -showBuildInfoAction hooks flags args = do +showBuildInfoAction :: UserHooks -> ShowBuildInfoFlags -> Args -> IO () +showBuildInfoAction hooks (ShowBuildInfoFlags flags fileOutput) args = do distPref <- findDistPrefOrDefault (buildDistPref flags) let verbosity = fromFlag $ buildVerbosity flags - flags' = flags { buildDistPref = toFlag distPref } - lbi <- getBuildConfig hooks verbosity distPref + let flags' = flags { buildDistPref = toFlag distPref + , buildCabalFilePath = maybeToFlag (cabalFilePath lbi) + } + progs <- reconfigurePrograms verbosity (buildProgramPaths flags') (buildProgramArgs flags') @@ -281,8 +283,12 @@ showBuildInfoAction hooks flags args = do let lbi' = lbi { withPrograms = progs } pkg_descr0 = localPkgDescr lbi' pkg_descr = updatePackageDescription pbi pkg_descr0 - -- TODO: Somehow don't ignore build hook? - showBuildInfo pkg_descr lbi' flags + -- TODO: Somehow don't ignore build hook? + buildInfoString <- showBuildInfo pkg_descr lbi' flags + + case fileOutput of + Nothing -> putStr buildInfoString + Just fp -> writeFile fp buildInfoString postBuild hooks args flags' pkg_descr lbi' diff --git a/Cabal/Distribution/Simple/Build.hs b/Cabal/Distribution/Simple/Build.hs index 3747d1f48fed8e3bc7a34deacb05d1c192f72829..331c367d8ded53d8726f7b675f63ef0677a1c85d 100644 --- a/Cabal/Distribution/Simple/Build.hs +++ b/Cabal/Distribution/Simple/Build.hs @@ -69,15 +69,12 @@ import Distribution.Simple.PreProcess import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program.Types import Distribution.Simple.Program.Db -import qualified Distribution.Simple.Program.HcPkg as HcPkg import Distribution.Simple.ShowBuildInfo import Distribution.Simple.BuildPaths import Distribution.Simple.Configure import Distribution.Simple.Register import Distribution.Simple.Test.LibV09 import Distribution.Simple.Utils - ( createDirectoryIfMissingVerbose, rewriteFile, rewriteFileEx - , die, die', info, debug, warn, setupMessage ) import Distribution.Simple.Utils.Json import Distribution.System @@ -136,13 +133,13 @@ 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 - -> IO () + -> IO String showBuildInfo pkg_descr lbi flags = do let verbosity = fromFlag (buildVerbosity flags) targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs flags) let targetsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) doc = mkBuildInfo pkg_descr lbi flags targetsToBuild - putStrLn $ renderJson doc "" + return $ renderJson doc "" repl :: PackageDescription -- ^ Mostly information from the .cabal file diff --git a/Cabal/Distribution/Simple/Setup.hs b/Cabal/Distribution/Simple/Setup.hs index cb3082801c350ad2aae646b66540eb5bd6e0def3..4630f20e0641c3ade67e4415685d023fb15ec51c 100644 --- a/Cabal/Distribution/Simple/Setup.hs +++ b/Cabal/Distribution/Simple/Setup.hs @@ -45,7 +45,7 @@ module Distribution.Simple.Setup ( HaddockFlags(..), emptyHaddockFlags, defaultHaddockFlags, haddockCommand, HscolourFlags(..), emptyHscolourFlags, defaultHscolourFlags, hscolourCommand, BuildFlags(..), emptyBuildFlags, defaultBuildFlags, buildCommand, - showBuildInfoCommand, + ShowBuildInfoFlags(..), defaultShowBuildFlags, showBuildInfoCommand, ReplFlags(..), defaultReplFlags, replCommand, CleanFlags(..), emptyCleanFlags, defaultCleanFlags, cleanCommand, RegisterFlags(..), emptyRegisterFlags, defaultRegisterFlags, registerCommand, @@ -1622,49 +1622,6 @@ instance Monoid CleanFlags where instance Semigroup CleanFlags where (<>) = gmappend --- ------------------------------------------------------------ --- * show-build-info flags --- ------------------------------------------------------------ - -showBuildInfoCommand :: ProgramConfiguration -> CommandUI BuildFlags -showBuildInfoCommand progConf = 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 progConf ---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 = defaultBuildFlags - , commandOptions = \showOrParseArgs -> - [ optionVerbosity - buildVerbosity (\v flags -> flags { buildVerbosity = v }) - - , optionDistPref - buildDistPref (\d flags -> flags { buildDistPref = d }) showOrParseArgs - ] - ++ buildOptions progConf showOrParseArgs - } - -- ------------------------------------------------------------ -- * Build flags -- ------------------------------------------------------------ @@ -2249,6 +2206,81 @@ 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 + } deriving Show + +defaultShowBuildFlags :: ShowBuildInfoFlags +defaultShowBuildFlags = + ShowBuildInfoFlags + { buildInfoBuildFlags = defaultBuildFlags + , buildInfoOutputFile = Nothing + } + +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 (\pf flags -> flags { buildInfoOutputFile = pf }) + (reqArg' "FILE" Just (maybe [] pure)) + ] + + } + +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/Distribution/Simple/ShowBuildInfo.hs b/Cabal/Distribution/Simple/ShowBuildInfo.hs index 21bbb44c8769709da8da2fd49cfd3860802a4b62..b13f95b78f07f41bf0e7c8565b01497896184da1 100644 --- a/Cabal/Distribution/Simple/ShowBuildInfo.hs +++ b/Cabal/Distribution/Simple/ShowBuildInfo.hs @@ -1,8 +1,9 @@ -- | -- 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 show-build-info@ --- command. +-- would use to build it. This can be produced with the +-- @cabal new-show-build-info@ command. +-- -- -- This format is intended for consumption by external tooling and should -- therefore be rather stable. Moreover, this allows tooling users to avoid @@ -13,42 +14,42 @@ -- Below is an example of the output this module produces, -- -- @ --- { "cabal_version": "1.23.0.0", +-- { "cabal-version": "1.23.0.0", -- "compiler": { -- "flavor": "GHC", --- "compiler_id": "ghc-7.10.2", +-- "compiler-id": "ghc-7.10.2", -- "path": "/usr/bin/ghc", -- }, -- "components": [ --- { "type": "library", --- "name": "CLibName", --- "compiler_args": +-- { "type": "lib", +-- "name": "lib:Cabal", +-- "compiler-args": -- ["-O", "-XHaskell98", "-Wall", -- "-package-id", "parallel-3.2.0.6-b79c38c5c25fff77f3ea7271851879eb"] -- "modules": ["Project.ModA", "Project.ModB", "Paths_project"], --- "source_files": [], --- "source_dirs": ["src"] +-- "src-files": [], +-- "src-dirs": ["src"] -- } -- ] -- } -- @ -- --- The @cabal_version@ property provides the version of the Cabal library +-- The @cabal-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. -- -- The @components@ property gives a list of the Cabal 'Component's defined by -- the package. Each has, -- --- * @type@: the type of the component (one of @library@, @executable@, --- @test-suite@, or @benchmark@) +-- * @type@: the type of the component (one of @lib@, @exe@, +-- @test@, @bench@, or @flib@) -- * @name@: a string serving to uniquely identify the component within the -- package. --- * @compiler_args@: the command-line arguments Cabal would pass to the +-- * @compiler-args@: the command-line arguments Cabal would pass to the -- compiler to compile the component -- * @modules@: the modules belonging to the component --- * @source_dirs@: a list of directories where the modules might be found --- * @source_files@: any other Haskell sources needed by the component +-- * @src-dirs@: a list of directories where the modules might be found +-- * @src-files@: any other Haskell sources needed by the component -- -- Note: At the moment this is only supported when using the GHC compiler. -- @@ -69,6 +70,7 @@ import Distribution.Simple.Utils (cabalVersion) import Distribution.Simple.Utils.Json import Distribution.Types.TargetInfo import Distribution.Text +import Distribution.Pretty -- | Construct a JSON document describing the build information for a package mkBuildInfo :: PackageDescription -- ^ Mostly information from the .cabal file @@ -83,42 +85,51 @@ mkBuildInfo pkg_descr lbi _flags targetsToBuild = info k .= v = (k, v) info = JsonObject - [ "cabal_version" .= JsonString (display cabalVersion) + [ "cabal-version" .= JsonString (display cabalVersion) , "compiler" .= mkCompilerInfo , "components" .= JsonArray (map mkComponentInfo componentsToBuild) ] mkCompilerInfo = JsonObject - [ "flavour" .= JsonString (show $ compilerFlavor $ compiler lbi) - , "compiler_id" .= JsonString (showCompilerId $ compiler lbi) + [ "flavour" .= JsonString (prettyShow $ compilerFlavor $ compiler lbi) + , "compiler-id" .= JsonString (showCompilerId $ compiler lbi) , "path" .= path ] where path = maybe JsonNull (JsonString . programPath) - $ lookupProgram ghcProgram (withPrograms lbi) + $ (flavorToProgram . compilerFlavor $ compiler lbi) + >>= flip lookupProgram (withPrograms lbi) + + 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 (show name) - , "compiler_args" .= JsonArray (map JsonString $ getCompilerArgs bi lbi clbi) + , "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) - , "source_files" .= JsonArray (map JsonString source_files) - , "source_dirs" .= JsonArray (map JsonString $ hsSourceDirs bi) + , "src-files" .= JsonArray (map JsonString sourceFiles) + , "src-dirs" .= JsonArray (map JsonString $ hsSourceDirs bi) ] where bi = componentBuildInfo comp Just comp = lookupComponent pkg_descr name compType = case comp of - CLib _ -> "library" - CExe _ -> "executable" - CTest _ -> "test-suite" - CBench _ -> "benchmark" - CFLib _ -> "foreign-library" + CLib _ -> "lib" + CExe _ -> "exe" + CTest _ -> "test" + CBench _ -> "bench" + CFLib _ -> "flib" modules = case comp of CLib lib -> explicitLibModules lib CExe exe -> exeModules exe _ -> [] - source_files = case comp of + sourceFiles = case comp of CLib _ -> [] CExe exe -> [modulePath exe] _ -> [] diff --git a/Cabal/Distribution/Simple/UserHooks.hs b/Cabal/Distribution/Simple/UserHooks.hs index 97f22933c5625c507daa05d67f00d05cab463a48..306092186d1e7fcc9bcd94fc0906773c71f41303 100644 --- a/Cabal/Distribution/Simple/UserHooks.hs +++ b/Cabal/Distribution/Simple/UserHooks.hs @@ -69,6 +69,7 @@ data UserHooks = UserHooks { -- |Hook to run before build command. Second arg indicates verbosity level. preBuild :: Args -> BuildFlags -> IO HookedBuildInfo, + -- |Over-ride this hook to get different behavior during build. buildHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO (), -- |Hook to run after build command. Second arg indicates verbosity level. diff --git a/Cabal/Distribution/Simple/Utils/Json.hs b/Cabal/Distribution/Simple/Utils/Json.hs index b8447fc507559b2f3b67d74fe270d50df50efded..f90f2f38aa2c4b6cf569b832baf5f121b05d529c 100644 --- a/Cabal/Distribution/Simple/Utils/Json.hs +++ b/Cabal/Distribution/Simple/Utils/Json.hs @@ -1,3 +1,5 @@ +-- | Utility json lib for Cabal +-- TODO: Remove it again. module Distribution.Simple.Utils.Json ( Json(..) , renderJson @@ -20,15 +22,25 @@ renderJson (JsonNumber n) = shows n renderJson (JsonObject attrs) = surround "{" "}" $ intercalate "," $ map render attrs where - render (k,v) = (surround "\"" "\"" $ showString k) . showString ":" . renderJson v -renderJson (JsonString s) = surround "\"" "\"" $ showString s + render (k,v) = (surround "\"" "\"" $ showString' k) . showString ":" . renderJson v +renderJson (JsonString s) = surround "\"" "\"" $ showString' s surround :: String -> String -> ShowS -> ShowS surround begin end middle = showString begin . middle . showString end +showString' :: String -> ShowS +showString' xs = showStringWorker xs + where + showStringWorker :: String -> ShowS + showStringWorker ('\"':as) = showString "\\\"" . showStringWorker as + showStringWorker ('\\':as) = showString "\\\\" . showStringWorker as + showStringWorker ('\'':as) = showString "\\\'" . showStringWorker as + showStringWorker (x:as) = showString [x] . showStringWorker as + showStringWorker [] = showString "" + intercalate :: String -> [ShowS] -> ShowS intercalate sep = go where go [] = id go [x] = x - go (x:xs) = x . showString sep . go xs + go (x:xs) = x . showString' sep . go xs