Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler/GHC/Driver/DynFlags.hs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ data DynFlags = DynFlags {
pluginPackageFlags :: [PackageFlag],
-- ^ The @-plugin-package-id@ flags from command line.
-- In *reverse* order that they're specified on the command line.
pluginPackageDBFlags :: [PackageDBFlag],
-- ^ The @-plugin-package-db@ flags from command line.
-- When non-empty, a separate 'UnitState' is built from these
-- databases for plugin loading (host packages for cross-compilation).
-- In *reverse* order that they're specified on the command line.
trustFlags :: [TrustFlag],
-- ^ The @-trust@ and @-distrust@ flags.
-- In *reverse* order that they're specified on the command line.
Expand Down Expand Up @@ -687,6 +692,7 @@ defaultDynFlags mySettings =
packageDBFlags = [],
packageFlags = [],
pluginPackageFlags = [],
pluginPackageDBFlags = [],
ignorePackageFlags = [],
trustFlags = [],
packageEnv = Nothing,
Expand Down Expand Up @@ -941,6 +947,7 @@ packageFlagsChanged idflags1 idflags0 =
packageFlags idflags1 /= packageFlags idflags0 ||
ignorePackageFlags idflags1 /= ignorePackageFlags idflags0 ||
pluginPackageFlags idflags1 /= pluginPackageFlags idflags0 ||
pluginPackageDBFlags idflags1 /= pluginPackageDBFlags idflags0 ||
trustFlags idflags1 /= trustFlags idflags0 ||
packageDBFlags idflags1 /= packageDBFlags idflags0 ||
packageGFlags idflags1 /= packageGFlags idflags0
Expand Down
10 changes: 10 additions & 0 deletions compiler/GHC/Driver/Env.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module GHC.Driver.Env
, hsc_all_home_unit_ids
, hscUpdateLoggerFlags
, hscUpdateHUG
, hscSetCurrentUnitState
, hscInsertHPT
, hscSetActiveHomeUnit
, hscSetActiveUnitId
Expand Down Expand Up @@ -144,6 +145,15 @@ hscInsertHPT hmi hsc_env = UnitEnv.insertHpt hmi (hsc_unit_env hsc_env)
hscUpdateHUG :: (HomeUnitGraph -> HomeUnitGraph) -> HscEnv -> HscEnv
hscUpdateHUG f hsc_env = hsc_env { hsc_unit_env = updateHug f (hsc_unit_env hsc_env) }

-- | Replace the 'UnitState' in the current 'HomeUnitEnv'.
-- Used by plugin loading to swap in a host-side 'UnitState' built from
-- @-plugin-package-db@ databases during cross-compilation.
hscSetCurrentUnitState :: UnitState -> HscEnv -> HscEnv
hscSetCurrentUnitState us hsc_env =
let uid = ue_currentUnit (hsc_unit_env hsc_env)
upd hue = hue { HUG.homeUnitEnv_units = us }
in hscUpdateHUG (HUG.unitEnv_adjust upd uid) hsc_env

setModuleGraph :: ModuleGraph -> HscEnv -> HscEnv
setModuleGraph mod_graph hsc_env = hsc_env { hsc_unit_env = (hsc_unit_env hsc_env) { ue_module_graph = mod_graph } }

Expand Down
12 changes: 12 additions & 0 deletions compiler/GHC/Driver/Session.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2154,6 +2154,8 @@ package_flags_deps = [
(NoArg (setGeneralFlag Opt_HideAllPackages))
, make_ord_flag defFlag "hide-all-plugin-packages"
(NoArg (setGeneralFlag Opt_HideAllPluginPackages))
, make_ord_flag defFlag "plugin-package-db" (HasArg addPluginPkgDb)
, make_ord_flag defFlag "clear-plugin-package-dbs" (NoArg clearPluginPkgDbs)
, make_ord_flag defFlag "package-env" (HasArg setPackageEnv)
, make_ord_flag defFlag "ignore-package" (HasArg ignorePackage)
, make_dep_flag defFlag "syslib" (HasArg exposePackage) "Use -package instead"
Expand Down Expand Up @@ -3175,6 +3177,16 @@ exposePluginPackage p =
exposePluginPackageId p =
upd (\s -> s{ pluginPackageFlags =
parsePackageFlag "-plugin-package-id" parseUnitArg p : pluginPackageFlags s })

addPluginPkgDb :: String -> DynP ()
addPluginPkgDb path =
upd (\s -> s{ pluginPackageDBFlags =
PackageDB (PkgDbPath path) : pluginPackageDBFlags s })

clearPluginPkgDbs :: DynP ()
clearPluginPkgDbs =
upd (\s -> s{ pluginPackageDBFlags = ClearPackageDBs : pluginPackageDBFlags s })

hidePackage p =
upd (\s -> s{ packageFlags = HidePackage p : packageFlags s })
ignorePackage p =
Expand Down
79 changes: 67 additions & 12 deletions compiler/GHC/Runtime/Loader.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{-# LANGUAGE CPP #-}

-- | Dynamically lookup up values from modules and loading them.
--
-- NOTE: This module is only compiled when flag(interpreter) is enabled
Expand Down Expand Up @@ -57,6 +59,7 @@ import GHC.Types.Name.Reader
import GHC.Types.Unique.DFM

import GHC.Unit.Finder ( findPluginModule, FindResult(..) )
import GHC.Unit.State ( initPluginUnitConfig, mkUnitState )
import GHC.Driver.Config.Diagnostic ( initIfaceMessageOpts )
import GHC.Unit.Module ( Module, ModuleName, thisGhcUnit, GenModule(moduleUnit), IsBootInterface(NotBoot) )
import GHC.Unit.Module.ModIface
Expand All @@ -69,7 +72,6 @@ import GHC.Utils.Error
import GHC.Utils.Outputable
import GHC.Utils.Exception

import Control.Monad ( unless )
import Data.Maybe ( mapMaybe )
import Unsafe.Coerce ( unsafeCoerce )
import GHC.Linker.Types
Expand Down Expand Up @@ -156,8 +158,12 @@ initializePlugins hsc_env

loadPlugins :: HscEnv -> IO ([LoadedPlugin], [Linkable], PkgsLoaded)
loadPlugins hsc_env
= do { unless (null to_load) $
checkExternalInterpreter hsc_env
= do { -- When -fexternal-interpreter is active, create a local Interp with
-- InternalInterp so plugins are loaded into GHC's own process.
-- TH/bytecode execution still goes through the external interpreter.
; hsc_env' <- if null to_load then return hsc_env
else withPluginInterp hsc_env
; let loadPlugin = loadPlugin' (mkVarOccFS (fsLit "plugin")) pluginTyConName hsc_env'
; plugins_with_deps <- mapM loadPlugin to_load
; let (plugins, ifaces, links, pkgs) = unzip4 plugins_with_deps
; return (zipWith attachOptions to_load (zip plugins ifaces), concat links, foldl' plusUDFM emptyUDFM pkgs)
Expand All @@ -171,23 +177,72 @@ loadPlugins hsc_env
where
options = [ option | (opt_mod_nm, option) <- pluginModNameOpts dflags
, opt_mod_nm == mod_nm ]
loadPlugin = loadPlugin' (mkVarOccFS (fsLit "plugin")) pluginTyConName hsc_env


loadFrontendPlugin :: HscEnv -> ModuleName -> IO (FrontendPlugin, [Linkable], PkgsLoaded)
loadFrontendPlugin hsc_env mod_name = do
checkExternalInterpreter hsc_env
hsc_env' <- withPluginInterp hsc_env
(plugin, _iface, links, pkgs)
<- loadPlugin' (mkVarOccFS (fsLit "frontendPlugin")) frontendPluginTyConName
hsc_env mod_name
hsc_env' mod_name
return (plugin, links, pkgs)

-- #14335
checkExternalInterpreter :: HscEnv -> IO ()
checkExternalInterpreter hsc_env = case interpInstance <$> hsc_interp hsc_env of
Just (ExternalInterp {})
-> throwIO (InstallationError "Plugins require -fno-external-interpreter")
_ -> pure ()
-- | When the external interpreter is active, provide an 'HscEnv' with a
-- local 'Interp' that uses 'InternalInterp' for loading plugins into GHC's
-- own process. Plugins are compiler extensions that need to run in the
-- compiler's address space — they cannot run in iserv because plugin values
-- (records of Haskell functions) must be directly callable from GHC.
--
-- This decouples plugin loading from TH\/bytecode execution: plugins use the
-- in-process RTS linker, while TH and GHCi continue to use the external
-- interpreter (iserv) as configured.
--
-- For cross-compilers without an internal interpreter, this is not possible
-- and we fall back to an error suggesting @-fplugin-library@ instead.
-- See #14335.
withPluginInterp :: HscEnv -> IO HscEnv
withPluginInterp hsc_env = case interpInstance <$> hsc_interp hsc_env of
Just (ExternalInterp {}) ->
#if defined(HAVE_INTERNAL_INTERPRETER)
do pluginInterp <- mkPluginInterp
let hsc_env1 = hsc_env { hsc_interp = Just pluginInterp }
-- When plugin-specific package DBs are configured (via
-- -plugin-package-db), build a separate UnitState from host
-- package DBs so that plugins resolve host-side libraries.
-- This is essential for cross-compilation where plugin packages
-- are built for the host, not the target.
case pluginPackageDBFlags (hsc_dflags hsc_env) of
[] -> return hsc_env1 -- no plugin DBs: use existing UnitState
_ -> do
let logger = hsc_logger hsc_env
dflags = hsc_dflags hsc_env
cfg = initPluginUnitConfig dflags
(pluginUS, _dbs) <- mkUnitState logger dflags cfg
return $ hscSetCurrentUnitState pluginUS hsc_env1
#else
throwIO (InstallationError $ unlines
[ "Plugins require the internal interpreter, which is not available"
, "in this cross-compiler build."
, "Use -fplugin-library to load plugins from shared libraries instead."
])
#endif
_ -> return hsc_env

#if defined(HAVE_INTERNAL_INTERPRETER)
-- | Create a local 'Interp' with 'InternalInterp' for loading plugins
-- into GHC's own process. The local interpreter has its own 'Loader'
-- state, independent of the external interpreter's, so plugin packages
-- are loaded into GHC's address space via the in-process RTS linker.
mkPluginInterp :: IO Interp
mkPluginInterp = do
loader <- uninitializedLoader
symbolCache <- mkInterpSymbolCache
return Interp
{ interpInstance = InternalInterp
, interpLoader = loader
, interpSymbolCache = symbolCache
}
#endif

loadPlugin' :: OccName -> Name -> HscEnv -> ModuleName -> IO (a, ModIface, [Linkable], PkgsLoaded)
loadPlugin' occ_name plugin_name hsc_env mod_name
Expand Down
36 changes: 36 additions & 0 deletions compiler/GHC/Unit/State.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ module GHC.Unit.State (
UnitErr (..),
emptyUnitState,
initUnits,
initPluginUnitConfig,
mkUnitState,
readUnitDatabases,
readUnitDatabase,
getUnitDbRefs,
Expand Down Expand Up @@ -79,6 +81,7 @@ import GHC.Prelude
import GHC.Driver.DynFlags

import GHC.Platform
import GHC.Platform.Host (hostPlatformArchOS)
import GHC.Platform.Ways

import GHC.Unit.Database
Expand Down Expand Up @@ -415,6 +418,39 @@ initUnitConfig dflags cached_dbs home_units =
offsetPackageDb (Just offset) (PackageDB (PkgDbPath p)) | OsPath.isRelative p = PackageDB (PkgDbPath (OsPath.unsafeEncodeUtf offset OsPath.</> p))
offsetPackageDb _ p = p

-- | Build a 'UnitConfig' for plugin packages using host-side package
-- databases. Used in cross-compilation scenarios where plugin packages
-- are built for the host platform, not the target.
--
-- When @-plugin-package-db@ flags are specified, this config is used to
-- create a separate 'UnitState' so that plugins resolve from host package
-- DBs while TH\/bytecode uses the target 'UnitState'.
initPluginUnitConfig :: DynFlags -> UnitConfig
initPluginUnitConfig dflags =
UnitConfig
{ unitConfigPlatformArchOS = hostPlatformArchOS
, unitConfigProgramName = programName dflags
, unitConfigWays = ways dflags
, unitConfigAllowVirtual = False

, unitConfigGlobalDB = globalPackageDatabasePath dflags
, unitConfigGHCDir = topDir dflags
, unitConfigDBName = "package.conf.d"

, unitConfigAutoLink = []
, unitConfigDistrustAll = False
, unitConfigHideAll = gopt Opt_HideAllPluginPackages dflags
, unitConfigHideAllPlugins = gopt Opt_HideAllPluginPackages dflags

, unitConfigDBCache = Nothing -- no cache, read from plugin DBs
, unitConfigFlagsDB = pluginPackageDBFlags dflags
, unitConfigFlagsExposed = pluginPackageFlags dflags
, unitConfigFlagsIgnored = []
, unitConfigFlagsTrusted = []
, unitConfigFlagsPlugins = pluginPackageFlags dflags
, unitConfigHomeUnits = Set.empty
}


-- | Map from 'ModuleName' to a set of module providers (i.e. a 'Module' and
-- its 'ModuleOrigin').
Expand Down
4 changes: 3 additions & 1 deletion testsuite/tests/plugins/T14335.stderr
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
ghc: Plugins require -fno-external-interpreter
Simple Plugin Passes Queried
Got options:
Simple Plugin Pass Run
2 changes: 1 addition & 1 deletion testsuite/tests/plugins/all.T
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ test('T14335',
[extra_files(['simple-plugin/', 'plugins01.hs']),

pre_cmd('$MAKE -s --no-print-directory -C simple-plugin package.plugins01 TOP={top}')],
compile_fail,
compile,
['-package-db simple-plugin/pkg.plugins01/local.package.conf -fplugin Simple.Plugin \
-fexternal-interpreter -package simple-plugin ' + config.plugin_way_flags])

Expand Down
Loading