mirror of
https://github.com/anoma/juvix.git
synced 2024-12-16 02:22:25 +03:00
178 lines
5.7 KiB
Haskell
178 lines
5.7 KiB
Haskell
|
module Commands.Extra.Compile where
|
||
|
|
||
|
import Commands.Base
|
||
|
import Commands.Extra.Compile.Options
|
||
|
import Data.ByteString qualified as BS
|
||
|
import Data.FileEmbed qualified as FE
|
||
|
import Juvix.Extra.Paths
|
||
|
import System.Environment
|
||
|
import System.Process qualified as P
|
||
|
|
||
|
runCommand :: forall r. Members '[Embed IO, App] r => CompileOptions -> Sem r ()
|
||
|
runCommand opts = do
|
||
|
root <- askRoot
|
||
|
let inputFile = opts ^. compileInputFile . pathPath
|
||
|
result <- embed (runCompile root inputFile opts)
|
||
|
case result of
|
||
|
Left err -> printFailureExit err
|
||
|
_ -> return ()
|
||
|
|
||
|
juvixIncludeDir :: FilePath
|
||
|
juvixIncludeDir = juvixBuildDir </> "include"
|
||
|
|
||
|
runCompile :: FilePath -> FilePath -> CompileOptions -> IO (Either Text ())
|
||
|
runCompile projRoot inputFile o = do
|
||
|
createDirectoryIfMissing True (projRoot </> juvixBuildDir)
|
||
|
createDirectoryIfMissing True (projRoot </> juvixIncludeDir)
|
||
|
prepareRuntime projRoot o
|
||
|
case o ^. compileTarget of
|
||
|
TargetWasm32Wasi -> runM (runError (clangWasmWasiCompile inputFile o))
|
||
|
TargetNative64 -> runM (runError (clangNativeCompile inputFile o))
|
||
|
TargetC -> return $ Right ()
|
||
|
|
||
|
prepareRuntime :: FilePath -> CompileOptions -> IO ()
|
||
|
prepareRuntime projRoot o = do
|
||
|
mapM_ writeHeader headersDir
|
||
|
case o ^. compileTarget of
|
||
|
TargetWasm32Wasi | o ^. compileDebug -> writeRuntime wasiDebugRuntime
|
||
|
TargetWasm32Wasi -> writeRuntime wasiReleaseRuntime
|
||
|
TargetNative64 | o ^. compileDebug -> writeRuntime nativeDebugRuntime
|
||
|
TargetNative64 -> writeRuntime nativeReleaseRuntime
|
||
|
TargetC -> return ()
|
||
|
where
|
||
|
wasiReleaseRuntime :: BS.ByteString
|
||
|
wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile)
|
||
|
|
||
|
nativeReleaseRuntime :: BS.ByteString
|
||
|
nativeReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.native64/libjuvix.a" >>= FE.embedFile)
|
||
|
|
||
|
wasiDebugRuntime :: BS.ByteString
|
||
|
wasiDebugRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi-debug/libjuvix.a" >>= FE.embedFile)
|
||
|
|
||
|
nativeDebugRuntime :: BS.ByteString
|
||
|
nativeDebugRuntime = $(FE.makeRelativeToProject "runtime/_build.native64-debug/libjuvix.a" >>= FE.embedFile)
|
||
|
|
||
|
headersDir :: [(FilePath, BS.ByteString)]
|
||
|
headersDir = $(FE.makeRelativeToProject "runtime/include" >>= FE.embedDir)
|
||
|
|
||
|
writeRuntime :: BS.ByteString -> IO ()
|
||
|
writeRuntime =
|
||
|
BS.writeFile (projRoot </> juvixBuildDir </> "libjuvix.a")
|
||
|
|
||
|
writeHeader :: (FilePath, BS.ByteString) -> IO ()
|
||
|
writeHeader (filePath, contents) = do
|
||
|
createDirectoryIfMissing True (projRoot </> juvixIncludeDir </> takeDirectory filePath)
|
||
|
BS.writeFile (projRoot </> juvixIncludeDir </> filePath) contents
|
||
|
|
||
|
clangNativeCompile ::
|
||
|
forall r.
|
||
|
Members '[Embed IO, Error Text] r =>
|
||
|
FilePath ->
|
||
|
CompileOptions ->
|
||
|
Sem r ()
|
||
|
clangNativeCompile inputFile o =
|
||
|
runClang (native64Args o outputFile inputFile)
|
||
|
where
|
||
|
outputFile :: FilePath
|
||
|
outputFile = maybe defaultOutputFile (^. pathPath) (o ^. compileOutputFile)
|
||
|
|
||
|
defaultOutputFile :: FilePath
|
||
|
defaultOutputFile
|
||
|
| o ^. compilePreprocess = takeBaseName inputFile <> ".out.c"
|
||
|
| o ^. compileAssembly = takeBaseName inputFile <> ".s"
|
||
|
| otherwise = takeBaseName inputFile
|
||
|
|
||
|
clangWasmWasiCompile ::
|
||
|
forall r.
|
||
|
Members '[Embed IO, Error Text] r =>
|
||
|
FilePath ->
|
||
|
CompileOptions ->
|
||
|
Sem r ()
|
||
|
clangWasmWasiCompile inputFile o = clangArgs >>= runClang
|
||
|
where
|
||
|
clangArgs :: Sem r [String]
|
||
|
clangArgs = wasiArgs o outputFile inputFile <$> sysrootEnvVar
|
||
|
|
||
|
outputFile :: FilePath
|
||
|
outputFile = maybe defaultOutputFile (^. pathPath) (o ^. compileOutputFile)
|
||
|
|
||
|
defaultOutputFile :: FilePath
|
||
|
defaultOutputFile
|
||
|
| o ^. compilePreprocess = takeBaseName inputFile <> ".out.c"
|
||
|
| o ^. compileAssembly = takeBaseName inputFile <> ".wat"
|
||
|
| otherwise = takeBaseName inputFile <> ".wasm"
|
||
|
|
||
|
sysrootEnvVar :: Sem r String
|
||
|
sysrootEnvVar =
|
||
|
fromMaybeM (throw msg) (embed (lookupEnv "WASI_SYSROOT_PATH"))
|
||
|
where
|
||
|
msg :: Text
|
||
|
msg = "Missing environment variable WASI_SYSROOT_PATH"
|
||
|
|
||
|
commonArgs :: CompileOptions -> FilePath -> [String]
|
||
|
commonArgs o outputFile =
|
||
|
["-E" | o ^. compilePreprocess]
|
||
|
<> ["-S" | o ^. compileAssembly]
|
||
|
<> (if o ^. compileDebug then ["-DDEBUG"] else ["-DNDEBUG"])
|
||
|
<> [ "-W",
|
||
|
"-Wall",
|
||
|
"-Wno-unused-parameter",
|
||
|
"-Wno-unused-label",
|
||
|
"-Werror",
|
||
|
"-std=c11",
|
||
|
"-I",
|
||
|
juvixIncludeDir,
|
||
|
"-o",
|
||
|
outputFile
|
||
|
]
|
||
|
<> ( if
|
||
|
| not (o ^. compilePreprocess || o ^. compileAssembly) ->
|
||
|
[ "-L",
|
||
|
juvixBuildDir
|
||
|
]
|
||
|
| otherwise -> []
|
||
|
)
|
||
|
|
||
|
native64Args :: CompileOptions -> FilePath -> FilePath -> [String]
|
||
|
native64Args o outputFile inputFile =
|
||
|
commonArgs o outputFile
|
||
|
<> [ "-DARCH_NATIVE64",
|
||
|
"-DAPI_LIBC",
|
||
|
"-m64",
|
||
|
"-O3",
|
||
|
inputFile
|
||
|
]
|
||
|
<> ( if
|
||
|
| not (o ^. compilePreprocess || o ^. compileAssembly) ->
|
||
|
["-ljuvix"]
|
||
|
| otherwise -> []
|
||
|
)
|
||
|
|
||
|
wasiArgs :: CompileOptions -> FilePath -> FilePath -> FilePath -> [String]
|
||
|
wasiArgs o outputFile inputFile sysrootPath =
|
||
|
commonArgs o outputFile
|
||
|
<> [ "-DARCH_WASM32",
|
||
|
"-DAPI_WASI",
|
||
|
"-Os",
|
||
|
"-nodefaultlibs",
|
||
|
"--target=wasm32-wasi",
|
||
|
"--sysroot",
|
||
|
sysrootPath,
|
||
|
inputFile
|
||
|
]
|
||
|
<> ( if
|
||
|
| not (o ^. compilePreprocess || o ^. compileAssembly) ->
|
||
|
["-ljuvix"]
|
||
|
| otherwise -> []
|
||
|
)
|
||
|
|
||
|
runClang ::
|
||
|
Members '[Embed IO, Error Text] r =>
|
||
|
[String] ->
|
||
|
Sem r ()
|
||
|
runClang args = do
|
||
|
(exitCode, _, err) <- embed (P.readProcessWithExitCode "clang" args "")
|
||
|
case exitCode of
|
||
|
ExitSuccess -> return ()
|
||
|
_ -> throw (pack err)
|