Skip to content

Commit

Permalink
Make installed geordi self-contained.
Browse files Browse the repository at this point in the history
  • Loading branch information
Eelis committed Apr 4, 2010
1 parent f49ce30 commit 8fe7fc6
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 89 deletions.
10 changes: 5 additions & 5 deletions README.xhtml
Expand Up @@ -48,21 +48,21 @@
(See <a href='http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31518'>GCC bug 31518</a> for more information.)
</p>
-->
<h3>1.2&emsp;Installing geordi's executables</h3>
<p>Geordi's executables (<kbd>geordi-local</kbd>, <kbd>geordi-irc</kbd>, and <kbd>geordi-testsuite</kbd>) need to be built with Cabal:</p><pre> runhaskell Setup.hs configure
<h3>1.2&emsp;Installing geordi</h3>
<p>Geordi needs to be built with Cabal:</p><pre> runhaskell Setup.hs configure
runhaskell Setup.hs build
runhaskell Setup.hs install</pre>
<p>If you don't need the IRC front-end, pass <kbd>-f-irc</kbd> in the configure step. If you want the XMPP front-end, pass <kbd>-fxmpp</kbd>. See the <a href='http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/builders.html'>Cabal manual</a> for more information on customizing the above.</p>

<h3>1.3&emsp;Setting up a chroot</h3>

<p>We now set up a directory for geordi to chroot into when it starts. Assuming you are still in the "geordi" directory obtained through git (or extracted from a snapshot), do the following:</p>
<p>To set up a directory for geordi to chroot into when it starts, do the following:</p>

<ol>
<li><p>Edit <kbd>$PREFIX/share/geordi-0/compile-config</kbd> to your liking. Check that the g++ path refers to the actual g++ binary, not some wrapper that adds output coloring or something.</p></li>
<li><p>On Debian and Ubuntu, replace</p><pre> group = "nobody"</pre><p>with</p><pre> group = "nogroup"</pre><p> in <kbd>$PREFIX/share/geordi-0/jail-config</kbd>.</p></li>
<li><kbd>scripts/mkrt</kbd><p>Creates <kbd>$PREFIX/share/geordi-0/rt</kbd>, which will be our chroot root, and copies various files into it that GCC needs in order to function.</p></li>
<li><kbd>scripts/compile-prelude</kbd><p>Produces <kbd>$PREFIX/share/geordi-0/rt/{prelude.hpp.gch,prelude.a,libtpreload.so.0.0}</kbd>.</p></li>
<li><kbd>geordi-mkrt</kbd><p>This creates <kbd>$PREFIX/share/geordi-0/rt</kbd>, which will be our chroot root, and copies various files into it that GCC needs in order to function.</p><p>If you ever make further modifications to <kbd>compile-config</kbd>, throw the old <kbd>rt</kbd> away and build a new one with <kbd>geordi-mkrt</kbd> (followed by <kbd>geordi-compile-prelude</kbd>).</p></li>
<li><kbd>geordi-compile-prelude</kbd><p>This compiles the prelude files in <kbd>$PREFIX/share/geordi-0/prelude</kbd> to produce <kbd>$PREFIX/share/geordi-0/rt/{prelude.hpp.gch,prelude.a,libtpreload.so.0.0}</kbd>.</p><p>If you ever make modifications to the prelude, re-running <kbd>geordi-compile-prelude</kbd> makes them take effect immediately.</p></li>
</ol>

<p>Now try running</p><pre> sudo geordi-local "&lt;&lt; 'x'"</pre>
Expand Down
27 changes: 19 additions & 8 deletions Setup.hs
@@ -1,13 +1,24 @@
import Distribution.Simple
import Distribution.Simple.Setup (ConfigFlags)
import Distribution.PackageDescription (PackageDescription)
import Distribution.Simple.Setup (ConfigFlags, InstallFlags)
import Distribution.PackageDescription (PackageDescription(..))
import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..))
import Distribution.Simple.InstallDirs (prefix, fromPathTemplate)
import Distribution.Simple.InstallDirs (initialPathTemplateEnv, prefix, bindir, datadir, datasubdir, fromPathTemplate, installDirsTemplateEnv, packageTemplateEnv, substPathTemplate)
import System.Posix.Files (setFileMode, ownerModes)

