From c9f02b27d4916f2317e7506e075ef94fb77ad405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20Hamb=C3=BCchen?= Date: Thu, 19 Jul 2018 00:49:00 +0200 Subject: [PATCH 1/2] Add `--enable-executable-static` flag. Fixes #391. Also update the docs for `--enable-executable-dynamic` as they were slightly misleading. --- Cabal/Distribution/Simple/Configure.hs | 8 ++++++++ Cabal/Distribution/Simple/GHC.hs | 6 ++++-- Cabal/Distribution/Simple/Setup.hs | 9 +++++++++ Cabal/Distribution/Types/LocalBuildInfo.hs | 1 + Cabal/doc/installing-packages.rst | 20 ++++++++++++++++--- Cabal/doc/nix-local-build.rst | 13 ++++++++++++ cabal-install/Distribution/Client/Config.hs | 1 + .../Distribution/Client/PackageHash.hs | 2 ++ .../Client/ProjectConfig/Legacy.hs | 3 +++ .../Client/ProjectConfig/Types.hs | 1 + .../Distribution/Client/ProjectPlanning.hs | 3 +++ .../Client/ProjectPlanning/Types.hs | 1 + cabal-install/Distribution/Client/Setup.hs | 2 ++ .../Distribution/Client/ProjectConfig.hs | 7 +++++-- 14 files changed, 70 insertions(+), 7 deletions(-) diff --git a/Cabal/Distribution/Simple/Configure.hs b/Cabal/Distribution/Simple/Configure.hs index 732753d8013..328dc2ae095 100644 --- a/Cabal/Distribution/Simple/Configure.hs +++ b/Cabal/Distribution/Simple/Configure.hs @@ -426,6 +426,11 @@ configure (pkg_descr0, pbi) cfg = do die' verbosity $ "--enable-tests/--enable-benchmarks are incompatible with" ++ " explicitly specifying a component to configure." + -- Some sanity checks related to dynamic/static linking. + when (fromFlag (configDynExe cfg) && fromFlag (configFullyStaticExe cfg)) $ + die' verbosity $ "--enable-executable-dynamic and --enable-executable-static" ++ + " are incompatible with each other." + -- allConstraints: The set of all 'Dependency's we have. Used ONLY -- to 'configureFinalizedPackage'. -- requiredDepsMap: A map from 'PackageName' to the specifically @@ -684,6 +689,8 @@ configure (pkg_descr0, pbi) cfg = do fromFlagOrDefault False $ configStaticLib cfg withDynExe_ = fromFlag $ configDynExe cfg + + withFullyStaticExe_ = fromFlag $ configFullyStaticExe cfg when (withDynExe_ && not withSharedLib_) $ warn verbosity $ "Executables will use dynamic linking, but a shared library " ++ "is not being built. Linking will fail if any executables " @@ -723,6 +730,7 @@ configure (pkg_descr0, pbi) cfg = do withSharedLib = withSharedLib_, withStaticLib = withStaticLib_, withDynExe = withDynExe_, + withFullyStaticExe = withFullyStaticExe_, withProfLib = False, withProfLibDetail = ProfDetailNone, withProfExe = False, diff --git a/Cabal/Distribution/Simple/GHC.hs b/Cabal/Distribution/Simple/GHC.hs index 721e65c7631..c8ee838bb17 100644 --- a/Cabal/Distribution/Simple/GHC.hs +++ b/Cabal/Distribution/Simple/GHC.hs @@ -572,7 +572,8 @@ buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do ghcOptHPCDir = hpcdir Hpc.Dyn } linkerOpts = mempty { - ghcOptLinkOptions = PD.ldOptions libBi, + ghcOptLinkOptions = PD.ldOptions libBi + ++ [ "-static" | withFullyStaticExe lbi ], ghcOptLinkLibs = extraLibs libBi, ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi, ghcOptLinkFrameworks = toNubListR $ PD.frameworks libBi, @@ -1254,7 +1255,8 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do ghcOptHPCDir = hpcdir Hpc.Dyn } linkerOpts = mempty { - ghcOptLinkOptions = PD.ldOptions bnfo, + ghcOptLinkOptions = PD.ldOptions bnfo + ++ [ "-static" | withFullyStaticExe lbi ], ghcOptLinkLibs = extraLibs bnfo, ghcOptLinkLibPath = toNubListR $ extraLibDirs bnfo, ghcOptLinkFrameworks = toNubListR $ diff --git a/Cabal/Distribution/Simple/Setup.hs b/Cabal/Distribution/Simple/Setup.hs index 9568322ff93..bc648395357 100644 --- a/Cabal/Distribution/Simple/Setup.hs +++ b/Cabal/Distribution/Simple/Setup.hs @@ -218,6 +218,8 @@ data ConfigFlags = ConfigFlags { configStaticLib :: Flag Bool, -- ^Build static library configDynExe :: Flag Bool, -- ^Enable dynamic linking of the -- executables. + configFullyStaticExe :: Flag Bool, -- ^Enable fully static linking of the + -- executables. configProfExe :: Flag Bool, -- ^Enable profiling in the -- executables. configProf :: Flag Bool, -- ^Enable profiling in the library @@ -300,6 +302,7 @@ instance Eq ConfigFlags where && equal configSharedLib && equal configStaticLib && equal configDynExe + && equal configFullyStaticExe && equal configProfExe && equal configProf && equal configProfDetail @@ -354,6 +357,7 @@ defaultConfigFlags progDb = emptyConfigFlags { configSharedLib = NoFlag, configStaticLib = NoFlag, configDynExe = Flag False, + configFullyStaticExe = Flag False, configProfExe = NoFlag, configProf = NoFlag, configProfDetail = NoFlag, @@ -492,6 +496,11 @@ configureOptions showOrParseArgs = configDynExe (\v flags -> flags { configDynExe = v }) (boolOpt [] []) + ,option "" ["executable-static"] + "Executable fully static linking" + configFullyStaticExe (\v flags -> flags { configFullyStaticExe = v }) + (boolOpt [] []) + ,option "" ["profiling"] "Executable and library profiling" configProf (\v flags -> flags { configProf = v }) diff --git a/Cabal/Distribution/Types/LocalBuildInfo.hs b/Cabal/Distribution/Types/LocalBuildInfo.hs index 2ca00f85f91..90bbf6dfef6 100644 --- a/Cabal/Distribution/Types/LocalBuildInfo.hs +++ b/Cabal/Distribution/Types/LocalBuildInfo.hs @@ -146,6 +146,7 @@ data LocalBuildInfo = LocalBuildInfo { withSharedLib :: Bool, -- ^Whether to build shared versions of libs. withStaticLib :: Bool, -- ^Whether to build static versions of libs (with all other libs rolled in) withDynExe :: Bool, -- ^Whether to link executables dynamically + withFullyStaticExe :: Bool, -- ^Whether to link executables fully statically withProfExe :: Bool, -- ^Whether to build executables for profiling. withProfLibDetail :: ProfDetailLevel, -- ^Level of automatic profile detail. withProfExeDetail :: ProfDetailLevel, -- ^Level of automatic profile detail. diff --git a/Cabal/doc/installing-packages.rst b/Cabal/doc/installing-packages.rst index d28aa7f45a2..2eb709e793f 100644 --- a/Cabal/doc/installing-packages.rst +++ b/Cabal/doc/installing-packages.rst @@ -1160,13 +1160,27 @@ Miscellaneous options .. option:: --enable-executable-dynamic - Link executables dynamically. The executable's library dependencies - should be built as shared objects. This implies :option:`--enable-shared` + Link dependent Haskell libraries into executables dynamically. + The executable's library dependencies must have been + built as shared objects. This implies :option:`--enable-shared` unless :option:`--disable-shared` is explicitly specified. .. option:: --disable-executable-dynamic - (default) Link executables statically. + (default) Link dependent Haskell libraries into executables statically. + Non-Haskell (C) libraries are still linked dynamically, including libc, + so the result is still not a fully static executable + unless :option:`--enable-executable-dynamic` is given. + +.. option:: --enable-executable-static + + Build fully static executables. + This link all dependent libraries into executables statically, + including libc. + +.. option:: --disable-executable-static + + (default) Do not build fully static executables. .. option:: --configure-option=str diff --git a/Cabal/doc/nix-local-build.rst b/Cabal/doc/nix-local-build.rst index d6c70c78f00..f75cd2965d9 100644 --- a/Cabal/doc/nix-local-build.rst +++ b/Cabal/doc/nix-local-build.rst @@ -1544,6 +1544,19 @@ Static linking options This uses GHCs ``-staticlib`` flag, which is available for iOS and with GHC 8.4 and later for other platforms as well. +.. cfg-field:: executable-static: boolean + --enable-executable-static + --disable-executable-static + :synopsis: Build fully static executables. + + + :default: False + + Build fully static executables. + This link all dependent libraries into executables statically, + including libc. + This passes ``-static`` and ``-optl=-static`` to GHC. + Foreign function interface options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/cabal-install/Distribution/Client/Config.hs b/cabal-install/Distribution/Client/Config.hs index 54b502ef1cc..2dc5356fef4 100644 --- a/cabal-install/Distribution/Client/Config.hs +++ b/cabal-install/Distribution/Client/Config.hs @@ -349,6 +349,7 @@ instance Semigroup SavedConfig where configSharedLib = combine configSharedLib, configStaticLib = combine configStaticLib, configDynExe = combine configDynExe, + configFullyStaticExe = combine configFullyStaticExe, configProfExe = combine configProfExe, configProfDetail = combine configProfDetail, configProfLibDetail = combine configProfLibDetail, diff --git a/cabal-install/Distribution/Client/PackageHash.hs b/cabal-install/Distribution/Client/PackageHash.hs index ebbe0b9b678..7606aea52b7 100644 --- a/cabal-install/Distribution/Client/PackageHash.hs +++ b/cabal-install/Distribution/Client/PackageHash.hs @@ -194,6 +194,7 @@ data PackageHashConfigInputs = PackageHashConfigInputs { pkgHashVanillaLib :: Bool, pkgHashSharedLib :: Bool, pkgHashDynExe :: Bool, + pkgHashFullyStaticExe :: Bool, pkgHashGHCiLib :: Bool, pkgHashProfLib :: Bool, pkgHashProfExe :: Bool, @@ -287,6 +288,7 @@ renderPackageHashInputs PackageHashInputs{ , opt "vanilla-lib" True display pkgHashVanillaLib , opt "shared-lib" False display pkgHashSharedLib , opt "dynamic-exe" False display pkgHashDynExe + , opt "fully-static-exe" False display pkgHashFullyStaticExe , opt "ghci-lib" False display pkgHashGHCiLib , opt "prof-lib" False display pkgHashProfLib , opt "prof-exe" False display pkgHashProfExe diff --git a/cabal-install/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/Distribution/Client/ProjectConfig/Legacy.hs index 2a373a5e8e8..f9eca3ca547 100644 --- a/cabal-install/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/Distribution/Client/ProjectConfig/Legacy.hs @@ -380,6 +380,7 @@ convertLegacyPerPackageFlags configFlags installFlags haddockFlags testFlags = configSharedLib = packageConfigSharedLib, configStaticLib = packageConfigStaticLib, configDynExe = packageConfigDynExe, + configFullyStaticExe = packageConfigFullyStaticExe, configProfExe = packageConfigProfExe, configProf = packageConfigProf, configProfDetail = packageConfigProfDetail, @@ -632,6 +633,7 @@ convertToLegacyAllPackageConfig configSharedLib = mempty, configStaticLib = mempty, configDynExe = mempty, + configFullyStaticExe = mempty, configProfExe = mempty, configProf = mempty, configProfDetail = mempty, @@ -701,6 +703,7 @@ convertToLegacyPerPackageConfig PackageConfig {..} = configSharedLib = packageConfigSharedLib, configStaticLib = packageConfigStaticLib, configDynExe = packageConfigDynExe, + configFullyStaticExe = packageConfigFullyStaticExe, configProfExe = packageConfigProfExe, configProf = packageConfigProf, configProfDetail = packageConfigProfDetail, diff --git a/cabal-install/Distribution/Client/ProjectConfig/Types.hs b/cabal-install/Distribution/Client/ProjectConfig/Types.hs index 3a17786d95b..aa218a826b3 100644 --- a/cabal-install/Distribution/Client/ProjectConfig/Types.hs +++ b/cabal-install/Distribution/Client/ProjectConfig/Types.hs @@ -242,6 +242,7 @@ data PackageConfig packageConfigSharedLib :: Flag Bool, packageConfigStaticLib :: Flag Bool, packageConfigDynExe :: Flag Bool, + packageConfigFullyStaticExe :: Flag Bool, packageConfigProf :: Flag Bool, --TODO: [code cleanup] sort out packageConfigProfLib :: Flag Bool, -- this duplication packageConfigProfExe :: Flag Bool, -- and consistency diff --git a/cabal-install/Distribution/Client/ProjectPlanning.hs b/cabal-install/Distribution/Client/ProjectPlanning.hs index 06fa83d8ddf..0eeb9feaff1 100644 --- a/cabal-install/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/Distribution/Client/ProjectPlanning.hs @@ -1819,6 +1819,7 @@ elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB elabSharedLib = pkgid `Set.member` pkgsUseSharedLibrary elabStaticLib = perPkgOptionFlag pkgid False packageConfigStaticLib elabDynExe = perPkgOptionFlag pkgid False packageConfigDynExe + elabFullyStaticExe = perPkgOptionFlag pkgid False packageConfigFullyStaticExe elabGHCiLib = perPkgOptionFlag pkgid False packageConfigGHCiLib --TODO: [required feature] needs to default to enabled on windows still elabProfExe = perPkgOptionFlag pkgid False packageConfigProf @@ -3272,6 +3273,7 @@ setupHsConfigureFlags (ReadyPackage elab@ElaboratedConfiguredPackage{..}) configStaticLib = toFlag elabStaticLib configDynExe = toFlag elabDynExe + configFullyStaticExe = toFlag elabFullyStaticExe configGHCiLib = toFlag elabGHCiLib configProfExe = mempty configProfLib = toFlag elabProfLib @@ -3625,6 +3627,7 @@ packageHashConfigInputs shared@ElaboratedSharedConfig{..} pkg = pkgHashVanillaLib = elabVanillaLib, pkgHashSharedLib = elabSharedLib, pkgHashDynExe = elabDynExe, + pkgHashFullyStaticExe = elabFullyStaticExe, pkgHashGHCiLib = elabGHCiLib, pkgHashProfLib = elabProfLib, pkgHashProfExe = elabProfExe, diff --git a/cabal-install/Distribution/Client/ProjectPlanning/Types.hs b/cabal-install/Distribution/Client/ProjectPlanning/Types.hs index 8d129c4ed50..d97899b0e28 100644 --- a/cabal-install/Distribution/Client/ProjectPlanning/Types.hs +++ b/cabal-install/Distribution/Client/ProjectPlanning/Types.hs @@ -248,6 +248,7 @@ data ElaboratedConfiguredPackage elabSharedLib :: Bool, elabStaticLib :: Bool, elabDynExe :: Bool, + elabFullyStaticExe :: Bool, elabGHCiLib :: Bool, elabProfLib :: Bool, elabProfExe :: Bool, diff --git a/cabal-install/Distribution/Client/Setup.hs b/cabal-install/Distribution/Client/Setup.hs index cf3b0a720f5..e3cb9c6b71f 100644 --- a/cabal-install/Distribution/Client/Setup.hs +++ b/cabal-install/Distribution/Client/Setup.hs @@ -560,6 +560,7 @@ filterConfigureFlags flags cabalLibVersion -- Cabal < 1.23 doesn't know about '--profiling-detail'. -- Cabal < 1.23 has a hacked up version of 'enable-profiling' -- which we shouldn't use. + -- Cabal < 1.23 doesn't know about '--enable/disable-executable-static'. (tryLibProfiling, tryExeProfiling) = computeEffectiveProfiling flags flags_1_23_0 = flags_1_25_0 { configProfDetail = NoFlag , configProfLibDetail = NoFlag @@ -567,6 +568,7 @@ filterConfigureFlags flags cabalLibVersion , configProf = NoFlag , configProfExe = Flag tryExeProfiling , configProfLib = Flag tryLibProfiling + , configFullyStaticExe = NoFlag } -- Cabal < 1.22 doesn't know about '--disable-debug-info'. diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs index 66ff8971a4f..0348637cbc3 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs @@ -544,6 +544,7 @@ instance Arbitrary PackageConfig where <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary + <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> shortListOf 5 arbitraryShortToken @@ -590,6 +591,7 @@ instance Arbitrary PackageConfig where , packageConfigSharedLib = x05 , packageConfigStaticLib = x42 , packageConfigDynExe = x06 + , packageConfigFullyStaticExe = x44 , packageConfigProf = x07 , packageConfigProfLib = x08 , packageConfigProfExe = x09 @@ -642,6 +644,7 @@ instance Arbitrary PackageConfig where , packageConfigSharedLib = x05' , packageConfigStaticLib = x42' , packageConfigDynExe = x06' + , packageConfigFullyStaticExe = x44' , packageConfigProf = x07' , packageConfigProfLib = x08' , packageConfigProfExe = x09' @@ -687,7 +690,7 @@ instance Arbitrary PackageConfig where , packageConfigTestFailWhenNoTestSuites = x48' , packageConfigTestTestOptions = x49' } | (((x00', x01', x02', x03', x04'), - (x05', x42', x06', x07', x08', x09'), + (x05', x42', x06', x44', x07', x08', x09'), (x10', x11', x12', x13', x14'), (x15', x16', x17', x18', x19')), ((x20', x20_1', x21', x22', x23', x24'), @@ -698,7 +701,7 @@ instance Arbitrary PackageConfig where (x44', x45', x46', x47', x48', x49'))) <- shrink (((preShrink_Paths x00, preShrink_Args x01, x02, x03, x04), - (x05, x42, x06, x07, x08, x09), + (x05, x42, x06, x44, x07, x08, x09), (x10, x11, map NonEmpty x12, x13, x14), (x15, map NonEmpty x16, map NonEmpty x17, From 2bff6ce3994cddccad5ba563138e73d401d635ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20Hamb=C3=BCchen?= Date: Thu, 19 Jul 2018 01:00:15 +0200 Subject: [PATCH 2/2] Changelog: Add entry for `--enable-executable-static` --- Cabal/ChangeLog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Cabal/ChangeLog.md b/Cabal/ChangeLog.md index 6cddff4af7e..d6897fb08cd 100644 --- a/Cabal/ChangeLog.md +++ b/Cabal/ChangeLog.md @@ -8,6 +8,15 @@ flavours to build and install from a .cabal file. * `autoconfUserHooks` now passes `--host=$HOST` when cross-compiling * Add a `LibraryVisibility` field to `InstalledPackageInfo` + * Static linking + * Add `--enable-executable-static` flag for building fully + static executables (GHC's normal "statish" linking links + Haskell libraries statically, but libc and system dependencies + dynamically). This new flag links everything statically. + * Note you likely want to link against `musl` or another libc that + supports fully static linking; + [`glibc` has some issues](https://sourceware.org/glibc/wiki/FAQ#Even_statically_linked_programs_need_some_shared_libraries_which_is_not_acceptable_for_me.__What_can_I_do.3F) + with fully static linking. ----