Hannes Siebenhandl pushed to branch wip/fendor/dep-json at Glasgow Haskell Compiler / GHC

Commits:

30 changed files:

Changes:

  • compiler/GHC/Driver/DynFlags.hs
    ... ... @@ -351,6 +351,7 @@ data DynFlags = DynFlags {
    351 351
     
    
    352 352
       --  For ghc -M
    
    353 353
       depMakefile           :: FilePath,
    
    354
    +  depJson               :: Maybe FilePath,
    
    354 355
       depIncludePkgDeps     :: Bool,
    
    355 356
       depIncludeCppDeps     :: Bool,
    
    356 357
       depExcludeMods        :: [ModuleName],
    
    ... ... @@ -694,6 +695,7 @@ defaultDynFlags mySettings =
    694 695
     
    
    695 696
             -- ghc -M values
    
    696 697
             depMakefile       = "Makefile",
    
    698
    +        depJson           = Nothing,
    
    697 699
             depIncludePkgDeps = False,
    
    698 700
             depIncludeCppDeps = False,
    
    699 701
             depExcludeMods    = [],
    

  • compiler/GHC/Driver/MakeFile.hs
    ... ... @@ -7,6 +7,8 @@
    7 7
     -- (c) The University of Glasgow 2005
    
    8 8
     --
    
    9 9
     -----------------------------------------------------------------------------
    
    10
    +{-# LANGUAGE DerivingVia #-}
    
    11
    +{-# LANGUAGE RecordWildCards #-}
    
    10 12
     
    
    11 13
     module GHC.Driver.MakeFile
    
    12 14
        ( doMkDependHS
    
    ... ... @@ -16,47 +18,68 @@ where
    16 18
     
    
    17 19
     import GHC.Prelude
    
    18 20
     
    
    19
    -import qualified GHC
    
    20
    -import GHC.Driver.Make
    
    21
    -import GHC.Driver.Monad
    
    21
    +import GHC qualified
    
    22
    +
    
    23
    +import GHC.Data.Bag (listToBag)
    
    24
    +import GHC.Data.FastString (lexicalCompareFS, unpackFS)
    
    25
    +import GHC.Data.Graph.Directed (SCC (..))
    
    26
    +import GHC.Data.OsPath
    
    27
    +
    
    22 28
     import GHC.Driver.DynFlags
    
    23
    -import GHC.Utils.Misc
    
    24 29
     import GHC.Driver.Env
    
    25 30
     import GHC.Driver.Errors.Types
    
    26
    -import GHC.Types.UnresolvedImport
    
    27
    -import qualified GHC.SysTools as SysTools
    
    28
    -import GHC.Data.Graph.Directed ( SCC(..) )
    
    29
    -import GHC.Data.OsPath (unsafeDecodeUtf)
    
    30
    -import GHC.Utils.Outputable
    
    31
    -import GHC.Utils.Panic
    
    32
    -import GHC.Types.SourceError
    
    33
    -import GHC.Types.SrcLoc
    
    34
    -import GHC.Types.PkgQual
    
    35
    -import Data.List (partition)
    
    36
    -import GHC.Utils.TmpFs
    
    31
    +import GHC.Driver.Make
    
    32
    +import GHC.Driver.Monad
    
    33
    +import GHC.Driver.Phases
    
    34
    +import GHC.Driver.Pipeline
    
    35
    +import GHC.Driver.Pipeline.Monad
    
    36
    +import GHC.Driver.Session
    
    37 37
     
    
    38
    +import GHC.Iface.Errors.Types
    
    38 39
     import GHC.Iface.Load (cannotFindModule)
    
    39 40
     
    
    41
    +import GHC.SysTools qualified as SysTools
    
    42
    +import GHC.Types.PkgQual
    
    43
    +import GHC.Types.SourceError
    
    44
    +import GHC.Types.SrcLoc
    
    45
    +import GHC.Types.Unique.Set (UniqSet)
    
    46
    +import GHC.Types.Unique.Set qualified as UniqSet
    
    47
    +import GHC.Types.UnresolvedImport
    
    48
    +
    
    49
    +import GHC.Unit.Finder
    
    50
    +import GHC.Unit.Info
    
    40 51
     import GHC.Unit.Module
    
    41
    -import GHC.Unit.Module.ModSummary
    
    42 52
     import GHC.Unit.Module.Graph
    
    43
    -import GHC.Unit.Finder
    
    53
    +import GHC.Unit.Module.ModSummary
    
    54
    +import GHC.Unit.State (lookupUnitId)
    
    44 55
     
    
    45
    -import GHC.Utils.Exception
    
    46 56
     import GHC.Utils.Error
    
    57
    +import GHC.Utils.Exception
    
    58
    +import GHC.Utils.Json
    
    47 59
     import GHC.Utils.Logger
    
    60
    +import GHC.Utils.Misc
    
    61
    +import GHC.Utils.Outputable
    
    62
    +import GHC.Utils.Panic
    
    63
    +import GHC.Utils.TmpFs
    
    48 64
     
    
    65
    +import Control.Monad (when)
    
    66
    +import Data.Either
    
    67
    +import Data.Foldable (traverse_)
    
    68
    +import Data.IORef
    
    69
    +import Data.List (partition)
    
    70
    +import Data.Map.Strict qualified as Map
    
    71
    +import Data.Maybe (isJust, isNothing)
    
    72
    +import Data.Semigroup qualified as Semigroup
    
    73
    +import Data.Set qualified as Set
    
    74
    +import GHC.Generics (Generic, Generically (..))
    
    49 75
     import System.Directory
    
    50
    -import System.FilePath
    
    76
    +import qualified System.Directory as Directory
    
    77
    +import System.OsPath as OsPath
    
    51 78
     import System.IO
    
    52
    -import System.IO.Error  ( isEOFError )
    
    53
    -import Control.Monad    ( when, forM_ )
    
    54
    -import Data.Maybe       ( isJust )
    
    55
    -import Data.IORef
    
    56
    -import qualified Data.Set as Set
    
    57
    -import GHC.Iface.Errors.Types
    
    58
    -import Data.Either
    
    59
    -import GHC.Data.Bag (listToBag)
    
    79
    +import System.IO.Error (isEOFError)
    
    80
    +import qualified System.OsString as OsString
    
    81
    +import qualified Data.Monoid as Monoid
    
    82
    +import qualified System.FilePath as FilePath
    
    60 83
     
    
    61 84
     -----------------------------------------------------------------
    
    62 85
     --
    
    ... ... @@ -98,27 +121,47 @@ doMkDependHS srcs = do
    98 121
     
    
    99 122
     doMkDependModuleGraph :: GhcMonad m =>  DynFlags -> ModuleGraph -> m ()
    
    100 123
     doMkDependModuleGraph dflags module_graph = do
    
    124
    +    hsc_env <- getSession
    
    101 125
         logger <- getLogger
    
    102
    -    tmpfs <- hsc_tmpfs <$> getSession
    
    103
    -    let excl_mods = depExcludeMods dflags
    
    126
    +    let
    
    127
    +      tmpfs = hsc_tmpfs hsc_env
    
    128
    +      excl_mods = depExcludeMods dflags
    
    104 129
     
    
    105
    -    files <- liftIO $ beginMkDependHS logger tmpfs dflags
    
    106
    -    let sorted = GHC.topSortModuleGraph False module_graph Nothing
    
    130
    +    root_ <- liftIO getCurrentDirectory
    
    131
    +    root <- encodeUtf root_
    
    107 132
     
    
    133
    +    let
    
    134
    +      backends = concat
    
    135
    +        [ [ initFileDepWriter logger root tmpfs dflags
    
    136
    +          ]
    
    137
    +        , [ initJsonDepWriter json_output dflags
    
    138
    +          | Just json_output <- [depJson dflags]
    
    139
    +          ]
    
    140
    +        ]
    
    141
    +
    
    142
    +
    
    143
    +    let sorted = GHC.topSortModuleGraph False module_graph Nothing
    
    108 144
         -- Print out the dependencies if wanted
    
    109 145
         liftIO $ debugTraceMsg logger 2 (text "Module dependencies" $$ ppr sorted)
    
    110 146
     
    
    111
    -    -- Process them one by one, dumping results into makefile
    
    112
    -    -- and complaining about cycles
    
    113
    -    hsc_env <- getSession
    
    114
    -    root <- liftIO getCurrentDirectory
    
    115
    -    mapM_ (liftIO . processDeps dflags hsc_env excl_mods root (mkd_tmp_hdl files)) sorted
    
    147
    +    liftIO $ do
    
    148
    +
    
    149
    +      -- Setup the writers
    
    150
    +      writers <- sequenceA backends
    
    151
    +      sinks <- traverse dw_beginWriter writers
    
    152
    +
    
    153
    +      -- Do the actual work
    
    154
    +      do
    
    155
    +        -- Process them one by one, dumping results and complaining about cycles
    
    156
    +        mapM_ (processDeps dflags hsc_env (UniqSet.mkUniqSet excl_mods) sinks) sorted
    
    116 157
     
    
    117
    -    -- If -ddump-mod-cycles, show cycles in the module graph
    
    118
    -    liftIO $ dumpModCycles logger module_graph
    
    158
    +        -- If -ddump-mod-cycles, show cycles in the module graph
    
    159
    +        liftIO $ dumpModCycles logger module_graph
    
    160
    +
    
    161
    +      -- Tidy up
    
    162
    +      do
    
    163
    +        traverse_ dw_endWriter writers
    
    119 164
     
    
    120
    -    -- Tidy up
    
    121
    -    liftIO $ endMkDependHS logger files
    
    122 165
     
    
    123 166
         -- Unconditional exiting is a bad idea.  If an error occurs we'll get an
    
    124 167
         --exception; if that is not caught it's fine, but at least we have a
    
    ... ... @@ -127,6 +170,200 @@ doMkDependModuleGraph dflags module_graph = do
    127 170
     
    
    128 171
         --`GHC.ghcCatch` \_ -> io $ exitWith (ExitFailure 1)
    
    129 172
     
    
    173
    +--------------------------------------------------------------------------------
    
    174
    +-- Types abstracting over the output
    
    175
    +--------------------------------------------------------------------------------
    
    176
    +
    
    177
    +data DepNode =
    
    178
    +  DepNode
    
    179
    +    { dn_mod :: ModuleWithIsBoot
    
    180
    +    , dn_src :: OsPath
    
    181
    +    , dn_obj :: OsPath
    
    182
    +    , dn_hi :: OsPath
    
    183
    +    , dn_preprocessing :: PreprocessingNode
    
    184
    +    }
    
    185
    +
    
    186
    +data PreprocessingNode = PreprocessingNode
    
    187
    +  { pn_preprocessor :: Maybe String
    
    188
    +  , pn_options :: [String]
    
    189
    +  }
    
    190
    +
    
    191
    +data Dep
    
    192
    +  = DepHi
    
    193
    +    { dep_mod :: Module
    
    194
    +    , dep_path :: OsPath
    
    195
    +    , dep_unit :: Maybe UnitInfo
    
    196
    +    , dep_local :: Bool
    
    197
    +    , dep_level :: ImportLevel
    
    198
    +    , dep_boot :: IsBootInterface
    
    199
    +    }
    
    200
    +  | DepCpp
    
    201
    +    { dep_path :: OsPath
    
    202
    +    }
    
    203
    +
    
    204
    +data DependencyWriter = DependencyWriter
    
    205
    +  { dw_beginWriter :: IO DepSink
    
    206
    +  , dw_endWriter :: IO ()
    
    207
    +  }
    
    208
    +
    
    209
    +data DepSink = DepSink
    
    210
    +  { ds_writeDependency :: DepNode -> [Dep] -> IO ()
    
    211
    +  }
    
    212
    +
    
    213
    +-----------------------------------------------------------------
    
    214
    +--
    
    215
    +--              processDeps
    
    216
    +--
    
    217
    +-----------------------------------------------------------------
    
    218
    +
    
    219
    +processDeps :: DynFlags
    
    220
    +            -> HscEnv
    
    221
    +            -> UniqSet ModuleName -- ^ Excludes
    
    222
    +            -> [DepSink]
    
    223
    +            -> SCC ModuleGraphNode
    
    224
    +            -> IO ()
    
    225
    +-- Write suitable dependencies to handle
    
    226
    +-- Always:
    
    227
    +--                      this.o : this.hs
    
    228
    +--
    
    229
    +-- If the dependency is on something other than a .hi file:
    
    230
    +--                      this.o this.p_o ... : dep
    
    231
    +-- otherwise
    
    232
    +--                      this.o ...   : dep.hi
    
    233
    +--                      this.p_o ... : dep.p_hi
    
    234
    +--                      ...
    
    235
    +-- (where .o is $osuf, and the other suffixes come from
    
    236
    +-- the cmdline -s options).
    
    237
    +--
    
    238
    +-- For {-# SOURCE #-} imports the "hi" will be "hi-boot".
    
    239
    +
    
    240
    +processDeps _ hsc_env _ _ (CyclicSCC nodes)
    
    241
    +  =     -- There shouldn't be any cycles; report them
    
    242
    +    throwOneError (initSourceErrorContext (hsc_dflags hsc_env)) $ cyclicModuleErr nodes
    
    243
    +
    
    244
    +processDeps _ hsc_env _ _ (AcyclicSCC (InstantiationNode _uid node))
    
    245
    +  =     -- There shouldn't be any backpack instantiations; report them as well
    
    246
    +    throwOneError (initSourceErrorContext (hsc_dflags hsc_env)) $
    
    247
    +      mkPlainErrorMsgEnvelope noSrcSpan $
    
    248
    +      GhcDriverMessage $ DriverInstantiationNodeInDependencyGeneration node
    
    249
    +
    
    250
    +processDeps _dflags _ _ _ (AcyclicSCC (LinkNode {})) = return ()
    
    251
    +processDeps _dflags _ _ _ (AcyclicSCC (UnitNode {})) = return ()
    
    252
    +processDeps _ _ _ _ (AcyclicSCC (ModuleNode _ (ModuleNodeFixed {})))
    
    253
    +  -- No dependencies needed for fixed modules (already compiled)
    
    254
    +  = return ()
    
    255
    +
    
    256
    +processDeps dflags hsc_env excl_mods sinks (AcyclicSCC (ModuleNode _ (ModuleNodeCompile node))) = do
    
    257
    +  pp <- preprocessor
    
    258
    +  let
    
    259
    +    dep_node = mkDepNode pp
    
    260
    +    find_deps imps = do
    
    261
    +      cpp_deps <- find_cpp_deps
    
    262
    +      import_deps <- find_import_deps imps
    
    263
    +      pure $ map Right cpp_deps ++ import_deps
    
    264
    +  (missing_dep_errs, deps) <- partitionEithers <$> find_deps (ms_imps node)
    
    265
    +
    
    266
    +  if null missing_dep_errs
    
    267
    +    then do
    
    268
    +      traverse_ (\ sink -> ds_writeDependency sink dep_node deps) sinks
    
    269
    +    else do
    
    270
    +      let sec = initSourceErrorContext (hsc_dflags hsc_env)
    
    271
    +      throwErrors sec (mkMessages (listToBag missing_dep_errs))
    
    272
    +  where
    
    273
    +    src_file = msHsFileOsPath node
    
    274
    +    mkDepNode preproc =
    
    275
    +      DepNode
    
    276
    +        { dn_mod = GWIB (ms_mod node) (isBootSummary node)
    
    277
    +        , dn_src = src_file
    
    278
    +        , dn_obj = msObjFileOsPath node
    
    279
    +        , dn_hi = msHiFileOsPath node
    
    280
    +        , dn_preprocessing = preproc
    
    281
    +        }
    
    282
    +
    
    283
    +    preprocessor :: IO PreprocessingNode
    
    284
    +    preprocessor
    
    285
    +      | Just src <- ml_hs_file (ms_location node)
    
    286
    +      = runPipeline (hsc_hooks hsc_env) $ do
    
    287
    +        let
    
    288
    +          (_, suffix) = FilePath.splitExtension src
    
    289
    +          lit | Unlit _ <- startPhase suffix = True
    
    290
    +              | otherwise = False
    
    291
    +          pipe_env = mkPipeEnv StopPreprocess src Nothing NoOutputFile
    
    292
    +        unlit_fn <- if lit then use (T_Unlit pipe_env hsc_env src) else pure src
    
    293
    +        (dflags1, opts, _, _) <- use (T_FileArgs (hsc_logger hsc_env) (ms_hspp_opts node) unlit_fn)
    
    294
    +        let pp = pgm_F dflags1
    
    295
    +        pure PreprocessingNode
    
    296
    +          { pn_preprocessor = if null pp then global_preprocessor else Just pp
    
    297
    +          , pn_options = opts
    
    298
    +          }
    
    299
    +      | otherwise
    
    300
    +      = pure PreprocessingNode
    
    301
    +          { pn_preprocessor = global_preprocessor
    
    302
    +          , pn_options = []
    
    303
    +          }
    
    304
    +
    
    305
    +    global_preprocessor :: Maybe String
    
    306
    +    global_preprocessor
    
    307
    +      | let pp = pgm_F dflags
    
    308
    +      , not (null pp)
    
    309
    +      = Just pp
    
    310
    +      | otherwise
    
    311
    +      = Nothing
    
    312
    +
    
    313
    +    -- Emit a dependency for each CPP import
    
    314
    +    -- CPP deps are discovered in the module parsing phase by parsing
    
    315
    +    -- comment lines left by the preprocessor.
    
    316
    +    -- Note that GHC.parseModule may throw an exception if the module
    
    317
    +    -- fails to parse, which may not be desirable (see #16616).
    
    318
    +    find_cpp_deps :: IO [Dep]
    
    319
    +    find_cpp_deps =
    
    320
    +      if depIncludeCppDeps dflags
    
    321
    +        then do
    
    322
    +          session <- Session <$> newIORef hsc_env
    
    323
    +          parsedMod <- reflectGhc (GHC.parseModule node) session
    
    324
    +          pure (DepCpp . unsafeEncodeUtf <$> GHC.pm_extra_src_files parsedMod)
    
    325
    +        else
    
    326
    +          pure []
    
    327
    +
    
    328
    +    -- Emit a dependency for each import
    
    329
    +    find_import_deps :: [UnresolvedImport PkgQual] -> IO [Either (MsgEnvelope GhcMessage) Dep]
    
    330
    +    find_import_deps idecls =
    
    331
    +      sequence
    
    332
    +        [ findDependency hsc_env decl
    
    333
    +        | decl <- idecls
    
    334
    +        , let L _loc mod = ui_mod_name decl
    
    335
    +        , not $ mod `UniqSet.elementOfUniqSet` excl_mods
    
    336
    +        ]
    
    337
    +
    
    338
    +findDependency  :: HscEnv
    
    339
    +                -> UnresolvedImport PkgQual   -- The import to find
    
    340
    +                -> IO (Either (MsgEnvelope GhcMessage) Dep)  -- Interface file
    
    341
    +findDependency hsc_env imp = do
    
    342
    +  -- Find the module; this will be fast because
    
    343
    +  -- we've done it once during downsweep.
    
    344
    +  r <- resolveImport hsc_env imp
    
    345
    +  case r of
    
    346
    +    Found loc dep_mod ->
    
    347
    +      pure $ Right
    
    348
    +        DepHi
    
    349
    +          { dep_mod = dep_mod
    
    350
    +          , dep_path = ml_hi_file_ospath loc
    
    351
    +          , dep_unit = lookupUnitId (hsc_units hsc_env) (moduleUnitId dep_mod)
    
    352
    +          , dep_local = isJust (ml_hs_file loc)
    
    353
    +          , dep_boot = is_boot
    
    354
    +          , dep_level = ui_level imp
    
    355
    +          }
    
    356
    +
    
    357
    +    fail ->
    
    358
    +      return $
    
    359
    +        Left $
    
    360
    +          mkPlainErrorMsgEnvelope srcloc $
    
    361
    +          GhcDriverMessage $ DriverInterfaceError $
    
    362
    +             (Can'tFindInterface (cannotFindModule hsc_env mod_name fail) (LookingForModule mod_name is_boot))
    
    363
    +  where
    
    364
    +    L srcloc mod_name = ui_mod_name imp
    
    365
    +    is_boot           = ui_boot imp
    
    366
    +
    
    130 367
     -----------------------------------------------------------------
    
    131 368
     --
    
    132 369
     --              beginMkDependHs
    
    ... ... @@ -136,6 +373,18 @@ doMkDependModuleGraph dflags module_graph = do
    136 373
     --
    
    137 374
     -----------------------------------------------------------------
    
    138 375
     
    
    376
    +initFileDepWriter :: Logger -> OsPath -> TmpFs -> DynFlags -> IO DependencyWriter
    
    377
    +initFileDepWriter logger root tmpfs dflags = do
    
    378
    +  files <- beginMkDependHS logger tmpfs dflags
    
    379
    +  pure DependencyWriter
    
    380
    +    { dw_beginWriter = do
    
    381
    +        pure $ DepSink $ \ node deps -> do
    
    382
    +          writeDependencies (depIncludePkgDeps dflags) root (mkd_tmp_hdl files) suffixes node deps
    
    383
    +    , dw_endWriter = endMkDependHS logger files
    
    384
    +    }
    
    385
    +  where
    
    386
    +    suffixes = map unsafeEncodeUtf (depSuffixes dflags)
    
    387
    +
    
    139 388
     data MkDepFiles
    
    140 389
       = MkDep { mkd_make_file :: FilePath,          -- Name of the makefile
    
    141 390
                 mkd_make_hdl  :: Maybe Handle,      -- Handle for the open makefile
    
    ... ... @@ -151,7 +400,7 @@ beginMkDependHS logger tmpfs dflags = do
    151 400
     
    
    152 401
             -- open the makefile
    
    153 402
       let makefile = depMakefile dflags
    
    154
    -  exists <- doesFileExist makefile
    
    403
    +  exists <- Directory.doesFileExist makefile
    
    155 404
       mb_make_hdl <-
    
    156 405
             if not exists
    
    157 406
             then return Nothing
    
    ... ... @@ -188,141 +437,53 @@ beginMkDependHS logger tmpfs dflags = do
    188 437
       return (MkDep { mkd_make_file = makefile, mkd_make_hdl = mb_make_hdl,
    
    189 438
                       mkd_tmp_file  = tmp_file, mkd_tmp_hdl  = tmp_hdl})
    
    190 439
     
    
    440
    +writeDependencies ::
    
    441
    +  Bool ->
    
    442
    +  OsPath ->
    
    443
    +  Handle ->
    
    444
    +  [OsString] ->
    
    445
    +  -- ^ Suffixes
    
    446
    +  DepNode ->
    
    447
    +  [Dep] ->
    
    448
    +  IO ()
    
    449
    +writeDependencies include_pkgs root hdl suffixes node deps =
    
    450
    +  traverse_ write tasks
    
    451
    +  where
    
    452
    +    tasks = source_dep : boot_dep ++ concatMap import_dep deps
    
    453
    +
    
    454
    +    -- Emit std dependency of the object(s) on the source file
    
    455
    +    -- Something like       A.o : A.hs
    
    456
    +    source_dep = (obj_files, dn_src)
    
    457
    +
    
    458
    +    -- add dependency between objects and their corresponding .hi-boot
    
    459
    +    -- files if the module has a corresponding .hs-boot file (#14482)
    
    460
    +    boot_dep = case gwib_isBoot dn_mod of
    
    461
    +      IsBoot -> [([obj], hi) | (obj, hi) <- zip (suffixed (removeBootSuffix dn_obj)) (suffixed dn_hi)]
    
    462
    +      NotBoot -> []
    
    463
    +
    
    464
    +    -- Add one dependency for each suffix;
    
    465
    +    -- e.g.         A.o   : B.hi
    
    466
    +    --              A.x_o : B.x_hi
    
    467
    +    import_dep = \case
    
    468
    +      DepHi {dep_path, dep_unit}
    
    469
    +        | isNothing dep_unit || include_pkgs
    
    470
    +        -> [([obj], hi) | (obj, hi) <- zip obj_files (suffixed dep_path)]
    
    191 471
     
    
    192
    ------------------------------------------------------------------
    
    193
    ---
    
    194
    ---              processDeps
    
    195
    ---
    
    196
    ------------------------------------------------------------------
    
    197
    -
    
    198
    -processDeps :: DynFlags
    
    199
    -            -> HscEnv
    
    200
    -            -> [ModuleName]
    
    201
    -            -> FilePath
    
    202
    -            -> Handle           -- Write dependencies to here
    
    203
    -            -> SCC ModuleGraphNode
    
    204
    -            -> IO ()
    
    205
    --- Write suitable dependencies to handle
    
    206
    --- Always:
    
    207
    ---                      this.o : this.hs
    
    208
    ---
    
    209
    --- If the dependency is on something other than a .hi file:
    
    210
    ---                      this.o this.p_o ... : dep
    
    211
    --- otherwise
    
    212
    ---                      this.o ...   : dep.hi
    
    213
    ---                      this.p_o ... : dep.p_hi
    
    214
    ---                      ...
    
    215
    --- (where .o is $osuf, and the other suffixes come from
    
    216
    --- the cmdline -s options).
    
    217
    ---
    
    218
    --- For {-# SOURCE #-} imports the "hi" will be "hi-boot".
    
    219
    -
    
    220
    -processDeps _ hsc_env _ _ _ (CyclicSCC nodes)
    
    221
    -  =     -- There shouldn't be any cycles; report them
    
    222
    -    throwOneError (initSourceErrorContext (hsc_dflags hsc_env)) $ cyclicModuleErr nodes
    
    223
    -
    
    224
    -processDeps _ hsc_env _ _ _ (AcyclicSCC (InstantiationNode _uid node))
    
    225
    -  =     -- There shouldn't be any backpack instantiations; report them as well
    
    226
    -    throwOneError (initSourceErrorContext (hsc_dflags hsc_env)) $
    
    227
    -      mkPlainErrorMsgEnvelope noSrcSpan $
    
    228
    -      GhcDriverMessage $ DriverInstantiationNodeInDependencyGeneration node
    
    229
    -
    
    230
    -processDeps _dflags _ _ _ _ (AcyclicSCC (LinkNode {})) = return ()
    
    231
    -processDeps _dflags _ _ _ _ (AcyclicSCC (UnitNode {})) = return ()
    
    232
    -processDeps _ _ _ _ _ (AcyclicSCC (ModuleNode _ (ModuleNodeFixed {})))
    
    233
    -  -- No dependencies needed for fixed modules (already compiled)
    
    234
    -  = return ()
    
    235
    -processDeps dflags hsc_env excl_mods root hdl (AcyclicSCC (ModuleNode _ (ModuleNodeCompile node)))
    
    236
    -  = do  { let extra_suffixes = depSuffixes dflags
    
    237
    -              include_pkg_deps = depIncludePkgDeps dflags
    
    238
    -              src_file  = msHsFilePath node
    
    239
    -              obj_file  = msObjFilePath node
    
    240
    -              obj_files = insertSuffixes obj_file extra_suffixes
    
    241
    -
    
    242
    -
    
    243
    -
    
    244
    -                -- Emit std dependency of the object(s) on the source file
    
    245
    -                -- Something like       A.o : A.hs
    
    246
    -        ; writeDependency root hdl obj_files src_file
    
    247
    -
    
    248
    -          -- add dependency between objects and their corresponding .hi-boot
    
    249
    -          -- files if the module has a corresponding .hs-boot file (#14482)
    
    250
    -        ; when (isBootSummary node == IsBoot) $ do
    
    251
    -            let hi_boot = msHiFilePath node
    
    252
    -            let obj     = unsafeDecodeUtf $ removeBootSuffix (msObjFileOsPath node)
    
    253
    -            forM_ extra_suffixes $ \suff -> do
    
    254
    -               let way_obj     = insertSuffixes obj     [suff]
    
    255
    -               let way_hi_boot = insertSuffixes hi_boot [suff]
    
    256
    -               mapM_ (writeDependency root hdl way_obj) way_hi_boot
    
    257
    -
    
    258
    -                -- Emit a dependency for each CPP import
    
    259
    -        ; when (depIncludeCppDeps dflags) $ do
    
    260
    -            -- CPP deps are discovered in the module parsing phase by parsing
    
    261
    -            -- comment lines left by the preprocessor.
    
    262
    -            -- Note that GHC.parseModule may throw an exception if the module
    
    263
    -            -- fails to parse, which may not be desirable (see #16616).
    
    264
    -          { session <- Session <$> newIORef hsc_env
    
    265
    -          ; parsedMod <- reflectGhc (GHC.parseModule node) session
    
    266
    -          ; mapM_ (writeDependency root hdl obj_files)
    
    267
    -                  (GHC.pm_extra_src_files parsedMod)
    
    268
    -          }
    
    269
    -
    
    270
    -                -- Emit a dependency for each import
    
    271
    -
    
    272
    -        ; let find_deps imps = sequence
    
    273
    -                    [ findDependency hsc_env imp include_pkg_deps
    
    274
    -                    | imp <- imps
    
    275
    -                    , unLoc (ui_mod_name imp) `notElem` excl_mods
    
    276
    -                    ]
    
    277
    -
    
    278
    -              do_imp hi_file = do
    
    279
    -                let hi_files = insertSuffixes hi_file extra_suffixes
    
    280
    -                    write_dep (obj,hi) = writeDependency root hdl [obj] hi
    
    472
    +        | otherwise
    
    473
    +        -> []
    
    281 474
     
    
    282
    -                 -- Add one dependency for each suffix;
    
    283
    -                 -- e.g.         A.o   : B.hi
    
    284
    -                 --              A.x_o : B.x_hi
    
    285
    -                mapM_ write_dep (obj_files `zip` hi_files)
    
    475
    +      DepCpp {dep_path} -> [(obj_files, dep_path)]
    
    286 476
     
    
    287
    -        ; (missing_dep_errs, deps) <- partitionEithers <$> find_deps (ms_imps node)
    
    477
    +    write (from, to) = writeDependency root hdl from to
    
    288 478
     
    
    289
    -        ; if null missing_dep_errs
    
    290
    -            then mapM_ (mapM_ do_imp) deps
    
    291
    -            else do
    
    292
    -              let sec = initSourceErrorContext (hsc_dflags hsc_env)
    
    293
    -              throwErrors sec (mkMessages (listToBag missing_dep_errs))
    
    294
    -        }
    
    479
    +    obj_files = suffixed dn_obj
    
    295 480
     
    
    296
    -findDependency  :: HscEnv
    
    297
    -                -> UnresolvedImport PkgQual   -- The import to find
    
    298
    -                -> Bool                 -- Record dependency on package modules
    
    299
    -                -> IO (Either (MsgEnvelope GhcMessage) (Maybe FilePath))  -- Interface file
    
    300
    -findDependency hsc_env imp include_pkg_deps = do
    
    301
    -  -- Find the module; this will be fast because
    
    302
    -  -- we've done it once during downsweep.
    
    303
    -  r <- resolveImport hsc_env imp
    
    304
    -  case r of
    
    305
    -    Found loc _
    
    306
    -        -- Home package: just depend on the .hi or hi-boot file
    
    307
    -        | isJust (ml_hs_file loc) || include_pkg_deps
    
    308
    -        -> return (Right (Just (ml_hi_file loc)))
    
    481
    +    suffixed f = insertSuffixes f suffixes
    
    309 482
     
    
    310
    -        -- Not in this package: we don't need a dependency
    
    311
    -        | otherwise
    
    312
    -        -> return (Right Nothing)
    
    313
    -
    
    314
    -    fail ->
    
    315
    -      return $
    
    316
    -        Left $
    
    317
    -          mkPlainErrorMsgEnvelope srcloc $
    
    318
    -          GhcDriverMessage $ DriverInterfaceError $
    
    319
    -             (Can'tFindInterface (cannotFindModule hsc_env mod_name fail) (LookingForModule mod_name is_boot))
    
    320
    -  where
    
    321
    -    L srcloc mod_name = ui_mod_name imp
    
    322
    -    is_boot           = ui_boot imp
    
    483
    +    DepNode {dn_src, dn_obj, dn_hi, dn_mod} = node
    
    323 484
     
    
    324 485
     -----------------------------
    
    325
    -writeDependency :: FilePath -> Handle -> [FilePath] -> FilePath -> IO ()
    
    486
    +writeDependency :: OsPath -> Handle -> [OsPath] -> OsPath -> IO ()
    
    326 487
     -- (writeDependency r h [t1,t2] dep) writes to handle h the dependency
    
    327 488
     --      t1 t2 : dep
    
    328 489
     writeDependency root hdl targets dep
    
    ... ... @@ -330,33 +491,30 @@ writeDependency root hdl targets dep
    330 491
                --     c:/foo/...
    
    331 492
                -- on Windows as make gets confused by the :
    
    332 493
                -- Making relative deps avoids some instances of this.
    
    333
    -           dep' = makeRelative root dep
    
    334
    -           forOutput = escapeSpaces . reslash Forwards . normalise
    
    494
    +           dep' = OsPath.makeRelative root dep
    
    495
    +           forOutput = escapeSpaces . reslash Forwards . unsafeDecodeUtf . OsPath.normalise
    
    335 496
                output = unwords (map forOutput targets) ++ " : " ++ forOutput dep'
    
    336 497
            hPutStrLn hdl output
    
    337 498
     
    
    338 499
     -----------------------------
    
    339 500
     insertSuffixes
    
    340
    -        :: FilePath     -- Original filename;   e.g. "foo.o"
    
    341
    -        -> [String]     -- Suffix prefixes      e.g. ["x_", "y_"]
    
    342
    -        -> [FilePath]   -- Zapped filenames     e.g. ["foo.x_o", "foo.y_o"]
    
    501
    +        :: OsPath     -- Original filename;   e.g. "foo.o"
    
    502
    +        -> [OsString]     -- Suffix prefixes      e.g. ["x_", "y_"]
    
    503
    +        -> [OsPath]   -- Zapped filenames     e.g. ["foo.x_o", "foo.y_o"]
    
    343 504
             -- Note that the extra bit gets inserted *before* the old suffix
    
    344 505
             -- We assume the old suffix contains no dots, so we know where to
    
    345 506
             -- split it
    
    346 507
     insertSuffixes file_name extras
    
    347
    -  = [ basename <.> (extra ++ suffix) | extra <- extras ]
    
    508
    +  = [ basename <.> (extra Monoid.<> suffix) | extra <- extras ]
    
    348 509
       where
    
    349
    -    (basename, suffix) = case splitExtension file_name of
    
    510
    +    (basename, suffix) = case OsPath.splitExtension file_name of
    
    350 511
                              -- Drop the "." from the extension
    
    351
    -                         (b, s) -> (b, drop 1 s)
    
    512
    +                         (b, s) -> (b, OsString.drop 1 s)
    
    352 513
     
    
    353 514
     
    
    354 515
     -----------------------------------------------------------------
    
    355
    ---
    
    356
    ---              endMkDependHs
    
    357
    ---      Complete the makefile, close the tmp file etc
    
    358
    ---
    
    359
    ------------------------------------------------------------------
    
    516
    +-- endMkDependHs
    
    517
    +-- Complete the makefile, close the tmp file etc
    
    360 518
     
    
    361 519
     endMkDependHS :: Logger -> MkDepFiles -> IO ()
    
    362 520
     
    
    ... ... @@ -385,6 +543,277 @@ endMkDependHS logger
    385 543
       showPass logger "Installing new makefile"
    
    386 544
       SysTools.copyFile tmp_file makefile
    
    387 545
     
    
    546
    +-----------------------------------------------------------------
    
    547
    +--
    
    548
    +--              JSON MkDepends output
    
    549
    +--
    
    550
    +-----------------------------------------------------------------
    
    551
    +
    
    552
    +initJsonDepWriter :: FilePath -> DynFlags -> IO DependencyWriter
    
    553
    +initJsonDepWriter output dflags = do
    
    554
    +  json_var <- mkJsonOutput output initDepJson
    
    555
    +  pure DependencyWriter
    
    556
    +    { dw_beginWriter =
    
    557
    +        pure $ DepSink $ \ node deps ->
    
    558
    +          updateJson json_var (updateDepJson (depIncludePkgDeps dflags) node deps)
    
    559
    +    , dw_endWriter =
    
    560
    +        writeJsonOutput json_var
    
    561
    +    }
    
    562
    +
    
    563
    +--------------------------------------------------------------------------------
    
    564
    +-- Output interface for json dumps
    
    565
    +
    
    566
    +-- | Resources for a json dump option, used in "GHC.Driver.MakeFile".
    
    567
    +-- The flag @-dep-json@ add an additional output target for dependency
    
    568
    +-- diagnostics.
    
    569
    +data JsonOutput a =
    
    570
    +  JsonOutput {
    
    571
    +    -- | This ref is updated in @processDeps@ incrementally, using a
    
    572
    +    -- flag-specific type.
    
    573
    +    json_ref :: IORef a,
    
    574
    +
    
    575
    +    -- | The output file path specified as argument to the flag.
    
    576
    +    json_path :: FilePath
    
    577
    +  }
    
    578
    +
    
    579
    +-- | TODO: @fendor
    
    580
    +mkJsonOutput ::
    
    581
    +  FilePath ->
    
    582
    +  IO (IORef a) ->
    
    583
    +  IO (JsonOutput a)
    
    584
    +mkJsonOutput json_path mk_ref = do
    
    585
    +  json_ref <- mk_ref
    
    586
    +  pure JsonOutput {json_ref, json_path}
    
    587
    +
    
    588
    +-- | Update the dump data in 'json_ref' if the output target is present.
    
    589
    +updateJson :: JsonOutput a -> (a -> a) -> IO ()
    
    590
    +updateJson JsonOutput {json_ref} f = modifyIORef' json_ref f
    
    591
    +
    
    592
    +-- | Write a json object to the flag-dependent file if the output target is
    
    593
    +-- present.
    
    594
    +writeJsonOutput ::
    
    595
    +  ToJson a =>
    
    596
    +  JsonOutput a ->
    
    597
    +  IO ()
    
    598
    +writeJsonOutput JsonOutput {json_ref, json_path} = do
    
    599
    +  payload <- readIORef json_ref
    
    600
    +  writeJsonFile payload json_path
    
    601
    +
    
    602
    +--------------------------------------------------------------------------------
    
    603
    +-- Output helpers
    
    604
    +
    
    605
    +writeJsonFile :: ToJson a => a -> FilePath -> IO ()
    
    606
    +writeJsonFile doc p = do
    
    607
    +  withAtomicRename p
    
    608
    +    $ \tmp -> writeFile tmp $ showSDocUnsafe $ renderJSON $ json doc
    
    609
    +
    
    610
    +--------------------------------------------------------------------------------
    
    611
    +-- Payload for -dep-json
    
    612
    +
    
    613
    +newtype DPackageId = DPackageId PackageId
    
    614
    +  deriving newtype (Eq)
    
    615
    +
    
    616
    +instance Ord DPackageId where
    
    617
    +  DPackageId (PackageId d1) `compare` DPackageId (PackageId d2) = d1 `lexicalCompareFS` d2
    
    618
    +
    
    619
    +data ModuleNodeDeps = ModuleNodeDeps
    
    620
    +  { source :: OsPath
    
    621
    +  , imports :: ImportDeps
    
    622
    +  , cpp :: Set.Set OsPath
    
    623
    +  , options :: [String]
    
    624
    +  , preprocessor :: Maybe FilePath
    
    625
    +  }
    
    626
    +  deriving stock (Generic)
    
    627
    +  deriving (Semigroup, Monoid) via (Generically ModuleNodeDeps)
    
    628
    +
    
    629
    +addImport :: ImportDep -> ModuleNodeDeps -> ModuleNodeDeps
    
    630
    +addImport import_dep mod_node_deps =
    
    631
    +  mod_node_deps
    
    632
    +    { imports = ImportDeps $ Map.insertWith Set.union uid (Set.singleton import_dep) (getImports $ imports mod_node_deps)
    
    633
    +    }
    
    634
    +  where
    
    635
    +    uid = toUnitId $ moduleUnit $ imp_mod import_dep
    
    636
    +
    
    637
    +addCpp :: OsPath -> ModuleNodeDeps -> ModuleNodeDeps
    
    638
    +addCpp p mod_node_deps =
    
    639
    +  mod_node_deps
    
    640
    +    { cpp = Set.insert p (cpp mod_node_deps)
    
    641
    +    }
    
    642
    +
    
    643
    +newtype ImportDeps = ImportDeps { getImports :: Map.Map UnitId (Set.Set ImportDep) }
    
    644
    +  deriving stock (Generic)
    
    645
    +  deriving (Semigroup, Monoid) via (Generically ImportDeps)
    
    646
    +
    
    647
    +data ImportDep = ImportDep
    
    648
    +  { imp_mod :: Module
    
    649
    +  , imp_level :: ImportLevel
    
    650
    +  , imp_isBoot :: IsBootInterface
    
    651
    +  } deriving (Eq, Ord)
    
    652
    +
    
    653
    +mkImportDep :: Module -> ImportLevel -> IsBootInterface -> ImportDep
    
    654
    +mkImportDep modl level isBoot = ImportDep
    
    655
    +  { imp_mod = modl
    
    656
    +  , imp_level = level
    
    657
    +  , imp_isBoot = isBoot
    
    658
    +  }
    
    659
    +
    
    660
    +data HomeUnitDeps = HomeUnitDeps
    
    661
    +  { homeModules :: Map.Map ModuleWithIsBoot ModuleNodeDeps
    
    662
    +  }
    
    663
    +  deriving stock (Generic)
    
    664
    +  deriving (Semigroup, Monoid) via (Generically HomeUnitDeps)
    
    665
    +
    
    666
    +data ExtUnitDep = ExtUnitDep
    
    667
    +  { extUnitId :: UnitId
    
    668
    +  -- ^ The 'UnitId' of a unit. This is assumed to be globally unique.
    
    669
    +  , extUnitName :: String
    
    670
    +  , extUnitPackageId :: DPackageId
    
    671
    +  } deriving (Eq, Ord)
    
    672
    +
    
    673
    +data DepJson = DepJson
    
    674
    +  { homeUnitDeps :: Map.Map UnitId HomeUnitDeps
    
    675
    +  , externalDeps :: Set.Set ExtUnitDep
    
    676
    +  }
    
    677
    +
    
    678
    +instance ToJson DepJson where
    
    679
    +  json (DepJson homeUnits extDeps) =
    
    680
    +    JSObject $
    
    681
    +      [ homeUnit huid hud
    
    682
    +      | (huid, hud) <- Map.toList homeUnits
    
    683
    +      ] ++
    
    684
    +      [ mkExtDepObj dep
    
    685
    +      | dep <- Set.toList extDeps
    
    686
    +      ]
    
    687
    +    where
    
    688
    +      homeUnit uid hud =
    
    689
    +        ( unitIdString uid
    
    690
    +        , JSObject
    
    691
    +            [
    
    692
    +              ( "modules"
    
    693
    +              , JSObject
    
    694
    +                  [ (homeUnitModuleKey modl, homeUnitModule deps)
    
    695
    +                  | (modl, deps) <- Map.toList $ homeModules hud
    
    696
    +                  ]
    
    697
    +              )
    
    698
    +            ]
    
    699
    +        )
    
    700
    +
    
    701
    +      homeUnitModuleKey :: ModuleWithIsBoot -> String
    
    702
    +      homeUnitModuleKey t = moduleKey (gwib_mod t) (gwib_isBoot t)
    
    703
    +
    
    704
    +      moduleKey :: Module -> IsBootInterface -> String
    
    705
    +      moduleKey t b = moduleNameString (moduleName t) ++ case b of
    
    706
    +        IsBoot -> "[boot]"
    
    707
    +        NotBoot -> ""
    
    708
    +
    
    709
    +      homeUnitModule :: ModuleNodeDeps -> JsonDoc
    
    710
    +      homeUnitModule ModuleNodeDeps {source, imports, cpp, options, preprocessor} =
    
    711
    +        JSObject
    
    712
    +          [ ("source", JSString . showOsPath $ normalise source)
    
    713
    +          , ("imports", importsObj imports)
    
    714
    +          , ("includes", strArray cpp showOsPath)
    
    715
    +          , ("options", JSArray $ map JSString options)
    
    716
    +          , ("preprocessor", maybe JSNull JSString preprocessor)
    
    717
    +          ]
    
    718
    +
    
    719
    +      importsObj :: ImportDeps -> JsonDoc
    
    720
    +      importsObj importDeps = JSArray $ map importObj (Map.toList $ getImports importDeps)
    
    721
    +
    
    722
    +      importObj :: (UnitId, Set.Set ImportDep) -> JsonDoc
    
    723
    +      importObj (uid, importSet) =
    
    724
    +        JSObject
    
    725
    +          [ (unitIdString uid, array importSet importDepObj )
    
    726
    +          ]
    
    727
    +
    
    728
    +      importDepObj :: ImportDep -> JsonDoc
    
    729
    +      importDepObj ImportDep {imp_mod, imp_level, imp_isBoot}
    
    730
    +        | NormalLevel <- imp_level = JSString (moduleKey imp_mod imp_isBoot)
    
    731
    +        | otherwise = JSObject
    
    732
    +          [ ("module", JSString (moduleKey imp_mod imp_isBoot))
    
    733
    +          , ("level" , importLevel imp_level)
    
    734
    +          ]
    
    735
    +
    
    736
    +      importLevel :: ImportLevel -> JsonDoc
    
    737
    +      importLevel lvl =
    
    738
    +        JSString $ case lvl of
    
    739
    +          NormalLevel -> "normal"
    
    740
    +          SpliceLevel -> "splice"
    
    741
    +          QuoteLevel -> "quote"
    
    742
    +
    
    743
    +      mkExtDepObj :: ExtUnitDep -> (String, JsonDoc)
    
    744
    +      mkExtDepObj ExtUnitDep{..} =
    
    745
    +        ( unitIdString extUnitId
    
    746
    +        , JSObject
    
    747
    +            [ ("package-name", JSString extUnitName)
    
    748
    +            , ("package-id", JSString $ showPackageId extUnitPackageId)
    
    749
    +            ]
    
    750
    +        )
    
    751
    +
    
    752
    +      array values render = JSArray (fmap render (Set.toList values))
    
    753
    +      strArray values render = JSArray (fmap (JSString . render) (Set.toList values))
    
    754
    +
    
    755
    +      showOsPath = unsafeDecodeUtf
    
    756
    +      showPackageId (DPackageId (PackageId pid)) = unpackFS pid
    
    757
    +
    
    758
    +initDepJson :: IO (IORef DepJson)
    
    759
    +initDepJson = newIORef $ DepJson Map.empty Set.empty
    
    760
    +
    
    761
    +insertDepJson :: ModuleWithIsBoot -> ModuleNodeDeps -> Set.Set ExtUnitDep -> DepJson -> DepJson
    
    762
    +insertDepJson target modNode extUnits (DepJson m0 e0) =
    
    763
    +  DepJson
    
    764
    +    { homeUnitDeps =
    
    765
    +        Map.insertWith
    
    766
    +          (Semigroup.<>)
    
    767
    +          modUnitId
    
    768
    +          (HomeUnitDeps $ Map.singleton target modNode)
    
    769
    +          m0
    
    770
    +    , externalDeps =
    
    771
    +        Set.union e0 extUnits
    
    772
    +    }
    
    773
    +  where
    
    774
    +    modUnitId = toUnitId $ moduleUnit $ gwib_mod target
    
    775
    +
    
    776
    +updateDepJson :: Bool -> DepNode -> [Dep] -> DepJson -> DepJson
    
    777
    +updateDepJson include_pkgs DepNode {..} deps =
    
    778
    +  insertDepJson dn_mod payload externalDeps
    
    779
    +  where
    
    780
    +    (externalDeps, payload) = foldl' go (Set.empty, initial_node_data) deps
    
    781
    +
    
    782
    +    initial_node_data =
    
    783
    +      ModuleNodeDeps
    
    784
    +        { source = dn_src
    
    785
    +        , preprocessor = pn_preprocessor dn_preprocessing
    
    786
    +        , options = pn_options dn_preprocessing
    
    787
    +        , cpp = Set.empty
    
    788
    +        , imports = ImportDeps Map.empty
    
    789
    +        }
    
    790
    +
    
    791
    +    go (extDeps, node_data) = \ case
    
    792
    +      DepHi {dep_mod, dep_local, dep_unit, dep_boot, dep_level}
    
    793
    +        | dep_local
    
    794
    +        -> (extDeps, addImport (mkImportDep dep_mod dep_level dep_boot) node_data)
    
    795
    +
    
    796
    +        | include_pkgs
    
    797
    +        , Just unit <- dep_unit
    
    798
    +        , let PackageName nameFS = unitPackageName unit
    
    799
    +              name = unpackFS nameFS
    
    800
    +              withLibName (PackageName c) = name ++ ":" ++ unpackFS c
    
    801
    +              lname = maybe name withLibName (unitComponentName unit)
    
    802
    +              newExtDep = ExtUnitDep
    
    803
    +                { extUnitId = unitId unit
    
    804
    +                , extUnitName = lname
    
    805
    +                , extUnitPackageId = DPackageId $ unitPackageId unit
    
    806
    +                }
    
    807
    +        ->
    
    808
    +          ( Set.insert newExtDep extDeps
    
    809
    +          , addImport (mkImportDep dep_mod dep_level dep_boot) node_data
    
    810
    +          )
    
    811
    +
    
    812
    +        | otherwise
    
    813
    +        -> (extDeps, node_data)
    
    814
    +
    
    815
    +      DepCpp {dep_path} ->
    
    816
    +        (extDeps, addCpp dep_path node_data)
    
    388 817
     
    
    389 818
     -----------------------------------------------------------------
    
    390 819
     --              Module cycles
    

  • compiler/GHC/Driver/Pipeline.hs
    ... ... @@ -820,13 +820,13 @@ preprocessPipeline pipe_env hsc_env input_fn = do
    820 820
           use (T_Unlit pipe_env hsc_env input_fn)
    
    821 821
     
    
    822 822
     
    
    823
    -  (dflags1, p_warns1, warns1) <- use (T_FileArgs hsc_env unlit_fn)
    
    823
    +  (dflags1, _, p_warns1, warns1) <- use (fileArgsPhase hsc_env unlit_fn)
    
    824 824
       let hsc_env1 = hscSetFlags dflags1 hsc_env
    
    825 825
     
    
    826 826
       (cpp_fn, hsc_env2)
    
    827 827
         <- runAfterFlag hsc_env1 (Cpp HsSrcFile) (xopt LangExt.Cpp) (unlit_fn, hsc_env1) $ do
    
    828 828
               cpp_fn <- use (T_Cpp pipe_env hsc_env1 unlit_fn)
    
    829
    -          (dflags2, _, _) <- use (T_FileArgs hsc_env1 cpp_fn)
    
    829
    +          (dflags2, _, _, _) <- use (fileArgsPhase hsc_env1 cpp_fn)
    
    830 830
               let hsc_env2 = hscSetFlags dflags2 hsc_env1
    
    831 831
               return (cpp_fn, hsc_env2)
    
    832 832
     
    
    ... ... @@ -841,7 +841,8 @@ preprocessPipeline pipe_env hsc_env input_fn = do
    841 841
               then return (dflags1, p_warns1, warns1)
    
    842 842
               else do
    
    843 843
                 -- Reparse with original hsc_env so that we don't get duplicated options
    
    844
    -            use (T_FileArgs hsc_env pp_fn)
    
    844
    +            (dflagsPP, _, p_warns3, warns3) <- use (fileArgsPhase hsc_env pp_fn)
    
    845
    +            pure (dflagsPP, p_warns3, warns3)
    
    845 846
     
    
    846 847
       let print_config = initPrintConfig dflags3
    
    847 848
       liftIO (printOrThrowDiagnostics (hsc_logger hsc_env) print_config (initDiagOpts dflags3) (GhcPsMessage <$> p_warns3))
    
    ... ... @@ -865,6 +866,9 @@ preprocessPipeline pipe_env hsc_env input_fn = do
    865 866
               runAfter phase def
    
    866 867
                $ phaseIfFlag hsc_env flag def action
    
    867 868
     
    
    869
    +        fileArgsPhase env fn =
    
    870
    +          T_FileArgs (hsc_logger env) (hsc_dflags env) fn
    
    871
    +
    
    868 872
     -- | The complete compilation pipeline, from start to finish
    
    869 873
     fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, RecompLinkables)
    
    870 874
     fullPipeline pipe_env hsc_env pp_fn src_flavour = do
    

  • compiler/GHC/Driver/Pipeline/Execute.hs
    ... ... @@ -104,7 +104,7 @@ runPhase :: TPhase out -> IO out
    104 104
     runPhase (T_Unlit pipe_env hsc_env inp_path) = do
    
    105 105
       out_path <- phaseOutputFilenameNew (Cpp HsSrcFile) pipe_env hsc_env Nothing
    
    106 106
       runUnlitPhase hsc_env inp_path out_path
    
    107
    -runPhase (T_FileArgs hsc_env inp_path) = getFileArgs hsc_env inp_path
    
    107
    +runPhase (T_FileArgs logger dflags inp_path) = getFileArgsNoHsc logger dflags inp_path
    
    108 108
     runPhase (T_Cpp pipe_env hsc_env inp_path) = do
    
    109 109
       out_path <- phaseOutputFilenameNew (HsPp HsSrcFile) pipe_env hsc_env Nothing
    
    110 110
       runCppPhase hsc_env inp_path out_path
    
    ... ... @@ -620,17 +620,21 @@ runUnlitPhase hsc_env input_fn output_fn = do
    620 620
     
    
    621 621
         return output_fn
    
    622 622
     
    
    623
    -getFileArgs :: HscEnv -> FilePath -> IO ((DynFlags, Messages PsMessage, Messages DriverMessage))
    
    623
    +getFileArgs :: HscEnv -> FilePath -> IO ((DynFlags, [String], Messages PsMessage, Messages DriverMessage))
    
    624 624
     getFileArgs hsc_env input_fn = do
    
    625 625
       let dflags0 = hsc_dflags hsc_env
    
    626 626
           logger  = hsc_logger hsc_env
    
    627
    -      parser_opts = initParserOpts dflags0
    
    627
    +  getFileArgsNoHsc logger dflags0 input_fn
    
    628
    +
    
    629
    +getFileArgsNoHsc :: Logger -> DynFlags -> FilePath -> IO ((DynFlags, [String], Messages PsMessage, Messages DriverMessage))
    
    630
    +getFileArgsNoHsc logger dflags0 input_fn = do
    
    631
    +  let parser_opts = initParserOpts dflags0
    
    628 632
           sec = initSourceErrorContext dflags0
    
    629 633
       (warns0, src_opts) <- getOptionsFromFile parser_opts sec (supportedLanguagePragmas dflags0) input_fn
    
    630 634
       (dflags1, unhandled_flags, warns)
    
    631 635
         <- parseDynamicFilePragma logger dflags0 src_opts
    
    632 636
       checkProcessArgsResult dflags0 unhandled_flags
    
    633
    -  return (dflags1, warns0, warns)
    
    637
    +  return (dflags1, map unLoc src_opts, warns0, warns)
    
    634 638
     
    
    635 639
     runCppPhase :: HscEnv -> FilePath -> FilePath -> IO FilePath
    
    636 640
     runCppPhase hsc_env input_fn output_fn = do
    

  • compiler/GHC/Driver/Pipeline/Phases.hs
    ... ... @@ -13,6 +13,7 @@ import GHC.Driver.Errors.Types
    13 13
     import GHC.Fingerprint.Type
    
    14 14
     import GHC.Unit.Module.Location ( ModLocation )
    
    15 15
     import GHC.Unit.Module.ModIface
    
    16
    +import GHC.Utils.Logger
    
    16 17
     import GHC.Driver.Phases
    
    17 18
     
    
    18 19
     import Language.Haskell.Syntax.Module.Name ( ModuleName )
    
    ... ... @@ -25,7 +26,7 @@ import GHC.Unit.Home.ModInfo
    25 26
     -- phase if the inputs have been modified.
    
    26 27
     data TPhase res where
    
    27 28
       T_Unlit :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath
    
    28
    -  T_FileArgs :: HscEnv -> FilePath -> TPhase (DynFlags, Messages PsMessage, Messages DriverMessage)
    
    29
    +  T_FileArgs :: Logger -> DynFlags -> FilePath -> TPhase ((DynFlags, [String], Messages PsMessage, Messages DriverMessage))
    
    29 30
       T_Cpp   :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath
    
    30 31
       T_HsPp  :: PipeEnv -> HscEnv -> FilePath -> FilePath -> TPhase FilePath
    
    31 32
       T_HscRecomp :: PipeEnv -> HscEnv -> FilePath -> HscSource -> TPhase (HscEnv, ModSummary, HscRecompStatus)
    

  • compiler/GHC/Driver/Session.hs
    ... ... @@ -791,6 +791,12 @@ addOptCmmP f = alterToolSettings $ \s -> s
    791 791
     setDepMakefile :: FilePath -> DynFlags -> DynFlags
    
    792 792
     setDepMakefile f d = d { depMakefile = f }
    
    793 793
     
    
    794
    +setDepJson :: FilePath -> DynFlags -> DynFlags
    
    795
    +setDepJson f d = d { depJson = Just f }
    
    796
    +
    
    797
    +unsetDepJson :: DynFlags -> DynFlags
    
    798
    +unsetDepJson d = d { depJson = Nothing }
    
    799
    +
    
    794 800
     setDepIncludeCppDeps :: Bool -> DynFlags -> DynFlags
    
    795 801
     setDepIncludeCppDeps b d = d { depIncludeCppDeps = b }
    
    796 802
     
    
    ... ... @@ -1261,6 +1267,8 @@ dynamic_flags_deps = [
    1261 1267
             -------- ghc -M -----------------------------------------------------
    
    1262 1268
       , make_ord_flag defGhcFlag "dep-suffix"              (hasArg addDepSuffix)
    
    1263 1269
       , make_ord_flag defGhcFlag "dep-makefile"            (hasArg setDepMakefile)
    
    1270
    +  , make_ord_flag defGhcFlag "dep-json"                (hasArg setDepJson)
    
    1271
    +  , make_ord_flag defGhcFlag "no-dep-json"             (noArg unsetDepJson)
    
    1264 1272
       , make_ord_flag defGhcFlag "include-cpp-deps"
    
    1265 1273
             (noArg (setDepIncludeCppDeps True))
    
    1266 1274
       , make_ord_flag defGhcFlag "include-pkg-deps"
    

  • docs/users_guide/separate_compilation.rst
    ... ... @@ -1503,6 +1503,23 @@ generation are:
    1503 1503
         ``.depend`` and then ``include`` the file ``.depend`` into
    
    1504 1504
         ``Makefile``.
    
    1505 1505
     
    
    1506
    +.. ghc-flag:: -dep-json ⟨file⟩
    
    1507
    +    :shortdesc: Also emit ⟨file⟩ as a JSON file containing dependencies
    
    1508
    +    :type: dynamic
    
    1509
    +    :reverse: -no-dep-json
    
    1510
    +    :category: redirect-output
    
    1511
    +
    
    1512
    +    In addition to the makefile, also emit ⟨file⟩ as a JSON file
    
    1513
    +    containing the same dependencies info, so it can be parsed by
    
    1514
    +    external build systems. The JSON file contains a single object,
    
    1515
    +    mapping each target to a list of dependencies.
    
    1516
    +    In addition to the makefile, each module's payload will contain the
    
    1517
    +    values of ``OPTIONS`` and ``LANGUAGE`` pragmas of the source
    
    1518
    +    file, so it can be parsed by external build systems. Each ``LANGUAGE``
    
    1519
    +    pragma is represented as an option as well, e.g.
    
    1520
    +    ``{-# LANGUAGE TemplateHaskell #-}`` is represented as
    
    1521
    +    ``"-XTemplateHaskell"``.
    
    1522
    +
    
    1506 1523
     .. ghc-flag:: -dep-suffix ⟨suffix⟩
    
    1507 1524
         :shortdesc: Make dependencies that declare that files with suffix
    
    1508 1525
             ``.⟨suf⟩⟨osuf⟩`` depend on interface files with suffix ``.⟨suf⟩hi``
    

  • testsuite/driver/testlib.py
    ... ... @@ -20,6 +20,7 @@ from pathlib import Path, PurePath
    20 20
     import collections
    
    21 21
     import collections.abc
    
    22 22
     import subprocess
    
    23
    +import json
    
    23 24
     
    
    24 25
     from testglobals import config, ghc_env, default_testopts, brokens, t, \
    
    25 26
                             TestRun, TestResult, TestOptions, PerfMetric
    
    ... ... @@ -3265,6 +3266,34 @@ def normalise_asm( s: str ) -> str:
    3265 3266
               out.append(ins[0])
    
    3266 3267
         return '\n'.join(out)
    
    3267 3268
     
    
    3269
    +def normalise_dep_json(s: str) -> str:
    
    3270
    +    """
    
    3271
    +    Normalise the json output of `ghc -M -dep-json output.json`
    
    3272
    +    """
    
    3273
    +
    
    3274
    +    def normalise_includes(j):
    
    3275
    +        # Only normalise proper json objects.
    
    3276
    +        # Strings might be parsed as json strings, but we that's not what we are looking for
    
    3277
    +        if not isinstance(j, dict):
    
    3278
    +            return j
    
    3279
    +        for unit in j.values():
    
    3280
    +            for module in unit.get("modules", {}).values():
    
    3281
    +                rewritten = [
    
    3282
    +                    "<absolute-path>/" + p.rsplit("/", 1)[-1] if p.startswith("/") else p
    
    3283
    +                    for p in module["includes"]
    
    3284
    +                ]
    
    3285
    +                # we sort to make sure this never
    
    3286
    +                module["includes"] = sorted(rewritten)
    
    3287
    +        return j
    
    3288
    +
    
    3289
    +    output = ""
    
    3290
    +    try:
    
    3291
    +        normalised_dep_json = normalise_includes(json.loads(s))
    
    3292
    +        output = json.dumps(normalised_dep_json, indent=2, sort_keys=True, ensure_ascii=False)
    
    3293
    +    except json.JSONDecodeError:
    
    3294
    +        output = line
    
    3295
    +    return output
    
    3296
    +
    
    3268 3297
     def safe_print(s: str) -> None:
    
    3269 3298
         s2 = s.encode(sys.stdout.encoding, errors='replace').decode(sys.stdout.encoding)
    
    3270 3299
         print(s2)
    

  • testsuite/tests/driver/T24384/A.hs
    1
    +{-# language Strict #-}
    
    2
    +{-# language CPP #-}
    
    3
    +{-# options_ghc -fexpose-all-unfoldings #-}
    
    4
    +module A where
    
    5
    +
    
    6
    +#include "foo.h"
    
    7
    +import {-# source #-} C

  • testsuite/tests/driver/T24384/C.hs
    1
    +module C where
    
    2
    +
    
    3
    +import A
    
    4
    +import Data.Set
    
    5
    +
    
    6
    +data C = C

  • testsuite/tests/driver/T24384/C.hs-boot
    1
    +module C where
    
    2
    +
    
    3
    +import E
    
    4
    +
    
    5
    +data C

  • testsuite/tests/driver/T24384/D.hs
    1
    +module D where
    
    2
    +
    
    3
    +import B
    
    4
    +import C

  • testsuite/tests/driver/T24384/E.hs
    1
    +module E where
    
    2
    +
    
    3
    +import Language.Haskell.TH.Syntax
    
    4
    +import Dep
    
    5
    +import DepPublic
    
    6
    +
    
    7
    +e :: Q Exp
    
    8
    +e = lift (5 :: Integer)
    
    9
    +
    
    10
    +edep :: ()
    
    11
    +edep = dep
    
    12
    +
    
    13
    +edepPub :: ()
    
    14
    +edepPub = depPub

  • testsuite/tests/driver/T24384/Makefile
    1
    +TOP=../../..
    
    2
    +include $(TOP)/mk/boilerplate.mk
    
    3
    +include $(TOP)/mk/test.mk
    
    4
    +
    
    5
    +
    
    6
    +PKGCONF_LOCAL=./db.package.conf
    
    7
    +GHC_PKG_LOCAL='$(GHC_PKG)' --no-user-package-db -f $(PKGCONF_LOCAL)
    
    8
    +
    
    9
    +T24384_prep:
    
    10
    +	rm -r $(PKGCONF_LOCAL) || true
    
    11
    +	$(GHC_PKG_LOCAL) init $(PKGCONF_LOCAL) 2>&1 >/dev/null
    
    12
    +
    
    13
    +	cd dep-internal && \
    
    14
    +		'$(TEST_HC)' $(TEST_HC_OPTS) $(WAY_FLAGS) -hisuf=$(ghciWayExt) $(ghciWayFlags) \
    
    15
    +			-v0 -fno-code -fwrite-interface -hidir outdir -this-unit-id dep-internal-1-XXX  \
    
    16
    +			-package base \
    
    17
    +			-i. DepInternal
    
    18
    +	$(GHC_PKG_LOCAL) register --force dep-internal/dep-internal.pkg 2>&1 >/dev/null
    
    19
    +
    
    20
    +	cd dep-public && \
    
    21
    +		'$(TEST_HC)' $(TEST_HC_OPTS) $(WAY_FLAGS) -hisuf=$(ghciWayExt) $(ghciWayFlags) \
    
    22
    +			-v0 -fno-code -fwrite-interface -hidir outdir -this-unit-id dep-public-1-XXX  \
    
    23
    +			-package base \
    
    24
    +			-i. DepPublic
    
    25
    +	$(GHC_PKG_LOCAL) register --force dep-public/dep-public.pkg 2>&1 >/dev/null
    
    26
    +
    
    27
    +	cd dep && \
    
    28
    +		'$(TEST_HC)' $(TEST_HC_OPTS) $(WAY_FLAGS) -hisuf=$(ghciWayExt) $(ghciWayFlags) \
    
    29
    +			-v0 -fno-code -fwrite-interface -hidir outdir -this-unit-id dep-1-XXX \
    
    30
    +			-package-db ../$(PKGCONF_LOCAL) -package-id dep-internal-1-XXX \
    
    31
    +			-i. Dep
    
    32
    +	$(GHC_PKG_LOCAL) register --force dep/dep.pkg 2>&1 >/dev/null
    
    33
    +
    
    34
    +T24384a: T24384_prep
    
    35
    +	'$(TEST_HC)' -M -dep-makefile T24384a.Makefile -include-pkg-deps -include-cpp-deps \
    
    36
    +		-hide-all-packages -package base -package containers -package template-haskell \
    
    37
    +		-package-db ./$(PKGCONF_LOCAL) -package-id dep-1-XXX -package-id dep-public-1-XXX \
    
    38
    +		A.hs lib/B.hs C.hs D.hs E.hs
    
    39
    +
    
    40
    +	cat T24384a.Makefile
    
    41
    +
    
    42
    +T24384b: T24384_prep
    
    43
    +	'$(TEST_HC)' -M -dep-json dep.json -include-pkg-deps -include-cpp-deps \
    
    44
    +		-hide-all-packages -package base -package containers -package template-haskell \
    
    45
    +		-package-db ./$(PKGCONF_LOCAL) -package-id dep-1-XXX -package-id dep-public-1-XXX \
    
    46
    +		A.hs lib/B.hs C.hs D.hs E.hs
    
    47
    +
    
    48
    +	cat dep.json
    
    49
    +
    
    50
    +T24384c: T24384_prep
    
    51
    +	'$(TEST_HC)' -M -dep-json dep.json \
    
    52
    +		-hide-all-packages -package base -package containers -package template-haskell \
    
    53
    +		-package-db ./$(PKGCONF_LOCAL) -package-id dep-1-XXX -package-id dep-public-1-XXX \
    
    54
    +		A.hs lib/B.hs C.hs D.hs E.hs
    
    55
    +
    
    56
    +	cat dep.json

  • testsuite/tests/driver/T24384/T24384a.stdout
    1
    +# DO NOT DELETE: Beginning of Haskell dependencies
    
    2
    +E.o : E.hs
    
    3
    +E.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/Prelude.hi
    
    4
    +E.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/GHC/Essentials.hi
    
    5
    +E.o : dep-public/outdir/DepPublic.hi
    
    6
    +E.o : dep/outdir/Dep.hi
    
    7
    +E.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/template-haskell-2.25.0.0-inplace/Language/Haskell/TH/Syntax.hi
    
    8
    +C.o-boot : C.hs-boot
    
    9
    +C.o : C.hi-boot
    
    10
    +C.o-boot : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/Prelude.hi
    
    11
    +C.o-boot : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/GHC/Essentials.hi
    
    12
    +C.o-boot : E.hi
    
    13
    +A.o : A.hs
    
    14
    +A.o : foo.h
    
    15
    +A.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/rts-1.0.3/include/ghcversion.h
    
    16
    +A.o : /usr/include/stdc-predef.h
    
    17
    +A.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/Prelude.hi
    
    18
    +A.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/GHC/Essentials.hi
    
    19
    +A.o : C.hi-boot
    
    20
    +C.o : C.hs
    
    21
    +C.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/Prelude.hi
    
    22
    +C.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/GHC/Essentials.hi
    
    23
    +C.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/containers-0.8-inplace/Data/Set.hi
    
    24
    +C.o : A.hi
    
    25
    +lib/B.o-boot : lib/B.hs-boot
    
    26
    +lib/B.o : lib/B.hi-boot
    
    27
    +lib/B.o-boot : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/Prelude.hi
    
    28
    +lib/B.o-boot : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/GHC/Essentials.hi
    
    29
    +lib/B.o-boot : A.hi
    
    30
    +lib/B.o : lib/B.hs
    
    31
    +lib/B.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/Prelude.hi
    
    32
    +lib/B.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/GHC/Essentials.hi
    
    33
    +lib/B.o : A.hi
    
    34
    +D.o : D.hs
    
    35
    +D.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/Prelude.hi
    
    36
    +D.o : /home/hugin/Documents/haskell/ghc-homes/ghc-dep-json/_build/stage1/lib/../lib/x86_64-linux-ghc-10.1.20260917-inplace/base-4.23.0.0-inplace/GHC/Essentials.hi
    
    37
    +D.o : C.hi
    
    38
    +D.o : lib/B.hi
    
    39
    +# DO NOT DELETE: End of Haskell dependencies

  • testsuite/tests/driver/T24384/T24384b.stdout
    1
    +{
    
    2
    +  "base-<VERSION>-<HASH>": {
    
    3
    +    "package-id": "base-<VERSION>-<HASH>",
    
    4
    +    "package-name": "base"
    
    5
    +  },
    
    6
    +  "containers-<VERSION>-<HASH>": {
    
    7
    +    "package-id": "containers-<VERSION>-<HASH>",
    
    8
    +    "package-name": "containers"
    
    9
    +  },
    
    10
    +  "dep-1-XXX": {
    
    11
    +    "package-id": "dep-1",
    
    12
    +    "package-name": "dep"
    
    13
    +  },
    
    14
    +  "dep-public-1-XXX": {
    
    15
    +    "package-id": "dep-public-1",
    
    16
    +    "package-name": "dep-public"
    
    17
    +  },
    
    18
    +  "main": {
    
    19
    +    "modules": {
    
    20
    +      "A": {
    
    21
    +        "imports": [
    
    22
    +          {
    
    23
    +            "base-<VERSION>-<HASH>": [
    
    24
    +              "GHC.Essentials",
    
    25
    +              "Prelude"
    
    26
    +            ]
    
    27
    +          },
    
    28
    +          {
    
    29
    +            "main": [
    
    30
    +              "C[boot]"
    
    31
    +            ]
    
    32
    +          }
    
    33
    +        ],
    
    34
    +        "includes": [
    
    35
    +          "<absolute-path>/ghcversion.h",
    
    36
    +          "<absolute-path>/stdc-predef.h",
    
    37
    +          "foo.h"
    
    38
    +        ],
    
    39
    +        "options": [
    
    40
    +          "-XStrict",
    
    41
    +          "-XCPP",
    
    42
    +          "-fexpose-all-unfoldings"
    
    43
    +        ],
    
    44
    +        "preprocessor": null,
    
    45
    +        "source": "A.hs"
    
    46
    +      },
    
    47
    +      "B": {
    
    48
    +        "imports": [
    
    49
    +          {
    
    50
    +            "base-<VERSION>-<HASH>": [
    
    51
    +              "GHC.Essentials",
    
    52
    +              "Prelude"
    
    53
    +            ]
    
    54
    +          },
    
    55
    +          {
    
    56
    +            "main": [
    
    57
    +              "A"
    
    58
    +            ]
    
    59
    +          }
    
    60
    +        ],
    
    61
    +        "includes": [],
    
    62
    +        "options": [
    
    63
    +          "-F",
    
    64
    +          "-pgmF",
    
    65
    +          "./preproc.sh"
    
    66
    +        ],
    
    67
    +        "preprocessor": "./preproc.sh",
    
    68
    +        "source": "lib/B.hs"
    
    69
    +      },
    
    70
    +      "B[boot]": {
    
    71
    +        "imports": [
    
    72
    +          {
    
    73
    +            "base-<VERSION>-<HASH>": [
    
    74
    +              "GHC.Essentials",
    
    75
    +              "Prelude"
    
    76
    +            ]
    
    77
    +          },
    
    78
    +          {
    
    79
    +            "main": [
    
    80
    +              "A"
    
    81
    +            ]
    
    82
    +          }
    
    83
    +        ],
    
    84
    +        "includes": [],
    
    85
    +        "options": [
    
    86
    +          "-F",
    
    87
    +          "-pgmF",
    
    88
    +          "./preproc.sh"
    
    89
    +        ],
    
    90
    +        "preprocessor": "./preproc.sh",
    
    91
    +        "source": "lib/B.hs-boot"
    
    92
    +      },
    
    93
    +      "C": {
    
    94
    +        "imports": [
    
    95
    +          {
    
    96
    +            "base-<VERSION>-<HASH>": [
    
    97
    +              "GHC.Essentials",
    
    98
    +              "Prelude"
    
    99
    +            ]
    
    100
    +          },
    
    101
    +          {
    
    102
    +            "containers-<VERSION>-<HASH>": [
    
    103
    +              "Data.Set"
    
    104
    +            ]
    
    105
    +          },
    
    106
    +          {
    
    107
    +            "main": [
    
    108
    +              "A"
    
    109
    +            ]
    
    110
    +          }
    
    111
    +        ],
    
    112
    +        "includes": [],
    
    113
    +        "options": [],
    
    114
    +        "preprocessor": null,
    
    115
    +        "source": "C.hs"
    
    116
    +      },
    
    117
    +      "C[boot]": {
    
    118
    +        "imports": [
    
    119
    +          {
    
    120
    +            "base-<VERSION>-<HASH>": [
    
    121
    +              "GHC.Essentials",
    
    122
    +              "Prelude"
    
    123
    +            ]
    
    124
    +          },
    
    125
    +          {
    
    126
    +            "main": [
    
    127
    +              "E"
    
    128
    +            ]
    
    129
    +          }
    
    130
    +        ],
    
    131
    +        "includes": [],
    
    132
    +        "options": [],
    
    133
    +        "preprocessor": null,
    
    134
    +        "source": "C.hs-boot"
    
    135
    +      },
    
    136
    +      "D": {
    
    137
    +        "imports": [
    
    138
    +          {
    
    139
    +            "base-<VERSION>-<HASH>": [
    
    140
    +              "GHC.Essentials",
    
    141
    +              "Prelude"
    
    142
    +            ]
    
    143
    +          },
    
    144
    +          {
    
    145
    +            "main": [
    
    146
    +              "B",
    
    147
    +              "C"
    
    148
    +            ]
    
    149
    +          }
    
    150
    +        ],
    
    151
    +        "includes": [],
    
    152
    +        "options": [],
    
    153
    +        "preprocessor": null,
    
    154
    +        "source": "D.hs"
    
    155
    +      },
    
    156
    +      "E": {
    
    157
    +        "imports": [
    
    158
    +          {
    
    159
    +            "base-<VERSION>-<HASH>": [
    
    160
    +              "GHC.Essentials",
    
    161
    +              "Prelude"
    
    162
    +            ]
    
    163
    +          },
    
    164
    +          {
    
    165
    +            "dep-1-XXX": [
    
    166
    +              "Dep"
    
    167
    +            ]
    
    168
    +          },
    
    169
    +          {
    
    170
    +            "dep-public-1-XXX": [
    
    171
    +              "DepPublic"
    
    172
    +            ]
    
    173
    +          },
    
    174
    +          {
    
    175
    +            "template-haskell-<VERSION>-<HASH>": [
    
    176
    +              "Language.Haskell.TH.Syntax"
    
    177
    +            ]
    
    178
    +          }
    
    179
    +        ],
    
    180
    +        "includes": [],
    
    181
    +        "options": [],
    
    182
    +        "preprocessor": null,
    
    183
    +        "source": "E.hs"
    
    184
    +      }
    
    185
    +    }
    
    186
    +  },
    
    187
    +  "template-haskell-<VERSION>-<HASH>": {
    
    188
    +    "package-id": "template-haskell-<VERSION>-<HASH>",
    
    189
    +    "package-name": "template-haskell"
    
    190
    +  }
    
    191
    +}

  • testsuite/tests/driver/T24384/T24384b.stdout-mingw
    1
    +{
    
    2
    +  "base-<VERSION>-<HASH>": {
    
    3
    +    "package-id": "base-<VERSION>-<HASH>",
    
    4
    +    "package-name": "base"
    
    5
    +  },
    
    6
    +  "containers-<VERSION>-<HASH>": {
    
    7
    +    "package-id": "containers-<VERSION>-<HASH>",
    
    8
    +    "package-name": "containers"
    
    9
    +  },
    
    10
    +  "dep-1-XXX": {
    
    11
    +    "package-id": "dep-1",
    
    12
    +    "package-name": "dep"
    
    13
    +  },
    
    14
    +  "dep-public-1-XXX": {
    
    15
    +    "package-id": "dep-public-1",
    
    16
    +    "package-name": "dep-public"
    
    17
    +  },
    
    18
    +  "main": {
    
    19
    +    "modules": {
    
    20
    +      "A": {
    
    21
    +        "imports": [
    
    22
    +          {
    
    23
    +            "base-<VERSION>-<HASH>": [
    
    24
    +              "GHC.Essentials",
    
    25
    +              "Prelude"
    
    26
    +            ]
    
    27
    +          },
    
    28
    +          {
    
    29
    +            "main": [
    
    30
    +              "C[boot]"
    
    31
    +            ]
    
    32
    +          }
    
    33
    +        ],
    
    34
    +        "includes": [
    
    35
    +          "<absolute-path>/ghcversion.h",
    
    36
    +          "<absolute-path>/stdc-predef.h",
    
    37
    +          "foo.h"
    
    38
    +        ],
    
    39
    +        "options": [
    
    40
    +          "-XStrict",
    
    41
    +          "-XCPP",
    
    42
    +          "-fexpose-all-unfoldings"
    
    43
    +        ],
    
    44
    +        "preprocessor": null,
    
    45
    +        "source": "A.hs"
    
    46
    +      },
    
    47
    +      "B": {
    
    48
    +        "imports": [
    
    49
    +          {
    
    50
    +            "base-<VERSION>-<HASH>": [
    
    51
    +              "GHC.Essentials",
    
    52
    +              "Prelude"
    
    53
    +            ]
    
    54
    +          },
    
    55
    +          {
    
    56
    +            "main": [
    
    57
    +              "A"
    
    58
    +            ]
    
    59
    +          }
    
    60
    +        ],
    
    61
    +        "includes": [],
    
    62
    +        "options": [
    
    63
    +          "-F",
    
    64
    +          "-pgmF",
    
    65
    +          "./preproc.sh"
    
    66
    +        ],
    
    67
    +        "preprocessor": "./preproc.sh",
    
    68
    +        "source": "lib/B.hs"
    
    69
    +      },
    
    70
    +      "B[boot]": {
    
    71
    +        "imports": [
    
    72
    +          {
    
    73
    +            "base-<VERSION>-<HASH>": [
    
    74
    +              "GHC.Essentials",
    
    75
    +              "Prelude"
    
    76
    +            ]
    
    77
    +          },
    
    78
    +          {
    
    79
    +            "main": [
    
    80
    +              "A"
    
    81
    +            ]
    
    82
    +          }
    
    83
    +        ],
    
    84
    +        "includes": [],
    
    85
    +        "options": [
    
    86
    +          "-F",
    
    87
    +          "-pgmF",
    
    88
    +          "./preproc.sh"
    
    89
    +        ],
    
    90
    +        "preprocessor": "./preproc.sh",
    
    91
    +        "source": "lib/B.hs-boot"
    
    92
    +      },
    
    93
    +      "C": {
    
    94
    +        "imports": [
    
    95
    +          {
    
    96
    +            "base-<VERSION>-<HASH>": [
    
    97
    +              "GHC.Essentials",
    
    98
    +              "Prelude"
    
    99
    +            ]
    
    100
    +          },
    
    101
    +          {
    
    102
    +            "containers-<VERSION>-<HASH>": [
    
    103
    +              "Data.Set"
    
    104
    +            ]
    
    105
    +          },
    
    106
    +          {
    
    107
    +            "main": [
    
    108
    +              "A"
    
    109
    +            ]
    
    110
    +          }
    
    111
    +        ],
    
    112
    +        "includes": [],
    
    113
    +        "options": [],
    
    114
    +        "preprocessor": null,
    
    115
    +        "source": "C.hs"
    
    116
    +      },
    
    117
    +      "C[boot]": {
    
    118
    +        "imports": [
    
    119
    +          {
    
    120
    +            "base-<VERSION>-<HASH>": [
    
    121
    +              "GHC.Essentials",
    
    122
    +              "Prelude"
    
    123
    +            ]
    
    124
    +          },
    
    125
    +          {
    
    126
    +            "main": [
    
    127
    +              "E"
    
    128
    +            ]
    
    129
    +          }
    
    130
    +        ],
    
    131
    +        "includes": [],
    
    132
    +        "options": [],
    
    133
    +        "preprocessor": null,
    
    134
    +        "source": "C.hs-boot"
    
    135
    +      },
    
    136
    +      "D": {
    
    137
    +        "imports": [
    
    138
    +          {
    
    139
    +            "base-<VERSION>-<HASH>": [
    
    140
    +              "GHC.Essentials",
    
    141
    +              "Prelude"
    
    142
    +            ]
    
    143
    +          },
    
    144
    +          {
    
    145
    +            "main": [
    
    146
    +              "B",
    
    147
    +              "C"
    
    148
    +            ]
    
    149
    +          }
    
    150
    +        ],
    
    151
    +        "includes": [],
    
    152
    +        "options": [],
    
    153
    +        "preprocessor": null,
    
    154
    +        "source": "D.hs"
    
    155
    +      },
    
    156
    +      "E": {
    
    157
    +        "imports": [
    
    158
    +          {
    
    159
    +            "base-<VERSION>-<HASH>": [
    
    160
    +              "GHC.Essentials",
    
    161
    +              "Prelude"
    
    162
    +            ]
    
    163
    +          },
    
    164
    +          {
    
    165
    +            "dep-1-XXX": [
    
    166
    +              "Dep"
    
    167
    +            ]
    
    168
    +          },
    
    169
    +          {
    
    170
    +            "dep-public-1-XXX": [
    
    171
    +              "DepPublic"
    
    172
    +            ]
    
    173
    +          },
    
    174
    +          {
    
    175
    +            "template-haskell-<VERSION>-<HASH>": [
    
    176
    +              "Language.Haskell.TH.Syntax"
    
    177
    +            ]
    
    178
    +          }
    
    179
    +        ],
    
    180
    +        "includes": [],
    
    181
    +        "options": [],
    
    182
    +        "preprocessor": null,
    
    183
    +        "source": "E.hs"
    
    184
    +      }
    
    185
    +    }
    
    186
    +  },
    
    187
    +  "template-haskell-<VERSION>-<HASH>": {
    
    188
    +    "package-id": "template-haskell-<VERSION>-<HASH>",
    
    189
    +    "package-name": "template-haskell"
    
    190
    +  }
    
    191
    +}

  • testsuite/tests/driver/T24384/T24384c.stdout
    1
    +{
    
    2
    +  "main": {
    
    3
    +    "modules": {
    
    4
    +      "A": {
    
    5
    +        "imports": [
    
    6
    +          {
    
    7
    +            "main": [
    
    8
    +              "C[boot]"
    
    9
    +            ]
    
    10
    +          }
    
    11
    +        ],
    
    12
    +        "includes": [],
    
    13
    +        "options": [
    
    14
    +          "-XStrict",
    
    15
    +          "-XCPP",
    
    16
    +          "-fexpose-all-unfoldings"
    
    17
    +        ],
    
    18
    +        "preprocessor": null,
    
    19
    +        "source": "A.hs"
    
    20
    +      },
    
    21
    +      "B": {
    
    22
    +        "imports": [
    
    23
    +          {
    
    24
    +            "main": [
    
    25
    +              "A"
    
    26
    +            ]
    
    27
    +          }
    
    28
    +        ],
    
    29
    +        "includes": [],
    
    30
    +        "options": [
    
    31
    +          "-F",
    
    32
    +          "-pgmF",
    
    33
    +          "./preproc.sh"
    
    34
    +        ],
    
    35
    +        "preprocessor": "./preproc.sh",
    
    36
    +        "source": "lib/B.hs"
    
    37
    +      },
    
    38
    +      "B[boot]": {
    
    39
    +        "imports": [
    
    40
    +          {
    
    41
    +            "main": [
    
    42
    +              "A"
    
    43
    +            ]
    
    44
    +          }
    
    45
    +        ],
    
    46
    +        "includes": [],
    
    47
    +        "options": [
    
    48
    +          "-F",
    
    49
    +          "-pgmF",
    
    50
    +          "./preproc.sh"
    
    51
    +        ],
    
    52
    +        "preprocessor": "./preproc.sh",
    
    53
    +        "source": "lib/B.hs-boot"
    
    54
    +      },
    
    55
    +      "C": {
    
    56
    +        "imports": [
    
    57
    +          {
    
    58
    +            "main": [
    
    59
    +              "A"
    
    60
    +            ]
    
    61
    +          }
    
    62
    +        ],
    
    63
    +        "includes": [],
    
    64
    +        "options": [],
    
    65
    +        "preprocessor": null,
    
    66
    +        "source": "C.hs"
    
    67
    +      },
    
    68
    +      "C[boot]": {
    
    69
    +        "imports": [
    
    70
    +          {
    
    71
    +            "main": [
    
    72
    +              "E"
    
    73
    +            ]
    
    74
    +          }
    
    75
    +        ],
    
    76
    +        "includes": [],
    
    77
    +        "options": [],
    
    78
    +        "preprocessor": null,
    
    79
    +        "source": "C.hs-boot"
    
    80
    +      },
    
    81
    +      "D": {
    
    82
    +        "imports": [
    
    83
    +          {
    
    84
    +            "main": [
    
    85
    +              "B",
    
    86
    +              "C"
    
    87
    +            ]
    
    88
    +          }
    
    89
    +        ],
    
    90
    +        "includes": [],
    
    91
    +        "options": [],
    
    92
    +        "preprocessor": null,
    
    93
    +        "source": "D.hs"
    
    94
    +      },
    
    95
    +      "E": {
    
    96
    +        "imports": [],
    
    97
    +        "includes": [],
    
    98
    +        "options": [],
    
    99
    +        "preprocessor": null,
    
    100
    +        "source": "E.hs"
    
    101
    +      }
    
    102
    +    }
    
    103
    +  }
    
    104
    +}

  • testsuite/tests/driver/T24384/all.T
    1
    +test_extras = extra_files(
    
    2
    +  ['A.hs', 'lib/', 'C.hs', 'C.hs-boot', 'D.hs', 'E.hs',
    
    3
    +  'foo.h',
    
    4
    +  'dep', 'dep-internal', 'dep-public',
    
    5
    +  'preproc.sh'
    
    6
    +  ])
    
    7
    +
    
    8
    +normalisers = [ normalise_version('base', 'containers', 'template-haskell')
    
    9
    +              , normalise_slashes
    
    10
    +              , normalise_fun(normalise_dep_json)
    
    11
    +              ]
    
    12
    +
    
    13
    +test('T24384a', [ test_extras, pre_cmd('$MAKE -s --no-print-directory T24384_prep'), *normalisers ], makefile_test, [])
    
    14
    +test('T24384b', [ test_extras, pre_cmd('$MAKE -s --no-print-directory T24384_prep'), *normalisers ], makefile_test, [])
    
    15
    +test('T24384c', [ test_extras, pre_cmd('$MAKE -s --no-print-directory T24384_prep'), *normalisers ], makefile_test, [])

  • testsuite/tests/driver/T24384/dep-internal/DepInternal.hs
    1
    +module DepInternal where
    
    2
    +depInt :: ()
    
    3
    +depInt = ()

  • testsuite/tests/driver/T24384/dep-internal/dep-internal.pkg
    1
    +name: dep-internal
    
    2
    +version: 1
    
    3
    +id: dep-internal-1-XXX
    
    4
    +key: dep-internal-1-XXX
    
    5
    +visibility:           public
    
    6
    +abi: inplace
    
    7
    +exposed-modules:      DepInternal
    
    8
    +depends:
    
    9
    +import-dirs: ${pkgroot}/dep-internal/outdir
    
    10
    +library-dirs: ${pkgroot}/dep-internal/outdir
    
    11
    +include-dirs:

  • testsuite/tests/driver/T24384/dep-public/DepPublic.hs
    1
    +module DepPublic where
    
    2
    +depPub :: ()
    
    3
    +depPub = ()

  • testsuite/tests/driver/T24384/dep-public/dep-public.pkg
    1
    +name: dep-public
    
    2
    +version: 1
    
    3
    +id: dep-public-1-XXX
    
    4
    +key: dep-public-1-XXX
    
    5
    +visibility:           public
    
    6
    +abi: inplace
    
    7
    +exposed-modules:      DepPublic
    
    8
    +depends:
    
    9
    +import-dirs: ${pkgroot}/dep-public/outdir
    
    10
    +library-dirs: ${pkgroot}/dep-public/outdir
    
    11
    +include-dirs:

  • testsuite/tests/driver/T24384/dep/Dep.hs
    1
    +module Dep where
    
    2
    +import DepInternal
    
    3
    +dep :: ()
    
    4
    +dep = depInt

  • testsuite/tests/driver/T24384/dep/dep.pkg
    1
    +name: dep
    
    2
    +version: 1
    
    3
    +id: dep-1-XXX
    
    4
    +key: dep-1-XXX
    
    5
    +visibility:           public
    
    6
    +abi: inplace
    
    7
    +exposed-modules:      Dep
    
    8
    +depends:              dep-internal-1-XXX
    
    9
    +import-dirs: ${pkgroot}/dep/outdir
    
    10
    +library-dirs: ${pkgroot}/dep/outdir
    
    11
    +include-dirs:

  • testsuite/tests/driver/T24384/foo.h

  • testsuite/tests/driver/T24384/format_json.sh
    1
    +#!/usr/bin/env bash
    
    2
    +set -euo pipefail
    
    3
    +
    
    4
    +# Packages whose ids are rewritten to <pkg>-<VERSION>-<HASH>
    
    5
    +pkgs='base|containers|template-haskell'
    
    6
    +
    
    7
    +usage() {
    
    8
    +  cat <<EOF
    
    9
    +Usage: $(basename "$0") FILE
    
    10
    +
    
    11
    +Format one of the test -dep-json output file with jq and normalise it:
    
    12
    +  - absolute paths in "includes" become <absolute-path>/<filename>,
    
    13
    +    and the "includes" lists are sorted
    
    14
    +  - ids of the packages ${packages[*]}
    
    15
    +    (e.g. base-4.23.0.0-inplace) become <pkg>-<VERSION>-<HASH>
    
    16
    +
    
    17
    +Writes the output to FILE
    
    18
    +EOF
    
    19
    +}
    
    20
    +
    
    21
    +case "${1:-}" in -h|--help) usage; exit 0 ;; esac
    
    22
    +[ $# -eq 1 ] || { usage >&2; exit 1; }
    
    23
    +
    
    24
    +# Escape regex metacharacters (and the / delimiter) for sed
    
    25
    +esc() { sed 's/[][\.*^$+?(){}|/]/\\&/g'; }
    
    26
    +
    
    27
    +json=$(jq --sort-keys '
    
    28
    +  (.. | objects | select(has("includes")) | .includes) |=
    
    29
    +    (map(if startswith("/") then "<absolute-path>/" + (split("/") | last) else . end) | sort)
    
    30
    +' "$1")
    
    31
    +
    
    32
    +tmp=$(mktemp "$1.XXXXXX")
    
    33
    +trap 'rm -f "$tmp"' EXIT
    
    34
    +sed -E "s/($pkgs)-[0-9.]+(-[0-9a-zA-Z+]+)?(-[0-9a-zA-Z]+)?/\\1-<VERSION>-<HASH>/g" <<<"$json" > "$tmp"
    
    35
    +mv "$tmp" "$1"

  • testsuite/tests/driver/T24384/lib/B.hs
    1
    +{-# options_ghc -F -pgmF ./preproc.sh #-}
    
    2
    +module B where

  • testsuite/tests/driver/T24384/lib/B.hs-boot
    1
    +{-# options_ghc -F -pgmF ./preproc.sh #-}
    
    2
    +module B where

  • testsuite/tests/driver/T24384/preproc.sh
    1
    +#!/usr/bin/env bash
    
    2
    +
    
    3
    +sed '/preproc/d' $2 > $3
    
    4
    +echo 'import A' >> $3