Always use -Xlinker for -rpath
authorBartosz Nitka <niteria@gmail.com>
Sat, 21 Jan 2017 17:59:55 +0000 (09:59 -0800)
committerBartosz Nitka <niteria@gmail.com>
Sat, 21 Jan 2017 18:00:12 +0000 (10:00 -0800)
Currently we use `-Wl` which takes a list of
comma-separated options. Unfortunately that
breaks when you use it with `-rpath` and
a path that has commas in them.
Buck, the build system, produces paths with
commas in them.

`-Xlinker` doesn't have this disadvantage
and as far as I can tell is supported by
both `gcc` and `clang`. Anecdotally `nvcc`
supports `-Xlinker`, but not `-Wl`.

Test Plan: ./validate, harbourmaster

Reviewers: nomeata, simonmar, austin, bgamari, hvr

Reviewed By: simonmar, bgamari

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D2971

compiler/ghci/Linker.hs
compiler/main/DriverPipeline.hs
compiler/main/SysTools.hs
testsuite/driver/extra_files.py
testsuite/tests/th/TH_linker/Dummy.hs [new file with mode: 0644]
testsuite/tests/th/TH_linker/Main.hs [new file with mode: 0644]
testsuite/tests/th/TH_linker/Makefile [new file with mode: 0644]
testsuite/tests/th/TH_linker/all.T [new file with mode: 0644]
testsuite/tests/th/TH_linker/path_with_commas.stdout [new file with mode: 0644]
testsuite/tests/th/TH_linker/test.pkg [new file with mode: 0644]

index 9252489..76c1cda 100644 (file)
@@ -880,18 +880,23 @@ dynLoadObjs hsc_env pls objs = do
                         concatMap
                             (\(lp, l) ->
                                  [ Option ("-L" ++ lp)
-                                 , Option ("-Wl,-rpath")
-                                 , Option ("-Wl," ++ lp)
+                                 , Option "-Xlinker"
+                                 , Option "-rpath"
+                                 , Option "-Xlinker"
+                                 , Option lp
                                  , Option ("-l" ++  l)
                                  ])
                             (temp_sos pls)
                         ++ concatMap
                              (\lp ->
                                  [ Option ("-L" ++ lp)
-                                 , Option ("-Wl,-rpath")
-                                 , Option ("-Wl," ++ lp)
+                                 , Option "-Xlinker"
+                                 , Option "-rpath"
+                                 , Option "-Xlinker"
+                                 , Option lp
                                  ])
                              minus_big_ls
+                        -- See Note [-Xlinker -rpath vs -Wl,-rpath]
                         ++ map (\l -> Option ("-l" ++ l)) minus_ls,
                       -- Add -l options and -L options from dflags.
                       --
index a54e05c..08af37c 100644 (file)
@@ -1742,6 +1742,20 @@ getHCFilePackages filename =
 -- read any interface files), so the user must explicitly specify all
 -- the packages.
 
+{-
+Note [-Xlinker -rpath vs -Wl,-rpath]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-Wl takes a comma-separated list of options which in the case of
+-Wl,-rpath -Wl,some,path,with,commas parses the the path with commas
+as separate options.
+Buck, the build system, produces paths with commas in them.
+
+-Xlinker doesn't have this disadvantage and as far as I can tell
+it is supported by both gcc and clang. Anecdotally nvcc supports
+-Xlinker, but not -Wl.
+-}
+
 linkBinary :: DynFlags -> [FilePath] -> [InstalledUnitId] -> IO ()
 linkBinary = linkBinary' False
 
@@ -1770,8 +1784,9 @@ linkBinary' staticLink dflags o_files dep_packages = do
                             then "$ORIGIN" </>
                                  (l `makeRelativeTo` full_output_fn)
                             else l
+                  -- See Note [-Xlinker -rpath vs -Wl,-rpath]
                   rpath = if gopt Opt_RPath dflags
-                          then ["-Wl,-rpath",      "-Wl," ++ libpath]
+                          then ["-Xlinker", "-rpath", "-Xlinker", libpath]
                           else []
                   -- Solaris 11's linker does not support -rpath-link option. It silently
                   -- ignores it and then complains about next option which is -l<some
@@ -1781,7 +1796,7 @@ linkBinary' staticLink dflags o_files dep_packages = do
                   -- elf_begin: I/O error: region read: Is a directory
                   rpathlink = if (platformOS platform) == OSSolaris2
                               then []
-                              else ["-Wl,-rpath-link", "-Wl," ++ l]
+                              else ["-Xlinker", "-rpath-link", "-Xlinker", l]
               in ["-L" ++ l] ++ rpathlink ++ rpath
          | osMachOTarget (platformOS platform) &&
            dynLibLoader dflags == SystemDependent &&
@@ -1791,7 +1806,7 @@ linkBinary' staticLink dflags o_files dep_packages = do
                             then "@loader_path" </>
                                  (l `makeRelativeTo` full_output_fn)
                             else l
-              in ["-L" ++ l] ++ ["-Wl,-rpath", "-Wl," ++ libpath]
+              in ["-L" ++ l] ++ ["-Xlinker", "-rpath", "-Xlinker", libpath]
          | otherwise = ["-L" ++ l]
 
     let dead_strip = if osSubsectionsViaSymbols (platformOS platform)
