mirror of
https://github.com/anoma/juvix.git
synced 2024-12-20 21:31:48 +03:00
785f02ddc9
Previously the doctor help links would point to the Juvix documentation dev URLs. Now the doctor help links point to the version of the documentation corresponding to the compiler version. ``` $ juvix doctor > Checking for clang... > Checking clang version... > Checking for wasm-ld... > Checking that clang supports wasm32... > Checking that clang supports wasm32-wasi... > Checking that WASI_SYSROOT_PATH is set... ! Environment variable WASI_SYSROOT_PATH is missing ! https://docs.juvix.org/0.5.2/reference/tooling/doctor/#environment-variable-wasi_sysroot_path-is-not-set > Checking for wasmer... > Checking latest Juvix release on Github... ``` Spotted by @agureev
178 lines
6.9 KiB
Haskell
178 lines
6.9 KiB
Haskell
module Commands.Doctor where
|
|
|
|
import Commands.Base hiding (info)
|
|
import Commands.Doctor.Options
|
|
import Commands.Extra.Compile
|
|
import Data.Aeson
|
|
import Data.Aeson.TH
|
|
import Juvix.Extra.Version qualified as V
|
|
import Network.HTTP.Simple
|
|
import Safe (headMay)
|
|
import System.Environment qualified as E
|
|
import System.Process qualified as P
|
|
import Text.Read (readMaybe)
|
|
|
|
newtype GithubRelease = GithubRelease {_githubReleaseTagName :: Maybe Text}
|
|
deriving stock (Eq, Show, Generic)
|
|
|
|
$( deriveFromJSON
|
|
defaultOptions
|
|
{ fieldLabelModifier = camelTo2 '_' . dropPrefix "_githubRelease"
|
|
}
|
|
''GithubRelease
|
|
)
|
|
|
|
data DocumentedWarning
|
|
= NoClang
|
|
| OldClang
|
|
| NoWasmLd
|
|
| NoWasm32Target
|
|
| NoWasm32WasiTarget
|
|
| NoSysroot
|
|
| NoWasmer
|
|
|
|
data DocumentedMessage = DocumentedMessage
|
|
{ _documentedMessageUrl :: Text,
|
|
_documentedMessageMessage :: Text
|
|
}
|
|
|
|
makeLenses ''GithubRelease
|
|
makeLenses ''DoctorOptions
|
|
makeLenses ''DocumentedMessage
|
|
|
|
minimumClangVersion :: Integer
|
|
minimumClangVersion = 13
|
|
|
|
documentedMessage :: DocumentedWarning -> DocumentedMessage
|
|
documentedMessage w = uncurry DocumentedMessage (first (baseUrl <>) warningInfo)
|
|
where
|
|
warningInfo :: (Text, Text)
|
|
warningInfo = case w of
|
|
NoClang -> ("could-not-find-the-clang-command", "Could not find the clang command")
|
|
OldClang -> ("newer-clang-version-required", "Clang version " <> show minimumClangVersion <> " or newer required")
|
|
NoWasmLd -> ("could-not-find-the-wasm-ld-command", "Could not find the wasm-ld command")
|
|
NoWasm32Target -> ("clang-does-not-support-the-wasm32-target", "Clang does not support the wasm32 target")
|
|
NoWasm32WasiTarget -> ("clang-does-not-support-the-wasm32-wasi-target", "Clang does not support the wasm32-wasi target")
|
|
NoSysroot -> ("environment-variable-wasi_sysroot_path-is-not-set", "Environment variable WASI_SYSROOT_PATH is missing")
|
|
NoWasmer -> ("could-not-find-the-wasmer-command", "Could not find the wasmer command")
|
|
|
|
baseUrl :: Text
|
|
baseUrl = "https://docs.juvix.org/" <> V.versionDoc <> "/reference/tooling/doctor/#"
|
|
|
|
heading :: (Member Log r) => Text -> Sem r ()
|
|
heading = log . ("> " <>)
|
|
|
|
warning :: (Member Log r) => Text -> Sem r ()
|
|
warning = log . (" ! " <>)
|
|
|
|
info :: (Member Log r) => Text -> Sem r ()
|
|
info = log . (" | " <>)
|
|
|
|
type DoctorEff = '[Log, Embed IO, App]
|
|
|
|
checkCmdOnPath :: (Members DoctorEff r) => String -> [Text] -> Sem r ()
|
|
checkCmdOnPath cmd errMsg =
|
|
whenM (isNothing <$> findExecutable (relFile cmd)) (mapM_ warning errMsg)
|
|
|
|
-- | Check that wasm-ld exists in the same LLVM distribution as the clang command
|
|
checkWasmLd :: (Members DoctorEff r) => Path Abs File -> [Text] -> Sem r ()
|
|
checkWasmLd clangPath errMsg =
|
|
unlessM (isExecutable (parent clangPath <//> $(mkRelFile "wasm-ld"))) (mapM_ warning errMsg)
|
|
|
|
checkClangTargetSupported :: (Members DoctorEff r) => Path Abs File -> String -> [Text] -> Sem r ()
|
|
checkClangTargetSupported clangPath target errMsg = do
|
|
(code, _, _) <-
|
|
embed
|
|
( P.readProcessWithExitCode
|
|
(toFilePath clangPath)
|
|
["-target", target, "--print-supported-cpus"]
|
|
""
|
|
)
|
|
unless (code == ExitSuccess) (mapM_ warning errMsg)
|
|
|
|
checkClangVersion :: (Members DoctorEff r) => Path Abs File -> Integer -> [Text] -> Sem r ()
|
|
checkClangVersion clangPath expectedVersion errMsg = do
|
|
versionString <- embed (P.readProcess (toFilePath clangPath) ["-dumpversion"] "")
|
|
case headMay (splitOn "." versionString) >>= readMaybe of
|
|
Just majorVersion -> unless (majorVersion >= expectedVersion) (mapM_ warning errMsg)
|
|
Nothing -> warning "Could not determine clang version"
|
|
|
|
checkEnvVarSet :: (Members DoctorEff r) => String -> [Text] -> Sem r ()
|
|
checkEnvVarSet var errMsg = do
|
|
whenM (isNothing <$> embed (E.lookupEnv var)) (mapM_ warning errMsg)
|
|
|
|
getLatestRelease :: (Members '[Embed IO, Fail] r) => Sem r GithubRelease
|
|
getLatestRelease = do
|
|
request' <- failFromException (parseRequest "https://api.github.com/repos/anoma/juvix/releases/latest")
|
|
let request = setRequestHeaders [("user-agent", "curl/7.79.1"), ("Accept", "application/vnd.github+json")] request'
|
|
response <- failFromException (httpJSON request)
|
|
return (getResponseBody response)
|
|
|
|
checkVersion :: (Members DoctorEff r) => Sem r ()
|
|
checkVersion = do
|
|
heading "Checking latest Juvix release on Github..."
|
|
let tagName = "v" <> V.versionDoc
|
|
response <- runFail getLatestRelease
|
|
case response of
|
|
Just release -> case release ^. githubReleaseTagName of
|
|
Just latestTagName -> unless (tagName == latestTagName) (warning ("Newer Juvix version is available from https://github.com/anoma/juvix/releases/tag/" <> latestTagName))
|
|
Nothing -> warning "Tag name is not present in release JSON from Github API"
|
|
Nothing -> warning "Network error when fetching data from Github API"
|
|
|
|
renderDocumentedWarning :: DocumentedWarning -> [Text]
|
|
renderDocumentedWarning w = [dmsg ^. documentedMessageMessage, dmsg ^. documentedMessageUrl]
|
|
where
|
|
dmsg :: DocumentedMessage
|
|
dmsg = documentedMessage w
|
|
|
|
documentedCheck ::
|
|
([Text] -> Sem r ()) -> DocumentedWarning -> Sem r ()
|
|
documentedCheck check w = check (renderDocumentedWarning w)
|
|
|
|
findClangPath :: (Members DoctorEff r) => Sem r (Maybe ClangPath)
|
|
findClangPath = findClang
|
|
|
|
checkClang :: forall r. (Members DoctorEff r) => Bool -> Sem r ()
|
|
checkClang printVerbose = do
|
|
heading "Checking for clang..."
|
|
clangPath <- findClangPath
|
|
case clangPath of
|
|
Just cp -> when printVerbose (printVerbosePath cp)
|
|
Nothing -> mapM_ warning (renderDocumentedWarning NoClang)
|
|
|
|
mapM_ checkClangProperties (extractClangPath <$> clangPath)
|
|
|
|
heading "Checking that WASI_SYSROOT_PATH is set..."
|
|
documentedCheck (checkEnvVarSet "WASI_SYSROOT_PATH") NoSysroot
|
|
where
|
|
checkClangProperties :: Path Abs File -> Sem r ()
|
|
checkClangProperties p = do
|
|
heading "Checking clang version..."
|
|
documentedCheck (checkClangVersion p minimumClangVersion) OldClang
|
|
heading "Checking for wasm-ld..."
|
|
documentedCheck (checkWasmLd p) NoWasmLd
|
|
heading "Checking that clang supports wasm32..."
|
|
documentedCheck (checkClangTargetSupported p "wasm32") NoWasm32Target
|
|
heading "Checking that clang supports wasm32-wasi..."
|
|
documentedCheck (checkClangTargetSupported p "wasm32-wasi") NoWasm32WasiTarget
|
|
|
|
baseClangMsg :: Path Abs File -> Text
|
|
baseClangMsg p = "Found clang at " <> show p
|
|
|
|
printVerbosePath :: ClangPath -> Sem r ()
|
|
printVerbosePath = \case
|
|
ClangEnvVarPath p -> info (baseClangMsg p <> (" using " <> pack llvmDistEnvironmentVar <> " environment variable"))
|
|
ClangSystemPath p -> info (baseClangMsg p <> (" using system PATH"))
|
|
|
|
checkWasmer :: (Members DoctorEff r) => Sem r ()
|
|
checkWasmer = do
|
|
heading "Checking for wasmer..."
|
|
documentedCheck (checkCmdOnPath "wasmer") NoWasmer
|
|
|
|
runCommand :: (Members DoctorEff r) => DoctorOptions -> Sem r ()
|
|
runCommand opts = do
|
|
checkClang (opts ^. doctorVerbose)
|
|
checkWasmer
|
|
offlineMode <- (^. globalOffline) <$> askGlobalOptions
|
|
unless offlineMode checkVersion
|