mirror of
https://github.com/anoma/juvix.git
synced 2024-12-13 19:49:20 +03:00
3b3ea45da9
* Renaming MiniJuvix to Juvix * Make Ormolu happy * Make Hlint happy * Remove redundant imports * Fix shell tests and add target ci to our Makefile * Make pre-commit happy
182 lines
5.6 KiB
Haskell
182 lines
5.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 = RuntimeStandalone | RuntimeLibC
|
|
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 RuntimeStandalone
|
|
<> showDefaultWith runtimeShow
|
|
<> help "select a runtime: standalone, libc"
|
|
)
|
|
|
|
_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
|
|
"standalone" -> Right RuntimeStandalone
|
|
"libc" -> Right RuntimeLibC
|
|
s -> Left $ "unrecognised runtime: " <> s
|
|
|
|
runtimeShow :: CompileRuntime -> String
|
|
runtimeShow = \case
|
|
RuntimeStandalone -> "standalone"
|
|
RuntimeLibC -> "libc"
|
|
|
|
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 -> clangCompile projRoot compileInputFile o
|
|
TargetC -> return (Right ())
|
|
|
|
prepareRuntime :: FilePath -> CompileOptions -> IO ()
|
|
prepareRuntime projRoot o = do
|
|
mapM_ writeRuntime runtimeProjectDir
|
|
where
|
|
standaloneRuntimeDir :: [(FilePath, BS.ByteString)]
|
|
standaloneRuntimeDir = $(FE.makeRelativeToProject "minic-runtime/standalone" >>= FE.embedDir)
|
|
|
|
libCRuntimeDir :: [(FilePath, BS.ByteString)]
|
|
libCRuntimeDir = $(FE.makeRelativeToProject "minic-runtime/libc" >>= FE.embedDir)
|
|
|
|
builtinCRuntimeDir :: [(FilePath, BS.ByteString)]
|
|
builtinCRuntimeDir = $(FE.makeRelativeToProject "minic-runtime/builtins" >>= FE.embedDir)
|
|
|
|
runtimeProjectDir :: [(FilePath, BS.ByteString)]
|
|
runtimeProjectDir = case o ^. compileRuntime of
|
|
RuntimeStandalone -> standaloneRuntimeDir <> builtinCRuntimeDir
|
|
RuntimeLibC -> libCRuntimeDir <> builtinCRuntimeDir
|
|
|
|
writeRuntime :: (FilePath, BS.ByteString) -> IO ()
|
|
writeRuntime (filePath, contents) =
|
|
BS.writeFile (projRoot </> juvixBuildDir </> takeFileName filePath) contents
|
|
|
|
clangCompile :: FilePath -> FilePath -> CompileOptions -> IO (Either Text ())
|
|
clangCompile projRoot compileInputFile o = do
|
|
v <- sysrootEnvVar
|
|
case v of
|
|
Left s -> return (Left s)
|
|
Right sysrootPath -> withSysrootPath sysrootPath
|
|
where
|
|
sysrootEnvVar :: IO (Either Text String)
|
|
sysrootEnvVar =
|
|
maybeToEither "Missing environment variable WASI_SYSROOT_PATH"
|
|
<$> lookupEnv "WASI_SYSROOT_PATH"
|
|
|
|
withSysrootPath :: String -> IO (Either Text ())
|
|
withSysrootPath sysrootPath = runClang clangArgs
|
|
where
|
|
clangArgs :: [String]
|
|
clangArgs = case o ^. compileRuntime of
|
|
RuntimeStandalone -> standaloneArgs projRoot sysrootPath outputFile inputFile
|
|
RuntimeLibC -> libcArgs sysrootPath outputFile inputFile
|
|
|
|
outputFile :: FilePath
|
|
outputFile = fromMaybe (takeBaseName compileInputFile <> ".wasm") (o ^. compileOutputFile)
|
|
|
|
inputFile :: FilePath
|
|
inputFile = inputCFile projRoot compileInputFile
|
|
|
|
standaloneArgs :: FilePath -> FilePath -> FilePath -> FilePath -> [String]
|
|
standaloneArgs projRoot sysrootPath wasmOutputFile inputFile =
|
|
commonArgs sysrootPath wasmOutputFile
|
|
<> [projRoot </> juvixBuildDir </> "walloc.c", inputFile]
|
|
|
|
libcArgs :: FilePath -> FilePath -> FilePath -> [String]
|
|
libcArgs sysrootPath wasmOutputFile inputFile =
|
|
commonArgs sysrootPath wasmOutputFile
|
|
<> ["-lc", inputFile]
|
|
|
|
commonArgs :: FilePath -> FilePath -> [String]
|
|
commonArgs sysrootPath wasmOutputFile =
|
|
[ "-nodefaultlibs",
|
|
"-std=c99",
|
|
"-Oz",
|
|
"-I",
|
|
juvixBuildDir,
|
|
"--target=wasm32-wasi",
|
|
"--sysroot",
|
|
sysrootPath,
|
|
"-o",
|
|
wasmOutputFile
|
|
]
|
|
|
|
runClang ::
|
|
[String] ->
|
|
IO (Either Text ())
|
|
runClang args = do
|
|
(exitCode, _, err) <- P.readProcessWithExitCode "clang" args ""
|
|
case exitCode of
|
|
ExitSuccess -> return (Right ())
|
|
_ -> return (Left (pack err))
|