{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Test.Hspec.BenchGolden.Arch
(
detectArchitecture
, getArchId
, getArchFromEnv
, sanitizeForFilename
) where
import Control.Exception (catch, SomeException)
import Data.Char (isAlphaNum)
import Data.Text (Text)
import qualified Data.Text as T
import System.Environment (lookupEnv)
import System.Info (arch, os)
import System.Process (readProcess)
import Test.Hspec.BenchGolden.Types (ArchConfig(..))
detectArchitecture :: IO ArchConfig
detectArchitecture :: IO ArchConfig
detectArchitecture = do
Maybe Text
envArch <- IO (Maybe Text)
getArchFromEnv
case Maybe Text
envArch of
Just Text
customArch -> ArchConfig -> IO ArchConfig
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ArchConfig -> IO ArchConfig) -> ArchConfig -> IO ArchConfig
forall a b. (a -> b) -> a -> b
$ ArchConfig
{ archId :: Text
archId = Text
customArch
, archOS :: Text
archOS = String -> Text
T.pack String
os
, archCPU :: Text
archCPU = String -> Text
T.pack String
arch
, archModel :: Maybe Text
archModel = Text -> Maybe Text
forall a. a -> Maybe a
Just Text
customArch
}
Maybe Text
Nothing -> do
Maybe Text
model <- IO (Maybe Text)
getCPUModel
let archConfig :: ArchConfig
archConfig = ArchConfig
{ archId :: Text
archId = Text -> Text -> Maybe Text -> Text
buildArchId (String -> Text
T.pack String
arch) (String -> Text
T.pack String
os) Maybe Text
model
, archOS :: Text
archOS = String -> Text
T.pack String
os
, archCPU :: Text
archCPU = String -> Text
T.pack String
arch
, archModel :: Maybe Text
archModel = Maybe Text
model
}
ArchConfig -> IO ArchConfig
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ArchConfig
archConfig
buildArchId :: Text -> Text -> Maybe Text -> Text
buildArchId :: Text -> Text -> Maybe Text -> Text
buildArchId Text
cpu Text
osName Maybe Text
maybeModel =
let base :: Text
base = Text
cpu Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
osName
in case Maybe Text
maybeModel of
Maybe Text
Nothing -> Text
base
Just Text
model -> Text
base Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
sanitizeForFilename Text
model
getArchId :: IO Text
getArchId :: IO Text
getArchId = ArchConfig -> Text
archId (ArchConfig -> Text) -> IO ArchConfig -> IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ArchConfig
detectArchitecture
getArchFromEnv :: IO (Maybe Text)
getArchFromEnv :: IO (Maybe Text)
getArchFromEnv = (String -> Text) -> Maybe String -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> Text
T.pack (Maybe String -> Maybe Text)
-> IO (Maybe String) -> IO (Maybe Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO (Maybe String)
lookupEnv String
"GOLDS_GYM_ARCH"
getCPUModel :: IO (Maybe Text)
getCPUModel :: IO (Maybe Text)
getCPUModel = do
#if defined(darwin_HOST_OS)
getDarwinCPUModel
#elif defined(linux_HOST_OS)
IO (Maybe Text)
getLinuxCPUModel
#elif defined(mingw32_HOST_OS)
getWindowsCPUModel
#else
return Nothing
#endif
#if defined(darwin_HOST_OS)
getDarwinCPUModel :: IO (Maybe Text)
getDarwinCPUModel = do
result <- safeReadProcess "sysctl" ["-n", "machdep.cpu.brand_string"] ""
case result of
Nothing -> do
chipResult <- safeReadProcess "sysctl" ["-n", "machdep.cpu.brand"] ""
case chipResult of
Nothing -> do
armResult <- safeReadProcess "uname" ["-m"] ""
case armResult of
Just m | "arm" `T.isInfixOf` T.toLower m -> return $ Just "Apple_Silicon"
_ -> return Nothing
Just chip -> return $ Just $ cleanCPUName chip
Just name -> return $ Just $ cleanCPUName name
#endif
#if defined(linux_HOST_OS)
getLinuxCPUModel :: IO (Maybe Text)
getLinuxCPUModel :: IO (Maybe Text)
getLinuxCPUModel = do
Maybe Text
result <- String -> [String] -> String -> IO (Maybe Text)
safeReadProcess String
"grep" [String
"-m1", String
"model name", String
"/proc/cpuinfo"] String
""
case Maybe Text
result of
Maybe Text
Nothing -> Maybe Text -> IO (Maybe Text)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Text
forall a. Maybe a
Nothing
Just Text
line ->
let parts :: [Text]
parts = HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
":" Text
line
in case [Text]
parts of
[Text
_, Text
name] -> Maybe Text -> IO (Maybe Text)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Text -> IO (Maybe Text)) -> Maybe Text -> IO (Maybe Text)
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Text -> Text
cleanCPUName Text
name
[Text]
_ -> Maybe Text -> IO (Maybe Text)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Text
forall a. Maybe a
Nothing
#endif
#if defined(mingw32_HOST_OS)
getWindowsCPUModel :: IO (Maybe Text)
getWindowsCPUModel = do
result <- safeReadProcess "wmic" ["cpu", "get", "name"] ""
case result of
Nothing -> return Nothing
Just output ->
let ls = filter (not . T.null) $ T.lines output
in case drop 1 ls of
(name:_) -> return $ Just $ cleanCPUName name
_ -> return Nothing
#endif
safeReadProcess :: FilePath -> [String] -> String -> IO (Maybe Text)
safeReadProcess :: String -> [String] -> String -> IO (Maybe Text)
safeReadProcess String
cmd [String]
args String
input =
(Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> (String -> Text) -> String -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Maybe Text) -> IO String -> IO (Maybe Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String] -> String -> IO String
readProcess String
cmd [String]
args String
input)
IO (Maybe Text)
-> (SomeException -> IO (Maybe Text)) -> IO (Maybe Text)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` (\(SomeException
_ :: SomeException) -> Maybe Text -> IO (Maybe Text)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Text
forall a. Maybe a
Nothing)
cleanCPUName :: Text -> Text
cleanCPUName :: Text -> Text
cleanCPUName = Text -> Text
T.strip (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Text] -> Text
T.unwords ([Text] -> Text) -> (Text -> [Text]) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.words
sanitizeForFilename :: Text -> Text
sanitizeForFilename :: Text -> Text
sanitizeForFilename = (Char -> Char) -> Text -> Text
T.map Char -> Char
sanitizeChar
where
sanitizeChar :: Char -> Char
sanitizeChar Char
c
| Char -> Bool
isAlphaNum Char
c = Char
c
| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'-' = Char
c
| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_' = Char
c
| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ' = Char
'_'
| Bool
otherwise = Char
'_'