packages feed

bitcoin-hs-0.0.1: Setup.hs

-- really fragile hack to build the assembly code into the library
-- unfortunately, cabal is not exactly helpful for this task, so to speak...

{-# LANGUAGE CPP #-}

import Control.Monad
import Data.Maybe

import Distribution.Simple
import Distribution.Simple.Setup
import Distribution.Simple.UserHooks
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Program
import Distribution.PackageDescription

import System.Process   -- System.Cmd
import System.Exit
import System.FilePath
import System.Directory

--------------------------------------------------------------------------------
-- * system-dependent configuration

data OS 
  = Linux 
  | Windows 
  | MacOSX 
  deriving (Eq,Show)

data CallConv 
  = CDecl 
  | Win64 
  | SystemV 
  deriving (Eq,Show)

host_bits :: Int 
#if defined i386_HOST_ARCH
host_bits = 32
#elif defined x86_64_HOST_ARCH
host_bits = 64
#else
#error cannot figure out host architecture (for nasm)
#endif

host_os :: OS
#if defined mingw32_HOST_OS           
host_os = Windows
#elif defined linux_HOST_OS
host_os = Linux
#elif defined darwin_HOST_OS
host_os = MacOSX
#else
#error cannot figure out target operating system (for nasm)
#endif

objfmt :: String
objfmt = case host_os of
  Windows -> "-fwin"   ++ show host_bits
  Linux   -> "-felf"   ++ show host_bits
  MacOSX  -> "-fmacho" ++ show host_bits

callConv :: CallConv
callConv = case host_bits of
  32 -> CDecl
  64 -> case host_os of
    Windows -> Win64
    _       -> SystemV

--------------------------------------------------------------------------------

mySubDir :: FilePath
mySubDir = "Bitcoin/Crypto/cbits/"

theFlags :: LocalBuildInfo -> FlagAssignment
theFlags lbi = configConfigurationsFlags $ configFlags lbi

doCompileWithAsm :: LocalBuildInfo -> Bool
doCompileWithAsm lbi = 
  case lookup (FlagName "x86asm") (theFlags lbi) of
    Nothing -> False
    Just b  -> b

targetDir :: LocalBuildInfo -> FilePath
targetDir lbi = buildDir lbi </> mySubDir 

--------------------------------------------------------------------------------

myBuildHook pkgdesc localbuildinfo userhooks buildflags = do  

  when (doCompileWithAsm localbuildinfo) $ do
    putStrLn "now trying to run nasm to compile our sweet hand-written assembly code..."

    let nasmpath = "nasm"

    findExecutable nasmpath >>= \mb -> case mb of
      Nothing -> error "nasm executable not found in path - either install it or use the cabal flag -f-X86ASM"
      Just p  -> putStrLn $ "nasm executable is: " ++ p

    let srcdir = "." </> mySubDir
        tgtdir = targetDir localbuildinfo

    createDirectoryIfMissing True tgtdir  -- in a clean build, the dist/build directory doesn't exist yet...

    let dcallconv = case callConv of
          CDecl   -> "-DCALLCONV_CDECL"
          Win64   -> "-DCALLCONV_WIN64"
          SystemV -> "-DCALLCONV_SYSTEMV"

    let asmsrc = case host_bits of
          32 -> srcdir </> "asm_modp_x86.asm"
          64 -> srcdir </> "asm_modp_x64.asm"

    let objfile = case host_bits of
          32 -> tgtdir </> "asm_modp_x86.o"
          64 -> tgtdir </> "asm_modp_x64.o"

    let cmd = unwords [ nasmpath , objfmt , dcallconv , asmsrc , "-o" , objfile ]

    exitcode <- system cmd
    case exitcode of
      ExitSuccess   -> return ()
      ExitFailure k -> error ("nasm failed with exist code " ++ show k)

{-    
    putStrLn "now let's create a library out of the object file..."

    let cmd = unwords ["ar", "rcs" , tgtdir </> "libmodp.a" , tgtdir </> "asm_modp.o" ]
    exitcode <- system cmd
    case exitcode of
      ExitSuccess -> return ()
      ExitFailure k -> error ("ar failed with exist code " ++ show k)
-}

  -- print localbuildinfo

  (buildHook simpleUserHooks) pkgdesc localbuildinfo userhooks buildflags 

--------------------------------------------------------------------------------

myConfHook (genpkgdesc,hookbuildinfo) cfgflags = do
  lbi <- (confHook simpleUserHooks) (genpkgdesc,hookbuildinfo) cfgflags

  print $ theFlags lbi
  
  putStrLn $ "word size:  " ++ show host_bits ++ " bits"
  putStrLn $ "obj format: " ++ objfmt

  if (not $ doCompileWithAsm lbi) 
    then return lbi
    else do

      putStrLn "now trying configure this mess to use our object file later on..."

      let lpd   = localPkgDescr lbi
          lib   = fromJust (library lpd)
          libbi = libBuildInfo lib

      let tgtdir = targetDir lbi
      createDirectoryIfMissing True tgtdir    -- in a clean build, the dist/build directory doesn't exist yet...

      fulltgtdir <- canonicalizePath tgtdir   -- this may fail if the directory does not exist???

      let objfile = case host_bits of
            32 -> fulltgtdir </> "asm_modp_x86.o"
            64 -> fulltgtdir </> "asm_modp_x64.o"

      -- now, seriously, cabal please...
      let progconf = withPrograms lbi
          progconf' = userSpecifyArgs "ar" [objfile] 
                    $ userSpecifyArgs "ld" [objfile] 
                    $ progconf
          libbi' = libbi

{-
      let libbi' = libbi { ldOptions = ldOptions libbi ++ [objfile] }
      let libbi' = libbi { options = options libbi ++ [(GHC,[objfile])] }
      let libbi' = libbi  
            { extraLibs    = extraLibs    libbi ++ ["modp"] 
            , extraLibDirs = extraLibDirs libbi ++ [fulltgtdir] 
            }
-}

      -- print libbi'

      let lib' = lib { libBuildInfo = libbi' }
          lpd' = lpd { library = Just lib' }

      return $ lbi { localPkgDescr = lpd' , withPrograms = progconf' }

--------------------------------------------------------------------------------

main :: IO ()
main = do
  defaultMainWithHooks $ simpleUserHooks { confHook = myConfHook , buildHook = myBuildHook }