Hadrian: improve bindist rule
authorAlp Mestanogullari <alp@well-typed.com>
Tue, 27 Nov 2018 12:17:53 +0000 (13:17 +0100)
committerAlp Mestanogullari <alpmestan@gmail.com>
Tue, 27 Nov 2018 12:18:44 +0000 (13:18 +0100)
As outlined in #15925, hadrian bindists had not made a clear choice with
respect to relocatable GHCs and wrapper scripts. This commit implements
the policy described in the ticket. That is:

- the bindists ship {bin, lib} as they are, modulo the addition of
  haddock from stage2/bin
- we now _always_ generate wrapper scripts for all the programs that
  are in the bindist's bin/ directory

The idea being that anyone on Linux/Windows/OS X can just unpack
the binary distribution anywhere and start using bin/ghc, while the
installation process systematicaly generates wrapper scripts.

Test Plan: hadrian/build.sh binary-dist ; cd
_build/bindist/ghc-X.Y.Z-arch/; configure --prefix=/tmp/foo && make
install

Reviewers: snowleopard, bgamari, angerman

Reviewed By: snowleopard, bgamari, angerman

Subscribers: rwbarton, carter

GHC Trac Issues: #15925

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

hadrian/src/Rules/BinaryDist.hs

index 667fbf1..9cf442c 100644 (file)
@@ -10,6 +10,84 @@ import Settings
 import Target
 import Utilities
 
