diff options
Diffstat (limited to 'hadrian/src/Rules/BinaryDist.hs')
-rw-r--r-- | hadrian/src/Rules/BinaryDist.hs | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/hadrian/src/Rules/BinaryDist.hs b/hadrian/src/Rules/BinaryDist.hs new file mode 100644 index 0000000000..f0aeb4b827 --- /dev/null +++ b/hadrian/src/Rules/BinaryDist.hs @@ -0,0 +1,294 @@ +module Rules.BinaryDist where + +import Hadrian.Haskell.Cabal + +import Context +import Expression +import Oracles.Setting +import Packages +import Settings +import Target +import Utilities + +bindistRules :: Rules () +bindistRules = do + root <- buildRootRules + phony "binary-dist" $ do + -- We 'need' all binaries and libraries + targets <- mapM pkgTarget =<< stagePackages Stage1 + need targets + version <- setting ProjectVersion + targetPlatform <- setting TargetPlatformFull + hostOs <- setting BuildOs + hostArch <- setting BuildArch + rtsDir <- pkgIdentifier rts + + let ghcBuildDir = root -/- stageString Stage1 + bindistFilesDir = root -/- "bindist" -/- ghcVersionPretty + ghcVersionPretty = "ghc-" ++ version ++ "-" ++ targetPlatform + distDir = hostArch ++ "-" ++ hostOs ++ "-ghc-" ++ version + rtsIncludeDir = ghcBuildDir -/- "lib" -/- distDir -/- rtsDir + -/- "include" + + -- We create the bindist directory at <root>/bindist/ghc-X.Y.Z-platform/ + -- and populate it with Stage2 build results + createDirectory bindistFilesDir + copyDirectory (ghcBuildDir -/- "bin") bindistFilesDir + copyDirectory (ghcBuildDir -/- "lib") bindistFilesDir + copyDirectory (rtsIncludeDir) bindistFilesDir + {- TODO: Should we ship docs? + need ["docs"] + copyDirectory (root -/- "docs") bindistFilesDir -} + + -- We then 'need' all the files necessary to configure and install + -- (as in, './configure [...] && make install') this build on some + -- other machine. + need $ map (bindistFilesDir -/-) + (["configure", "Makefile"] ++ bindistInstallFiles) + need $ map ((bindistFilesDir -/- "wrappers") -/-) ["check-api-annotations" + , "check-ppr", "ghc", "ghc-iserv", "ghc-pkg", "ghc-split" + , "ghci-script", "ghci", "haddock", "hpc", "hp2ps", "hsc2hs" + , "runghc"] + + -- Finally, we create the archive <root>/bindist/ghc-X.Y.Z-platform.tar.xz + command [Cwd $ root -/- "bindist"] "tar" + [ "-c", "--xz", "-f" + , ghcVersionPretty <.> "tar.xz" + , ghcVersionPretty ] + + -- Prepare binary distribution configure script + -- (generated under <ghc root>/distrib/configure by 'autoreconf') + root -/- "bindist" -/- "ghc-*" -/- "configure" %> \configurePath -> do + ghcRoot <- topDirectory + copyFile (ghcRoot -/- "aclocal.m4") (ghcRoot -/- "distrib" -/- "aclocal.m4") + buildWithCmdOptions [] $ + target (vanillaContext Stage1 ghc) (Autoreconf $ ghcRoot -/- "distrib") [] [] + -- We clean after ourselves, moving the configure script we generated in + -- our bindist dir + removeFile (ghcRoot -/- "distrib" -/- "aclocal.m4") + moveFile (ghcRoot -/- "distrib" -/- "configure") configurePath + + -- Generate the Makefile that enables the "make install" part + root -/- "bindist" -/- "ghc-*" -/- "Makefile" %> \makefilePath -> + writeFile' makefilePath bindistMakefile + + root -/- "bindist" -/- "ghc-*" -/- "wrappers/*" %> \wrapperPath -> + writeFile' wrapperPath $ wrapper (takeFileName wrapperPath) + + -- Copy various configure-related files needed for a working + -- './configure [...] && make install' workflow + -- (see the list of files needed in the 'binary-dist' rule above, before + -- creating the archive). + forM_ bindistInstallFiles $ \file -> + root -/- "bindist" -/- "ghc-*" -/- file %> \dest -> do + ghcRoot <- topDirectory + copyFile (ghcRoot -/- fixup file) dest + + where + 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. +bindistInstallFiles :: [FilePath] +bindistInstallFiles = + [ "config.sub", "config.guess", "install-sh", "mk" -/- "config.mk.in" + , "mk" -/- "install.mk.in", "mk" -/- "project.mk", "settings.in", "README" + , "INSTALL" ] + +-- | This auxiliary function gives us a top-level 'Filepath' that we can 'need' +-- for all libraries and programs that are needed for a complete build. +-- For libraries, it returns the path to the @.conf@ file in the package +-- database. For programs, it returns the path to the compiled executable. +pkgTarget :: Package -> Action FilePath +pkgTarget pkg + | isLibrary pkg = pkgConfFile (vanillaContext Stage1 pkg) + | otherwise = programPath =<< programContext Stage1 pkg + +-- TODO: Augment this Makefile to match the various parameters that the current +-- bindist scripts support. +-- | A trivial Makefile that only takes @$prefix@ into account, and not e.g +-- @$datadir@ (for docs) and other variables, yet. +bindistMakefile :: String +bindistMakefile = unlines + [ "MAKEFLAGS += --no-builtin-rules" + , ".SUFFIXES:" + , "" + , "include mk/install.mk" + , "include mk/config.mk" + , "" + , ".PHONY: default" + , "default:" + , "\t@echo 'Run \"make install\" to install'" + , "\t@false" + , "" + , "#------------------------------------------------------------------------------" + , "# INSTALL RULES" + , "" + , "# Hacky function to check equality of two strings" + , "# TODO : find if a better function exists" + , "eq=$(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))" + , "" + , "define installscript" + , "# $1 = package name" + , "# $2 = wrapper path" + , "# $3 = bindir" + , "# $4 = ghcbindir" + , "# $5 = Executable binary path" + , "# $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" + , "\trm -f $2" + , "\t$(CREATE_SCRIPT) $2" + , "\t@echo \"#!$(SHELL)\" >> $2" + , "\t@echo \"exedir=\\\"$4\\\"\" >> $2" + , "\t@echo \"exeprog=\\\"$1\\\"\" >> $2" + , "\t@echo \"executablename=\\\"$5\\\"\" >> $2" + , "\t@echo \"bindir=\\\"$3\\\"\" >> $2" + , "\t@echo \"libdir=\\\"$6\\\"\" >> $2" + , "\t@echo \"docdir=\\\"$7\\\"\" >> $2" + , "\t@echo \"includedir=\\\"$8\\\"\" >> $2" + , "\t@echo \"\" >> $2 " + , "\tcat wrappers/$1 >> $2" + , "\t$(EXECUTABLE_FILE) $2 ;" + , "endef" + , "" + , "# 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" + , "" + , "# 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" + , "" + , "# We need to install binaries relative to libraries." + , "BINARIES = $(wildcard ./bin/*)" + , "install_bin:" + , "\t@echo \"Copying Binaries to $(GHCBINDIR)\"" + , "\t$(INSTALL_DIR) \"$(GHCBINDIR)\"" + , "\tfor i in $(BINARIES); do \\" + , "\t\tcp -R $$i \"$(GHCBINDIR)\"; \\" + , "\tdone" + , "\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" + , "" + , "LIBRARIES = $(wildcard ./lib/*)" + , "install_lib:" + , "\t@echo \"Copying libraries to $(libdir)\"" + , "\t$(INSTALL_DIR) \"$(libdir)\"" + , "\tfor i in $(LIBRARIES); do \\" + , "\t\tcp -R $$i \"$(libdir)/\"; \\" + , "\tdone" + , "" + , "INCLUDES = $(wildcard ./include/*)" + , "install_includes:" + , "\t@echo \"Copying libraries to $(includedir)\"" + , "\t$(INSTALL_DIR) \"$(includedir)\"" + , "\tfor i in $(INCLUDES); do \\" + , "\t\tcp -R $$i \"$(includedir)/\"; \\" + , "\tdone" + , "" + , "DOCS = $(wildcard ./docs/*)" + , "install_docs:" + , "\t@echo \"Copying libraries to $(docdir)\"" + , "\t$(INSTALL_DIR) \"$(docdir)\"" + , "\tfor i in $(DOCS); do \\" + , "\t\tcp -R $$i \"$(docdir)/\"; \\" + , "\tdone" + , "" + , "BINARY_NAMES=$(shell ls ./bin/)" + , "install_wrappers:" + , "\t@echo \"Installing Wrapper scripts\"" + , "\t$(INSTALL_DIR) \"$(bindir)\"" + , "\t$(foreach p, $(BINARY_NAMES),\\" + , "\t\t$(call installscript,$p,$(bindir)/$p,$(bindir),$(GHCBINDIR),$(GHCBINDIR)/$p,$(libdir),$(docdir),$(includedir)))" + , "" + , "# END INSTALL" + , "# -----------------------------------------------------------------------------" ] + +wrapper :: FilePath -> String +wrapper "ghc" = ghcWrapper +wrapper "ghc-pkg" = ghcPkgWrapper +wrapper "ghci" = ghciWrapper +wrapper "ghci-script" = ghciScriptWrapper +wrapper "haddock" = haddockWrapper +wrapper "hsc2hs" = hsc2hsWrapper +wrapper "runghc" = runGhcWrapper +wrapper _ = commonWrapper + +-- | Wrapper scripts for different programs. Common is default wrapper. + +ghcWrapper :: String +ghcWrapper = "exec \"$executablename\" -B\"$libdir\" ${1+\"$@\"}\n" + +ghcPkgWrapper :: String +ghcPkgWrapper = unlines + [ "PKGCONF=\"$libdir/package.conf.d\"" + , "exec \"$executablename\" --global-package-db \"$PKGCONF\" ${1+\"$@\"}" ] + +ghciWrapper :: String +ghciWrapper = "exec \"$executablename\" --interactive \"$@\"\n" + +haddockWrapper :: String +haddockWrapper = "exec \"$executablename\" -B\"$libdir\" -l\"$libdir\" ${1+\"$@\"}\n" + +commonWrapper :: String +commonWrapper = "exec \"$executablename\" ${1+\"$@\"}\n" + +hsc2hsWrapper :: String +hsc2hsWrapper = unlines + [ "HSC2HS_EXTRA=\"--cflag=-fno-stack-protector --lflag=-fuse-ld=gold\"" + , "tflag=\"--template=$libdir/template-hsc.h\"" + , "Iflag=\"-I$includedir/\"" + , "for arg do" + , " case \"$arg\" in" + , "# On OS X, we need to specify -m32 or -m64 in order to get gcc to" + , "# build binaries for the right target. We do that by putting it in" + , "# HSC2HS_EXTRA. When cabal runs hsc2hs, it passes a flag saying which" + , "# gcc to use, so if we set HSC2HS_EXTRA= then we don't get binaries" + , "# for the right platform. So for now we just don't set HSC2HS_EXTRA=" + , "# but we probably want to revisit how this works in the future." + , "# -c*) HSC2HS_EXTRA=;;" + , "# --cc=*) HSC2HS_EXTRA=;;" + , " -t*) tflag=;;" + , " --template=*) tflag=;;" + , " --) break;;" + , " esac" + , "done" + , "exec \"$executablename\" ${tflag:+\"$tflag\"} $HSC2HS_EXTRA ${1+\"$@\"} \"$Iflag\"" ] + +runGhcWrapper :: String +runGhcWrapper = "exec \"$executablename\" -f \"$exedir/ghc\" ${1+\"$@\"}\n" + +-- | We need to ship ghci executable, which basically just calls ghc with +-- | --interactive flag. +ghciScriptWrapper :: String +ghciScriptWrapper = unlines + [ "DIR=`dirname \"$0\"`" + , "executable=\"$DIR/ghc\"" + , "exec $executable --interactive \"$@\"" ] |