git-annex/Build/Standalone.hs
Joey Hess c85d5a0dc8
OsPath conversion of linuxstandalone builder
Sponsored-by: Joshua Antonishen
2025-02-11 12:12:27 -04:00

240 lines
7.9 KiB
Haskell

{- Makes standalone bundle.
-
- Copyright 2012-2020 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import System.Environment (getArgs)
import Control.Monad.IfElse
import System.Posix.Files
import Control.Monad
import qualified Data.ByteString.Lazy as L
import qualified Data.Map as M
import Utility.SafeCommand
import Utility.Process
import Utility.OsPath
import Utility.Path
import Utility.Path.AbsRel
import Utility.Directory
import Utility.Env
import Utility.SystemDirectory
import Build.BundledPrograms
#ifdef darwin_HOST_OS
import System.IO
import Build.OSXMkLibs (mklibs)
import Build.Version
import Utility.Split
#else
import Build.LinuxMkLibs (mklibs)
import Utility.FileMode
#endif
progDir :: OsPath -> OsPath
#ifdef darwin_HOST_OS
progDir topdir = topdir
#else
progDir topdir = topdir </> literalOsPath "bin"
#endif
extraProgDir :: OsPath -> OsPath
extraProgDir topdir = topdir </> "extra"
installProg :: OsPath -> OsPath -> IO (OsPath, OsPath)
installProg dir prog = searchPath (fromOsPath prog) >>= go
where
go Nothing = error $ "cannot find " ++ fromOsPath prog ++ " in PATH"
go (Just f) = do
let dest = dir </> takeFileName f
unlessM (boolSystem "install" [File (fromOsPath f), File (fromOsPath dest)]) $
error $ "install failed for " ++ fromOsPath prog
return (dest, f)
installBundledPrograms :: OsPath -> IO (M.Map OsPath OsPath)
installBundledPrograms topdir = M.fromList . concat <$> mapM go
[ (progDir topdir, map toOsPath preferredBundledPrograms)
, (extraProgDir topdir, map toOsPath extraBundledPrograms)
]
where
go (dir, progs) = do
createDirectoryIfMissing True dir
forM progs $ installProg dir
installGitLibs :: OsPath -> IO ()
installGitLibs topdir = do
-- install git-core programs; these are run by the git command
createDirectoryIfMissing True gitcoredestdir
execpath <- getgitpath "exec-path"
cfs <- dirContents execpath
forM_ cfs $ \f -> do
let f' = fromOsPath f
destf <- (gitcoredestdir </>)
<$> relPathDirToFile execpath f
createDirectoryIfMissing True (takeDirectory destf)
issymlink <- isSymbolicLink <$> getSymbolicLinkStatus f'
if issymlink
then do
-- many git-core files may symlink to eg
-- ../../bin/git, which is located outside
-- the git-core directory. The target of
-- such links is installed into the progDir
-- (if not already there), and the links
-- repointed to it.
--
-- Other git-core files symlink to a file
-- beside them in the directory. Those
-- links can be copied as-is.
linktarget <- toOsPath <$> readSymbolicLink f'
if takeFileName linktarget == linktarget
then cp f destf
else do
let linktarget' = progDir topdir </> takeFileName linktarget
unlessM (doesFileExist linktarget') $ do
createDirectoryIfMissing True (takeDirectory linktarget')
L.readFile f' >>= L.writeFile (fromOsPath linktarget')
removeWhenExistsWith removeFile destf
rellinktarget <- relPathDirToFile
(takeDirectory destf)
(linktarget')
createSymbolicLink (fromOsPath rellinktarget) (fromOsPath destf)
else cp f destf
-- install git's template files
-- git does not have an option to get the path of these,
-- but they're architecture independent files, so are located
-- next to the --man-path, in eg /usr/share/git-core
manpath <- getgitpath "man-path"
let templatepath = manpath </> literalOsPath ".." </> literalOsPath "git-core" </> literalOsPath "templates"
tfs <- dirContents templatepath
forM_ tfs $ \f -> do
destf <- (templatedestdir </>)
<$> relPathDirToFile templatepath f
createDirectoryIfMissing True (takeDirectory destf)
cp f destf
where
gitcoredestdir = topdir </> literalOsPath "git-core"
templatedestdir = topdir </> literalOsPath "templates"
getgitpath v = do
let opt = "--" ++ v
ls <- lines <$> readProcess "git" [opt]
case ls of
[] -> error $ "git " ++ opt ++ "did not output a location"
(p:_) -> return (toOsPath p)
cp :: OsPath -> OsPath -> IO ()
cp src dest = do
removeWhenExistsWith removeFile dest
unlessM (boolSystem "cp" [Param "-a", File (fromOsPath src), File (fromOsPath dest)]) $
error "cp failed"
installMagic :: OsPath -> IO ()
#ifdef darwin_HOST_OS
installMagic topdir = getEnv "OSX_MAGIC_FILE" >>= \case
Nothing -> hPutStrLn stderr "OSX_MAGIC_FILE not set; not including it"
Just f -> do
let mdir = topdir </> literalOsPath "magic"
createDirectoryIfMissing True mdir
unlessM (boolSystem "cp" [File f, File (fromOsPath (mdir </> literalOsPath "magic.mgc"))]) $
error "cp failed"
#else
installMagic topdir = do
let mdir = topdir </> literalOsPath "magic"
createDirectoryIfMissing True mdir
unlessM (boolSystem "cp" [File "/usr/share/file/magic.mgc", File (fromOsPath (mdir </> literalOsPath "magic.mgc"))]) $
error "cp failed"
#endif
installLocales :: OsPath -> IO ()
#ifdef darwin_HOST_OS
installLocales _ = return ()
#else
installLocales topdir =
cp (literalOsPath "/usr/share/i18n") (topdir </> "i18n")
#endif
installSkel :: OsPath -> OsPath -> IO ()
#ifdef darwin_HOST_OS
installSkel _topdir basedir = do
whenM (doesDirectoryExist basedir) $
removeDirectoryRecursive basedir
createDirectoryIfMissing True (takeDirectory basedir)
unlessM (boolSystem "cp" [Param "-R", File "standalone/osx/git-annex.app", File (fromOsPath basedir)]) $
error "cp failed"
#else
installSkel topdir _basedir = do
whenM (doesDirectoryExist topdir) $
removeDirectoryRecursive topdir
createDirectoryIfMissing True (takeDirectory topdir)
unlessM (boolSystem "cp" [Param "-R", File "standalone/linux/skel", File (fromOsPath topdir)]) $
error "cp failed"
#endif
installSkelRest :: OsPath -> OsPath -> Bool -> IO ()
#ifdef darwin_HOST_OS
installSkelRest _topdir basedir _hwcaplibs = do
plist <- lines <$> readFile "standalone/osx/Info.plist.template"
version <- getVersion
writeFile (fromOsPath (basedir </> literalOsPath "Contents" </> literalOsPath "Info.plist"))
(unlines (map (expandversion version) plist))
where
expandversion v l = replace "GIT_ANNEX_VERSION" v l
#else
installSkelRest topdir _basedir hwcaplibs = do
runshell <- lines <$> readFile "standalone/linux/skel/runshell"
-- GIT_ANNEX_PACKAGE_INSTALL can be set by a distributor and
-- runshell will be modified
gapi <- getEnv "GIT_ANNEX_PACKAGE_INSTALL"
writeFile (fromOsPath (topdir </> literalOsPath "runshell"))
(unlines (map (expandrunshell gapi) runshell))
modifyFileMode
(topdir </> literalOsPath "runshell")
(addModes executeModes)
where
expandrunshell (Just gapi) l@"GIT_ANNEX_PACKAGE_INSTALL=" = l ++ gapi
-- This is an optimisation, that avoids the linker looking in
-- several directories for hwcap optimised libs, when there are
-- none.
expandrunshell _ l@"LD_HWCAP_MASK=" = l ++ if not hwcaplibs
then "0"
else ""
expandrunshell _ l = l
#endif
installGitAnnex :: OsPath -> IO ()
#ifdef darwin_HOST_OS
installGitAnnex topdir = go topdir
#else
installGitAnnex topdir = go (topdir </> literalOsPath "bin")
#endif
where
go bindir = do
createDirectoryIfMissing True bindir
unlessM (boolSystem "cp" [File "git-annex", File (fromOsPath bindir)]) $
error "cp failed"
unlessM (boolSystem "strip" [File (fromOsPath (bindir </> literalOsPath "git-annex"))]) $
error "strip failed"
createSymbolicLink "git-annex" (fromOsPath (bindir </> literalOsPath "git-annex-shell"))
createSymbolicLink "git-annex" (fromOsPath (bindir </> literalOsPath "git-remote-tor-annex"))
createSymbolicLink "git-annex" (fromOsPath (bindir </> literalOsPath "git-remote-annex"))
main :: IO ()
main = getArgs >>= go . map toOsPath
where
go (topdir:basedir:[]) = do
installSkel topdir basedir
installGitAnnex topdir
installedbins <- installBundledPrograms topdir
installGitLibs topdir
installMagic topdir
installLocales topdir
hwcaplibs <- mklibs topdir installedbins
installSkelRest topdir basedir hwcaplibs
go _ = error "specify topdir and basedir"