From 4fcb881ebca771f70d56111df20c70a26af5e59c Mon Sep 17 00:00:00 2001 From: Jan Mas Rovira Date: Wed, 24 May 2023 15:42:20 +0200 Subject: [PATCH] Add `main` field to `juvix.yaml` (#2120) - Closes #2067 This pr adds the field `main` to `juvix.yaml`. This field is optional and should contain a path to a juvix file that is meant to be used for the `compile` (and `dev compile`) command when no file is given as an argument in the CLI. This makes it possible to simply run `juvix compile` if the `main` is specified in the `jvuix.yaml`. I have updated the `juvix.yaml` of the milestone examples. --------- Co-authored-by: Paul Cadman Co-authored-by: Jonathan Cubides --- app/App.hs | 23 ++++++++++++++- app/Commands/Compile.hs | 4 +-- app/Commands/Dev/Asm/Compile.hs | 4 +-- app/Commands/Dev/Core/Compile.hs | 2 +- app/Commands/Dev/Core/Compile/Base.hs | 2 +- app/Commands/Extra/Compile.hs | 2 +- app/Commands/Extra/Compile/Options.hs | 4 +-- app/Commands/Init.hs | 1 + examples/milestone/Collatz/juvix.yaml | 1 + examples/milestone/Fibonacci/juvix.yaml | 1 + examples/milestone/Hanoi/juvix.yaml | 1 + examples/milestone/HelloWorld/juvix.yaml | 1 + examples/milestone/PascalsTriangle/juvix.yaml | 1 + examples/milestone/TicTacToe/juvix.yaml | 1 + src/Juvix/Compiler/Pipeline/Package.hs | 18 +++++++++--- src/Juvix/Prelude/Prepath.hs | 4 ++- tests/smoke/Commands/compile.smoke.yaml | 28 +++++++++++++++++++ 17 files changed, 83 insertions(+), 15 deletions(-) diff --git a/app/App.hs b/app/App.hs index 52bcfea2e6..4f6b1ad23a 100644 --- a/app/App.hs +++ b/app/App.hs @@ -6,6 +6,7 @@ import GlobalOptions import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.PathResolver import Juvix.Compiler.Pipeline import Juvix.Data.Error qualified as Error +import Juvix.Extra.Paths.Base import Juvix.Prelude.Pretty hiding ( Doc, ) @@ -23,6 +24,7 @@ data App m a where AskPackageGlobal :: App m Bool AskGlobalOptions :: App m GlobalOptions FromAppPathFile :: AppPath File -> App m (Path Abs File) + GetMainFile :: Maybe (AppPath File) -> App m (Path Abs File) FromAppPathDir :: AppPath Dir -> App m (Path Abs Dir) RenderStdOut :: (HasAnsiBackend a, HasTextBackend a) => a -> App m () RunPipelineEither :: AppPath File -> Sem PipelineEff a -> App m (Either JuvixError (ResolverState, a)) @@ -48,6 +50,7 @@ runAppIO args@RunAppIOArgs {..} = interpret $ \case AskPackageGlobal -> return (_runAppIOArgsRoots ^. rootsPackageGlobal) FromAppPathFile p -> embed (prepathToAbsFile invDir (p ^. pathPath)) + GetMainFile m -> getMainFile' m FromAppPathDir p -> embed (prepathToAbsDir invDir (p ^. pathPath)) RenderStdOut t | _runAppIOArgsGlobalOptions ^. globalOnlyErrors -> return () @@ -77,10 +80,28 @@ runAppIO args@RunAppIOArgs {..} = ExitJuvixError e -> do printErr e embed exitFailure - ExitMsg exitCode t -> embed (putStrLn t >> hFlush stdout >> exitWith exitCode) + ExitMsg exitCode t -> exitMsg' exitCode t SayRaw b -> embed (ByteString.putStr b) where + exitMsg' :: ExitCode -> Text -> Sem r x + exitMsg' exitCode t = embed (putStrLn t >> hFlush stdout >> exitWith exitCode) + getMainFile' :: Maybe (AppPath File) -> Sem r (Path Abs File) + getMainFile' = \case + Just p -> embed (prepathToAbsFile invDir (p ^. pathPath)) + Nothing -> case pkg ^. packageMain of + Just p -> embed (prepathToAbsFile invDir p) + Nothing -> missingMainErr + missingMainErr :: Sem r x + missingMainErr = + exitMsg' + (ExitFailure 1) + ( "A path to the main file must be given in the CLI or specified in the `main` field of the " + <> pack (toFilePath juvixYamlFile) + <> " file" + ) invDir = _runAppIOArgsRoots ^. rootsInvokeDir + pkg :: Package + pkg = _runAppIOArgsRoots ^. rootsPackage g :: GlobalOptions g = _runAppIOArgsGlobalOptions printErr e = diff --git a/app/Commands/Compile.hs b/app/Commands/Compile.hs index c1680b454f..20006617a0 100644 --- a/app/Commands/Compile.hs +++ b/app/Commands/Compile.hs @@ -11,8 +11,8 @@ import Juvix.Compiler.Core.Transformation.DisambiguateNames qualified as Core runCommand :: (Members '[Embed IO, App] r) => CompileOptions -> Sem r () runCommand opts@CompileOptions {..} = do - inputFile <- fromAppPathFile _compileInputFile - Core.CoreResult {..} <- runPipeline _compileInputFile upToCore + inputFile <- getMainFile _compileInputFile + Core.CoreResult {..} <- runPipeline (AppPath (preFileFromAbs inputFile) True) upToCore let arg = Compile.PipelineArg { _pipelineArgFile = inputFile, diff --git a/app/Commands/Dev/Asm/Compile.hs b/app/Commands/Dev/Asm/Compile.hs index c3283ff426..ca5be51587 100644 --- a/app/Commands/Dev/Asm/Compile.hs +++ b/app/Commands/Dev/Asm/Compile.hs @@ -30,10 +30,10 @@ runCommand opts = do ensureDir buildDir cFile <- inputCFile file embed $ TIO.writeFile (toFilePath cFile) _resultCCode - Compile.runCommand opts {_compileInputFile = AppPath (preFileFromAbs cFile) False} + Compile.runCommand opts {_compileInputFile = Just (AppPath (preFileFromAbs cFile) False)} where getFile :: Sem r (Path Abs File) - getFile = fromAppPathFile (opts ^. compileInputFile) + getFile = getMainFile (opts ^. compileInputFile) getTarget :: CompileTarget -> Sem r Backend.Target getTarget = \case diff --git a/app/Commands/Dev/Core/Compile.hs b/app/Commands/Dev/Core/Compile.hs index 211cf7209c..50f504fd1d 100644 --- a/app/Commands/Dev/Core/Compile.hs +++ b/app/Commands/Dev/Core/Compile.hs @@ -21,4 +21,4 @@ runCommand opts = do TargetAsm -> runAsmPipeline arg where getFile :: Sem r (Path Abs File) - getFile = fromAppPathFile (opts ^. compileInputFile) + getFile = getMainFile (opts ^. compileInputFile) diff --git a/app/Commands/Dev/Core/Compile/Base.hs b/app/Commands/Dev/Core/Compile/Base.hs index de1e95c306..bb3bde0f1e 100644 --- a/app/Commands/Dev/Core/Compile/Base.hs +++ b/app/Commands/Dev/Core/Compile/Base.hs @@ -56,7 +56,7 @@ runCPipeline pa@PipelineArg {..} = do outfile <- Compile.outputFile _pipelineArgOptions _pipelineArgFile Compile.runCommand _pipelineArgOptions - { _compileInputFile = AppPath (preFileFromAbs cFile) False, + { _compileInputFile = Just (AppPath (preFileFromAbs cFile) False), _compileOutputFile = Just (AppPath (preFileFromAbs outfile) False) } where diff --git a/app/Commands/Extra/Compile.hs b/app/Commands/Extra/Compile.hs index 015e550561..412aa8518a 100644 --- a/app/Commands/Extra/Compile.hs +++ b/app/Commands/Extra/Compile.hs @@ -10,7 +10,7 @@ import System.Process qualified as P runCommand :: forall r. (Members '[Embed IO, App] r) => CompileOptions -> Sem r () runCommand opts = do - inputFile <- fromAppPathFile (opts ^. compileInputFile) + inputFile <- getMainFile (opts ^. compileInputFile) result <- runCompile inputFile opts case result of Left err -> printFailureExit err diff --git a/app/Commands/Extra/Compile/Options.hs b/app/Commands/Extra/Compile/Options.hs index 81022f4650..5f658676f3 100644 --- a/app/Commands/Extra/Compile/Options.hs +++ b/app/Commands/Extra/Compile/Options.hs @@ -30,7 +30,7 @@ data CompileOptions = CompileOptions _compileTerm :: Bool, _compileOutputFile :: Maybe (AppPath File), _compileTarget :: CompileTarget, - _compileInputFile :: AppPath File, + _compileInputFile :: Maybe (AppPath File), _compileOptimizationLevel :: Maybe Int, _compileInliningDepth :: Int } @@ -100,7 +100,7 @@ parseCompileOptions supportedTargets parseInputFile = do ) _compileTarget <- optCompileTarget supportedTargets _compileOutputFile <- optional parseGenericOutputFile - _compileInputFile <- parseInputFile + _compileInputFile <- optional parseInputFile pure CompileOptions {..} optCompileTarget :: SupportedTargets -> Parser CompileTarget diff --git a/app/Commands/Init.hs b/app/Commands/Init.hs index 765c6c71b2..4a658fda4f 100644 --- a/app/Commands/Init.hs +++ b/app/Commands/Init.hs @@ -49,6 +49,7 @@ getPackage = do { _packageName = tproj, _packageVersion = tversion, _packageBuildDir = Nothing, + _packageMain = Nothing, _packageDependencies = [defaultStdlibDep] } diff --git a/examples/milestone/Collatz/juvix.yaml b/examples/milestone/Collatz/juvix.yaml index e42822a936..5c6cf6c3fb 100644 --- a/examples/milestone/Collatz/juvix.yaml +++ b/examples/milestone/Collatz/juvix.yaml @@ -1,2 +1,3 @@ name: Collatz +main: Collatz.juvix version: 0.1.0 diff --git a/examples/milestone/Fibonacci/juvix.yaml b/examples/milestone/Fibonacci/juvix.yaml index 80c88144a6..e487a4e337 100644 --- a/examples/milestone/Fibonacci/juvix.yaml +++ b/examples/milestone/Fibonacci/juvix.yaml @@ -1,2 +1,3 @@ name: Fibonacci +main: Fibonacci.juvix version: 0.1.0 diff --git a/examples/milestone/Hanoi/juvix.yaml b/examples/milestone/Hanoi/juvix.yaml index 9de3cb6dac..eb780c8949 100644 --- a/examples/milestone/Hanoi/juvix.yaml +++ b/examples/milestone/Hanoi/juvix.yaml @@ -1,2 +1,3 @@ name: Hanoi +main: Hanoi.juvix version: 0.1.0 diff --git a/examples/milestone/HelloWorld/juvix.yaml b/examples/milestone/HelloWorld/juvix.yaml index 454325b4fe..fff8f68dca 100644 --- a/examples/milestone/HelloWorld/juvix.yaml +++ b/examples/milestone/HelloWorld/juvix.yaml @@ -1,2 +1,3 @@ name: HelloWorld +main: HelloWorld.juvix version: 0.1.0 diff --git a/examples/milestone/PascalsTriangle/juvix.yaml b/examples/milestone/PascalsTriangle/juvix.yaml index e516228c15..25d92b2c1d 100644 --- a/examples/milestone/PascalsTriangle/juvix.yaml +++ b/examples/milestone/PascalsTriangle/juvix.yaml @@ -1,2 +1,3 @@ name: PascalsTriangle +main: PascalsTriangle.juvix version: 0.1.0 diff --git a/examples/milestone/TicTacToe/juvix.yaml b/examples/milestone/TicTacToe/juvix.yaml index 723a49718e..6e864d84ab 100644 --- a/examples/milestone/TicTacToe/juvix.yaml +++ b/examples/milestone/TicTacToe/juvix.yaml @@ -1,2 +1,3 @@ name: TicTacToe +main: CLI/TicTacToe.juvix version: 0.1.0 diff --git a/src/Juvix/Compiler/Pipeline/Package.hs b/src/Juvix/Compiler/Pipeline/Package.hs index d46df57cfe..56471a2ce9 100644 --- a/src/Juvix/Compiler/Pipeline/Package.hs +++ b/src/Juvix/Compiler/Pipeline/Package.hs @@ -8,6 +8,7 @@ module Juvix.Compiler.Pipeline.Package packageBuildDir, packageVersion, packageDependencies, + packageMain, rawPackage, readPackage, readPackageIO, @@ -49,7 +50,8 @@ data Package' (s :: IsProcessed) = Package { _packageName :: NameType s, _packageVersion :: VersionType s, _packageDependencies :: DependenciesType s, - _packageBuildDir :: Maybe (SomeBase Dir) + _packageBuildDir :: Maybe (SomeBase Dir), + _packageMain :: Maybe (Prepath File) } deriving stock (Generic) @@ -88,6 +90,7 @@ instance FromJSON RawPackage where _packageVersion <- keyMay "version" asText _packageDependencies <- keyMay "dependencies" fromAesonParser _packageBuildDir <- keyMay "build-dir" fromAesonParser + _packageMain <- keyMay "main" fromAesonParser return Package {..} err :: a err = error "Failed to parse juvix.yaml" @@ -99,6 +102,7 @@ emptyPackage = { _packageName = defaultPackageName, _packageVersion = defaultVersion, _packageDependencies = [defaultStdlibDep], + _packageMain = Nothing, _packageBuildDir = Nothing } @@ -108,7 +112,8 @@ rawPackage pkg = { _packageName = Just (pkg ^. packageName), _packageVersion = Just (prettySemVer (pkg ^. packageVersion)), _packageDependencies = Just (pkg ^. packageDependencies), - _packageBuildDir = pkg ^. packageBuildDir + _packageBuildDir = pkg ^. packageBuildDir, + _packageMain = pkg ^. packageMain } processPackage :: forall r. (Members '[Error Text] r) => Maybe (SomeBase Dir) -> RawPackage -> Sem r Package @@ -117,9 +122,13 @@ processPackage buildDir pkg = do base :: SomeBase Dir = fromMaybe (Rel relBuildDir) buildDir relStdlibDir stdlib = Dependency (mkPrepath (fromSomeDir base)) _packageDependencies = fromMaybe [stdlib] (pkg ^. packageDependencies) - _packageBuildDir = pkg ^. packageBuildDir _packageVersion <- getVersion - return Package {..} + return + Package + { _packageBuildDir = pkg ^. packageBuildDir, + _packageMain = pkg ^. packageMain, + .. + } where getVersion :: Sem r SemVer getVersion = case pkg ^. packageVersion of @@ -143,6 +152,7 @@ globalPackage = { _packageDependencies = [defaultStdlibDep], _packageName = "global-juvix-package", _packageVersion = defaultVersion, + _packageMain = Nothing, _packageBuildDir = Nothing } diff --git a/src/Juvix/Prelude/Prepath.hs b/src/Juvix/Prelude/Prepath.hs index 3ca688b6bf..0faf0d55b8 100644 --- a/src/Juvix/Prelude/Prepath.hs +++ b/src/Juvix/Prelude/Prepath.hs @@ -20,7 +20,9 @@ import System.Directory qualified as System import System.Environment -- | A file/directory path that may contain environmental variables -newtype Prepath d = Prepath {_prepath :: String} +newtype Prepath d = Prepath + { _prepath :: String + } deriving stock (Show, Eq, Data, Generic) makeLenses ''Prepath diff --git a/tests/smoke/Commands/compile.smoke.yaml b/tests/smoke/Commands/compile.smoke.yaml index 41ec1dfa56..68295bd2b6 100644 --- a/tests/smoke/Commands/compile.smoke.yaml +++ b/tests/smoke/Commands/compile.smoke.yaml @@ -11,6 +11,34 @@ tests: JUVIX_FILE exit-status: 0 + - name: hello-world-no-arg + command: + shell: + - bash + script: | + cd ./examples/milestone/HelloWorld + juvix compile + ./HelloWorld + exit-status: 0 + stdout: | + hello world! + + - name: hello-world-no-arg-error + command: + shell: + - bash + script: | + temp=$(mktemp -d) + trap 'rm -rf -- "$temp"' EXIT + cd ./examples/milestone/ + cp -r HelloWorld "$temp" + cd "$temp/HelloWorld" + sed -i '/^main:/d' juvix.yaml + juvix compile + exit-status: 1 + stdout: | + A path to the main file must be given in the CLI or specified in the `main` field of the juvix.yaml file + - name: hello-world command: shell: