diff options
-rw-r--r-- | compiler/iface/MkIface.hs | 59 | ||||
-rw-r--r-- | compiler/main/DynFlags.hs | 7 | ||||
-rw-r--r-- | docs/users_guide/extending_ghc.rst | 19 | ||||
-rw-r--r-- | testsuite/tests/plugins/T15858.script | 33 | ||||
-rw-r--r-- | testsuite/tests/plugins/T15858.stderr | 21 | ||||
-rw-r--r-- | testsuite/tests/plugins/all.T | 8 |
6 files changed, 132 insertions, 15 deletions
diff --git a/compiler/iface/MkIface.hs b/compiler/iface/MkIface.hs index c23f577bba..1ea608e787 100644 --- a/compiler/iface/MkIface.hs +++ b/compiler/iface/MkIface.hs @@ -1325,14 +1325,15 @@ checkVersions hsc_env mod_summary iface -- | Check if any plugins are requesting recompilation checkPlugins :: HscEnv -> ModIface -> IfG RecompileRequired checkPlugins hsc iface = liftIO $ do - -- [(ModuleName, Plugin, [Opts])] + new_fingerprint <- fingerprintPlugins hsc let old_fingerprint = mi_plugin_hash iface - res <- mconcat <$> mapM pluginRecompile' (plugins (hsc_dflags hsc)) - return (pluginRecompileToRecompileRequired old_fingerprint res) + pr <- mconcat <$> mapM pluginRecompile' (plugins (hsc_dflags hsc)) + return $ + pluginRecompileToRecompileRequired old_fingerprint new_fingerprint pr fingerprintPlugins :: HscEnv -> IO Fingerprint fingerprintPlugins hsc_env = do - fingerprintPlugins' $ plugins(hsc_dflags hsc_env) + fingerprintPlugins' $ plugins (hsc_dflags hsc_env) fingerprintPlugins' :: [PluginWithArgs] -> IO Fingerprint fingerprintPlugins' plugins = do @@ -1346,13 +1347,49 @@ fingerprintPlugins' plugins = do (MaybeRecompile fp) -> fp -pluginRecompileToRecompileRequired :: Fingerprint -> PluginRecompile -> RecompileRequired -pluginRecompileToRecompileRequired old_fp pr = - case pr of - NoForceRecompile -> UpToDate - ForceRecompile -> RecompBecause "Plugin forced recompilation" - MaybeRecompile fp -> if fp == old_fp then UpToDate - else RecompBecause "Plugin fingerprint changed" +pluginRecompileToRecompileRequired + :: Fingerprint -> Fingerprint -> PluginRecompile -> RecompileRequired +pluginRecompileToRecompileRequired old_fp new_fp pr + | old_fp == new_fp = + case pr of + NoForceRecompile -> UpToDate + + -- we already checked the fingerprint above so a mismatch is not possible + -- here, remember that: `fingerprint (MaybeRecomp x) == x`. + MaybeRecompile _ -> UpToDate + + -- when we have an impure plugin in the stack we have to unconditionally + -- recompile since it might integrate all sorts of crazy IO results into + -- its compilation output. + ForceRecompile -> RecompBecause "Impure plugin forced recompilation" + + | old_fp `elem` magic_fingerprints || + new_fp `elem` magic_fingerprints + -- The fingerprints do not match either the old or new one is a magic + -- fingerprint. This happens when non-pure plugins are added for the first + -- time or when we go from one recompilation strategy to another: (force -> + -- no-force, maybe-recomp -> no-force, no-force -> maybe-recomp etc.) + -- + -- For example when we go from from ForceRecomp to NoForceRecomp + -- recompilation is triggered since the old impure plugins could have + -- changed the build output which is now back to normal. + = RecompBecause "Plugins changed" + + | otherwise = + let reason = "Plugin fingerprint changed" in + case pr of + -- even though a plugin is forcing recompilation the fingerprint changed + -- which would cause recompilation anyways so we report the fingerprint + -- change instead. + ForceRecompile -> RecompBecause reason + + _ -> RecompBecause reason + + where + magic_fingerprints = + [ fingerprintString "NoForceRecompile" + , fingerprintString "ForceRecompile" + ] -- | Check if an hsig file needs recompilation because its diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs index be347d9367..02ad366338 100644 --- a/compiler/main/DynFlags.hs +++ b/compiler/main/DynFlags.hs @@ -2590,6 +2590,12 @@ setComponentId s d = addPluginModuleName :: String -> DynFlags -> DynFlags addPluginModuleName name d = d { pluginModNames = (mkModuleName name) : (pluginModNames d) } +clearPluginModuleNames :: DynFlags -> DynFlags +clearPluginModuleNames d = + d { pluginModNames = [] + , pluginModNameOpts = [] + , cachedPlugins = [] } + addPluginModuleNameOption :: String -> DynFlags -> DynFlags addPluginModuleNameOption optflag d = d { pluginModNameOpts = (mkModuleName m, option) : (pluginModNameOpts d) } where (m, rest) = break (== ':') optflag @@ -3488,6 +3494,7 @@ dynamic_flags_deps = [ ------ Plugin flags ------------------------------------------------ , make_ord_flag defGhcFlag "fplugin-opt" (hasArg addPluginModuleNameOption) , make_ord_flag defGhcFlag "fplugin" (hasArg addPluginModuleName) + , make_ord_flag defGhcFlag "fclear-plugins" (noArg clearPluginModuleNames) , make_ord_flag defGhcFlag "ffrontend-opt" (hasArg addFrontendPluginOption) ------ Optimisation flags ------------------------------------------ diff --git a/docs/users_guide/extending_ghc.rst b/docs/users_guide/extending_ghc.rst index 20d9674843..a913684a15 100644 --- a/docs/users_guide/extending_ghc.rst +++ b/docs/users_guide/extending_ghc.rst @@ -193,10 +193,11 @@ with ``-fexternal-interpreter`` let GHC developers know in :ghc-ticket:`14335`. Using compiler plugins ~~~~~~~~~~~~~~~~~~~~~~ -Plugins can be specified on the command line with the -:ghc-flag:`-fplugin=⟨module⟩` option where ⟨module⟩ is a -module in a registered package that exports a plugin. Arguments can be given to -plugins with the :ghc-flag:`-fplugin-opt=⟨module⟩:⟨args⟩` option. +Plugins can be added on the command line with the :ghc-flag:`-fplugin=⟨module⟩` +option where ⟨module⟩ is a module in a registered package that exports the +plugin. Arguments can be passed to the plugins with the +:ghc-flag:`-fplugin-opt=⟨module⟩:⟨args⟩` option. The list of enabled plugins can +be reset with the :ghc-flag:`-fclear-plugins` option. .. ghc-flag:: -fplugin=⟨module⟩ :shortdesc: Load a plugin exported by a given module @@ -215,6 +216,16 @@ plugins with the :ghc-flag:`-fplugin-opt=⟨module⟩:⟨args⟩` option. Give arguments to a plugin module; module must be specified with :ghc-flag:`-fplugin=⟨module⟩`. +.. ghc-flag:: -fclear-plugins + :shortdesc: Clear the list of active plugins + :type: dynamic + :category: plugins + + Clear the list of plugins previously specified with + :ghc-flag:`-fplugin`. This is useful in GHCi where simply removing the + :ghc-flag:`-fplugin` options from the command line is not possible. Instead + `:set -fclear-plugins` can be used. + As an example, in order to load the plugin exported by ``Foo.Plugin`` in the package ``foo-ghc-plugin``, and give it the parameter "baz", we diff --git a/testsuite/tests/plugins/T15858.script b/testsuite/tests/plugins/T15858.script new file mode 100644 index 0000000000..77a1b53557 --- /dev/null +++ b/testsuite/tests/plugins/T15858.script @@ -0,0 +1,33 @@ +:set -fobject-code +-- ^ Without this no recompilation happens at all in ghci, but that's a bug for +-- another ticket. + +:l plugin-recomp-test.hs + +-- switching to an impure plugin triggers recomp. +:! echo ==ImpurePlugin.0 >&2 +:set -fclear-plugins -fplugin ImpurePlugin +:l plugin-recomp-test.hs + +-- ..forever, this also triggers recomp. +:! echo ==ImpurePlugin.1 >&2 +:l plugin-recomp-test.hs + +-- switching from impure to pure plugin triggers recomp. +:! echo ==PurePlugin.0 >&2 +:set -fclear-plugins -fplugin PurePlugin +:l plugin-recomp-test.hs + +-- switching to a fingerprint plugin triggers recomp. +:! echo ==FingerprintPlugin.0 >&2 +:set -fclear-plugins -fplugin FingerprintPlugin +:l plugin-recomp-test.hs + +-- same fingerprint plugin doesn't trigger recomp. +:! echo ==FingerprintPlugin.1 >&2 +:l plugin-recomp-test.hs + +-- switching from fingerprint to pure plugin triggers recomp. +:! echo ==PurePlugin.1 >&2 +:set -fclear-plugins -fplugin PurePlugin +:l plugin-recomp-test.hs diff --git a/testsuite/tests/plugins/T15858.stderr b/testsuite/tests/plugins/T15858.stderr new file mode 100644 index 0000000000..db0610f587 --- /dev/null +++ b/testsuite/tests/plugins/T15858.stderr @@ -0,0 +1,21 @@ +==ImpurePlugin.0 +Simple Plugin Passes Queried +Got options: +Simple Plugin Pass Run +==ImpurePlugin.1 +Simple Plugin Passes Queried +Got options: +Simple Plugin Pass Run +==PurePlugin.0 +Simple Plugin Passes Queried +Got options: +Simple Plugin Pass Run +==FingerprintPlugin.0 +Simple Plugin Passes Queried +Got options: +Simple Plugin Pass Run +==FingerprintPlugin.1 +==PurePlugin.1 +Simple Plugin Passes Queried +Got options: +Simple Plugin Pass Run
\ No newline at end of file diff --git a/testsuite/tests/plugins/all.T b/testsuite/tests/plugins/all.T index 72a42da91a..3b031668bb 100644 --- a/testsuite/tests/plugins/all.T +++ b/testsuite/tests/plugins/all.T @@ -199,3 +199,11 @@ test('static-plugins', extra_run_opts('"' + config.libdir + '"')], compile_and_run, ['-package ghc -isimple-plugin/']) + +test('T15858', + [extra_files(['plugin-recomp/', 'plugin-recomp-test.hs']), +# only_ways([config.ghc_plugin_way]), + pre_cmd('$MAKE -s --no-print-directory -C plugin-recomp package.plugins01 TOP={top}'), + extra_hc_opts("-package-db plugin-recomp/pkg.plugins01/local.package.conf ") + ], + ghci_script, ['T15858.script']) |