472aeafc57
GitOrigin-RevId: c31898adf5a8ed202ce5bea9f347b1c6871f32d1
109 lines
5.1 KiB
Text
109 lines
5.1 KiB
Text
ghc-settings-edit is a small tool for changing certain fields in the settings
|
|
file that is part of every GHC installation (usually located at
|
|
lib/ghc-$version/lib/settings or lib/ghc-$version/settings). This is sometimes
|
|
necessary because GHC's build process leaks the tools used at build time into
|
|
the final settings file. This is fine, as long as the build and host platform
|
|
of the GHC build is the same since it will be possible to execute the tools
|
|
used at build time at run time. In case we are cross compiling GHC itself,
|
|
the settings file needs to be changed so that the correct tools are used in the
|
|
final installation. The GHC build system itself doesn't allow for this due to
|
|
its somewhat peculiar bootstrapping mechanism.
|
|
|
|
This tool was originally written by sternenseemann and is licensed under the MIT
|
|
license (as is nixpkgs) as well as the BSD 3 Clause license since it incorporates
|
|
some code from GHC. It is primarily intended for use in nixpkgs, so it should be
|
|
considered unstable: No guarantees about the stability of its command line
|
|
interface are made at this time.
|
|
|
|
> -- SPDX-License-Identifier: MIT AND BSD-3-Clause
|
|
> {-# LANGUAGE LambdaCase #-}
|
|
> module Main where
|
|
|
|
ghc-settings-edit requires no additional dependencies to the ones already
|
|
required to bootstrap GHC. This means that it only depends on GHC and core
|
|
libraries shipped with the compiler (base and containers). This property should
|
|
be preserved going forward as to not needlessly complicate bootstrapping GHC
|
|
in nixpkgs. Additionally, a wide range of library versions and thus GHC versions
|
|
should be supported (via CPP if necessary).
|
|
|
|
> import Control.Monad (foldM)
|
|
> import qualified Data.Map.Lazy as Map
|
|
> import System.Environment (getArgs, getProgName)
|
|
> import Text.Read (readEither)
|
|
|
|
Note that the containers dependency is needed to represent the contents of the
|
|
settings file. In theory, [(String, String)] (think lookup) would suffice, but
|
|
base doesn't provide any facilities for updating such lists. To avoid needlessly
|
|
reinventing the wheel here, we depend on an extra core library.
|
|
|
|
> type SettingsMap = Map.Map String String
|
|
|
|
ghc-settings-edit accepts the following arguments:
|
|
|
|
- The path to the settings file which is edited in place.
|
|
- For every field in the settings file to be updated, two arguments need to be
|
|
passed: the name of the field and its new value. Any number of these pairs
|
|
may be provided. If a field is missing from the given settings file,
|
|
it won't be added (see also below).
|
|
|
|
> usage :: String -> String
|
|
> usage name = "Usage: " ++ name ++ " FILE [KEY NEWVAL [KEY2 NEWVAL2 ...]]"
|
|
|
|
The arguments and the contents of the settings file are fed into the performEdits
|
|
function which implements the main logic of ghc-settings-edit (except IO).
|
|
|
|
> performEdits :: [String] -> String -> Either String String
|
|
> performEdits editArgs settingsString = do
|
|
|
|
First, the settings file is parsed and read into the SettingsMap structure. For
|
|
parsing, we can simply rely read, as GHC uses the familiar Read/Show format
|
|
(plus some formatting) for storing its settings. This is the main reason
|
|
ghc-settings-edit is written in Haskell: We don't need to roll our own parser.
|
|
|
|
> settingsMap <- Map.fromList <$> readEither settingsString
|
|
|
|
We also need to parse the remaining command line arguments (after the path)
|
|
which means splitting them into pairs of arguments describing the individual
|
|
edits. We use the chunkList utility function from GHC for this which is vendored
|
|
below. Since it doesn't guarantee that all sublists have the exact length given,
|
|
we'll have to check the length of the returned “pairs” later.
|
|
|
|
> let edits = chunkList 2 editArgs
|
|
|
|
Since each edit is a transformation of the SettingsMap, we use a fold to go
|
|
through the edits. The Either monad allows us to bail out if one is malformed.
|
|
The use of Map.adjust ensures that fields that aren't present in the original
|
|
settings file aren't added since the corresponding GHC installation wouldn't
|
|
understand them. Note that this is done silently which may be suboptimal:
|
|
It could be better to fail.
|
|
|
|
> show . Map.toList <$> foldM applyEdit settingsMap edits
|
|
> where
|
|
> applyEdit :: SettingsMap -> [String] -> Either String SettingsMap
|
|
> applyEdit m [key, newValue] = Right $ Map.adjust (const newValue) key m
|
|
> applyEdit _ _ = Left "Uneven number of edit arguments provided"
|
|
|
|
main just wraps performEdits and takes care of reading from and writing to the
|
|
given file.
|
|
|
|
> main :: IO ()
|
|
> main =
|
|
> getArgs >>= \case
|
|
> (settingsFile:edits) -> do
|
|
> orig <- readFile settingsFile
|
|
> case performEdits edits orig of
|
|
> Right edited -> writeFile settingsFile edited
|
|
> Left errorMsg -> error errorMsg
|
|
> _ -> do
|
|
> name <- getProgName
|
|
> error $ usage name
|
|
|
|
As mentioned, chunkList is taken from GHC, specifically GHC.Utils.Misc of GHC
|
|
verson 9.8.2. We don't depend on the ghc library directly (which would be
|
|
possible in theory) since there are no stability guarantees or deprecation
|
|
windows for the ghc's public library.
|
|
|
|
> -- | Split a list into chunks of /n/ elements
|
|
> chunkList :: Int -> [a] -> [[a]]
|
|
> chunkList _ [] = []
|
|
> chunkList n xs = as : chunkList n bs where (as,bs) = splitAt n xs
|