packages feed

cipher-aes128-0.1: Setup.hs

import Distribution.Simple
import Distribution.Simple.Setup
import Distribution.PackageDescription
import Distribution.Simple.Utils
import Distribution.Simple.Program
import Distribution.Verbosity
import System.Process
import System.Directory
import System.Exit

main = defaultMainWithHooks hk
 where
 hk = simpleUserHooks { preBuild = \as fs -> do
                                        b <- (preBuild simpleUserHooks) as fs
                                        checkAndAddAES fs b }

checkAndAddAES :: BuildFlags -> HookedBuildInfo -> IO HookedBuildInfo
checkAndAddAES fs x@(m,as) = do
        b  <- canUseAesIntrinsicsFlag fs
        b2 <- haveNIInstrs fs
        let bi = maybe emptyBuildInfo id m
            op = if b && b2 then addNIintrinsicOpt else id
        return (Just (op emptyBuildInfo), as)

addNIintrinsicOpt :: BuildInfo -> BuildInfo
addNIintrinsicOpt bi = bi { ccOptions = ccOptions bi ++ ["-maes", "-mssse3", "-DHAVE_AES_INTRINSICS", "-DHAVE_NI"] }

-- Detect if we have AES NI instructions for acceleration on x86 CPUs
haveNIInstrs :: BuildFlags -> IO Bool
haveNIInstrs cf = do
        -- FIXME cf is always empty, how do we ensure we're testing the right C compiler?
        -- TODO: I am hooking Cabal at the wrong spot, need to hook one stage
        -- later!
        let prog = lookup "cc" (buildProgramPaths cf)
            cc = maybe "gcc" id prog
        withTempDirectory normal "" "testNI" $ \tmpDir -> do
         writeFile (tmpDir ++ "/testNI.c")
               (unlines ["#include <stdint.h>",
                        "/**",
                        " * Returns zero if false, non-zero otherwise",
                        " */",
                        "int cpu_has_ni()",
                        "{",
                        "uint32_t ax,bx,cx,dx,func=1;",
                        "uint32_t regs[4];",
                        "asm volatile (\"cpuid\":",
                        "\"=a\" (ax), \"=b\" (bx), \"=c\" (cx), \"=d\" (dx) : \"a\" (func));",
                        "return (cx & 0x2000000);",
                        "}",
                        "int main()",
                        "{",
                        "    if(cpu_has_ni()) return 0; else return -1;",
                        "}"]
                                )
         ec <- rawSystemExitCode normal cc ["-maes",tmpDir ++ "/testNI.c"]
         notice normal $ "Result of AES NI Test: " ++ show (ec == ExitSuccess)
         return (ec == ExitSuccess)

canUseAesIntrinsicsFlag :: BuildFlags -> IO Bool
canUseAesIntrinsicsFlag cf = do
        -- FIXME cf is always empty, how do we ensure we're testing the right C compiler?
        -- TODO: I am hooking Cabal at the wrong spot, need to hook one stage
        -- later!
        let prog = lookup "cc" (buildProgramPaths cf)
            cc = maybe "gcc" id prog
        withTempDirectory normal "" "testIntrinsic" $ \tmpDir -> do
        writeFile (tmpDir ++ "/testIntrinsic.c")
                (unlines        [ "#include <wmmintrin.h>"
                                , "int main() {"
                                , "return 0; }"
                                ])
        ec <- rawSystemExitCode normal cc ["-maes",tmpDir ++ "/testIntrinsic.c"]
        notice normal $ "Result of NI Intrinsics Test: " ++ show (ec == ExitSuccess)
        return (ec == ExitSuccess)