Sven Tennie pushed to branch wip/supersven/hadrian-cross-stage3 at Glasgow Haskell Compiler / GHC

Commits:

4 changed files:

Changes:

  • .gitignore
    ... ... @@ -120,8 +120,6 @@ _darcs/
    120 120
     /compiler/GHC/CmmToLlvm/Version/Bounds.hs
    
    121 121
     /compiler/ghc.cabal
    
    122 122
     /compiler/ghc.cabal.old
    
    123
    -/stage1/distrib/configure.ac
    
    124
    -/stage2/distrib/configure.ac
    
    125 123
     /distrib/ghc.iss
    
    126 124
     /docs/index.html
    
    127 125
     /docs/man
    

  • hadrian/src/BindistConfig.hs
    ... ... @@ -3,28 +3,31 @@ module BindistConfig where
    3 3
     import Stage
    
    4 4
     import Oracles.Flag
    
    5 5
     import Expression
    
    6
    -data BindistConfig = BindistConfig
    
    7
    -    { library_stage :: Stage -- ^ The stage compiler which builds the libraries
    
    8
    -    , executable_stage :: Stage -- ^ The stage compiler which builds the executables
    
    9
    -    , bindistFolder :: FilePath -- ^ Parent folder under build root ("bindist" or "bindist-stage3")
    
    10
    -    }
    
    6
    +data BindistConfig = BindistConfig { library_stage :: Stage -- ^ The stage compiler which builds the libraries
    
    7
    +                                   , executable_stage :: Stage -- ^ The stage compiler which builds the executables
    
    8
    +                                   }
    
    9
    +
    
    11 10
     
    
    12 11
     -- | A bindist for when the host = target, non cross-compilation setting.
    
    13 12
     -- Both the libraries and final executables are built with stage1 compiler.
    
    14 13
     normalBindist :: BindistConfig
    
    15
    -normalBindist = BindistConfig { library_stage = Stage1, executable_stage = Stage1, bindistFolder = "bindist" }
    
    14
    +normalBindist = BindistConfig { library_stage = Stage1, executable_stage = Stage1 }
    
    16 15
     
    
    17 16
     -- | A bindist which contains a cross compiler (when host /= target)
    
    18 17
     -- The cross compiler is produced by the stage1 compiler, but then we must compile
    
    19 18
     -- all the boot libraries with the cross compiler (hence stage2 for libraries)
    
    20 19
     crossBindist :: BindistConfig
    
    21
    -crossBindist = BindistConfig { library_stage = Stage2, executable_stage = Stage1, bindistFolder = "bindist" }
    
    20
    +crossBindist = BindistConfig { library_stage = Stage2, executable_stage = Stage1 }
    
    22 21
     
    
    23 22
     -- | A bindist which contains executables for the target, which produce code for the
    
    24 23
     -- target. These are produced as "Stage3" build products, produced by a stage2 cross compiler.
    
    25 24
     targetBindist ::  BindistConfig
    
    26
    -targetBindist = BindistConfig { library_stage = Stage2, executable_stage = Stage2, bindistFolder = "bindist-stage3" }
    
    25
    +targetBindist = BindistConfig { library_stage = Stage2, executable_stage = Stage2 }
    
    27 26
     
    
    27
    +-- | Parent folder under build root ("bindist" or "bindist-stage3")
    
    28
    +bindistFolder :: BindistConfig ->  FilePath
    
    29
    +bindistFolder conf | executable_stage conf == Stage2 = "bindist-stage3"
    
    30
    +bindistFolder _conf = "bindist"
    
    28 31
     
    
    29 32
     -- | The implicit bindist config, if we don't know any better.
    
    30 33
     implicitBindistConfig :: Action BindistConfig
    

  • hadrian/src/Rules/BinaryDist.hs
    ... ... @@ -6,7 +6,6 @@ import Context
    6 6
     import Data.Either
    
    7 7
     import qualified Data.Set as Set
    
    8 8
     import Expression
    
    9
    -import Hadrian.Oracles.Path (fixUnixPathsOnWindows)
    
    10 9
     import Oracles.Flavour
    
    11 10
     import Oracles.Setting
    
    12 11
     import Packages
    
    ... ... @@ -14,8 +13,6 @@ import Rules.Generate (generateSettings)
    14 13
     import Settings
    
    15 14
     import qualified System.Directory.Extra as IO
    
    16 15
     import Settings.Program (programContext)
    
    17
    -import Target
    
    18
    -import Utilities
    
    19 16
     import BindistConfig
    
    20 17
     
    
    21 18
     {-
    
    ... ... @@ -168,7 +165,7 @@ buildBinDistDir root conf@BindistConfig{..} = do
    168 165
         distDir        <- Context.distDir (vanillaContext library_stage rts)
    
    169 166
     
    
    170 167
         let ghcBuildDir      = root -/- stageString library_stage
    
    171
    -        bindistFilesDir  = root -/- bindistFolder -/- ghcVersionPretty
    
    168
    +        bindistFilesDir  = root -/- bindistFolder conf -/- ghcVersionPretty
    
    172 169
             ghcVersionPretty = "ghc-" ++ version ++ "-" ++ targetPlatform
    
    173 170
             rtsIncludeDir    = distDir -/- "include"
    
    174 171
     
    
    ... ... @@ -390,64 +387,34 @@ bindistRules = do
    390 387
         phony "binary-dist-cross" $ buildBinDistX "binary-dist-dir-cross" "bindist" Xz
    
    391 388
         phony "binary-dist-stage3" $ buildBinDistX "binary-dist-dir-stage3" "bindist-stage3" Xz
    
    392 389
     
    
    393
    -    -- Prepare binary distribution configure script
    
    394
    -    -- (generated in a per-stage temporary distrib directory by 'autoreconf')
    
    395
    -    forM_ [("bindist", Stage1), ("bindist-stage3", Stage2)] $ \(folder, stage) ->
    
    396
    -        root -/- folder -/- "ghc-*" -/- "configure" %> generateConfigure root stage
    
    397
    -
    
    398
    -    -- Generate the Makefile that enables the "make install" part
    
    399
    -    forM_ ["bindist", "bindist-stage3"] $ \folder ->
    
    400
    -        root -/- folder -/- "ghc-*" -/- "Makefile" %> \makefilePath -> do
    
    401
    -            top <- topDirectory
    
    402
    -            copyFile (top -/- "hadrian" -/- "bindist" -/- "Makefile") makefilePath
    
    403
    -
    
    404
    -    -- Copy various configure-related files needed for a working
    
    405
    -    -- './configure [...] && make install' workflow
    
    406
    -    -- (see the list of files needed in the 'binary-dist' rule above, before
    
    407
    -    -- creating the archive).
    
    408
    -    forM_ ["bindist", "bindist-stage3"] $ \folder ->
    
    409
    -        forM_ bindistInstallFiles $ \file ->
    
    410
    -            root -/- folder -/- "ghc-*" -/- file %> \dest -> do
    
    411
    -                copyFile (fixup file) dest
    
    390
    +    forM_ [normalBindist, targetBindist] $ \bindistCfg -> do
    
    391
    +      let bindistFolderName = bindistFolder bindistCfg
    
    392
    +          stg = executable_stage bindistCfg
    
    393
    +      -- Copy the per-stage 'configure' (produced by autoreconf in Generate.hs)
    
    394
    +      -- from the build distrib dir into the bindist. Generating it there keeps
    
    395
    +      -- the autoreconf inputs (configure.ac, aclocal.m4, m4/*.m4) next to the
    
    396
    +      -- configure script and lets Shake track their changes correctly.
    
    397
    +      root -/- bindistFolderName -/- "ghc-*" -/- "configure" %> \configurePath -> do
    
    398
    +          let distribConfigure = root -/- stageString stg -/- "distrib" -/- "configure"
    
    399
    +          need [distribConfigure]
    
    400
    +          copyFile distribConfigure configurePath
    
    401
    +
    
    402
    +      -- Generate the Makefile that enables the "make install" part
    
    403
    +      root -/- bindistFolderName -/- "ghc-*" -/- "Makefile" %> \makefilePath -> do
    
    404
    +          top <- topDirectory
    
    405
    +          copyFile (top -/- "hadrian" -/- "bindist" -/- "Makefile") makefilePath
    
    406
    +
    
    407
    +      -- Copy various configure-related files needed for a working
    
    408
    +      -- './configure [...] && make install' workflow
    
    409
    +      -- (see the list of files needed in the 'binary-dist' rule above, before
    
    410
    +      -- creating the archive).
    
    411
    +      forM_ bindistInstallFiles $ \file ->
    
    412
    +          root -/- bindistFolderName -/- "ghc-*" -/- file %> \dest -> do
    
    413
    +              copyFile (fixup file) dest
    
    412 414
     
    
    413 415
       where
    
    414 416
         fixup f | f `elem` ["INSTALL", "README"] = "distrib" -/- f
    
    415 417
                 | otherwise                      = f
    
    416
    -    generateConfigure root stage configurePath = do
    
    417
    -        let acFile = stageString stage -/- "distrib" -/- "configure.ac"
    
    418
    -        need [acFile]
    
    419
    -        ghcRoot <- topDirectory
    
    420
    -        -- Use a per-stage temporary distrib directory so that Stage1 and
    
    421
    -        -- Stage2 configure generation can run concurrently without
    
    422
    -        -- clobbering each other's inputs/outputs.
    
    423
    -        let distribDir = root -/- ("distrib-" ++ stageString stage)
    
    424
    -        removeDirectory distribDir
    
    425
    -        createDirectory distribDir
    
    426
    -        copyFile (ghcRoot -/- acFile) (distribDir -/- "configure.ac")
    
    427
    -        copyFile (ghcRoot -/- "aclocal.m4") (distribDir -/- "aclocal.m4")
    
    428
    -        copyDirectory (ghcRoot -/- "m4") distribDir
    
    429
    -
    
    430
    -        -- Note [Autoreconf unix paths from ACLOCAL_PATH]
    
    431
    -        -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    432
    -        -- On Windows, autoreconf fails when the ACLOCAL_PATH env variable contains Windows-
    
    433
    -        -- style paths. This happens because MSYS2 automatically converts env variables to
    
    434
    -        -- Windows-style paths. To fix this, we convert ACLOCAL_PATH back to Unix style.
    
    435
    -        -- This is done both in the boot Python script and here when building a bindist.
    
    436
    -        win_host <- isWinHost
    
    437
    -        env <- if not win_host
    
    438
    -          then pure []
    
    439
    -          else do
    
    440
    -            aclocalPathMay <- getEnv "ACLOCAL_PATH"
    
    441
    -            case aclocalPathMay of
    
    442
    -              Nothing -> pure []
    
    443
    -              Just aclocalPath -> do
    
    444
    -                unixAclocalPath <- fixUnixPathsOnWindows aclocalPath
    
    445
    -                pure [AddEnv "ACLOCAL_PATH" unixAclocalPath]
    
    446
    -
    
    447
    -        buildWithCmdOptions env $
    
    448
    -            target (vanillaContext Stage1 ghc) (Autoreconf distribDir) [] []
    
    449
    -        moveFile (distribDir -/- "configure") configurePath
    
    450
    -        removeDirectory distribDir
    
    451 418
     
    
    452 419
     data Compressor = Gzip | Bzip2 | Xz
    
    453 420
                     deriving (Eq, Ord, Show)
    

  • hadrian/src/Rules/Generate.hs
    ... ... @@ -9,6 +9,7 @@ import qualified Data.Set as Set
    9 9
     import Base
    
    10 10
     import qualified Context
    
    11 11
     import Expression
    
    12
    +import Hadrian.Oracles.Path (fixUnixPathsOnWindows)
    
    12 13
     import Hadrian.Oracles.TextFile (lookupStageBuildConfig)
    
    13 14
     import Oracles.Flag hiding (arSupportsAtFile, arSupportsDashL)
    
    14 15
     import Oracles.ModuleFiles
    
    ... ... @@ -360,11 +361,6 @@ templateRule :: FilePath -> Interpolations -> Rules ()
    360 361
     templateRule outPath =
    
    361 362
       templateRuleFrom (outPath <.> "in") outPath
    
    362 363
     
    
    363
    -templateRuleForStages :: FilePath -> (Stage -> Interpolations) -> Rules ()
    
    364
    -templateRuleForStages outPath mkInterps =
    
    365
    -  forM_ [Stage1, Stage2] $ \stage ->
    
    366
    -    templateRuleFrom (outPath <.> "in") (stageString stage -/- outPath) (mkInterps stage)
    
    367
    -
    
    368 364
     templateRules :: Rules ()
    
    369 365
     templateRules = do
    
    370 366
       templateRule "compiler/ghc.cabal" $ projectVersion
    
    ... ... @@ -415,6 +411,7 @@ templateRules = do
    415 411
     
    
    416 412
     bindistRules :: Rules ()
    
    417 413
     bindistRules = do
    
    414
    +  root <- buildRootRules
    
    418 415
       templateRule ("mk" -/- "project.mk") $ mconcat
    
    419 416
         [ interpolateSetting "ProjectName" ProjectName
    
    420 417
         , interpolateSetting "ProjectVersion" ProjectVersion
    
    ... ... @@ -426,45 +423,125 @@ bindistRules = do
    426 423
     
    
    427 424
         , interpolateVar "HostOS_CPP" $ fmap cppify $ interp $ queryHost queryOS
    
    428 425
     
    
    429
    -    , interpolateVar "TargetPlatform" $ getTarget targetPlatformTriple
    
    430
    -    , interpolateVar "TargetPlatform_CPP" $ cppify <$> getTarget targetPlatformTriple
    
    431
    -    , interpolateVar "TargetArch_CPP" $ cppify <$> getTarget queryArch
    
    432
    -    , interpolateVar "TargetOS_CPP" $ cppify <$> getTarget queryOS
    
    433
    -    , interpolateVar "LLVMTarget" $ getTarget tgtLlvmTarget
    
    426
    +    -- Stage2 always targets the final  architecture. Thus, we can use a
    
    427
    +    -- constant stage here.
    
    428
    +    , interpolateVar "TargetPlatform" $ getTarget Stage2 targetPlatformTriple
    
    429
    +    , interpolateVar "TargetPlatform_CPP" $ cppify <$> getTarget Stage2 targetPlatformTriple
    
    430
    +    , interpolateVar "TargetArch_CPP" $ cppify <$> getTarget Stage2 queryArch
    
    431
    +    , interpolateVar "TargetOS_CPP" $ cppify <$> getTarget Stage2 queryOS
    
    432
    +    , interpolateVar "LLVMTarget" $ getTarget Stage2 tgtLlvmTarget
    
    434 433
         ]
    
    435
    -  templateRuleForStages ("distrib" -/- "configure.ac") $ \stage -> mconcat
    
    434
    +  forM_ [Stage1, Stage2] $ \stage ->
    
    435
    +    let crossStageInterps = Interpolations $ do
    
    436
    +          isCrossStage <- crossStage stage
    
    437
    +          targetPlatform <- setting TargetPlatformFull
    
    438
    +          -- For cross-compiled compilers we need to pretend that they were
    
    439
    +          -- build on the target. For regular commpilers we can assume that:
    
    440
    +          -- build == host == target
    
    441
    +          buildPlatform <-
    
    442
    +            if isCrossStage
    
    443
    +              then
    
    444
    +                interp $ queryBuild targetPlatformTriple
    
    445
    +              else getTarget stage targetPlatformTriple
    
    446
    +          hostPlatform <-
    
    447
    +            if isCrossStage
    
    448
    +              then
    
    449
    +                interp $ queryHost targetPlatformTriple
    
    450
    +              else getTarget stage targetPlatformTriple
    
    451
    +          baseUnitId <- pkgUnitId (if isCrossStage then succStage stage else stage) base
    
    452
    +          buildPlatformFull <- if isCrossStage then setting BuildPlatformFull else setting TargetPlatformFull
    
    453
    +          hostPlatformFull <- if isCrossStage then setting HostPlatformFull else setting TargetPlatformFull
    
    454
    +          pure
    
    455
    +            [ ("CrossCompilePrefix", if isCrossStage then targetPlatform <> "-" else "")
    
    456
    +            , ("TargetPlatformFull", targetPlatform)
    
    457
    +            , ("BuildPlatform", buildPlatform)
    
    458
    +            , ("HostPlatform", hostPlatform)
    
    459
    +            , ("BaseUnitId", baseUnitId)
    
    460
    +            , ("BuildPlatformFull", buildPlatformFull)
    
    461
    +            , ("HostPlatformFull", hostPlatformFull)
    
    462
    +            ]
    
    463
    +    in templateRuleFrom
    
    464
    +      ("distrib" -/- "configure.ac" <.> "in")
    
    465
    +      (root -/- stageString stage -/- "distrib" -/- "configure.ac")
    
    466
    +      $ mconcat
    
    436 467
         [ interpolateSetting "ConfiguredEmsdkVersion" EmsdkVersion
    
    437
    -    , interpolateVar "CrossCompilePrefix" $ do
    
    438
    -        isCross <- crossStage stage
    
    439
    -        target <- setting TargetPlatformFull
    
    440
    -        pure $ if isCross then target <> "-" else ""
    
    441
    -    , interpolateVar "LeadingUnderscore" $ yesNo <$> getTarget tgtSymbolsHaveLeadingUnderscore
    
    468
    +    , interpolateVar "LeadingUnderscore" $ yesNo <$> getTarget stage tgtSymbolsHaveLeadingUnderscore
    
    442 469
         , interpolateSetting "LlvmMaxVersion" LlvmMaxVersion
    
    443 470
         , interpolateSetting "LlvmMinVersion" LlvmMinVersion
    
    444
    -    , interpolateVar "LlvmTarget" $ getTarget tgtLlvmTarget
    
    471
    +    , interpolateVar "LlvmTarget" $ getTarget stage tgtLlvmTarget
    
    445 472
         , interpolateSetting "ProjectVersion" ProjectVersion
    
    446 473
         , interpolateVar "EnableDistroToolchain" $ interp (staged (lookupStageBuildConfig "settings-use-distro-mingw"))
    
    447
    -    , interpolateVar "TablesNextToCode" $ yesNo <$> getTarget tgtTablesNextToCode
    
    474
    +    , interpolateVar "TablesNextToCode" $ yesNo <$> getTarget stage tgtTablesNextToCode
    
    448 475
         , interpolateVar "TargetHasLibm" $ yesNo <$> interp (staged (buildFlag TargetHasLibm))
    
    449
    -    , interpolateVar "TargetPlatform" $ getTarget targetPlatformTriple
    
    450
    -    , interpolateVar "BuildPlatform"  $ ifM (not <$> crossStage stage) (getTarget targetPlatformTriple) (interp $ queryBuild targetPlatformTriple)
    
    451
    -    , interpolateVar "HostPlatform"   $ ifM (not <$> crossStage stage) (getTarget targetPlatformTriple) (interp $ queryHost targetPlatformTriple)
    
    452
    -    , interpolateVar "TargetWordBigEndian" $ getTarget isBigEndian
    
    453
    -    , interpolateVar "TargetWordSize" $ getTarget wordSize
    
    454
    -    , interpolateVar "Unregisterised" $ yesNo <$> getTarget tgtUnregisterised
    
    476
    +    , interpolateVar "TargetPlatform" $ getTarget stage targetPlatformTriple
    
    477
    +    , interpolateVar "TargetWordBigEndian" $ getTarget stage isBigEndian
    
    478
    +    , interpolateVar "TargetWordSize" $ getTarget stage wordSize
    
    479
    +    , interpolateVar "Unregisterised" $ yesNo <$> getTarget stage tgtUnregisterised
    
    455 480
         , interpolateVar "UseLibdw" $ fmap yesNo $ interp $ staged (fmap (isJust . tgtRTSWithLibdw) . targetStage)
    
    456
    -    , interpolateVar "UseLibffiForAdjustors" $ yesNo <$> getTarget tgtUseLibffiForAdjustors
    
    457
    -    , interpolateVar "BaseUnitId" $ do
    
    458
    -        isCross <- crossStage stage
    
    459
    -        pkgUnitId (if isCross then succStage stage else stage) base
    
    460
    -    , interpolateVar "GhcWithSMP" $ yesNo <$> targetSupportsSMP Stage2
    
    461
    -    , interpolateVar "TargetPlatformFull" (setting TargetPlatformFull)
    
    462
    -    , interpolateVar "BuildPlatformFull" $ ifM (not <$> crossStage stage) (setting TargetPlatformFull) (setting BuildPlatformFull)
    
    463
    -    , interpolateVar "HostPlatformFull" $ ifM (not <$> crossStage stage) (setting TargetPlatformFull) (setting HostPlatformFull)
    
    481
    +    , interpolateVar "UseLibffiForAdjustors" $ yesNo <$> getTarget stage tgtUseLibffiForAdjustors
    
    482
    +    , interpolateVar "GhcWithSMP" $ yesNo <$> targetSupportsSMP stage
    
    483
    +    , crossStageInterps
    
    464 484
         ]
    
    485
    +
    
    486
    +  -- Stage the autoreconf inputs (aclocal.m4 and the m4/ macro directory) next
    
    487
    +  -- to each per-stage generated configure.ac under _build, then run
    
    488
    +  -- 'autoreconf' to produce a per-stage 'configure' script there.
    
    489
    +  --
    
    490
    +  -- The 'Autoreconf' builder auto-needs <dir>/configure.ac (Builder.hs); we
    
    491
    +  -- also explicitly need the staged macros so editing them triggers
    
    492
    +  -- re-generation of 'configure'. BinaryDist.hs copies this configure into
    
    493
    +  -- the bindist; it no longer runs autoreconf itself.
    
    494
    +  forM_ [Stage1, Stage2] $ \stage -> do
    
    495
    +    let distribDir = root -/- stageString stage -/- "distrib"
    
    496
    +
    
    497
    +    distribDir -/- "aclocal.m4" %> \out -> do
    
    498
    +      top <- topDirectory
    
    499
    +      copyFile (top -/- "aclocal.m4") out
    
    500
    +
    
    501
    +    -- Autoconf auxiliary files required by autoreconf (config.sub,
    
    502
    +    -- config.guess, install-sh). They live in-tree at the repo root and must
    
    503
    +    -- be staged next to configure.ac for autoreconf to find them.
    
    504
    +    forM_ ["config.sub", "config.guess", "install-sh"] $ \f ->
    
    505
    +      distribDir -/- f %> \out -> do
    
    506
    +        top <- topDirectory
    
    507
    +        copyFile (top -/- f) out
    
    508
    +
    
    509
    +    distribDir -/- "m4/*.m4" %> \out -> do
    
    510
    +      top <- topDirectory
    
    511
    +      copyFile (top -/- "m4" -/- takeFileName out) out
    
    512
    +
    
    513
    +    distribDir -/- "configure" %> \_ -> do
    
    514
    +      top    <- topDirectory
    
    515
    +      m4Files <- getDirectoryFiles (top -/- "m4") ["*.m4"]
    
    516
    +      need $ [ distribDir -/- "configure.ac"
    
    517
    +             , distribDir -/- "config.sub"
    
    518
    +             , distribDir -/- "config.guess"
    
    519
    +             , distribDir -/- "install-sh"
    
    520
    +             , distribDir -/- "aclocal.m4"
    
    521
    +             ]
    
    522
    +           ++ [ distribDir -/- "m4" -/- takeFileName f | f <- m4Files ]
    
    523
    +
    
    524
    +      -- Note [Autoreconf unix paths from ACLOCAL_PATH]
    
    525
    +      -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    526
    +      -- On Windows, autoreconf fails when the ACLOCAL_PATH env variable
    
    527
    +      -- contains Windows-style paths. MSYS2 auto-converts env vars to
    
    528
    +      -- Windows-style, so we convert ACLOCAL_PATH back to Unix style here.
    
    529
    +      win_host <- isWinHost
    
    530
    +      env <- if not win_host
    
    531
    +        then pure []
    
    532
    +        else do
    
    533
    +          aclocalPathMay <- getEnv "ACLOCAL_PATH"
    
    534
    +          case aclocalPathMay of
    
    535
    +            Nothing -> pure []
    
    536
    +            Just aclocalPath -> do
    
    537
    +              unixAclocalPath <- fixUnixPathsOnWindows aclocalPath
    
    538
    +              pure [AddEnv "ACLOCAL_PATH" unixAclocalPath]
    
    539
    +
    
    540
    +      buildWithCmdOptions env $
    
    541
    +        target (vanillaContext stage ghc) (Autoreconf distribDir) [] []
    
    465 542
       where
    
    466 543
         interp = interpretInContext (semiEmptyTarget Stage2)
    
    467
    -    getTarget = interp . queryTarget Stage2
    
    544
    +    getTarget stage = interp . queryTarget stage
    
    468 545
     
    
    469 546
     -- | Given a 'String' replace characters '.' and '-' by underscores ('_') so that
    
    470 547
     -- the resulting 'String' is a valid C preprocessor identifier.