mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-25 08:23:09 +00:00
ghc-settings-edit: init at 0.1.0
This commit is contained in:
parent
d5f666f593
commit
bd331dc379
@ -17,6 +17,8 @@ self: super: {
|
||||
# from the latest master instead of the current version on Hackage.
|
||||
cabal2nix-unstable = self.callPackage ./cabal2nix-unstable.nix { };
|
||||
|
||||
ghc-settings-edit = self.callPackage ../tools/haskell/ghc-settings-edit { };
|
||||
|
||||
# https://github.com/channable/vaultenv/issues/1
|
||||
vaultenv = self.callPackage ../tools/haskell/vaultenv { };
|
||||
|
||||
|
33
pkgs/development/tools/haskell/ghc-settings-edit/default.nix
Normal file
33
pkgs/development/tools/haskell/ghc-settings-edit/default.nix
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
stdenv,
|
||||
ghc,
|
||||
lib,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "ghc-settings-edit";
|
||||
version = "0.1.0";
|
||||
# See the source code for an explanation
|
||||
src = ./ghc-settings-edit.lhs;
|
||||
dontUnpack = true;
|
||||
dontBuild = true;
|
||||
|
||||
nativeBuildInputs = [ ghc ];
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p "$out/bin"
|
||||
${ghc.targetPrefix}ghc --make "$src" -o "$out/bin/ghc-settings-edit"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
meta = {
|
||||
license = [
|
||||
lib.licenses.mit
|
||||
lib.licenses.bsd3
|
||||
];
|
||||
platforms = lib.platforms.all;
|
||||
description = "Tool for editing GHC's settings file";
|
||||
};
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
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
|
Loading…
Reference in New Issue
Block a user