main :: IO ()
main = defaultMainWithHooks $ autoconfUserHooks { postConf = myPostConf }
main = defaultMainWithHooks $ autoconfUserHooks { postInst = myPostInst }

myPostConf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ()
myPostConf args flags pkg_descr lbi = do
writeFile "prefix" $ fromPathTemplate $ prefix $ installDirTemplates lbi
postConf autoconfUserHooks args flags pkg_descr lbi
myPostInst :: Args -> InstallFlags -> PackageDescription -> LocalBuildInfo -> IO ()
myPostInst args flags pkg_descr lbi = do
(pre, _:post) <- fmap (span (/= "DATA=\"\"") . lines) $ readFile "scripts/compile-prelude"
let
idt = installDirTemplates lbi
env = installDirsTemplateEnv idt
idt' = fmap (fromPathTemplate
. substPathTemplate env
. substPathTemplate (packageTemplateEnv (package pkg_descr))) idt
-- This makes little sense to me, but it works...
to = bindir idt' ++ "/geordi-compile-prelude"
writeFile to $ unlines $ pre ++ ["DATA=\"" ++ datadir idt' ++ "/" ++ datasubdir idt' ++ "/\""] ++ post
setFileMode to ownerModes
postInst autoconfUserHooks args flags pkg_descr lbi
8 changes: 7 additions & 1 deletion geordi.cabal
Expand Up @@ -8,7 +8,7 @@ bug-reports: mailto:geordi@contacts.eelis.net
package-url: http://www.eelis.net/geordi/snapshots/
build-type: Custom

