Skip to content

Commit 182580e

Browse files
authored
Upgrade build to GHC 9.6 (#1245)
* Upgrade hevm to latest `echidna-patches` commit * Upgrade build to GHC 9.6 * Refactor terminal and signal support in preparation for Windows UI * Enable UI on all platforms * Use pkgsStatic for Linux pkgsMusl fails to build for some reason. * Remove conditional UI compilation All platforms now support the Echidna UI * Enable UI on ConHost as well See the introduction on https://hackage.haskell.org/package/ansi-terminal-1.1.1/docs/System-Console-ANSI.html * Enable `eager-blackholing` Observed a ~2% speedup on longer runs when compiling both hevm and echidna with this flag. Besides, the Haskell documentation encourages enabling it for parallel code: https://downloads.haskell.org/~ghc/9.6.5/docs/users_guide/using-concurrent.html#compile-time-options-for-smp-parallelism * Resolve hlint warnings * Update Linux Stack CI to 9.6 * Fix compilation failure after merge * Upgrade hevm to `echidna-patches-20240725` * Fix lint warnings, unused imports * flake: align nixpkgs with hevm * flake: fix libiconv for x86_64 darwin > app/utf8-troubleshoot/cbits/locale.c:10:10: error: > fatal error: 'libcharset.h' file not found > | > 10 | #include <libcharset.h> > | ^ > #include <libcharset.h> > ^~~~~~~~~~~~~~ Also link iconv dynamically on Darwin as well
1 parent a550094 commit 182580e

File tree

9 files changed

+149
-137
lines changed

9 files changed

+149
-137
lines changed

.github/container-linux-static/Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM alpine:3.18.4
1+
FROM alpine:3.18.6
22
# Based on https://github.com/fpco/alpine-haskell-stack/blob/9.2.8v2/ghc-Dockerfile
33

44
RUN apk upgrade --no-cache &&\
@@ -36,12 +36,12 @@ RUN ln -s /usr/lib/libncurses.a /usr/lib/libtinfo.a
3636
COPY stack-config.yaml /root/.stack/config.yaml
3737

3838
RUN cd /tmp && \
39-
curl -sSLo /tmp/ghc.tar.xz https://downloads.haskell.org/~ghc/9.4.7/ghc-9.4.7-x86_64-alpine3_12-linux.tar.xz && \
39+
curl -sSLo /tmp/ghc.tar.xz https://downloads.haskell.org/~ghc/9.6.5/ghc-9.6.5-x86_64-alpine3_12-linux.tar.xz && \
4040
tar xf ghc.tar.xz && \
41-
cd ghc-9.4.7-x86_64-unknown-linux && \
41+
cd ghc-9.6.5-x86_64-unknown-linux && \
4242
./configure --prefix=/usr/local && \
4343
make install && \
44-
rm -rf /tmp/ghc.tar.xz /tmp/ghc-9.4.7-x86_64-unknown-linux
44+
rm -rf /tmp/ghc.tar.xz /tmp/ghc-9.6.5-x86_64-unknown-linux
4545

4646
RUN apk upgrade --no-cache &&\
4747
apk add --no-cache \
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extra-include-dirs:
2+
- /usr/include
3+
extra-lib-dirs:
4+
- /lib
5+
- /usr/lib

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010

1111
env:
1212
# Tag for cache invalidation
13-
CACHE_VERSION: v6
13+
CACHE_VERSION: v7
1414

1515
jobs:
1616
build:
@@ -22,7 +22,7 @@ jobs:
2222
include:
2323
- os: ubuntu-20.04
2424
shell: bash
25-
container: "{\"image\": \"elopeztob/alpine-haskell-stack-echidna:ghc-9.4.7\", \"options\": \"--user 1001\"}"
25+
container: "{\"image\": \"elopeztob/alpine-haskell-stack-echidna:ghc-9.6.5\", \"options\": \"--user 1001\"}"
2626
- os: macos-13 # x86_64 macOS
2727
shell: bash
2828
- os: windows-latest
@@ -65,7 +65,7 @@ jobs:
6565
id: stack
6666
if: matrix.container == ''
6767
with:
68-
ghc-version: '9.4'
68+
ghc-version: '9.6'
6969
enable-stack: true
7070
stack-version: 'latest'
7171

flake.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
pkgs = nixpkgs.legacyPackages.${system};
1919
# prefer musl on Linux, static glibc + threading does not work properly
2020
# TODO: maybe only override it for echidna-redistributable?
21-
pkgsStatic = if pkgs.stdenv.hostPlatform.isLinux then pkgs.pkgsMusl else pkgs;
21+
pkgsStatic = if pkgs.stdenv.hostPlatform.isLinux then pkgs.pkgsStatic else pkgs;
2222
# this is not perfect for development as it hardcodes solc to 0.5.7, test suite runs fine though
2323
# would be great to integrate solc-select to be more flexible, improve this in future
2424
solc = pkgs.stdenv.mkDerivation {
@@ -47,39 +47,61 @@
4747

4848
ncurses-static = pkgsStatic.ncurses.override { enableStatic = true; };
4949

50-
hevm = pkgs: pkgs.haskell.lib.dontCheck (
51-
pkgs.haskellPackages.callCabal2nix "hevm" (pkgs.fetchFromGitHub {
50+
hsPkgs = ps :
51+
ps.haskellPackages.override {
52+
overrides = hfinal: hprev: {
53+
with-utf8 =
54+
if (with ps.stdenv; hostPlatform.isDarwin && hostPlatform.isx86)
55+
then ps.haskell.lib.compose.overrideCabal (_ : { extraLibraries = [ps.libiconv]; }) hprev.with-utf8
56+
else hprev.with-utf8;
57+
};
58+
};
59+
60+
cc-workaround-nix-23138 =
61+
pkgs.writeScriptBin "cc-workaround-nix-23138" ''
62+
if [ "$1" = "--print-file-name" ] && [ "$2" = "c++" ]; then
63+
echo c++
64+
else
65+
exec cc "$@"
66+
fi
67+
'';
68+
69+
hevm = pkgs: pkgs.lib.pipe ((hsPkgs pkgs).callCabal2nix "hevm" (pkgs.fetchFromGitHub {
5270
owner = "trail-of-forks";
5371
repo = "hevm";
54-
rev = "7d4344c5e71d14466e86331af064bab61d06bdad";
55-
sha256 = "sha256-kts6mdwx5KUrVdNztzewWgNM9xGViAhFIZPnWOUllOU=";
56-
}) { secp256k1 = pkgs.secp256k1; });
72+
rev = "3aba82f06a2d1e0a4a4c26458f747a46dad0e7e2";
73+
sha256 = "sha256-NXXhEqHTQEL2N9RhXa1eczIsQtIM3mvPfyWXlBXpxK4=";
74+
}) { secp256k1 = pkgs.secp256k1; })
75+
([
76+
pkgs.haskell.lib.compose.dontCheck
77+
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
78+
(pkgs.haskell.lib.compose.appendConfigureFlag "--ghc-options=-pgml=${cc-workaround-nix-23138}/bin/cc-workaround-nix-23138")
79+
]);
5780

58-
# FIXME: figure out solc situation, it conflicts with the one from
59-
# solc-select that is installed with slither, disable tests in the meantime
60-
echidna = pkgs: pkgs.haskell.lib.dontCheck (
61-
with pkgs; lib.pipe
62-
(haskellPackages.callCabal2nix "echidna" ./. { hevm = hevm pkgs; })
63-
[
81+
echidna = pkgs: with pkgs; lib.pipe
82+
((hsPkgs pkgs).callCabal2nix "echidna" ./. { hevm = hevm pkgs; })
83+
([
84+
# FIXME: figure out solc situation, it conflicts with the one from
85+
# solc-select that is installed with slither, disable tests in the meantime
86+
haskell.lib.compose.dontCheck
6487
(haskell.lib.compose.addTestToolDepends [ haskellPackages.hpack slither-analyzer solc ])
6588
(haskell.lib.compose.disableCabalFlag "static")
89+
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
90+
(pkgs.haskell.lib.compose.appendConfigureFlag "--ghc-options=-pgml=${cc-workaround-nix-23138}/bin/cc-workaround-nix-23138")
6691
]);
6792

6893
echidna-static = with pkgsStatic; lib.pipe
6994
(echidna pkgsStatic)
7095
[
7196
(haskell.lib.compose.appendConfigureFlags
72-
([
97+
[
7398
"--extra-lib-dirs=${stripDylib (gmp.override { withStatic = true; })}/lib"
7499
"--extra-lib-dirs=${stripDylib secp256k1-static}/lib"
75100
"--extra-lib-dirs=${stripDylib (libff.override { enableStatic = true; })}/lib"
76-
"--extra-lib-dirs=${zlib.static}/lib"
101+
"--extra-lib-dirs=${zlib.override { static = true; shared = false; }}/lib"
77102
"--extra-lib-dirs=${stripDylib (libffi.overrideAttrs (_: { dontDisableStatic = true; }))}/lib"
78103
"--extra-lib-dirs=${stripDylib (ncurses-static)}/lib"
79-
] ++ (if stdenv.hostPlatform.isDarwin then [
80-
"--extra-lib-dirs=${stripDylib (libiconv.override { enableStatic = true; })}/lib"
81-
"--extra-lib-dirs=${stripDylib (libcxxabi)}/lib"
82-
] else [])))
104+
])
83105
(haskell.lib.compose.enableCabalFlag "static")
84106
];
85107

@@ -108,10 +130,14 @@
108130
# get the list of dynamic libs from otool and tidy the output
109131
libs=$(${otool} -L $out/bin/echidna | tail -n +2 | sed 's/^[[:space:]]*//' | cut -d' ' -f1)
110132
# get the path for libcxx
111-
cxx=$(echo "$libs" | ${grep} '^/nix/store/.*-libcxx-')
133+
cxx=$(echo "$libs" | ${grep} '^/nix/store/.*/libc++\.')
134+
cxxabi=$(echo "$libs" | ${grep} '^/nix/store/.*/libc++abi\.')
135+
iconv=$(echo "$libs" | ${grep} '^/nix/store/.*/libiconv\.')
112136
# rewrite /nix/... library paths to point to /usr/lib
113137
chmod 777 $out/bin/echidna
114138
${install_name_tool} -change "$cxx" /usr/lib/libc++.1.dylib $out/bin/echidna
139+
${install_name_tool} -change "$cxxabi" /usr/lib/libc++abi.dylib $out/bin/echidna
140+
${install_name_tool} -change "$iconv" /usr/lib/libiconv.dylib $out/bin/echidna
115141
# fix TERMINFO path in ncurses
116142
${perl} -i -pe 's#(${ncurses-static}/share/terminfo)#"/usr/share/terminfo" . "\x0" x (length($1) - 19)#e' $out/bin/echidna
117143
# check that no nix deps remain
@@ -145,7 +171,11 @@
145171
devShell = with pkgs;
146172
haskellPackages.shellFor {
147173
packages = _: [ (echidna pkgs) ];
148-
shellHook = "hpack";
174+
shellHook = ''
175+
hpack
176+
'' + (if pkgs.stdenv.isDarwin then ''
177+
cabal configure --ghc-options=-pgml=${cc-workaround-nix-23138}/bin/cc-workaround-nix-23138
178+
'' else "");
149179
buildInputs = [
150180
solc
151181
slither-analyzer

lib/Echidna/UI.hs

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
11
{-# LANGUAGE CPP #-}
2-
{-# OPTIONS_GHC -Wno-unused-imports #-}
32

43
module Echidna.UI where
54

6-
#ifdef INTERACTIVE_UI
75
import Brick
86
import Brick.BChan
97
import Brick.Widgets.Dialog qualified as B
10-
import Data.Sequence ((|>))
11-
import Graphics.Vty (Config, Event(..), Key(..), Modifier(..), defaultConfig, inputMap, mkVty)
12-
import Graphics.Vty qualified as Vty
13-
import System.Posix
14-
import Echidna.UI.Widgets
15-
#endif
16-
178
import Control.Concurrent (killThread, threadDelay)
189
import Control.Exception (AsyncException)
1910
import Control.Monad
@@ -24,14 +15,20 @@ import Control.Monad.ST (RealWorld)
2415
import Data.ByteString.Lazy qualified as BS
2516
import Data.List.Split (chunksOf)
2617
import Data.Map (Map)
27-
import Data.Maybe (fromMaybe, isJust)
18+
import Data.Maybe (isJust)
19+
import Data.Sequence ((|>))
2820
import Data.Text (Text)
2921
import Data.Time
22+
import Graphics.Vty.Config (VtyUserConfig, defaultConfig, configInputMap)
23+
import Graphics.Vty.CrossPlatform (mkVty)
24+
import Graphics.Vty.Input.Events
25+
import Graphics.Vty qualified as Vty
26+
import System.Console.ANSI (hNowSupportsANSI)
27+
import System.Signal
3028
import UnliftIO
3129
( MonadUnliftIO, IORef, newIORef, readIORef, hFlush, stdout , writeIORef, timeout)
3230
import UnliftIO.Concurrent hiding (killThread, threadDelay)
3331

34-
import EVM.Solidity (SolcContract)
3532
import EVM.Types (Addr, Contract, VM, VMType(Concrete), W256)
3633

3734
import Echidna.ABI
@@ -43,11 +40,10 @@ import Echidna.Types.Campaign
4340
import Echidna.Types.Config
4441
import Echidna.Types.Corpus qualified as Corpus
4542
import Echidna.Types.Coverage (scoveragePoints)
46-
import Echidna.Types.Solidity (SolConf(..))
4743
import Echidna.Types.Test (EchidnaTest(..), didFail, isOptimizationTest)
4844
import Echidna.Types.Tx (Tx)
49-
import Echidna.Types.World (World)
5045
import Echidna.UI.Report
46+
import Echidna.UI.Widgets
5147
import Echidna.Utility (timePrefix, getTimestamp)
5248

5349
data UIEvent =
@@ -93,7 +89,6 @@ ui vm dict initialCorpus cliSelectedContract = do
9389
uncurry (spawnWorker env perWorkerTestLimit)
9490

9591
case effectiveMode of
96-
#ifdef INTERACTIVE_UI
9792
Interactive -> do
9893
-- Channel to push events to update UI
9994
uiChannel <- liftIO $ newBChan 1000
@@ -157,20 +152,17 @@ ui vm dict initialCorpus cliSelectedContract = do
157152
liftIO . putStrLn =<< ppCampaign vm states
158153

159154
pure states
160-
#else
161-
Interactive -> error "Interactive UI is not available"
162-
#endif
163155

164156
NonInteractive outputFormat -> do
165157
serverStopVar <- newEmptyMVar
166-
#ifdef INTERACTIVE_UI
167-
-- Handles ctrl-c, TODO: this doesn't work on Windows
158+
159+
-- Handles ctrl-c
168160
liftIO $ forM_ [sigINT, sigTERM] $ \sig ->
169-
let handler = Catch $ do
161+
let handler _ = do
170162
stopWorkers workers
171163
void $ tryPutMVar serverStopVar ()
172-
in installHandler sig handler Nothing
173-
#endif
164+
in installHandler sig handler
165+
174166
let forwardEvent ev = putStrLn =<< runReaderT (ppLogLine vm ev) env
175167
uiEventsForwarderStopVar <- spawnListener forwardEvent
176168

@@ -245,20 +237,19 @@ ui vm dict initialCorpus cliSelectedContract = do
245237
workerStates workers =
246238
forM workers $ \(_, stateRef) -> readIORef stateRef
247239

248-
#ifdef INTERACTIVE_UI
249240
-- | Order the workers to stop immediately
250241
stopWorkers :: MonadIO m => [(ThreadId, IORef WorkerState)] -> m ()
251242
stopWorkers workers =
252243
forM_ workers $ \(threadId, workerStateRef) -> do
253244
workerState <- readIORef workerStateRef
254245
liftIO $ mapM_ killThread (threadId : workerState.runningThreads)
255246

256-
vtyConfig :: IO Config
247+
vtyConfig :: IO VtyUserConfig
257248
vtyConfig = do
258-
config <- Vty.standardIOConfig
259-
pure config { inputMap = (Nothing, "\ESC[6;2~", EvKey KPageDown [MShift]) :
260-
(Nothing, "\ESC[5;2~", EvKey KPageUp [MShift]) :
261-
inputMap defaultConfig }
249+
pure defaultConfig { configInputMap = [
250+
(Nothing, "\ESC[6;2~", EvKey KPageDown [MShift]),
251+
(Nothing, "\ESC[5;2~", EvKey KPageUp [MShift])
252+
] }
262253

263254
-- | Check if we should stop drawing (or updating) the dashboard, then do the right thing.
264255
monitor :: MonadReader Env m => m (App UIState UIEvent Name)
@@ -336,16 +327,10 @@ monitor = do
336327
, appAttrMap = const attrs
337328
, appChooseCursor = neverShowCursor
338329
}
339-
#endif
340330

341331
-- | Heuristic check that we're in a sensible terminal (not a pipe)
342332
isTerminal :: IO Bool
343-
isTerminal =
344-
#ifdef INTERACTIVE_UI
345-
(&&) <$> queryTerminal (Fd 0) <*> queryTerminal (Fd 1)
346-
#else
347-
pure False
348-
#endif
333+
isTerminal = hNowSupportsANSI stdout
349334

350335
-- | Composes a compact text status line of the campaign
351336
statusLine

lib/Echidna/UI/Widgets.hs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
module Echidna.UI.Widgets where
44

5-
#ifdef INTERACTIVE_UI
6-
75
import Brick hiding (style)
86
import Brick.AttrMap qualified as A
97
import Brick.Widgets.Border
@@ -147,14 +145,14 @@ logPane uiState =
147145

148146
showLogLine :: (LocalTime, CampaignEvent) -> Widget Name
149147
showLogLine (time, event@(WorkerEvent workerId workerType _)) =
150-
(withAttr (attrName "time") $ str $ (timePrefix time) <> "[Worker " <> show workerId <> symSuffix <> "] ")
148+
withAttr (attrName "time") (str $ timePrefix time <> "[Worker " <> show workerId <> symSuffix <> "] ")
151149
<+> strBreak (ppCampaignEvent event)
152150
where
153151
symSuffix = case workerType of
154152
SymbolicWorker -> ", symbolic"
155153
_ -> ""
156154
showLogLine (time, event) =
157-
(withAttr (attrName "time") $ str $ (timePrefix time) <> " ") <+> strBreak (ppCampaignEvent event)
155+
withAttr (attrName "time") (str $ timePrefix time <> " ") <+> strBreak (ppCampaignEvent event)
158156

159157
summaryWidget :: Env -> UIState -> Widget Name
160158
summaryWidget env uiState =
@@ -187,7 +185,7 @@ summaryWidget env uiState =
187185
<=>
188186
str ("New coverage: " <> timeElapsed uiState uiState.lastNewCov <> " ago") <+> fill ' '
189187
rightSide =
190-
padLeft (Pad 1) $
188+
padLeft (Pad 1)
191189
(rpcInfoWidget uiState.fetchedContracts uiState.fetchedSlots env.chainId)
192190

193191
timeElapsed :: UIState -> LocalTime -> String
@@ -324,7 +322,7 @@ tracesWidget vm = do
324322
let traces = stripAnsiEscapeCodes $ showTraceTree dappInfo vm
325323
pure $
326324
if T.null traces then str ""
327-
else str "Traces" <+> str ":" <=> (txtBreak traces)
325+
else str "Traces" <+> str ":" <=> txtBreak traces
328326

329327
failWidget
330328
:: MonadReader Env m
@@ -343,7 +341,6 @@ failWidget b test = do
343341
( failureBadge <+> str (" with " ++ show test.result)
344342
, shrinkWidget b test <=> titleWidget <=> s <=> str " " <=> traces
345343
)
346-
where
347344

348345
optWidget
349346
:: MonadReader Env m
@@ -406,5 +403,3 @@ strBreak = strWrapWith $ defaultWrapSettings { breakLongWords = True }
406403

407404
txtBreak :: Text -> Widget n
408405
txtBreak = txtWrapWith $ defaultWrapSettings { breakLongWords = True }
409-
410-
#endif

0 commit comments

Comments
 (0)