Commit 98b0d05d authored by thoughtpolice's avatar thoughtpolice

Rework how iOS does linking (#8127)

iOS has some particular constraints about how applications can be built:

 * We must generate a static library (.a) since XCode does the final
   link.
 * We need to carefully give the right set of arguments to libtool in
   the case we're generating an archive.
 * Dynamic linking isn't supported.
 * It can only be done on OS X.

This patch cleans up all of the above. We add a new flag `-staticlib`
(only supported on Darwin) that allows us to produce archive files using
libtool, and a -pgmlibtool flag to control which 'libtool' executable to
use.

This fixes #8127. I believe this is the last piece missing from the iOS
cross compiler.
Authored-by: lukexi's avatarLuke Iannini <lukexi@me.com>
Authored-by: maxs's avatarMaxwell Swadling <maxwellswadling@gmail.com>
Authored-by: default avatarStephen Blackheath <...@blacksapphire.com>
Signed-off-by: thoughtpolice's avatarAustin Seipp <aseipp@pobox.com>
parent 4652a5d2
......@@ -464,6 +464,7 @@ AC_DEFUN([FP_SETTINGS],
SettingsPerlCommand="$PerlCmd"
SettingsDllWrapCommand="/bin/false"
SettingsWindresCommand="/bin/false"
SettingsLibtoolCommand="libtool"
SettingsTouchCommand='touch'
if test -z "$LlcCmd"
then
......@@ -490,6 +491,7 @@ AC_DEFUN([FP_SETTINGS],
AC_SUBST(SettingsPerlCommand)
AC_SUBST(SettingsDllWrapCommand)
AC_SUBST(SettingsWindresCommand)
AC_SUBST(SettingsLibtoolCommand)
AC_SUBST(SettingsTouchCommand)
AC_SUBST(SettingsLlcCommand)
AC_SUBST(SettingsOptCommand)
......
......@@ -59,7 +59,7 @@ import System.Directory
import System.FilePath
import System.IO
import Control.Monad
import Data.List ( isSuffixOf )
import Data.List ( isSuffixOf, isPrefixOf )
import Data.Maybe
import System.Environment
import Data.Char
......@@ -295,6 +295,9 @@ link NoLink _ _ _
link LinkBinary dflags batch_attempt_linking hpt
= link' dflags batch_attempt_linking hpt
link LinkStaticLib dflags batch_attempt_linking hpt
= link' dflags batch_attempt_linking hpt
link LinkDynLib dflags batch_attempt_linking hpt
= link' dflags batch_attempt_linking hpt
......@@ -311,6 +314,10 @@ link' dflags batch_attempt_linking hpt
| batch_attempt_linking
= do
let
staticLink = case ghcLink dflags of
LinkStaticLib -> True
_ -> platformBinariesAreStaticLibs (targetPlatform dflags)
home_mod_infos = eltsUFM hpt
-- the packages we depend on
......@@ -330,9 +337,9 @@ link' dflags batch_attempt_linking hpt
let getOfiles (LM _ _ us) = map nameOfObject (filter isObject us)
obj_files = concatMap getOfiles linkables
exe_file = exeFileName dflags
exe_file = exeFileName staticLink dflags
linking_needed <- linkingNeeded dflags linkables pkg_deps
linking_needed <- linkingNeeded dflags staticLink linkables pkg_deps
if not (gopt Opt_ForceRecomp dflags) && not linking_needed
then do debugTraceMsg dflags 2 (text exe_file <+> ptext (sLit "is up to date, linking not required."))
......@@ -343,9 +350,10 @@ link' dflags batch_attempt_linking hpt
-- Don't showPass in Batch mode; doLink will do that for us.
let link = case ghcLink dflags of
LinkBinary -> linkBinary
LinkDynLib -> linkDynLibCheck
other -> panicBadLink other
LinkBinary -> linkBinary
LinkStaticLib -> linkStaticLibCheck
LinkDynLib -> linkDynLibCheck
other -> panicBadLink other
link dflags obj_files pkg_deps
debugTraceMsg dflags 3 (text "link: done")
......@@ -359,12 +367,12 @@ link' dflags batch_attempt_linking hpt
return Succeeded
linkingNeeded :: DynFlags -> [Linkable] -> [PackageId] -> IO Bool
linkingNeeded dflags linkables pkg_deps = do
linkingNeeded :: DynFlags -> Bool -> [Linkable] -> [PackageId] -> IO Bool
linkingNeeded dflags staticLink linkables pkg_deps = do
-- if the modification time on the executable is later than the
-- modification times on all of the objects and libraries, then omit
-- linking (unless the -fforce-recomp flag was given).
let exe_file = exeFileName dflags
let exe_file = exeFileName staticLink dflags
e_exe_time <- tryIO $ getModificationUTCTime exe_file
case e_exe_time of
Left _ -> return True
......@@ -482,10 +490,11 @@ doLink dflags stop_phase o_files
| otherwise
= case ghcLink dflags of
NoLink -> return ()
LinkBinary -> linkBinary dflags o_files []
LinkDynLib -> linkDynLibCheck dflags o_files []
other -> panicBadLink other
NoLink -> return ()
LinkBinary -> linkBinary dflags o_files []
LinkStaticLib -> linkStaticLibCheck dflags o_files []
LinkDynLib -> linkDynLibCheck dflags o_files []
other -> panicBadLink other
-- ---------------------------------------------------------------------------
......@@ -1770,11 +1779,14 @@ getHCFilePackages filename =
-- the packages.
linkBinary :: DynFlags -> [FilePath] -> [PackageId] -> IO ()
linkBinary dflags o_files dep_packages = do
linkBinary = linkBinary' False
linkBinary' :: Bool -> DynFlags -> [FilePath] -> [PackageId] -> IO ()
linkBinary' staticLink dflags o_files dep_packages = do
let platform = targetPlatform dflags
mySettings = settings dflags
verbFlags = getVerbFlags dflags
output_fn = exeFileName dflags
output_fn = exeFileName staticLink dflags
-- get the full list of packages to link with, by combining the
-- explicit packages with the auto packages and all of their
......@@ -1815,13 +1827,15 @@ linkBinary dflags o_files dep_packages = do
extraLinkObj <- mkExtraObjToLinkIntoBinary dflags
noteLinkObjs <- mkNoteObjsToLinkIntoBinary dflags dep_packages
pkg_link_opts <- if platformBinariesAreStaticLibs platform
then -- If building an executable really means
-- making a static library (e.g. iOS), then
-- we don't want the options (like -lm)
-- that getPackageLinkOpts gives us. #7720
return []
else getPackageLinkOpts dflags dep_packages
pkg_link_opts <- do
(package_hs_libs, extra_libs, other_flags) <- getPackageLinkOpts dflags dep_packages
return $ if staticLink
then package_hs_libs -- If building an executable really means making a static
-- library (e.g. iOS), then we only keep the -l options for
-- HS packages, because libtool doesn't accept other options.
-- In the case of iOS these need to be added by hand to the
-- final link in Xcode.
else package_hs_libs ++ extra_libs ++ other_flags
pkg_framework_path_opts <-
if platformUsesFrameworks platform
......@@ -1869,14 +1883,17 @@ linkBinary dflags o_files dep_packages = do
let os = platformOS (targetPlatform dflags)
in if os == OSOsf3 then ["-lpthread", "-lexc"]
else if os `elem` [OSMinGW32, OSFreeBSD, OSOpenBSD,
OSNetBSD, OSHaiku, OSQNXNTO]
OSNetBSD, OSHaiku, OSQNXNTO, OSiOS]
then []
else ["-lpthread"]
| otherwise = []
rc_objs <- maybeCreateManifest dflags output_fn
SysTools.runLink dflags (
let link = if staticLink
then SysTools.runLibtool
else SysTools.runLink
link dflags (
map SysTools.Option verbFlags
++ [ SysTools.Option "-o"
, SysTools.FileOption "" output_fn
......@@ -1899,6 +1916,7 @@ linkBinary dflags o_files dep_packages = do
-- ld: warning: could not create compact unwind for .LFB3: non-standard register 5 being saved in prolog
-- on x86.
++ (if sLdSupportsCompactUnwind mySettings &&
not staticLink &&
platformOS platform == OSDarwin &&
platformArch platform `elem` [ArchX86, ArchX86_64]
then ["-Wl,-no_compact_unwind"]
......@@ -1911,7 +1929,8 @@ linkBinary dflags o_files dep_packages = do
-- whether this is something we ought to fix, but
-- for now this flags silences them.
++ (if platformOS platform == OSDarwin &&
platformArch platform == ArchX86
platformArch platform == ArchX86 &&
not staticLink
then ["-Wl,-read_only_relocs,suppress"]
else [])
......@@ -1937,17 +1956,20 @@ linkBinary dflags o_files dep_packages = do
throwGhcExceptionIO (InstallationError ("cannot move binary"))
exeFileName :: DynFlags -> FilePath
exeFileName dflags
exeFileName :: Bool -> DynFlags -> FilePath
exeFileName staticLink dflags
| Just s <- outputFile dflags =
case platformOS (targetPlatform dflags) of
case platformOS (targetPlatform dflags) of
OSMinGW32 -> s <?.> "exe"
OSiOS -> s <?.> "a"
_ -> s
_ -> if staticLink
then s <?.> "a"
else s
| otherwise =
if platformOS (targetPlatform dflags) == OSMinGW32
then "main.exe"
else "a.out"
else if staticLink
then "liba.a"
else "a.out"
where s <?.> ext | null (takeExtension s) = s <.> ext
| otherwise = s
......@@ -2014,6 +2036,13 @@ linkDynLibCheck dflags o_files dep_packages
linkDynLib dflags o_files dep_packages
linkStaticLibCheck :: DynFlags -> [String] -> [PackageId] -> IO ()
linkStaticLibCheck dflags o_files dep_packages
= do
when (platformOS (targetPlatform dflags) `notElem` [OSiOS, OSDarwin]) $
throwGhcExceptionIO (ProgramError "Static archive creation only supported on Darwin/OS X/iOS")
linkBinary' True dflags o_files dep_packages
-- -----------------------------------------------------------------------------
-- Running CPP
......
......@@ -66,7 +66,7 @@ module DynFlags (
ghcUsagePath, ghciUsagePath, topDir, tmpDir, rawSettings,
extraGccViaCFlags, systemPackageConfig,
pgm_L, pgm_P, pgm_F, pgm_c, pgm_s, pgm_a, pgm_l, pgm_dll, pgm_T,
pgm_sysman, pgm_windres, pgm_lo, pgm_lc,
pgm_sysman, pgm_windres, pgm_libtool, pgm_lo, pgm_lc,
opt_L, opt_P, opt_F, opt_c, opt_a, opt_l,
opt_windres, opt_lo, opt_lc,
......@@ -798,6 +798,7 @@ data Settings = Settings {
sPgm_T :: String,
sPgm_sysman :: String,
sPgm_windres :: String,
sPgm_libtool :: String,
sPgm_lo :: (String,[Option]), -- LLVM: opt llvm optimiser
sPgm_lc :: (String,[Option]), -- LLVM: llc static compiler
-- options for particular phases
......@@ -853,6 +854,8 @@ pgm_sysman :: DynFlags -> String
pgm_sysman dflags = sPgm_sysman (settings dflags)
pgm_windres :: DynFlags -> String
pgm_windres dflags = sPgm_windres (settings dflags)
pgm_libtool :: DynFlags -> String
pgm_libtool dflags = sPgm_libtool (settings dflags)
pgm_lo :: DynFlags -> (String,[Option])
pgm_lo dflags = sPgm_lo (settings dflags)
pgm_lc :: DynFlags -> (String,[Option])
......@@ -948,6 +951,7 @@ data GhcLink
| LinkInMemory -- ^ Use the in-memory dynamic linker (works for both
-- bytecode and object code).
| LinkDynLib -- ^ Link objects into a dynamic lib (DLL on Windows, DSO on ELF platforms)
| LinkStaticLib -- ^ Link objects into a static lib
deriving (Eq, Show)
isNoLink :: GhcLink -> Bool
......@@ -2044,6 +2048,7 @@ dynamic_flags = [
, Flag "pgml" (hasArg (\f -> alterSettings (\s -> s { sPgm_l = (f,[])})))
, Flag "pgmdll" (hasArg (\f -> alterSettings (\s -> s { sPgm_dll = (f,[])})))
, Flag "pgmwindres" (hasArg (\f -> alterSettings (\s -> s { sPgm_windres = f})))
, Flag "pgmlibtool" (hasArg (\f -> alterSettings (\s -> s { sPgm_libtool = f})))
-- need to appear before -optl/-opta to be parsed as LLVM flags.
, Flag "optlo" (hasArg (\f -> alterSettings (\s -> s { sOpt_lo = f : sOpt_lo s})))
......@@ -2078,6 +2083,7 @@ dynamic_flags = [
-------- Linking ----------------------------------------------------
, Flag "no-link" (noArg (\d -> d{ ghcLink=NoLink }))
, Flag "shared" (noArg (\d -> d{ ghcLink=LinkDynLib }))
, Flag "staticlib" (noArg (\d -> d{ ghcLink=LinkStaticLib }))
, Flag "dynload" (hasArg parseDynLibLoaderMode)
, Flag "dylib-install-name" (hasArg setDylibInstallName)
-- -dll-split is an internal flag, used only during the GHC build
......
......@@ -294,7 +294,7 @@ load how_much = do
let
main_mod = mainModIs dflags
a_root_is_Main = any ((==main_mod).ms_mod) mod_graph
do_linking = a_root_is_Main || no_hs_main || ghcLink dflags == LinkDynLib
do_linking = a_root_is_Main || no_hs_main || ghcLink dflags == LinkDynLib || ghcLink dflags == LinkStaticLib
when (ghcLink dflags == LinkBinary
&& isJust ofile && not do_linking) $
......
......@@ -867,16 +867,19 @@ getPackageLibraryPath dflags pkgs =
collectLibraryPaths :: [PackageConfig] -> [FilePath]
collectLibraryPaths ps = nub (filter notNull (concatMap libraryDirs ps))
-- | Find all the link options in these and the preload packages
getPackageLinkOpts :: DynFlags -> [PackageId] -> IO [String]
-- | Find all the link options in these and the preload packages,
-- returning (package hs lib options, extra library options, other flags)
getPackageLinkOpts :: DynFlags -> [PackageId] -> IO ([String], [String], [String])
getPackageLinkOpts dflags pkgs =
collectLinkOpts dflags `fmap` getPreloadPackagesAnd dflags pkgs
collectLinkOpts :: DynFlags -> [PackageConfig] -> [String]
collectLinkOpts dflags ps = concat (map all_opts ps)
where
libs p = packageHsLibs dflags p ++ extraLibraries p
all_opts p = map ("-l" ++) (libs p) ++ ldOptions p
collectLinkOpts :: DynFlags -> [PackageConfig] -> ([String], [String], [String])
collectLinkOpts dflags ps =
(
concatMap (map ("-l" ++) . packageHsLibs dflags) ps,
concatMap (map ("-l" ++) . extraLibraries) ps,
concatMap ldOptions ps
)
packageHsLibs :: DynFlags -> PackageConfig -> [String]
packageHsLibs dflags p = map (mkDynName . addSuffix) (hsLibraries p)
......
......@@ -15,7 +15,7 @@ module SysTools (
runUnlit, runCpp, runCc, -- [Option] -> IO ()
runPp, -- [Option] -> IO ()
runSplit, -- [Option] -> IO ()
runAs, runLink, -- [Option] -> IO ()
runAs, runLink, runLibtool, -- [Option] -> IO ()
runMkDLL,
runWindres,
runLlvmOpt,
......@@ -261,6 +261,7 @@ initSysTools mbMinusB
split_script = installed cGHC_SPLIT_PGM
windres_path <- getSetting "windres command"
libtool_path <- getSetting "libtool command"
tmpdir <- getTemporaryDirectory
......@@ -331,6 +332,7 @@ initSysTools mbMinusB
sPgm_T = touch_path,
sPgm_sysman = top_dir ++ "/ghc/rts/parallel/SysMan",
sPgm_windres = windres_path,
sPgm_libtool = libtool_path,
sPgm_lo = (lo_prog,[]),
sPgm_lc = (lc_prog,[]),
-- Hans: this isn't right in general, but you can
......@@ -717,6 +719,15 @@ runLink dflags args = do
mb_env <- getGccEnv args2
runSomethingFiltered dflags id "Linker" p args2 mb_env
runLibtool :: DynFlags -> [Option] -> IO ()
runLibtool dflags args = do
linkargs <- neededLinkArgs `fmap` getLinkerInfo dflags
let args1 = map Option (getOpts dflags opt_l)
args2 = [Option "-static"] ++ args1 ++ args ++ linkargs
libtool = pgm_libtool dflags
mb_env <- getGccEnv args2
runSomethingFiltered dflags id "Linker" libtool args2 mb_env
runMkDLL :: DynFlags -> [Option] -> IO ()
runMkDLL dflags args = do
let (p,args0) = pgm_dll dflags
......@@ -1220,7 +1231,8 @@ linkDynLib dflags0 o_files dep_packages
pkgs
_ ->
filter ((/= rtsPackageId) . packageConfigId) pkgs
let pkg_link_opts = collectLinkOpts dflags pkgs_no_rts
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
-- probably _stub.o files
let extra_ld_inputs = ldInputs dflags
......@@ -1315,6 +1327,7 @@ linkDynLib dflags0 o_files dep_packages
++ map Option pkg_lib_path_opts
++ map Option pkg_link_opts
)
OSiOS -> throwGhcExceptionIO (ProgramError "dynamic libraries are not supported on iOS target")
_ -> do
-------------------------------------------------------------------
-- Making a DSO
......
......@@ -2084,6 +2084,15 @@
<entry>dynamic</entry>
<entry>-</entry>
</row>
<row>
<entry><option>-staticlib</option></entry>
<entry>On Darwin/OS X/iOS only, generate a standalone static library
(as opposed to an executable).
This is the usual way to compile for iOS.
</entry>
<entry>dynamic</entry>
<entry>-</entry>
</row>
<row>
<entry><option>-fPIC</option></entry>
<entry>Generate position-independent code (where available)</entry>
......@@ -2105,14 +2114,14 @@
</row>
<row>
<entry><option>-framework</option> <replaceable>name</replaceable></entry>
<entry>On Darwin/MacOS X only, link in the framework <replaceable>name</replaceable>.
<entry>On Darwin/OS X/iOS only, link in the framework <replaceable>name</replaceable>.
This option corresponds to the <option>-framework</option> option for Apple's Linker.</entry>
<entry>dynamic</entry>
<entry>-</entry>
</row>
<row>
<entry><option>-framework-path</option> <replaceable>name</replaceable></entry>
<entry>On Darwin/MacOS X only, add <replaceable>dir</replaceable> to the list of
<entry>On Darwin/OS X/iOS only, add <replaceable>dir</replaceable> to the list of
directories searched for frameworks.
This option corresponds to the <option>-F</option> option for Apple's Linker.</entry>
<entry>dynamic</entry>
......@@ -2223,7 +2232,7 @@
<entry>Set the install name (via <literal>-install_name</literal> passed to Apple's
linker), specifying the full install path of the library file. Any libraries
or executables that link with it later will pick up that path as their
runtime search location for it. (Darwin/MacOS X only)</entry>
runtime search location for it. (Darwin/OS X only)</entry>
<entry>dynamic</entry>
<entry>-</entry>
</row>
......@@ -2351,6 +2360,13 @@
<entry>dynamic</entry>
<entry>-</entry>
</row>
<row>
<entry><option>-pgmlibtool</option> <replaceable>cmd</replaceable></entry>
<entry>Use <replaceable>cmd</replaceable> as the command for libtool
(with <option>-staticlib</option> only).</entry>
<entry>dynamic</entry>
<entry>-</entry>
</row>
</tbody>
</tgroup>
</informaltable>
......
......@@ -137,6 +137,17 @@
linkend="options-linker" />.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-pgmlibtool</option> <replaceable>cmd</replaceable>
<indexterm><primary><option>-pgmlibtool</option></primary></indexterm>
</term>
<listitem>
<para>Use <replaceable>cmd</replaceable> as the libtool command
(when using <option>-staticlib</option> only).</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
......@@ -721,7 +732,7 @@ $ cat foo.hspp</screen>
<indexterm><primary><option>-framework</option></primary></indexterm>
</term>
<listitem>
<para>On Darwin/MacOS X only, link in the framework <replaceable>name</replaceable>.
<para>On Darwin/OS X/iOS only, link in the framework <replaceable>name</replaceable>.
This option corresponds to the <option>-framework</option> option for Apple's Linker.
Please note that frameworks and packages are two different things - frameworks don't
contain any haskell code. Rather, they are Apple's way of packaging shared libraries.
......@@ -731,6 +742,21 @@ $ cat foo.hspp</screen>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-staticlib</option>
<indexterm><primary><option>-staticlib</option></primary></indexterm>
</term>
<listitem>
<para>On Darwin/OS X/iOS only, link all passed files into a static library suitable
for linking into an iOS (when using a cross-compiler) or Mac Xcode project. To control
the name, use the <option>-o</option> <replaceable>name</replaceable> option as usual.
The default name is <literal>liba.a</literal>.
This should nearly always be passed when compiling for iOS with a cross-compiler.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-L</option><replaceable>dir</replaceable>
......@@ -749,7 +775,7 @@ $ cat foo.hspp</screen>
<indexterm><primary><option>-framework-path</option></primary></indexterm>
</term>
<listitem>
<para>On Darwin/MacOS X only, prepend the directory <replaceable>dir</replaceable> to
<para>On Darwin/OS X/iOS only, prepend the directory <replaceable>dir</replaceable> to
the framework directories path. This option corresponds to the <option>-F</option>
option for Apple's Linker (<option>-F</option> already means something else for GHC).</para>
</listitem>
......@@ -1189,7 +1215,7 @@ $ cat foo.hspp</screen>
</indexterm>
</term>
<listitem>
<para>On Darwin/MacOS X, dynamic libraries are stamped at build time with an
<para>On Darwin/OS X, dynamic libraries are stamped at build time with an
"install name", which is the ultimate install path of the library file.
Any libraries or executables that subsequently link against it will pick
up that path as their runtime search location for it. By default, ghc sets
......
......@@ -14,6 +14,7 @@
("touch command", "@SettingsTouchCommand@"),
("dllwrap command", "@SettingsDllWrapCommand@"),
("windres command", "@SettingsWindresCommand@"),
("libtool command", "@SettingsLibtoolCommand@"),
("perl command", "@SettingsPerlCommand@"),
("target os", "@HaskellTargetOs@"),
("target arch", "@HaskellTargetArch@"),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment