mirror of
https://github.com/anoma/juvix.git
synced 2024-12-13 19:49:20 +03:00
433e882189
* Export all functions with alphanum names in entrypoint Set the export_name attribute on every function signature that has a fully alpha numeric name. * Adds a non-WASI target to compile command WASM libraries we want to run in the browser and in Anoma VM do not have access to the WASI runtime. For this usecase we need a target runtime that does not use the WASI runtime. A `wasi-` prefix is added to existing compile targets and introduce a new standalone target. This target does not have IO functions like `putStrLn` and `exit` calls `__builtin_trap` which corresponds to the `unreachable` WASM instruction. In the non-exhaustive case error a debug message is emitted to tell the user which function had the error. This is now abstracted to a `debug` function in the runtime which calls `putStrLn` in the case of WASI and is no-op in the case of non-WASI. Tests are added which check that exported names can be called with the correct name and result. * Moves walloc to a common directory
215 lines
6.6 KiB
Haskell
215 lines
6.6 KiB
Haskell
module Commands.Compile where
|
|
|
|
import Data.ByteString qualified as BS
|
|
import Data.FileEmbed qualified as FE
|
|
import Data.Text.IO qualified as TIO
|
|
import Juvix.Prelude hiding (Doc)
|
|
import Options.Applicative
|
|
import System.Environment
|
|
import System.Process qualified as P
|
|
|
|
juvixBuildDir :: FilePath
|
|
juvixBuildDir = ".juvix-build"
|
|
|
|
data CompileTarget = TargetC | TargetWasm
|
|
deriving stock (Show)
|
|
|
|
data CompileRuntime
|
|
= RuntimeWasiStandalone
|
|
| RuntimeWasiLibC
|
|
| RuntimeStandalone
|
|
deriving stock (Show)
|
|
|
|
data CompileOptions = CompileOptions
|
|
{ _compileTarget :: CompileTarget,
|
|
_compileRuntime :: CompileRuntime,
|
|
_compileOutputFile :: Maybe FilePath
|
|
}
|
|
|
|
makeLenses ''CompileOptions
|
|
|
|
parseCompile :: Parser CompileOptions
|
|
parseCompile = do
|
|
_compileTarget <-
|
|
option
|
|
(eitherReader parseTarget)
|
|
( long "target"
|
|
<> short 't'
|
|
<> metavar "TARGET"
|
|
<> value TargetWasm
|
|
<> showDefaultWith targetShow
|
|
<> help "select a target: wasm, c"
|
|
)
|
|
|
|
_compileRuntime <-
|
|
option
|
|
(eitherReader parseRuntime)
|
|
( long "runtime"
|
|
<> short 'r'
|
|
<> metavar "RUNTIME"
|
|
<> value RuntimeWasiStandalone
|
|
<> showDefaultWith runtimeShow
|
|
<> help "select a runtime: wasi-standalone, wasi-libc, standalone"
|
|
)
|
|
|
|
_compileOutputFile <-
|
|
optional $
|
|
option
|
|
str
|
|
( long "output"
|
|
<> short 'o'
|
|
<> metavar "OUTPUT_FILE"
|
|
<> help "Path to output file"
|
|
<> action "file"
|
|
)
|
|
pure CompileOptions {..}
|
|
where
|
|
parseTarget :: String -> Either String CompileTarget
|
|
parseTarget = \case
|
|
"wasm" -> Right TargetWasm
|
|
"c" -> Right TargetC
|
|
s -> Left $ "unrecognised target: " <> s
|
|
|
|
targetShow :: CompileTarget -> String
|
|
targetShow = \case
|
|
TargetC -> "c"
|
|
TargetWasm -> "wasm"
|
|
|
|
parseRuntime :: String -> Either String CompileRuntime
|
|
parseRuntime = \case
|
|
"wasi-standalone" -> Right RuntimeWasiStandalone
|
|
"wasi-libc" -> Right RuntimeWasiLibC
|
|
"standalone" -> Right RuntimeStandalone
|
|
s -> Left $ "unrecognised runtime: " <> s
|
|
|
|
runtimeShow :: CompileRuntime -> String
|
|
runtimeShow = \case
|
|
RuntimeWasiStandalone -> "wasi-standalone"
|
|
RuntimeWasiLibC -> "wasi-libc"
|
|
RuntimeStandalone -> "standalone"
|
|
|
|
inputCFile :: FilePath -> FilePath -> FilePath
|
|
inputCFile projRoot compileInputFile =
|
|
projRoot </> juvixBuildDir </> outputMiniCFile
|
|
where
|
|
outputMiniCFile :: FilePath
|
|
outputMiniCFile = takeBaseName compileInputFile <> ".c"
|
|
|
|
runCompile :: FilePath -> FilePath -> CompileOptions -> Text -> IO (Either Text ())
|
|
runCompile projRoot compileInputFile o minic = do
|
|
createDirectoryIfMissing True (projRoot </> juvixBuildDir)
|
|
TIO.writeFile (inputCFile projRoot compileInputFile) minic
|
|
prepareRuntime projRoot o
|
|
case o ^. compileTarget of
|
|
TargetWasm -> runM (runError (clangCompile projRoot compileInputFile o))
|
|
TargetC -> return (Right ())
|
|
|
|
prepareRuntime :: FilePath -> CompileOptions -> IO ()
|
|
prepareRuntime projRoot o = do
|
|
mapM_ writeRuntime runtimeProjectDir
|
|
where
|
|
wasiStandaloneRuntimeDir :: [(FilePath, BS.ByteString)]
|
|
wasiStandaloneRuntimeDir = $(FE.makeRelativeToProject "minic-runtime/wasi-standalone" >>= FE.embedDir)
|
|
|
|
standaloneRuntimeDir :: [(FilePath, BS.ByteString)]
|
|
standaloneRuntimeDir = $(FE.makeRelativeToProject "minic-runtime/standalone" >>= FE.embedDir)
|
|
|
|
wasiLibCRuntimeDir :: [(FilePath, BS.ByteString)]
|
|
wasiLibCRuntimeDir = $(FE.makeRelativeToProject "minic-runtime/wasi-libc" >>= FE.embedDir)
|
|
|
|
builtinCRuntimeDir :: [(FilePath, BS.ByteString)]
|
|
builtinCRuntimeDir = $(FE.makeRelativeToProject "minic-runtime/builtins" >>= FE.embedDir)
|
|
|
|
wallocDir :: [(FilePath, BS.ByteString)]
|
|
wallocDir = $(FE.makeRelativeToProject "minic-runtime/walloc" >>= FE.embedDir)
|
|
|
|
runtimeProjectDir :: [(FilePath, BS.ByteString)]
|
|
runtimeProjectDir = case o ^. compileRuntime of
|
|
RuntimeWasiStandalone -> wasiStandaloneRuntimeDir <> builtinCRuntimeDir <> wallocDir
|
|
RuntimeWasiLibC -> wasiLibCRuntimeDir <> builtinCRuntimeDir
|
|
RuntimeStandalone -> standaloneRuntimeDir <> builtinCRuntimeDir <> wallocDir
|
|
|
|
writeRuntime :: (FilePath, BS.ByteString) -> IO ()
|
|
writeRuntime (filePath, contents) =
|
|
BS.writeFile (projRoot </> juvixBuildDir </> takeFileName filePath) contents
|
|
|
|
clangCompile ::
|
|
forall r.
|
|
Members '[Embed IO, Error Text] r =>
|
|
FilePath ->
|
|
FilePath ->
|
|
CompileOptions ->
|
|
Sem r ()
|
|
clangCompile projRoot compileInputFile o = clangArgs >>= runClang
|
|
where
|
|
clangArgs :: Sem r [String]
|
|
clangArgs = case o ^. compileRuntime of
|
|
RuntimeStandalone ->
|
|
return (standaloneLibArgs projRoot outputFile inputFile)
|
|
RuntimeWasiStandalone -> wasiStandaloneArgs projRoot outputFile inputFile <$> sysrootEnvVar
|
|
RuntimeWasiLibC -> wasiLibcArgs outputFile inputFile <$> sysrootEnvVar
|
|
|
|
outputFile :: FilePath
|
|
outputFile = fromMaybe (takeBaseName compileInputFile <> ".wasm") (o ^. compileOutputFile)
|
|
|
|
inputFile :: FilePath
|
|
inputFile = inputCFile projRoot compileInputFile
|
|
|
|
sysrootEnvVar :: Sem r String
|
|
sysrootEnvVar =
|
|
fromMaybeM (throw msg) (embed (lookupEnv "WASI_SYSROOT_PATH"))
|
|
where
|
|
msg :: Text
|
|
msg = "Missing environment variable WASI_SYSROOT_PATH"
|
|
|
|
commonArgs :: FilePath -> [String]
|
|
commonArgs wasmOutputFile =
|
|
[ "-nodefaultlibs",
|
|
"-std=c99",
|
|
"-Oz",
|
|
"-I",
|
|
juvixBuildDir,
|
|
"-o",
|
|
wasmOutputFile
|
|
]
|
|
|
|
standaloneLibArgs :: FilePath -> FilePath -> FilePath -> [String]
|
|
standaloneLibArgs projRoot wasmOutputFile inputFile =
|
|
commonArgs wasmOutputFile
|
|
<> [ "--target=wasm32",
|
|
"-nostartfiles",
|
|
"-Wl,--no-entry",
|
|
projRoot </> juvixBuildDir </> "walloc.c",
|
|
inputFile
|
|
]
|
|
|
|
wasiStandaloneArgs :: FilePath -> FilePath -> FilePath -> FilePath -> [String]
|
|
wasiStandaloneArgs projRoot wasmOutputFile inputFile sysrootPath =
|
|
wasiCommonArgs sysrootPath wasmOutputFile
|
|
<> [ projRoot </> juvixBuildDir </> "walloc.c",
|
|
inputFile
|
|
]
|
|
|
|
wasiLibcArgs :: FilePath -> FilePath -> FilePath -> [String]
|
|
wasiLibcArgs wasmOutputFile inputFile sysrootPath =
|
|
wasiCommonArgs sysrootPath wasmOutputFile
|
|
<> ["-lc", inputFile]
|
|
|
|
wasiCommonArgs :: FilePath -> FilePath -> [String]
|
|
wasiCommonArgs sysrootPath wasmOutputFile =
|
|
commonArgs wasmOutputFile
|
|
<> [ "--target=wasm32-wasi",
|
|
"--sysroot",
|
|
sysrootPath
|
|
]
|
|
|
|
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)
|