data-files: jail-config compile-config
data-files: jail-config compile-config prelude/*.hpp prelude/*.cpp

flag irc
description: Build IRC front end
Expand All @@ -23,6 +23,12 @@ flag testsuite
description: Build test suite
default: True

executable geordi-mkrt
extensions: UnicodeSyntax
main-is: Mkrt.hs
hs-source-dirs: src
build-depends: filepath, process, base, parallel, mtl, syb, unix, utf8-string, network, containers, readline, parsec<3, Diff, directory, regex-compat, base-unicode-symbols>=0.1.4

executable geordi-local
other-modules: Sys, EvalCxx, SysCalls, Flock, Ptrace
ghc-options: -Wall -fno-warn-unused-binds -fno-warn-unused-do-bind -fno-warn-orphans
Expand Down
25 changes: 15 additions & 10 deletions scripts/compile-prelude
@@ -1,14 +1,19 @@
#!/bin/sh
set -eu
DATA=`cat prefix`/share/geordi-0
RT="${DATA}/rt"

DATA=""
if [ "${DATA}" = "" ] ; then
echo "Run geordi-compile-prelude instead."
exit 1
fi

. "${DATA}/compile-config"
cd prelude
cd "${DATA}/prelude"
$GXX $COMPILE_FLAGS -c prelude.cpp tracked.cpp type_strings.cpp more_ostreaming.cpp
cd ..
rm -f "${RT}/prelude.a"
ar -rsc "${RT}/prelude.a" prelude/*.o
$GXX $COMPILE_FLAGS -c -x c++-header prelude/prelude.hpp -o "${RT}/prelude.hpp.gch"
chmod 644 "${RT}/prelude.a" "${RT}/prelude.hpp.gch"
cp prelude/terse.hpp "${RT}/"
$GXX $COMPILE_FLAGS -fPIC -shared -Wl,-soname,libtpreload.so.0 -o "${RT}/libtpreload.so.0.0" prelude/tpreload.cpp
cd "${DATA}"
rm -f rt/prelude.a
ar -rsc rt/prelude.a prelude/*.o
$GXX $COMPILE_FLAGS -c -x c++-header prelude/prelude.hpp -o rt/prelude.hpp.gch
chmod 644 rt/prelude.a rt/prelude.hpp.gch
cp prelude/terse.hpp rt/
$GXX $COMPILE_FLAGS -fPIC -shared -Wl,-soname,libtpreload.so.0 -o rt/libtpreload.so.0.0 prelude/tpreload.cpp
39 changes: 0 additions & 39 deletions scripts/mkrt

This file was deleted.

4 changes: 0 additions & 4 deletions scripts/parse-ldd-output.hs

This file was deleted.

30 changes: 30 additions & 0 deletions src/CompileConfig.hs
@@ -0,0 +1,30 @@
{-# LANGUAGE PatternGuards #-}

module CompileConfig where

import qualified Data.Map as Map
import qualified Data.Char as Char
import qualified Data.Maybe as Maybe
import Control.Applicative ((<*>))
import Paths_geordi (getDataFileName)
import Prelude hiding ((.))
import Util (readFileNow, (.))

data CompileConfig = CompileConfig { gxxPath :: FilePath, compileFlags, linkFlags :: [String] }

readCompileConfig :: IO CompileConfig
readCompileConfig = do
l lines . (getDataFileName file >>= readFileNow)
let
m = Map.fromList $ Maybe.catMaybes $ (uncurry parseLine .) $ zip [1..] l
var k = maybe (fail $ "Missing variable in " ++ file ++ ": " ++ k) return (Map.lookup k m)
CompileConfig . var "GXX" <*> (words . var "COMPILE_FLAGS") <*> (words . var "LINK_FLAGS")
where
file = "compile-config"
parseLine :: Int -> String -> Maybe (String, String)
parseLine linenum line
| s@(c:_) <- dropWhile Char.isSpace line, c /= '#' =
case span (/= '=') s of
(key, _ : right) | [(value, _)] reads right -> Just (key, value)
_ -> error $ "Syntax error on line " ++ show linenum ++ " in " ++ file ++ "."
| otherwise = Nothing
23 changes: 1 addition & 22 deletions src/EvalCxx.hsc
Expand Up @@ -63,15 +63,12 @@ import qualified System.Directory
import qualified System.Posix.Process (getProcessID)
import qualified SysCalls
import qualified System.Posix.Internals
import qualified Data.Map as Map
import qualified Data.Char as Char
import qualified Data.Maybe as Maybe

import Paths_geordi (getDataFileName)

import Sys (wait, WaitResult(..), strsignal, syscall_off, syscall_ret, fdOfFd, nonblocking_read, chroot, strerror)
import SysCalls (SysCall(..))
import Control.Applicative ((<*>))
import Control.Monad (when, forM_)
import Control.Monad.Fix (fix)
import Foreign (alloca, (.|.))
Expand All @@ -84,6 +81,7 @@ import System.Posix.User
(getGroupEntryForName, getUserEntryForName, setGroupID, setUserID, groupID, userID)
import System.Posix
(Signal, sigALRM, sigSTOP, sigTRAP, sigKILL, sigSEGV, createPipe, setFdOption, executeFile, raiseSignal, ProcessID, openFd, defaultFileFlags, forkProcess, dupTo, stdError, stdOutput, scheduleAlarm, OpenMode(..), exitImmediately, FdOption(..), Resource(..), ResourceLimit(..), ResourceLimits(..), setResourceLimit)
import CompileConfig

#ifdef __x86_64__
import Foreign ((.&.))
Expand Down Expand Up @@ -235,25 +233,6 @@ jail = do
setGroupID gid
setUserID uid

data CompileConfig = CompileConfig { gxxPath :: FilePath, compileFlags, linkFlags :: [String] }

readCompileConfig :: IO CompileConfig
readCompileConfig = do
l <- lines . (getDataFileName file >>= readFileNow)
let
m = Map.fromList $ Maybe.catMaybes $ (uncurry parseLine .) $ zip [1..] l
var k = maybe (fail $ "Missing variable in " ++ file ++ ": " ++ k) return (Map.lookup k m)
CompileConfig . var "GXX" <*> (words . var "COMPILE_FLAGS") <*> (words . var "LINK_FLAGS")
where
file = "compile-config"
parseLine :: Int -> String -> Maybe (String, String)
parseLine linenum line
| s@(c:_) <- dropWhile Char.isSpace line, c /= '#' =
case span (/= '=') s of
(key, _ : right) | [(value, _)] <- reads right -> Just (key, value)
_ -> error $ "Syntax error on line " ++ show linenum ++ " in " ++ file ++ "."
| otherwise = Nothing

data Request = Request { code :: String, also_run, no_warn :: Bool }

pass_env :: String -> Bool
Expand Down
81 changes: 81 additions & 0 deletions src/Mkrt.hs
@@ -0,0 +1,81 @@
{-# LANGUAGE UnicodeSyntax, PatternGuards #-}

import System.Posix (createFile, createDirectory, closeFd,
FileMode, unionFileModes, accessModes, nullFileMode,
ownerReadMode, ownerWriteMode, ownerExecuteMode,
groupReadMode, groupWriteMode, groupExecuteMode,
otherReadMode, otherWriteMode, otherExecuteMode,
setFileCreationMask)
import System.Process (readProcessWithExitCode)
import System.Exit (ExitCode(..))
import System.Directory (createDirectoryIfMissing, copyFile, doesFileExist)
import System.FilePath (takeDirectory, (</>))
import System.IO (hFlush, stdout)
import System.IO.Unsafe (unsafePerformIO)
import System.Environment (getEnv)
import Control.Monad (when, forM)
import Text.Regex (matchRegex, mkRegex)
import Data.Maybe (catMaybes)
import Data.List (nub)
import Util (findM, (.))
import Prelude hiding ((.))
import Prelude.Unicode
import Paths_geordi (getDataFileName)
import CompileConfig

split_paths :: String [FilePath]
split_paths [] = []
split_paths s | (f, r) span (/= ':') s = f : split_paths (drop 1 r)

which :: String IO (Maybe FilePath)
which s = getEnv "PATH" >>= findM doesFileExist . (s:) . map (</> s) . filter (not . null) . split_paths

modes :: [FileMode] FileMode
modes = foldl1 unionFileModes

readModes, writeModes, executeModes :: FileMode
readModes = modes [ownerReadMode, groupReadMode, otherReadMode]
writeModes = modes [ownerWriteMode, groupWriteMode, otherWriteMode]
executeModes = modes [ownerExecuteMode, groupExecuteMode, otherExecuteMode]

ldd :: FilePath IO [FilePath]
ldd f = do
(status, out, err) readProcessWithExitCode "ldd" [f] ""
if status ExitSuccess then error err else do
return $ map head $ catMaybes $ map (matchRegex $ mkRegex "[[:blank:]](/[^[:blank:]]*)") $ lines $ out

compiler_files :: IO [FilePath]
compiler_files = (nub .) $ do
gxx gxxPath . readCompileConfig
let
query_gxx q = do
(status, out, err) readProcessWithExitCode gxx [q] ""
if status /= ExitSuccess then error err else do
return $ head $ lines out
fs (concat .) $ forM l $ \f do
out query_gxx $ "-print-file-name=" ++ f
return $ if out f then [out] else []
fs' (concat .) $ forM ["as", "ld"] $ \p do
mf query_gxx ("-print-prog-name=" ++ p) >>= which
case mf of
Nothing error $ "could not find " ++ p
Just f (f:) . ldd f
gxxlibs ldd gxx
return $ gxx : gxxlibs ++ fs ++ fs'
where l = words "crt1.o crti.o crtn.o crtbegin.o crtend.o libgcc.a libgcc_s.so libstdc++.so libstdc++.so.6 libmcheck.a libc.so libc_nonshared.a libm.so libm.so.6 libc.so.6 libgcc_s.so.1"

main :: IO ()
main = do
setFileCreationMask $ modes [groupWriteMode, otherWriteMode]
rt getDataFileName "rt"
putStr $ "Setting up " ++ rt ++ " ..."
hFlush stdout
(compiler_files >>=) $ mapM_ $ \f do
let to = rt ++ "/" ++ f -- can't use </> here because f is absolute
createDirectoryIfMissing True $ takeDirectory to
copyFile f to
setFileCreationMask nullFileMode
createFile (rt </> "lock") readModes >>= closeFd
createFile (rt </> "t") accessModes >>= closeFd
forM ["t.cpp", "t.s", "t.o"] $ (>>= closeFd) . flip createFile (unionFileModes writeModes readModes) . (rt </>)
putStrLn " done."
6 changes: 6 additions & 0 deletions src/Util.hs
Expand Up @@ -132,6 +132,12 @@ pairs :: [a] → [(a, a)]
pairs (x:y:z) = (x,y) : pairs z
pairs _ = []

findM :: Monad m (a m Bool) [a] m (Maybe a)
findM _ [] = return Nothing
findM p (x:xs) = do
b p x
if b then return (Just x) else findM p xs

-- Test utilities

fail_test :: (Show a, Show b) String a b IO ()
Expand Down

0 comments on commit 8fe7fc6

Please sign in to comment.