diff --git a/compiler/GHC/Driver/Flags.hs b/compiler/GHC/Driver/Flags.hs index 93748bbc0689cd4e115bfbc185a83ee34e367b22..a827ffe3150ea0eddaddc1504ffa575266a4affd 100644 --- a/compiler/GHC/Driver/Flags.hs +++ b/compiler/GHC/Driver/Flags.hs @@ -274,6 +274,7 @@ data GeneralFlag | Opt_KeepCAFs | Opt_KeepGoing | Opt_ByteCode + | Opt_LinkRts -- output style opts | Opt_ErrorSpans -- Include full span info in error messages, diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs index 15cce2f11d200a62c321bad3f9ff30a7e923f045..83e637401e28888cf17006d5462c9aa68b988b84 100644 --- a/compiler/GHC/Driver/Pipeline.hs +++ b/compiler/GHC/Driver/Pipeline.hs @@ -1937,7 +1937,14 @@ linkStaticLib dflags o_files dep_units = do output_exists <- doesFileExist full_output_fn (when output_exists) $ removeFile full_output_fn - pkg_cfgs <- getPreloadUnitsAnd dflags dep_units + pkg_cfgs_init <- getPreloadUnitsAnd dflags dep_units + + let pkg_cfgs + | gopt Opt_LinkRts dflags + = pkg_cfgs_init + | otherwise + = filter ((/= rtsUnitId) . unitId) pkg_cfgs_init + archives <- concatMapM (collectArchives dflags) pkg_cfgs ar <- foldl mappend diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs index 85eca17aed1703ba352d359ad46adcb40a7560f1..a558ceae96bdbdef91b6f70226215b75bb30a47a 100644 --- a/compiler/GHC/Driver/Session.hs +++ b/compiler/GHC/Driver/Session.hs @@ -2435,7 +2435,7 @@ dynamic_flags_deps = [ , make_ord_flag defGhcFlag "shared" (noArg (\d -> d { ghcLink=LinkDynLib })) , make_ord_flag defGhcFlag "staticlib" - (noArg (\d -> d { ghcLink=LinkStaticLib })) + (noArg (\d -> setGeneralFlag' Opt_LinkRts (d { ghcLink=LinkStaticLib }))) , make_ord_flag defGhcFlag "dynload" (hasArg parseDynLibLoaderMode) , make_ord_flag defGhcFlag "dylib-install-name" (hasArg setDylibInstallName) @@ -3599,7 +3599,8 @@ fFlagsDeps = [ flagSpec "hide-source-paths" Opt_HideSourcePaths, flagSpec "show-loaded-modules" Opt_ShowLoadedModules, flagSpec "whole-archive-hs-libs" Opt_WholeArchiveHsLibs, - flagSpec "keep-cafs" Opt_KeepCAFs + flagSpec "keep-cafs" Opt_KeepCAFs, + flagSpec "link-rts" Opt_LinkRts ] ++ fHoleFlags diff --git a/compiler/GHC/SysTools.hs b/compiler/GHC/SysTools.hs index ab83b3bf2a94ee6e2a33a8eba1fb79a35275d465..0b71e8ccdebf58dce2729c8230fe072a68562b4f 100644 --- a/compiler/GHC/SysTools.hs +++ b/compiler/GHC/SysTools.hs @@ -274,7 +274,9 @@ linkDynLib dflags0 o_files dep_packages pkgs_no_rts = case os of OSMinGW32 -> pkgs - _ -> + _ | gopt Opt_LinkRts dflags -> + pkgs + | otherwise -> filter ((/= rtsUnitId) . unitId) pkgs let pkg_link_opts = let (package_hs_libs, extra_libs, other_flags) = collectLinkOpts dflags pkgs_no_rts in package_hs_libs ++ extra_libs ++ other_flags diff --git a/docs/users_guide/8.12.1-notes.rst b/docs/users_guide/8.12.1-notes.rst index 1eb577c36e7a82a30ce295587752ec87b4c4d00b..b7f0444d6989fb85aa273f2065e9d406e493e71c 100644 --- a/docs/users_guide/8.12.1-notes.rst +++ b/docs/users_guide/8.12.1-notes.rst @@ -216,6 +216,9 @@ Language Compiler ~~~~~~~~ +- A new flag :ghc-flag:`-flink-rts` to enable linking the RTS when linking + shared libraries. + GHCi ~~~~ diff --git a/docs/users_guide/phases.rst b/docs/users_guide/phases.rst index 5975962370d1790742f40e91b9184249ba68283d..c7a17f6475bc4376592202dc18bb819e6e00d2cf 100644 --- a/docs/users_guide/phases.rst +++ b/docs/users_guide/phases.rst @@ -735,6 +735,8 @@ for example). :type: dynamic :category: linking + :implies: :ghc-flag:`-flink-rts` + Link all passed files into a static library suitable for linking. To control the name, use the :ghc-flag:`-o ⟨file⟩` option as usual. The default name is ``liba.a``. @@ -826,6 +828,23 @@ for example). libraries at runtime. See :ref:`finding-shared-libs` for a description of each mode. +.. ghc-flag:: -flink-rts + :shortdesc: Link the runtime when generating a shared or static library + :type: dynamic + :category: linking + + When linking shared libraries (:ghc-flag:`-shared`) GHC does not + automatically link the RTS. This is to allow choosing the RTS flavour + (:ghc-flag:`-threaded`, :ghc-flag:`-eventlog`, etc) when linking an + executable. + However when the shared library is the intended product it is useful to be + able to reverse this default. See :ref:`shared-libraries-c-api` for an + usage example. + + When linking a static library (:ghc-flag:`-staticlib`) GHC links the RTS + automatically, you can reverse this behaviour by reversing this flag: + ``-fno-link-rts``. + .. ghc-flag:: -main-is ⟨thing⟩ :shortdesc: Set main module and function :type: dynamic diff --git a/docs/users_guide/shared_libs.rst b/docs/users_guide/shared_libs.rst index 7e525019ca373fdf82f43297c2545238b8321eea..4c9ad621f7cb07df2d5cf8b8ee6abcdd4450b971 100644 --- a/docs/users_guide/shared_libs.rst +++ b/docs/users_guide/shared_libs.rst @@ -92,6 +92,8 @@ files to use the extension ``.dyn_hi``. The other requirements are the same as for C libraries and are described below, in particular the use of the flags :ghc-flag:`-dynamic`, :ghc-flag:`-fPIC` and :ghc-flag:`-shared`. +.. _shared-libraries-c-api: + Shared libraries that export a C API ------------------------------------ @@ -115,19 +117,24 @@ the :ghc-flag:`-dynamic`, :ghc-flag:`-fPIC` and :ghc-flag:`-shared` flags: .. code-block:: none - ghc --make -dynamic -shared -fPIC Foo.hs -o libfoo.so + ghc --make -dynamic -shared -fPIC -flink-rts Foo.hs -o libfoo.so As before, the :ghc-flag:`-dynamic` flag specifies that this library links -against the shared library versions of the ``rts`` and ``base`` package. The -:ghc-flag:`-fPIC` flag is required for all code that will end up in a shared -library. The :ghc-flag:`-shared` flag specifies to make a shared library rather -than a program. To make this clearer we can break this down into separate -compilation and link steps: +against the shared library versions of the ``base`` package. +:ghc-flag:`-flink-rts` additionally links against the shared library version of +the ``rts`` package (linking against the ``rts`` package is not enabled by +default when building shared libraries). You may also omit ``-flink-rts`` +and link the RTS library into your final executable. + +The :ghc-flag:`-fPIC` flag is required for all code that will end up in a +shared library. The :ghc-flag:`-shared` flag specifies to make a shared library +rather than a program. To make this clearer we can break this down into +separate compilation and link steps: .. code-block:: none ghc -dynamic -fPIC -c Foo.hs - ghc -dynamic -shared Foo.o -o libfoo.so + ghc -dynamic -shared -flink-rts Foo.o -o libfoo.so In principle you can use :ghc-flag:`-shared` without :ghc-flag:`-dynamic` in the link step. That means to statically link the runtime system and all of the base diff --git a/testsuite/tests/dynlibs/Makefile b/testsuite/tests/dynlibs/Makefile index 2a530a5aebf82906617bbbfe01fd6a43b34c2f1c..19009605eac7d61f342040b7f99efef74070b561 100644 --- a/testsuite/tests/dynlibs/Makefile +++ b/testsuite/tests/dynlibs/Makefile @@ -14,7 +14,7 @@ T3807: # libraries. This is done to allow the RTS flavour to be chosen later (i.e. # when linking an executable). # Hence we must explicitly linking with the RTS here. - '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 --make -dynamic -fPIC -shared T3807Export.hs T3807-export.c -o T3807test.so -lHSrts-ghc`'$(TEST_HC)' $(TEST_HC_OPTS) --numeric-version` + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 --make -dynamic -fPIC -shared T3807Export.hs T3807-export.c -o T3807test.so -flink-rts '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -no-auto-link-packages -no-hs-main T3807-load.c -o T3807-load -ldl ./T3807-load @@ -61,3 +61,33 @@ T5373: T13702: '$(TEST_HC)' -v0 -dynamic -rdynamic -fPIC -pie T13702.hs ./T13702 + +.PHONY: T18072 +T18072: + $(RM) -rf T18072/ + mkdir T18072 + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072 \ + -dynamic -fPIC -c T18072.hs + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072 \ + -dynamic -shared -flink-rts T18072/T18072.o -o T18072/T18072.so + ldd T18072/T18072.so | grep libHSrts >/dev/null + +.PHONY: T18072debug +T18072debug: + $(RM) -rf T18072debug/ + mkdir T18072debug + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072debug \ + -dynamic -fPIC -c T18072.hs + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072debug \ + -dynamic -shared -flink-rts -debug T18072debug/T18072.o -o T18072debug/T18072.so + ldd T18072debug/T18072.so | grep libHSrts | grep _debug >/dev/null + +.PHONY: T18072static +T18072static: + $(RM) -rf T18072static/ + mkdir T18072static + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072static \ + -c T18072.hs + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072static \ + -staticlib -fno-link-rts T18072static/T18072.o -o T18072static/T18072.a + ar t T18072static/T18072.a | grep RtsSymbols.o > /dev/null && exit 1 || exit 0 diff --git a/testsuite/tests/dynlibs/T18072.hs b/testsuite/tests/dynlibs/T18072.hs new file mode 100644 index 0000000000000000000000000000000000000000..39a174f2aef6a6ca58b5b1743bfe567366cef4ef --- /dev/null +++ b/testsuite/tests/dynlibs/T18072.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE ForeignFunctionInterface #-} + +module T18072 where + +import Foreign.C + +foo :: IO CInt +foo = return 3 + +foreign export ccall foo :: IO CInt diff --git a/testsuite/tests/dynlibs/all.T b/testsuite/tests/dynlibs/all.T index aaa7a627747997dd5abf3086e754a286d9dd0e5c..b7272d4bac260643ecd37113d85e835208d4f92f 100644 --- a/testsuite/tests/dynlibs/all.T +++ b/testsuite/tests/dynlibs/all.T @@ -7,3 +7,12 @@ test('T5373', [req_shared_libs], makefile_test, []) # It's not clear exactly what platforms we can expect this to succeed on. test('T13702', unless(opsys('linux'), skip), makefile_test, []) + +# test that -shared and -flink-rts actually links the rts +test('T18072', [req_shared_libs, unless(opsys('linux'), skip)], makefile_test, []) + +# test that -shared and -flink-rts respects alternative RTS flavours +test('T18072debug', [extra_files(['T18072.hs']), req_shared_libs, unless(opsys('linux'), skip)], makefile_test, []) + +# check that -staticlib and -fno-link-rts results in an archive without the RTR libary +test('T18072static', [extra_files(['T18072.hs']), unless(opsys('linux'), skip)], makefile_test, [])