2022-07-14 12:49:19 +00:00
{ lib , stdenv
, fetchurl , perl , gcc
, ncurses5
, ncurses6 , gmp , libiconv , numactl , libffi
, llvmPackages
, coreutils
, targetPackages
# minimal = true; will remove files that aren't strictly necessary for
# regular builds and GHC bootstrapping.
# This is "useful" for staying within hydra's output limits for at least the
# aarch64-linux architecture.
, minimal ? false
} :
# Prebuilt only does native
assert stdenv . targetPlatform == stdenv . hostPlatform ;
let
downloadsUrl = " h t t p s : / / d o w n l o a d s . h a s k e l l . o r g / g h c " ;
2022-09-30 11:47:45 +00:00
# Copy sha256 from https://downloads.haskell.org/~ghc/9.2.4/SHA256SUMS
version = " 9 . 2 . 4 " ;
2022-07-14 12:49:19 +00:00
# Information about available bindists that we use in the build.
#
# # Bindist library checking
#
# The field `archSpecificLibraries` also provides a way for us get notified
# early when the upstream bindist changes its dependencies (e.g. because a
# newer Debian version is used that uses a new `ncurses` version).
#
# Usage:
#
# * You can find the `fileToCheckFor` of libraries by running `readelf -d`
# on the compiler binary (`exePathForLibraryCheck`).
# * To skip library checking for an architecture,
# set `exePathForLibraryCheck = null`.
# * To skip file checking for a specific arch specfic library,
# set `fileToCheckFor = null`.
ghcBinDists = {
# Binary distributions for the default libc (e.g. glibc, or libSystem on Darwin)
# nixpkgs uses for the respective system.
defaultLibc = {
i686-linux = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - i 3 8 6 - d e b 9 - l i n u x . t a r . x z " ;
2022-09-30 11:47:45 +00:00
sha256 = " 5 d c 1 e b 9 c 6 5 f 0 1 b 1 e 5 c 5 6 9 3 a f 7 2 a f 0 7 a 4 e 9 e 7 5 c 6 9 2 0 e 6 2 0 f d 5 9 8 d a e e f a 8 0 4 4 8 7 a " ;
2022-07-14 12:49:19 +00:00
} ;
exePathForLibraryCheck = " g h c / s t a g e 2 / b u i l d / t m p / g h c - s t a g e 2 " ;
archSpecificLibraries = [
{ nixPackage = gmp ; fileToCheckFor = null ; }
# The i686-linux bindist provided by GHC HQ is currently built on Debian 9,
# which link it against `libtinfo.so.5` (ncurses 5).
# Other bindists are linked `libtinfo.so.6` (ncurses 6).
{ nixPackage = ncurses5 ; fileToCheckFor = " l i b t i n f o . s o . 5 " ; }
] ;
} ;
x86_64-linux = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - x 8 6 _ 6 4 - d e b 1 0 - l i n u x . t a r . x z " ;
2022-09-30 11:47:45 +00:00
sha256 = " a 7 7 a 9 1 a 3 9 d 9 b 0 1 6 7 1 2 4 b 7 e 9 7 6 4 8 b 2 b 5 2 9 7 3 a e 0 9 7 8 c b 2 5 9 e 0 d 4 4 f 0 7 5 2 a 7 5 0 3 7 c b " ;
2022-07-14 12:49:19 +00:00
} ;
exePathForLibraryCheck = " g h c / s t a g e 2 / b u i l d / t m p / g h c - s t a g e 2 " ;
archSpecificLibraries = [
{ nixPackage = gmp ; fileToCheckFor = null ; }
{ nixPackage = ncurses6 ; fileToCheckFor = " l i b t i n f o . s o . 6 " ; }
] ;
} ;
aarch64-linux = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - a a r c h 6 4 - d e b 1 0 - l i n u x . t a r . x z " ;
2022-09-30 11:47:45 +00:00
sha256 = " f c 7 d b c 6 b a e 3 6 e a 5 a c 3 0 b 7 e 9 a 2 6 3 b 7 e 5 b e 3 b 4 5 b 0 e b 3 e 8 9 3 a d 0 b c 2 c 9 5 0 a 6 1 f 1 4 e c " ;
2022-07-14 12:49:19 +00:00
} ;
exePathForLibraryCheck = " g h c / s t a g e 2 / b u i l d / t m p / g h c - s t a g e 2 " ;
archSpecificLibraries = [
{ nixPackage = gmp ; fileToCheckFor = null ; }
{ nixPackage = ncurses6 ; fileToCheckFor = " l i b t i n f o . s o . 6 " ; }
{ nixPackage = numactl ; fileToCheckFor = null ; }
] ;
} ;
x86_64-darwin = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - x 8 6 _ 6 4 - a p p l e - d a r w i n . t a r . x z " ;
2022-09-30 11:47:45 +00:00
sha256 = " f 2 e 8 3 6 6 f d 3 7 5 4 d d 9 3 8 8 5 1 0 7 9 2 a b a 2 d 2 a b e c b 1 c 2 f 7 f 1 e 5 5 5 5 f 6 0 6 5 c 3 c 5 e 2 f f e c 4 " ;
2022-07-14 12:49:19 +00:00
} ;
exePathForLibraryCheck = null ; # we don't have a library check for darwin yet
archSpecificLibraries = [
{ nixPackage = gmp ; fileToCheckFor = null ; }
{ nixPackage = ncurses6 ; fileToCheckFor = null ; }
{ nixPackage = libiconv ; fileToCheckFor = null ; }
] ;
2023-02-16 17:41:37 +00:00
isHadrian = true ;
2022-07-14 12:49:19 +00:00
} ;
aarch64-darwin = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - a a r c h 6 4 - a p p l e - d a r w i n . t a r . x z " ;
2022-09-30 11:47:45 +00:00
sha256 = " 8 c f 8 4 0 8 5 4 4 a 1 a 4 3 a d f 1 b b b b 0 d d 6 b 0 7 4 e f a d f f c 6 8 b f a 1 a 7 9 2 9 4 7 c 5 2 e 8 2 5 1 7 1 2 2 4 " ;
2022-07-14 12:49:19 +00:00
} ;
exePathForLibraryCheck = null ; # we don't have a library check for darwin yet
archSpecificLibraries = [
{ nixPackage = gmp ; fileToCheckFor = null ; }
{ nixPackage = ncurses6 ; fileToCheckFor = null ; }
{ nixPackage = libiconv ; fileToCheckFor = null ; }
] ;
2023-02-16 17:41:37 +00:00
isHadrian = true ;
2022-07-14 12:49:19 +00:00
} ;
} ;
# Binary distributions for the musl libc for the respective system.
musl = {
x86_64-linux = {
variantSuffix = " - m u s l " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - x 8 6 _ 6 4 - a l p i n e 3 . 1 2 - l i n u x - g m p . t a r . x z " ;
2022-09-30 11:47:45 +00:00
sha256 = " 0 2 6 3 4 8 9 4 7 d 3 0 a 1 5 6 b 8 4 d e 5 d 6 a f e a a 4 8 f d c b 2 7 9 5 b 4 7 9 5 4 c d 8 3 4 1 d b 0 0 d 3 2 6 3 a 4 8 1 " ;
2022-07-14 12:49:19 +00:00
} ;
isStatic = true ;
2023-02-16 17:41:37 +00:00
isHadrian = true ;
2022-07-14 12:49:19 +00:00
# We can't check the RPATH for statically linked executable
exePathForLibraryCheck = null ;
archSpecificLibraries = [
{ nixPackage = gmp . override { withStatic = true ; } ; fileToCheckFor = null ; }
] ;
} ;
} ;
} ;
distSetName = if stdenv . hostPlatform . isMusl then " m u s l " else " d e f a u l t L i b c " ;
binDistUsed = ghcBinDists . ${ distSetName } . ${ stdenv . hostPlatform . system }
or ( throw " c a n n o t b o o t s t r a p G H C o n t h i s p l a t f o r m ( ' ${ stdenv . hostPlatform . system } ' w i t h l i b c ' ${ distSetName } ' ) " ) ;
gmpUsed = ( builtins . head (
builtins . filter (
drv : lib . hasPrefix " g m p " ( drv . nixPackage . name or " " )
) binDistUsed . archSpecificLibraries
) ) . nixPackage ;
# GHC has other native backends (like PowerPC), but here only the ones
# we ship bindists for matter.
useLLVM = ! ( stdenv . targetPlatform . isx86
|| ( stdenv . targetPlatform . isAarch64 && stdenv . targetPlatform . isDarwin ) ) ;
libPath =
lib . makeLibraryPath (
# Add arch-specific libraries.
map ( { nixPackage , . . . }: nixPackage ) binDistUsed . archSpecificLibraries
) ;
libEnvVar = lib . optionalString stdenv . hostPlatform . isDarwin " D Y "
+ " L D _ L I B R A R Y _ P A T H " ;
runtimeDeps = [
targetPackages . stdenv . cc
targetPackages . stdenv . cc . bintools
coreutils # for cat
]
++ lib . optionals useLLVM [
( lib . getBin llvmPackages . llvm )
]
# On darwin, we need unwrapped bintools as well (for otool)
++ lib . optionals ( stdenv . targetPlatform . linker == " c c t o o l s " ) [
targetPackages . stdenv . cc . bintools . bintools
] ;
in
stdenv . mkDerivation rec {
inherit version ;
pname = " g h c - b i n a r y ${ binDistUsed . variantSuffix } " ;
src = fetchurl binDistUsed . src ;
nativeBuildInputs = [ perl ] ;
# Set LD_LIBRARY_PATH or equivalent so that the programs running as part
# of the bindist installer can find the libraries they expect.
# Cannot patchelf beforehand due to relative RPATHs that anticipate
# the final install location.
$ { libEnvVar } = libPath ;
postUnpack =
# Verify our assumptions of which `libtinfo.so` (ncurses) version is used,
# so that we know when ghc bindists upgrade that and we need to update the
# version used in `libPath`.
lib . optionalString
( binDistUsed . exePathForLibraryCheck != null )
# Note the `*` glob because some GHCs have a suffix when unpacked, e.g.
# the musl bindist has dir `ghc-VERSION-x86_64-unknown-linux/`.
# As a result, don't shell-quote this glob when splicing the string.
( let buildExeGlob = '' g h c - ${ version } * / " ${ binDistUsed . exePathForLibraryCheck } " '' ; in
lib . concatStringsSep " \n " [
( ''
echo " C h e c k i n g t h a t g h c b i n a r y e x i s t s i n b i n d i s t a t ${ buildExeGlob } "
if ! test - e $ { buildExeGlob } ; then
echo > & 2 " G H C b i n a r y ${ binDistUsed . exePathForLibraryCheck } c o u l d n o t b e f o u n d i n t h e b i n d i s t b u i l d d i r e c t o r y ( a t ${ buildExeGlob } ) f o r a r c h ${ stdenv . hostPlatform . system } , p l e a s e c h e c k t h a t g h c B i n D i s t s c o r r e c t l y r e f l e c t t h e b i n d i s t d e p e n d e n c i e s ! " ; exit 1 ;
fi
'' )
( lib . concatMapStringsSep
" \n "
( { fileToCheckFor , nixPackage }:
lib . optionalString ( fileToCheckFor != null ) ''
echo " C h e c k i n g b i n d i s t f o r ${ fileToCheckFor } t o e n s u r e t h a t i s s t i l l u s e d "
if ! readelf - d $ { buildExeGlob } | grep " ${ fileToCheckFor } " ; then
echo > & 2 " F i l e ${ fileToCheckFor } c o u l d n o t b e f o u n d i n ${ binDistUsed . exePathForLibraryCheck } f o r a r c h ${ stdenv . hostPlatform . system } , p l e a s e c h e c k t h a t g h c B i n D i s t s c o r r e c t l y r e f l e c t t h e b i n d i s t d e p e n d e n c i e s ! " ; exit 1 ;
fi
echo " C h e c k i n g t h a t t h e n i x p a c k a g e ${ nixPackage } c o n t a i n s ${ fileToCheckFor } "
if ! test - e " ${ lib . getLib nixPackage } / l i b / ${ fileToCheckFor } " ; then
echo > & 2 " N i x p a c k a g e ${ nixPackage } d i d n o t c o n t a i n ${ fileToCheckFor } f o r a r c h ${ stdenv . hostPlatform . system } , p l e a s e c h e c k t h a t g h c B i n D i s t s c o r r e c t l y r e f l e c t t h e b i n d i s t d e p e n d e n c i e s ! " ; exit 1 ;
fi
''
)
binDistUsed . archSpecificLibraries
)
] )
# GHC has dtrace probes, which causes ld to try to open /usr/lib/libdtrace.dylib
# during linking
+ lib . optionalString stdenv . isDarwin ''
export NIX_LDFLAGS + = " - n o _ d t r a c e _ d o f "
# not enough room in the object files for the full path to libiconv :(
for exe in $ ( find . - type f - executable ) ; do
isScript $ exe && continue
ln - fs $ { libiconv } /lib/libiconv.dylib $ ( dirname $ exe ) /libiconv.dylib
install_name_tool - change /usr/lib/libiconv.2.dylib @ executable_path/libiconv.dylib - change /usr/local/lib/gcc/6/libgcc_s.1.dylib $ { gcc . cc . lib } /lib/libgcc_s.1.dylib $ exe
done
'' +
# Some scripts used during the build need to have their shebangs patched
''
patchShebangs ghc- $ { version } /utils /
patchShebangs ghc- $ { version } /configure
'' +
# We have to patch the GMP paths for the integer-gmp package.
''
find . - name ghc-bignum . buildinfo \
- exec sed - i " s @ e x t r a - l i b - d i r s : @ e x t r a - l i b - d i r s : ${ lib . getLib gmpUsed } / l i b @ " { } \ ;
# we need to modify the package db directly for hadrian bindists
find . - name ' ghc-bignum * . conf' \
- exec sed - e ' / ^ [ a-z- ] * library-dirs/a \ $ { lib . getLib gmpUsed } /lib ' - i { } \ ;
'' + l i b . o p t i o n a l S t r i n g s t d e n v . i s D a r w i n ''
# we need to modify the package db directly for hadrian bindists
# (all darwin bindists are hadrian-based for 9.2.2)
find . - name ' base * . conf' \
- exec sed - e ' / ^ [ a-z- ] * library-dirs/a \ $ { lib . getLib libiconv } /lib ' - i { } \ ;
# To link RTS in the end we also need libffi now
find . - name ' rts * . conf' \
- exec sed - e ' / ^ [ a-z- ] * library-dirs/a \ $ { lib . getLib libffi } /lib ' \
- e ' s @ /Library/Developer/. * /usr/include/ffi @ $ { lib . getDev libffi } /include @ ' \
- i { } \ ;
'' +
# aarch64 does HAVE_NUMA so -lnuma requires it in library-dirs in rts/package.conf.in
# FFI_LIB_DIR is a good indication of places it must be needed.
lib . optionalString ( stdenv . hostPlatform . isLinux && stdenv . hostPlatform . isAarch64 ) ''
find . - name package . conf . in \
- exec sed - i " s @ F F I _ L I B _ D I R @ F F I _ L I B _ D I R ${ numactl . out } / l i b @ g " { } \ ;
'' +
# Rename needed libraries and binaries, fix interpreter
lib . optionalString stdenv . isLinux ''
find . - type f - executable - exec patchelf \
- - interpreter $ { stdenv . cc . bintools . dynamicLinker } { } \ ;
'' ;
# fix for `configure: error: Your linker is affected by binutils #16177`
preConfigure = lib . optionalString
stdenv . targetPlatform . isAarch32
" L D = l d . g o l d " ;
configurePlatforms = [ ] ;
configureFlags = [
" - - w i t h - g m p - i n c l u d e s = ${ lib . getDev gmpUsed } / i n c l u d e "
# Note `--with-gmp-libraries` does nothing for GHC bindists:
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6124
] ++ lib . optional stdenv . isDarwin " - - w i t h - g c c = ${ ./gcc-clang-wrapper.sh } "
# From: https://github.com/NixOS/nixpkgs/pull/43369/commits
++ lib . optional stdenv . hostPlatform . isMusl " - - d i s a b l e - l d - o v e r r i d e " ;
# No building is necessary, but calling make without flags ironically
# calls install-strip ...
dontBuild = true ;
# Patch scripts to include runtime dependencies in $PATH.
postInstall = ''
for i in " $ o u t / b i n / " * ; do
test ! - h " $ i " || continue
isScript " $ i " || continue
sed - i - e ' 2 i export PATH = " ${ lib . makeBinPath runtimeDeps } : $ P A T H " ' " $ i "
done
'' ;
# Apparently necessary for the ghc Alpine (musl) bindist:
# When we strip, and then run the
# patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
# below, running ghc (e.g. during `installCheckPhase)` gives some apparently
# corrupted rpath or whatever makes the loader work on nonsensical strings:
# running install tests
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: : symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: ir6zf6c9f86pfx8sr30n2vjy-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: y/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20> : symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20> ?: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# This is extremely bogus and should be investigated.
dontStrip = if stdenv . hostPlatform . isMusl then true else false ; # `if` for explicitness
# On Linux, use patchelf to modify the executables so that they can
# find editline/gmp.
postFixup = lib . optionalString ( stdenv . isLinux && ! ( binDistUsed . isStatic or false ) )
( if stdenv . hostPlatform . isAarch64 then
# Keep rpath as small as possible on aarch64 for patchelf#244. All Elfs
# are 2 directories deep from $out/lib, so pooling symlinks there makes
# a short rpath.
''
( cd $ out/lib ; ln - s $ { ncurses6 . out } /lib/libtinfo.so.6 )
( cd $ out/lib ; ln - s $ { lib . getLib gmpUsed } /lib/libgmp.so.10 )
( cd $ out/lib ; ln - s $ { numactl . out } /lib/libnuma.so.1 )
for p in $ ( find " $ o u t / l i b " - type f - name " * \. s o * " ) ; do
( cd $ out/lib ; ln - s $ p )
done
for p in $ ( find " $ o u t / l i b " - type f - executable ) ; do
if isELF " $ p " ; then
echo " P a t c h e l f i n g $ p "
patchelf - - set-rpath " \$ O R I G I N : \$ O R I G I N / . . / . . " $ p
fi
done
''
else
''
for p in $ ( find " $ o u t " - type f - executable ) ; do
if isELF " $ p " ; then
echo " P a t c h e l f i n g $ p "
patchelf - - set-rpath " ${ libPath } : $ ( p a t c h e l f - - p r i n t - r p a t h $ p ) " $ p
fi
done
'' ) + l i b . o p t i o n a l S t r i n g s t d e n v . i s D a r w i n ''
# not enough room in the object files for the full path to libiconv :(
for exe in $ ( find " $ o u t " - type f - executable ) ; do
isScript $ exe && continue
ln - fs $ { libiconv } /lib/libiconv.dylib $ ( dirname $ exe ) /libiconv.dylib
install_name_tool - change /usr/lib/libiconv.2.dylib @ executable_path/libiconv.dylib - change /usr/local/lib/gcc/6/libgcc_s.1.dylib $ { gcc . cc . lib } /lib/libgcc_s.1.dylib $ exe
done
for file in $ ( find " $ o u t " - name setup-config ) ; do
substituteInPlace $ file - - replace /usr/bin/ranlib " $ ( t y p e - P r a n l i b ) "
done
'' +
lib . optionalString minimal ''
# Remove profiling files
find $ out - type f - name ' * . p_o' - delete
find $ out - type f - name ' * . p_hi' - delete
find $ out - type f - name ' * _p . a' - delete
# `-f` because e.g. musl bindist does not have this file.
rm - f $ out/lib/ghc- * /bin/ghc-iserv-prof
# Hydra will redistribute this derivation, so we have to keep the docs for
# legal reasons (retaining the legal notices etc)
# As a last resort we could unpack the docs separately and symlink them in.
# They're in $out/share/{doc,man}.
''
# Recache package db which needs to happen for Hadrian bindists
# where we modify the package db before installing
+ ''
" $ o u t / b i n / g h c - p k g " - - package-db = " $ o u t / l i b / " ghc- * /package.conf.d recache
'' ;
# In nixpkgs, musl based builds currently enable `pie` hardening by default
# (see `defaultHardeningFlags` in `make-derivation.nix`).
# But GHC cannot currently produce outputs that are ready for `-pie` linking.
# Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
# See:
# * https://github.com/NixOS/nixpkgs/issues/129247
# * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
hardeningDisable = lib . optional stdenv . targetPlatform . isMusl " p i e " ;
doInstallCheck = true ;
installCheckPhase = ''
# Sanity check, can ghc create executables?
cd $ TMP
mkdir test-ghc ; cd test-ghc
cat > main . hs < < EOF
{ - # LANGUAGE TemplateHaskell #-}
module Main where
main = putStrLn \ $ ( [ | " y e s " | ] )
EOF
env - i $ out/bin/ghc - - make main . hs || exit 1
echo compilation ok
[ $ ( ./main ) == " y e s " ]
'' ;
passthru = {
targetPrefix = " " ;
enableShared = true ;
inherit llvmPackages ;
# Our Cabal compiler name
haskellCompilerName = " g h c - ${ version } " ;
2023-02-16 17:41:37 +00:00
} // lib . optionalAttrs ( binDistUsed . isHadrian or false ) {
# Normal GHC derivations expose the hadrian derivation used to build them
# here. In the case of bindists we just make sure that the attribute exists,
# as it is used for checking if a GHC derivation has been built with hadrian.
# The isHadrian mechanism will become obsolete with GHCs that use hadrian
# exclusively, i.e. 9.6 (and 9.4?).
hadrian = null ;
2022-07-14 12:49:19 +00:00
} ;
meta = rec {
homepage = " h t t p : / / h a s k e l l . o r g / g h c " ;
description = " T h e G l a s g o w H a s k e l l C o m p i l e r " ;
license = lib . licenses . bsd3 ;
# HACK: since we can't encode the libc / abi in platforms, we need
# to make the platform list dependent on the evaluation platform
# in order to avoid eval errors with musl which supports less
# platforms than the default libcs (i. e. glibc / libSystem).
# This is done for the benefit of Hydra, so `packagePlatforms`
# won't return any platforms that would cause an evaluation
# failure for `pkgsMusl.haskell.compiler.ghc922Binary`, as
# long as the evaluator runs on a platform that supports
# `pkgsMusl`.
platforms = builtins . attrNames ghcBinDists . ${ distSetName } ;
hydraPlatforms = builtins . filter ( p : minimal || p != " a a r c h 6 4 - l i n u x " ) platforms ;
maintainers = lib . teams . haskell . members ;
} ;
}