Skip to content

Commit 5300d02

Browse files
committed
Add --enable-library-bytecode option for building bytecode libraries
The new `Cabal` and `cabal-install` option tells GHC to build a bytecode library. * When GHC is invoked, bytecode objects are also produced. * A bytecode library is created during linking. * The bytecode library is installed when the package is installed. * The `bytecode-library-dirs` field can be populated to instruct GHC where to find the bytecode library for a package. (This can be useful if the bytecode libraries are distributed separately from the other libraries). The bytecode object files are produced alongside an existing build way. They are prioritised in this order (`-dyn-too`, `-dynamic`, `-static`). Bytecode libraries can't be produced standalone at the moment. You have to produce them alongside an existing build way. This is due to limitations in GHC. At the moment, there is not support implemented for creating profiled bytecode libraries. Implements haskell/cabal-proposals#2 Fixes #11188
1 parent a3162da commit 5300d02

File tree

56 files changed

+529
-42
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+529
-42
lines changed

Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ data InstalledPackageInfo = InstalledPackageInfo
7373
, libraryDirs :: [FilePath]
7474
, libraryDirsStatic :: [FilePath]
7575
, libraryDynDirs :: [FilePath]
76+
, libraryBytecodeDirs :: [FilePath]
7677
-- ^ overrides 'libraryDirs'
7778
, dataDir :: FilePath
7879
, hsLibraries :: [String]
@@ -160,6 +161,7 @@ emptyInstalledPackageInfo =
160161
, hsLibraries = []
161162
, extraLibraries = []
162163
, extraLibrariesStatic = []
164+
, libraryBytecodeDirs = []
163165
, extraGHCiLibraries = []
164166
, includeDirs = []
165167
, includes = []

Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ ipiFieldGrammar =
108108
<@> monoidalFieldAla "library-dirs" (alaList' FSep FilePathNT) L.libraryDirs
109109
<@> monoidalFieldAla "library-dirs-static" (alaList' FSep FilePathNT) L.libraryDirsStatic
110110
<@> monoidalFieldAla "dynamic-library-dirs" (alaList' FSep FilePathNT) L.libraryDynDirs
111+
<@> monoidalFieldAla "bytecode-library-dirs" (alaList' FSep FilePathNT) L.libraryBytecodeDirs
111112
<@> optionalFieldDefAla "data-dir" FilePathNT L.dataDir ""
112113
<@> monoidalFieldAla "hs-libraries" (alaList' FSep Token) L.hsLibraries
113114
<@> monoidalFieldAla "extra-libraries" (alaList' FSep Token) L.extraLibraries

Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ libraryDynDirs :: Lens' InstalledPackageInfo [FilePath]
123123
libraryDynDirs f s = fmap (\x -> s{T.libraryDynDirs = x}) (f (T.libraryDynDirs s))
124124
{-# INLINE libraryDynDirs #-}
125125

126+
libraryBytecodeDirs :: Lens' InstalledPackageInfo [FilePath]
127+
libraryBytecodeDirs f s = fmap (\x -> s{T.libraryBytecodeDirs = x}) (f (T.libraryBytecodeDirs s))
128+
{-# INLINE libraryBytecodeDirs #-}
129+
126130
dataDir :: Lens' InstalledPackageInfo FilePath
127131
dataDir f s = fmap (\x -> s{T.dataDir = x}) (f (T.dataDir s))
128132
{-# INLINE dataDir #-}

Cabal/src/Distribution/Simple/BuildPaths.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module Distribution.Simple.BuildPaths
4242
, mkSharedLibName
4343
, mkProfSharedLibName
4444
, mkStaticLibName
45+
, mkBytecodeLibName
4546
, mkGenericSharedBundledLibName
4647
, exeExtension
4748
, objExtension
@@ -409,6 +410,9 @@ mkStaticLibName platform (CompilerId compilerFlavor compilerVersion) lib =
409410
where
410411
comp = prettyShow compilerFlavor ++ prettyShow compilerVersion
411412

413+
mkBytecodeLibName :: CompilerId -> UnitId -> String
414+
mkBytecodeLibName _comp lib = getHSLibraryName lib <.> ".bytecodelib"
415+
412416
-- | Create a library name for a bundled shared library from a given name.
413417
-- This matches the naming convention for shared libraries as implemented in
414418
-- GHC's packageHsLibs function in the Packages module.
Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
{-# LANGUAGE LambdaCase #-}
22

3-
module Distribution.Simple.BuildWay where
3+
module Distribution.Simple.BuildWay
4+
( BuildWay (..)
5+
, buildWayObjectExtension
6+
, buildWayInterfaceExtension
7+
) where
48

59
data BuildWay = StaticWay | DynWay | ProfWay | ProfDynWay
610
deriving (Eq, Ord, Show, Read, Enum)
711

8-
-- | Returns the object/interface extension prefix for the given build way (e.g. "dyn_" for 'DynWay')
9-
buildWayPrefix :: BuildWay -> String
10-
buildWayPrefix = \case
11-
StaticWay -> ""
12-
ProfWay -> "p_"
13-
DynWay -> "dyn_"
14-
ProfDynWay -> "p_dyn_"
12+
-- | Returns the object extension for the given build way (e.g. "dyn_o" for 'DynWay' on ELF)
13+
buildWayObjectExtension :: String -> BuildWay -> String
14+
buildWayObjectExtension ext = \case
15+
StaticWay -> ext
16+
ProfWay -> "p_" ++ ext
17+
DynWay -> "dyn_" ++ ext
18+
ProfDynWay -> "p_dyn_" ++ ext
19+
20+
buildWayInterfaceExtension :: BuildWay -> String
21+
buildWayInterfaceExtension = buildWayObjectExtension "hi"

Cabal/src/Distribution/Simple/Compiler.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ module Distribution.Simple.Compiler
7979
, profilingVanillaSupported
8080
, profilingVanillaSupportedOrUnknown
8181
, dynamicSupported
82+
, bytecodeArtifactsSupported
8283
, backpackSupported
8384
, arResponseFilesSupported
8485
, arDashLSupported
@@ -575,6 +576,14 @@ profilingDynamicSupportedOrUnknown comp =
575576
dynamicSupported :: Compiler -> Maybe Bool
576577
dynamicSupported comp = waySupported "dyn" comp
577578

579+
-- | Does the compiler support producing bytecode objects and bytecode libraries?
580+
bytecodeArtifactsSupported :: Compiler -> Bool
581+
bytecodeArtifactsSupported comp
582+
| GHC <- compilerFlavor comp
583+
, compilerVersion comp >= mkVersion [9, 15, 0] =
584+
True
585+
| otherwise = False
586+
578587
-- | Does this compiler support a package database entry with:
579588
-- "visibility"?
580589
libraryVisibilitySupported :: Compiler -> Bool

Cabal/src/Distribution/Simple/Configure.hs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@ computeLocalBuildConfig cfg comp programDb = do
753753
-- into a huge .a archive) via GHCs -staticlib flag.
754754
fromFlagOrDefault False $ configStaticLib cfg
755755

756+
withBytecodeLib_ =
757+
fromFlagOrDefault False $ configBytecodeLib cfg
758+
756759
withDynExe_ = fromFlag $ configDynExe cfg
757760

758761
withFullyStaticExe_ = fromFlag $ configFullyStaticExe cfg
@@ -780,12 +783,22 @@ computeLocalBuildConfig cfg comp programDb = do
780783
strip_lib <- strip_libexe "library" configStripLibs
781784
strip_exe <- strip_libexe "executable" configStripExes
782785

786+
checkedWithBytecodeLib <-
787+
if bytecodeArtifactsSupported comp
788+
then return withBytecodeLib_
789+
else do
790+
when withBytecodeLib_ $
791+
warn verbosity $
792+
"This compiler does not support bytecode libraries; ignoring --enable-library-bytecode"
793+
return False
794+
783795
let buildOptions =
784796
setCoverage . setProfiling $
785797
LBC.BuildOptions
786798
{ withVanillaLib = fromFlag $ configVanillaLib cfg
787799
, withSharedLib = withSharedLib_
788800
, withStaticLib = withStaticLib_
801+
, withBytecodeLib = checkedWithBytecodeLib
789802
, withDynExe = withDynExe_
790803
, withFullyStaticExe = withFullyStaticExe_
791804
, withProfLib = False

Cabal/src/Distribution/Simple/Errors.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ data CabalException
170170
| MissingCoveredInstalledLibrary UnitId
171171
| SetupHooksException SetupHooksException
172172
| MultiReplDoesNotSupportComplexReexportedModules PackageName ComponentName
173+
| StandaloneBytecodeNotSupportedYet
173174
deriving (Show)
174175

175176
exceptionCode :: CabalException -> Int
@@ -304,6 +305,7 @@ exceptionCode e = case e of
304305
SetupHooksException err ->
305306
setupHooksExceptionCode err
306307
MultiReplDoesNotSupportComplexReexportedModules{} -> 9355
308+
StandaloneBytecodeNotSupportedYet -> 9356
307309

308310
versionRequirement :: VersionRange -> String
309311
versionRequirement range
@@ -804,3 +806,8 @@ exceptionMessage e = case e of
804806
++ prettyShow pname
805807
++ " a module renaming was found.\n"
806808
++ "Multi-repl does not work with complicated reexported-modules until GHC-9.12."
809+
StandaloneBytecodeNotSupportedYet ->
810+
unlines $
811+
[ "The ecosystem doesn't support building packages with just bytecode libraries yet."
812+
, "If you have run into this error, please comment on issue #<TODO>"
813+
]

Cabal/src/Distribution/Simple/GHC.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,8 @@ installLib verbosity lbi targetDir dynlibTargetDir _builtDir pkg lib clbi = do
986986

987987
-- copy the built library files over:
988988
when (has_code && hasLib) $ do
989+
whenBytecodeLib $ installOrdinary builtDir targetDir bytecodeLibName
990+
989991
forM_ libWays $ \w -> case w of
990992
StaticWay -> do
991993
sequence_
@@ -1095,6 +1097,7 @@ installLib verbosity lbi targetDir dynlibTargetDir _builtDir pkg lib clbi = do
10951097
platform = hostPlatform lbi
10961098
uid = componentUnitId clbi
10971099
profileLibName = mkProfLibName uid
1100+
bytecodeLibName = mkBytecodeLibName compiler_id uid
10981101
ghciLibName = Internal.mkGHCiLibName uid
10991102
ghciProfLibName = Internal.mkGHCiProfLibName uid
11001103

@@ -1111,6 +1114,7 @@ installLib verbosity lbi targetDir dynlibTargetDir _builtDir pkg lib clbi = do
11111114
_ -> False
11121115
has_code = not (componentIsIndefinite clbi)
11131116
whenGHCi = when (hasLib && withGHCiLib lbi && has_code)
1117+
whenBytecodeLib = when (hasLib && withBytecodeLib lbi && has_code)
11141118

11151119
-- -----------------------------------------------------------------------------
11161120
-- Registering

Cabal/src/Distribution/Simple/GHC/Build/Link.hs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg li
279279
staticLibFilePath =
280280
buildTargetDir
281281
</> makeRelativePathEx (mkStaticLibName (hostPlatform lbi) compiler_id uid)
282+
bytecodeLibFilePath =
283+
buildTargetDir
284+
</> makeRelativePathEx (mkBytecodeLibName compiler_id uid)
282285
ghciLibFilePath = buildTargetDir </> makeRelativePathEx (Internal.mkGHCiLibName uid)
283286
ghciProfLibFilePath = buildTargetDir </> makeRelativePathEx (Internal.mkGHCiProfLibName uid)
284287
libInstallPath =
@@ -295,23 +298,29 @@ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg li
295298
libInstallPath
296299
</> mkProfSharedLibName (hostPlatform lbi) compiler_id uid
297300

298-
getObjFiles :: BuildWay -> IO [SymbolicPath Pkg File]
299-
getObjFiles way =
301+
getObjWayFiles :: BuildWay -> IO [SymbolicPath Pkg File]
302+
getObjWayFiles w = getObjFiles (buildWayObjectExtension objExtension w) (buildWayObjectExtension objExtension w)
303+
304+
getObjBytecodeWayFiles :: BuildWay -> IO [SymbolicPath Pkg File]
305+
getObjBytecodeWayFiles companion_way = getObjFiles "gbc" (buildWayObjectExtension objExtension companion_way)
306+
307+
getObjFiles :: String -> String -> IO [SymbolicPath Pkg File]
308+
getObjFiles hs_ext obj_ext =
300309
mconcat
301310
[ Internal.getHaskellObjects
302311
implInfo
303312
lib
304313
lbi
305314
clbi
306315
buildTargetDir
307-
(buildWayPrefix way ++ objExtension)
316+
hs_ext
308317
True
309-
, pure $ map (srcObjPath way) extraSources
318+
, pure $ map (srcObjPath obj_ext) extraSources
310319
, catMaybes
311320
<$> sequenceA
312321
[ findFileCwdWithExtension
313322
mbWorkDir
314-
[Suffix $ buildWayPrefix way ++ objExtension]
323+
[Suffix $ obj_ext]
315324
[buildTargetDir]
316325
xPath
317326
| ghcVersion < mkVersion [7, 2] -- ghc-7.2+ does not make _stub.o files
@@ -323,16 +332,16 @@ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg li
323332

324333
-- Get the @.o@ path from a source path (e.g. @.hs@),
325334
-- in the library target build directory.
326-
srcObjPath :: BuildWay -> SymbolicPath Pkg File -> SymbolicPath Pkg File
327-
srcObjPath way srcPath =
335+
srcObjPath :: String -> SymbolicPath Pkg File -> SymbolicPath Pkg File
336+
srcObjPath obj_ext srcPath =
328337
case symbolicPathRelative_maybe objPath of
329338
-- Absolute path: should already be in the target build directory
330339
-- (e.g. a preprocessed file)
331340
-- TODO: assert this?
332341
Nothing -> objPath
333342
Just objRelPath -> coerceSymbolicPath buildTargetDir </> objRelPath
334343
where
335-
objPath = srcPath `replaceExtensionSymbolicPath` (buildWayPrefix way ++ objExtension)
344+
objPath = srcPath `replaceExtensionSymbolicPath` obj_ext
336345

337346
-- I'm fairly certain that, just like the executable, we can keep just the
338347
-- module input list, and point to the right sources dir (as is already
@@ -343,6 +352,7 @@ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg li
343352
-- we could more easily merge the two.
344353
--
345354
-- Right now, instead, we pass the path to each object file.
355+
ghcBaseLinkArgs :: GhcOptions
346356
ghcBaseLinkArgs =
347357
mempty
348358
{ -- TODO: This basically duplicates componentGhcOptions.
@@ -435,11 +445,17 @@ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg li
435445
, -- TODO: Shouldn't this use cleanedExtraLibDirsStatic instead?
436446
ghcOptLinkLibPath = toNubListR $ cleanedExtraLibDirs
437447
}
448+
ghcBytecodeLinkArgs objectFiles =
449+
(ghcSharedLinkArgs objectFiles)
450+
{ ghcOptBytecodeLib = toFlag True
451+
, ghcOptInputFiles = toNubListR $ map coerceSymbolicPath objectFiles
452+
, ghcOptOutputFile = toFlag bytecodeLibFilePath
453+
}
438454

439-
staticObjectFiles <- getObjFiles StaticWay
440-
profObjectFiles <- getObjFiles ProfWay
441-
dynamicObjectFiles <- getObjFiles DynWay
442-
profDynamicObjectFiles <- getObjFiles ProfDynWay
455+
staticObjectFiles <- getObjWayFiles StaticWay
456+
profObjectFiles <- getObjWayFiles ProfWay
457+
dynamicObjectFiles <- getObjWayFiles DynWay
458+
profDynamicObjectFiles <- getObjWayFiles ProfDynWay
443459

444460
let
445461
linkWay = \case
@@ -457,6 +473,10 @@ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg li
457473
runGhcProg $ ghcProfSharedLinkArgs profDynamicObjectFiles
458474
DynWay -> do
459475
runGhcProg $ ghcSharedLinkArgs dynamicObjectFiles
476+
-- The .gbc files were built with DynWay if both are enabled.
477+
when (withBytecodeLib lbi) $ do
478+
bytecodeObjectFiles <- getObjBytecodeWayFiles DynWay
479+
runGhcProg $ ghcBytecodeLinkArgs bytecodeObjectFiles
460480
StaticWay -> do
461481
when (withVanillaLib lbi) $ do
462482
Ar.createArLibArchive verbosity lbi vanillaLibFilePath staticObjectFiles
@@ -470,6 +490,10 @@ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg li
470490
staticObjectFiles
471491
when (withStaticLib lbi) $ do
472492
runGhcProg $ ghcStaticLinkArgs staticObjectFiles
493+
-- The .gbc files were built with DynWay if both are enabled.
494+
when (withBytecodeLib lbi && (DynWay `notElem` wantedWays)) $ do
495+
bytecodeObjectFiles <- getObjBytecodeWayFiles StaticWay
496+
runGhcProg $ ghcBytecodeLinkArgs bytecodeObjectFiles
473497

474498
-- ROMES: Why exactly branch on staticObjectFiles, rather than any other build
475499
-- kind that we might have wanted instead?

0 commit comments

Comments
 (0)