+{-
+Note [Binary distributions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Hadrian produces binary distributions under:
+  <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>.tar.xz
+
+It is generated by creating an archive from:
+  <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/
+
+It does so by following the steps below.
+
+- make sure we have a complete stage 2 compiler + haddock
+
+- copy the bin and lib directories of the compiler we built:
+    <build root>/stage1/{bin, lib}
+  to
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/{bin, lib}
+
+- copy the generated docs (user guide, haddocks, etc):
+    <build root>/docs/
+  to
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/docs/
+
+- copy haddock (built by our stage2 compiler):
+    <build root>/stage2/bin/haddock
+  to
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/bin/haddock
+
+- use autoreconf to generate a `configure` script from
+  aclocal.m4 and distrib/configure.ac, that we move to:
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/configure
+
+- write a (fixed) Makefile capable of supporting 'make install' to:
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/Makefile
+
+- write some (fixed) supporting bash code for the wrapper scripts to:
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/wrappers/<program>
+
+  where <program> is the name of the executable that the bash file will
+  help wrapping.
+
+- copy supporting configure/make related files
+  (see @bindistInstallFiles@) to:
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/<file>
+
+- create a .tar.xz archive of the directory:
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>/
+  at
+    <build root>/bindist/ghc-<X>.<Y>.<Z>-<arch>-<os>.tar.xz
+
+
+Note [Wrapper scripts and binary distributions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Users of Linux, FreeBSD, Windows and OS X can unpack a
+binary distribution produced by hadrian for their arch
+and OS and start using @bin/ghc@, @bin/ghc-pkg@ and so on
+right away, without even having to configure or install
+the distribution. They would then be using the real executables
+directly, not through wrapper scripts.
+
+This works because GHCs produced by hadrian on those systems
+are relocatable. This means that you can copy the @bin@ and @lib@
+dirs anywhere and GHC will keep working, as long as both
+directories sit next to each other. (This is achieved by having
+GHC look up its $libdir relatively to where the GHC executable
+resides.)
+
+It is however still possible (and simple) to install a GHC
+distribution that uses wrapper scripts. From the unpacked archive,
+you can simply do:
+
+  ./configure --prefix=<path> [... other configure options ...]
+  make install
+
+-}
+
 bindistRules :: Rules ()
 bindistRules = do
     root <- buildRootRules
@@ -17,6 +95,7 @@ bindistRules = do
         -- We 'need' all binaries and libraries
         targets <- mapM pkgTarget =<< stagePackages Stage1
         need targets
+
         version        <- setting ProjectVersion
         targetPlatform <- setting TargetPlatformFull
         cabalHostOs    <- cabalOsString <$> setting BuildOs
@@ -36,9 +115,13 @@ bindistRules = do
         copyDirectory (ghcBuildDir -/- "bin") bindistFilesDir
         copyDirectory (ghcBuildDir -/- "lib") bindistFilesDir
         copyDirectory (rtsIncludeDir)         bindistFilesDir
-        {- TODO: Should we ship docs?
         need ["docs"]
-        copyDirectory (root -/- "docs") bindistFilesDir -}
+        copyDirectory (root -/- "docs") bindistFilesDir
+
+        -- We copy the binary (<build root>/stage2/bin/haddock) to
+        -- the bindist's bindir (<build root>/bindist/ghc-.../bin/).
+        haddockPath <- programPath (vanillaContext Stage2 haddock)
+        copyFile haddockPath (bindistFilesDir -/- "bin" -/- "haddock")
 
         -- We then 'need' all the files necessary to configure and install
         -- (as in, './configure [...] && make install') this build on some
@@ -88,9 +171,8 @@ bindistRules = do
     fixup f | f `elem` ["INSTALL", "README"] = "distrib" -/- f
             | otherwise                      = f
 
--- TODO: This list is surely incomplete -- fix this.
 -- | A list of files that allow us to support a simple
--- @./configure [--prefix=PATH] && make install@ workflow.
+-- @./configure [...] && make install@ workflow.
 bindistInstallFiles :: [FilePath]
 bindistInstallFiles =
     [ "config.sub", "config.guess", "install-sh", "mk" -/- "config.mk.in"
@@ -123,7 +205,7 @@ bindistMakefile = unlines
     , "\t@echo 'Run \"make install\" to install'"
     , "\t@false"
     , ""
-    , "#------------------------------------------------------------------------------"
+    , "#-----------------------------------------------------------------------"
     , "# INSTALL RULES"
     , ""
     , "# Hacky function to check equality of two strings"
@@ -139,12 +221,11 @@ bindistMakefile = unlines
     , "# $6 = Library Directory"
     , "# $7 = Docs Directory"
     , "# $8 = Includes Directory"
-    , "# We are installing wrappers to programs by searching corresponding wrappers."
-    , "# If wrapper is not found, we are attaching the common wrapper to it "
-    , "# This implementation is a bit hacky and depends on consistency of program"
-    , "# names. For hadrian build this will work as programs have a consistent "
-    , "# naming procefure. This file is tested on Linux(Ubuntu)"
-    , "# TODO : Check implementation in other distributions"
+    , "# We are installing wrappers to programs by searching corresponding"
+    , "# wrappers. If wrapper is not found, we are attaching the common wrapper"
+    , "# to it. This implementation is a bit hacky and depends on consistency"
+    , "# of program names. For hadrian build this will work as programs have a"
+    , "# consistent naming procedure."
     , "\trm -f $2"
     , "\t$(CREATE_SCRIPT) $2"
     , "\t@echo \"#!$(SHELL)\" >>  $2"
@@ -162,40 +243,29 @@ bindistMakefile = unlines
     , ""
     , "# QUESTION : should we use shell commands?"
     , ""
-    , "# Due to the fact that package database is configured relatively"
-    , "# We do not change the relative paths of executables and libraries"
-    , "# But instead use wrapper scripts whenever necessary"
-    , "LIBPARENT = $(shell dirname $(libdir))"
-    , "GHCBINDIR = \"$(LIBPARENT)/bin\""
     , ""
     , ".PHONY: install"
-    , "install: install_bin install_lib install_includes"
-    , ""
-    , "# Check if we need to install docs"
-    , "ifeq \"DOCS\" \"YES\""
-    , "install: install_docs"
-    , "endif"
+    , "install: install_lib install_bin install_includes"
+    , "install: install_docs install_wrappers install_ghci"
     , ""
-    , "# If the relative path of binaries and libraries are altered, we will need to"
-    , "# install additional wrapper scripts at bindir."
-    , "ifneq \"$(LIBPARENT)/bin\" \"$(bindir)\""
-    , "install: install_wrappers"
-    , "endif"
+    , "ActualBinsDir=${ghclibdir}/bin"
+    , "WrapperBinsDir=${bindir}"
     , ""
     , "# We need to install binaries relative to libraries."
     , "BINARIES = $(wildcard ./bin/*)"
     , "install_bin:"
-    , "\t@echo \"Copying Binaries to $(GHCBINDIR)\""
-    , "\t$(INSTALL_DIR) \"$(GHCBINDIR)\""
+    , "\t@echo \"Copying binaries to $(ActualBinsDir)\""
+    , "\t$(INSTALL_DIR) \"$(ActualBinsDir)\""
     , "\tfor i in $(BINARIES); do \\"
-    , "\t\tcp -R $$i \"$(GHCBINDIR)\"; \\"
+    , "\t\tcp -R $$i \"$(ActualBinsDir)\"; \\"
     , "\tdone"
+    , ""
+    , "install_ghci:"
     , "\t@echo \"Copying and installing ghci\""
-    , "\trm -f $(GHCBINDIR)/dir"
-    , "\t$(CREATE_SCRIPT) $(GHCBINDIR)/ghci"
-    , "\t@echo \"#!$(SHELL)\" >>  $(GHCBINDIR)/ghci"
-    , "\tcat wrappers/ghci-script >> $(GHCBINDIR)/ghci"
-    , "\t$(EXECUTABLE_FILE) $(GHCBINDIR)/ghci"
+    , "\t$(CREATE_SCRIPT) $(WrapperBinsDir)/ghci"
+    , "\t@echo \"#!$(SHELL)\" >>  $(WrapperBinsDir)/ghci"
+    , "\tcat wrappers/ghci-script >> $(WrapperBinsDir)/ghci"
+    , "\t$(EXECUTABLE_FILE) $(WrapperBinsDir)/ghci"
     , ""
     , "LIBRARIES = $(wildcard ./lib/*)"
     , "install_lib:"
@@ -224,12 +294,15 @@ bindistMakefile = unlines
     , "BINARY_NAMES=$(shell ls ./bin/)"
     , "install_wrappers:"
     , "\t@echo \"Installing Wrapper scripts\""
-    , "\t$(INSTALL_DIR) \"$(bindir)\""
+    , "\t$(INSTALL_DIR) \"$(WrapperBinsDir)\""
     , "\t$(foreach p, $(BINARY_NAMES),\\"
-    , "\t\t$(call installscript,$p,$(bindir)/$p,$(bindir),$(GHCBINDIR),$(GHCBINDIR)/$p,$(libdir),$(docdir),$(includedir)))"
+    , "\t\t$(call installscript,$p,$(WrapperBinsDir)/$p," ++
+      "$(WrapperBinsDir),$(ActualBinsDir),$(ActualBinsDir)/$p," ++
+      "$(libdir),$(docdir),$(includedir)))"
     , ""
     , "# END INSTALL"
-    , "# -----------------------------------------------------------------------------" ]
+    , "# ----------------------------------------------------------------------"
+    ]
 
 wrapper :: FilePath -> String
 wrapper "ghc"         = ghcWrapper