From fe23629b147d419053052e6e881f6e8ddfbf3bae Mon Sep 17 00:00:00 2001
From: Matthew Pickering <matthewtpickering@gmail.com>
Date: Thu, 17 Aug 2023 10:47:46 +0100
Subject: [PATCH] hadrian: Add reloc-binary-dist-* targets

This adds a command line option to build a "relocatable" bindist.

The bindist is created by first creating a normal bindist and then
installing it using the `RelocatableBuild=YES` option. This creates a
bindist without any wrapper scripts pointing to the libdir.

The motivation for this feature is that we want to ship relocatable
bindists on windows and this method is more uniform than the ad-hoc
method which lead to bugs such as #23608 and #23476

The relocatable bindist can be built with the "reloc-binary-dist" target
and supports the same suffixes as the normal "binary-dist" command to
specify the compression style.
---
 hadrian/README.md               |  7 +++++
 hadrian/src/Rules/BinaryDist.hs | 49 ++++++++++++++++++++++++---------
 2 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/hadrian/README.md b/hadrian/README.md
index c01f806a8ead..19fe12c10e9a 100644
--- a/hadrian/README.md
+++ b/hadrian/README.md
@@ -325,6 +325,13 @@ $ ./configure [--prefix=PATH] && make install
 
 workflow, for now.
 
+Note: On windows you need to use the `reloc-binary-dist` target.
+
+#### Relocatable Binary Distribution
+
+If you require a relocatable binary distribution (for example on Windows), then you
+can build the `reloc-binary-dist` target.
+
 ### Building and installing GHC
 
 You can get Hadrian to build _and_ install a binary distribution in one go
diff --git a/hadrian/src/Rules/BinaryDist.hs b/hadrian/src/Rules/BinaryDist.hs
index dfe2ec8d2beb..3b39ad1453a4 100644
--- a/hadrian/src/Rules/BinaryDist.hs
+++ b/hadrian/src/Rules/BinaryDist.hs
@@ -110,20 +110,40 @@ other, the install script:
 
 -}
 
+data Relocatable = Relocatable | NotRelocatable
+
+installTo :: Relocatable -> String -> Action ()
+installTo relocatable prefix = do
+    root <- buildRoot
+    version        <- setting ProjectVersion
+    targetPlatform <- setting TargetPlatformFull
+    let ghcVersionPretty = "ghc-" ++ version ++ "-" ++ targetPlatform
+        bindistFilesDir  = root -/- "bindist" -/- ghcVersionPretty
+    runBuilder (Configure bindistFilesDir) ["--prefix="++prefix] [] []
+    let env = case relocatable of
+                Relocatable -> [AddEnv "RelocatableBuild" "YES"]
+                NotRelocatable -> []
+    runBuilderWithCmdOptions env (Make bindistFilesDir) ["install"] [] []
+
 bindistRules :: Rules ()
 bindistRules = do
     root <- buildRootRules
-    phony "install" $ do
+    phony "reloc-binary-dist-dir" $ do
         need ["binary-dist-dir"]
+        cwd <- liftIO $ IO.getCurrentDirectory
         version        <- setting ProjectVersion
         targetPlatform <- setting TargetPlatformFull
         let ghcVersionPretty = "ghc-" ++ version ++ "-" ++ targetPlatform
-            bindistFilesDir  = root -/- "bindist" -/- ghcVersionPretty
-            prefixErr = "You must specify a path with --prefix when using the"
+        let prefix = cwd -/- root -/- "reloc-bindist" -/- ghcVersionPretty
+        installTo Relocatable prefix
+
+
+    phony "install" $ do
+        need ["binary-dist-dir"]
+        let prefixErr = "You must specify a path with --prefix when using the"
                      ++ " 'install' rule"
         installPrefix <- fromMaybe (error prefixErr) <$> cmdPrefix
-        runBuilder (Configure bindistFilesDir) ["--prefix="++installPrefix] [] []
-        runBuilder (Make bindistFilesDir) ["install"] [] []
+        installTo NotRelocatable installPrefix
 
     phony "binary-dist-dir" $ do
         -- We 'need' all binaries and libraries
@@ -284,10 +304,12 @@ bindistRules = do
                 IO.removeFile unversioned_wrapper_path <|> return ()
                 IO.createFileLink versioned_wrapper unversioned_wrapper_path
 
+    let buildBinDist = buildBinDistX "binary-dist-dir" "bindist"
+        buildBinDistReloc = buildBinDistX "reloc-binary-dist-dir" "reloc-bindist"
 
-    let buildBinDist :: Compressor -> Action ()
-        buildBinDist compressor = do
-            need ["binary-dist-dir"]
+        buildBinDistX :: String -> FilePath -> Compressor -> Action ()
+        buildBinDistX target bindist_folder compressor = do
+            need [target]
 
             version        <- setting ProjectVersion
             targetPlatform <- setting TargetPlatformFull
@@ -296,15 +318,16 @@ bindistRules = do
 
             -- Finally, we create the archive <root>/bindist/ghc-X.Y.Z-platform.tar.xz
             tarPath <- builderPath (Tar Create)
-            cmd [Cwd $ root -/- "bindist"] tarPath
+            cmd [Cwd $ root -/- bindist_folder] tarPath
                 [ "-c", compressorTarFlag compressor, "-f"
                 , ghcVersionPretty <.> "tar" <.> compressorExtension compressor
                 , ghcVersionPretty ]
 
-    phony "binary-dist" $ buildBinDist Xz
-    phony "binary-dist-gzip" $ buildBinDist Gzip
-    phony "binary-dist-bzip2" $ buildBinDist Bzip2
-    phony "binary-dist-xz" $ buildBinDist Xz
+    forM_ [("binary", buildBinDist), ("reloc-binary", buildBinDistReloc)] $ \(name, mk_bindist) -> do
+      phony (name <> "-dist") $ mk_bindist Xz
+      phony (name <> "-dist-gzip") $ mk_bindist Gzip
+      phony (name <> "-dist-bzip2") $ mk_bindist Bzip2
+      phony (name <> "-dist-xz") $ mk_bindist Xz
 
     -- Prepare binary distribution configure script
     -- (generated under <ghc root>/distrib/configure by 'autoreconf')
-- 
GitLab