index ea3c461..5bd9fd1 100644 (file)
@@ -1588,7 +1588,8 @@ linkDynLib dflags0 o_files dep_packages
              osMachOTarget (platformOS (targetPlatform dflags)) ) &&
            dynLibLoader dflags == SystemDependent &&
            WayDyn `elem` ways dflags
-            = ["-L" ++ l, "-Wl,-rpath", "-Wl," ++ l]
+            = ["-L" ++ l, "-Xlinker", "-rpath", "-Xlinker", l]
+              -- See Note [-Xlinker -rpath vs -Wl,-rpath]
          | otherwise = ["-L" ++ l]
 
     let lib_paths = libraryPaths dflags
index 217e5d4..3f2cf5c 100644 (file)
@@ -428,6 +428,7 @@ extra_src_files = {
   'parser.prog001': ['Read006.hs', 'Read007.hs'],
   'pat-syn-bundle': ['Bundle1.hs', 'BundleInternal1.hs'],
   'pat-syn-trans-bundle': ['Bundle.hs', 'BundleInternal.hs', 'TransBundle.hs'],
+  'path_with_commas': ['test.pkg', 'Main.hs', 'Dummy.hs'],
   'pkg02': ['A.hs', 'Foreign.hs'],
   'plugins01': ['simple-plugin/'],
   'plugins02': ['simple-plugin/'],
diff --git a/testsuite/tests/th/TH_linker/Dummy.hs b/testsuite/tests/th/TH_linker/Dummy.hs
new file mode 100644 (file)
index 0000000..9be471c
--- /dev/null
@@ -0,0 +1 @@
+module Dummy where
diff --git a/testsuite/tests/th/TH_linker/Main.hs b/testsuite/tests/th/TH_linker/Main.hs
new file mode 100644 (file)
index 0000000..0530199
--- /dev/null
@@ -0,0 +1,7 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Main where
+
+import Language.Haskell.TH
+
+main :: IO ()
+main = putStrLn $(return $ LitE $ StringL "hello")
diff --git a/testsuite/tests/th/TH_linker/Makefile b/testsuite/tests/th/TH_linker/Makefile
new file mode 100644 (file)
index 0000000..84f7760
--- /dev/null
@@ -0,0 +1,40 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+DIR='test,path'
+PKGCONF=$(DIR)/local.package.conf
+LOCAL_GHC_PKG = '$(GHC_PKG)' --no-user-package-db -f $(PKGCONF)
+
+ifeq "$(WINDOWS)" "YES"
+DLL = lib$1.dll
+else ifeq "$(DARWIN)" "YES"
+DLL = lib$1.dylib
+else
+DLL = lib$1.so
+endif
+
+ifeq "$(WINDOWS)" "YES"
+EXE = $1.exe
+else ifeq "$(DARWIN)" "YES"
+EXE = $1
+else
+EXE = $1
+endif
+
+# Check if paths with commas work
+# Previously, the linker would use -Wl,-rpath -Wl,test,path to pass the
+# path and that would treat test and path as 2 different arguments
+path_with_commas :
+       @rm -rf $(DIR)
+       mkdir -p $(DIR)
+       $(LOCAL_GHC_PKG) init $(PKGCONF)
+       # Create shared library 'foo' from Dummy.hs
+       '$(TEST_HC)' $(TEST_HC_OPTS) -shared -dynamic Dummy.hs -odir $(DIR) -hidir $(DIR) -o $(DIR)/$(call DLL,foo) > /dev/null
+       # Make a package that forces trying to load it
+       $(LOCAL_GHC_PKG) register --force test.pkg
+
+       @rm -rf Main.{hi,o} Main
+       # Because we compile with -package testpkg, TH has to load 'foo'
+       '$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) -package-db $(PKGCONF) -package testpkg Main.hs
+       ./$(call EXE,Main)
diff --git a/testsuite/tests/th/TH_linker/all.T b/testsuite/tests/th/TH_linker/all.T
new file mode 100644 (file)
index 0000000..8feac99
--- /dev/null
@@ -0,0 +1,4 @@
+test('path_with_commas',
+     ignore_stderr,
+     run_command,
+     ['$MAKE -s --no-print-directory path_with_commas'])
diff --git a/testsuite/tests/th/TH_linker/path_with_commas.stdout b/testsuite/tests/th/TH_linker/path_with_commas.stdout
new file mode 100644 (file)
index 0000000..0621c24
--- /dev/null
@@ -0,0 +1,4 @@
+Reading package info from "test.pkg" ... done.
+[1 of 1] Compiling Main             ( Main.hs, Main.o )
+Linking Main ...
+hello
diff --git a/testsuite/tests/th/TH_linker/test.pkg b/testsuite/tests/th/TH_linker/test.pkg
new file mode 100644 (file)
index 0000000..2fc82cb
--- /dev/null
@@ -0,0 +1,7 @@
+name: testpkg
+version: 1.2.3.4
+id: testpkg-1.2.3.4-XXX
+key: testpkg-1.2.3.4-XXX
+exposed: True
+library-dirs: "${pkgroot}"
+extra-libraries: foo