diff --git a/Makefile b/Makefile index d90b93244e..d86c8c7e5d 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ METAFILES:=README.md \ LICENSE.md STACKFLAGS?=--jobs $(THREADS) -STACKTESTFLAGS?=--ta --hide-successes --ta --ansi-tricks=false +STACKTESTFLAGS?=--ta --hide-successes --ta --ansi-tricks=false --ta "+RTS -N -RTS" SMOKEFLAGS?=--color --diff=git STACK?=stack diff --git a/app/Commands/Init.hs b/app/Commands/Init.hs index 217c6ebcf2..48d9ffe562 100644 --- a/app/Commands/Init.hs +++ b/app/Commands/Init.hs @@ -6,6 +6,7 @@ import Data.Text qualified as Text import Data.Versions import Juvix.Compiler.Pipeline.Package import Juvix.Data.Effect.Fail.Extra qualified as Fail +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.Paths import Juvix.Prelude import Juvix.Prelude.Pretty @@ -60,7 +61,7 @@ checkNotInProject = checkPackage :: forall r. (Members '[Embed IO] r) => Sem r () checkPackage = do cwd <- getCurrentDir - ep <- runError @JuvixError (loadPackageFileIO cwd DefaultBuildDir) + ep <- runError @JuvixError (runTaggedLockPermissive (loadPackageFileIO cwd DefaultBuildDir)) case ep of Left {} -> do say "Package.juvix is invalid. Please raise an issue at https://github.com/anoma/juvix/issues" diff --git a/app/Commands/Repl.hs b/app/Commands/Repl.hs index 1749cc101c..828e1868e7 100644 --- a/app/Commands/Repl.hs +++ b/app/Commands/Repl.hs @@ -39,6 +39,7 @@ import Juvix.Compiler.Pipeline.Setup (entrySetup) import Juvix.Data.CodeAnn (Ann) import Juvix.Data.Effect.Git import Juvix.Data.Effect.Process +import Juvix.Data.Effect.TaggedLock import Juvix.Data.Error.GenericError qualified as Error import Juvix.Data.NameKind import Juvix.Extra.Paths qualified as P @@ -152,6 +153,7 @@ loadDefaultPrelude = whenJustM defaultPreludeEntryPoint $ \e -> do . runFilesIO . runError @JuvixError . runReader e + . runTaggedLockPermissive . runLogIO . runProcessIO . runError @GitProcessError diff --git a/app/Main.hs b/app/Main.hs index af9e810623..82e4590545 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -7,6 +7,7 @@ import CommonOptions import Data.String.Interpolate (i) import GlobalOptions import Juvix.Compiler.Pipeline.Root +import Juvix.Data.Effect.TaggedLock import TopCommand import TopCommand.Options @@ -18,7 +19,7 @@ main = do mbuildDir <- mapM (prepathToAbsDir invokeDir) (_runAppIOArgsGlobalOptions ^? globalBuildDir . _Just . pathPath) mainFile <- topCommandInputPath cli mapM_ checkMainFile mainFile - _runAppIOArgsRoot <- findRootAndChangeDir (containingDir <$> mainFile) mbuildDir invokeDir + _runAppIOArgsRoot <- findRootAndChangeDir LockModePermissive (containingDir <$> mainFile) mbuildDir invokeDir runFinal . resourceToIOFinal . embedToFinal @IO diff --git a/package.yaml b/package.yaml index b1f4392f2c..c068c65861 100644 --- a/package.yaml +++ b/package.yaml @@ -54,6 +54,7 @@ dependencies: - exceptions == 0.10.* - extra == 1.7.* - file-embed == 0.0.* + - filelock == 0.1.* - filepath == 1.4.* - githash == 0.1.* - hashable == 1.4.* @@ -174,6 +175,8 @@ tests: - juvix verbatim: default-language: GHC2021 + ghc-options: + - -threaded benchmarks: juvix-bench: diff --git a/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs b/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs index 66ef97fd10..7463d87ddc 100644 --- a/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs +++ b/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs @@ -25,6 +25,7 @@ import Juvix.Compiler.Pipeline.Lockfile import Juvix.Compiler.Pipeline.Package import Juvix.Compiler.Pipeline.Package.Loader.EvalEff import Juvix.Data.Effect.Git +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.Paths import Juvix.Extra.Stdlib (ensureStdlib) import Juvix.Prelude @@ -43,7 +44,7 @@ mkPackage mpackageEntry _packageRoot = do mkPackageInfo :: forall r. - (Members '[Files, Error JuvixError, Reader ResolverEnv, Error DependencyError, GitClone] r) => + (Members '[TaggedLock, Files, Error JuvixError, Reader ResolverEnv, Error DependencyError, GitClone] r) => Maybe EntryPoint -> Path Abs Dir -> Package -> @@ -162,7 +163,7 @@ resolveDependency i = case i ^. packageDepdendencyInfoDependency of registerDependencies' :: forall r. - (Members '[Reader EntryPoint, State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => + (Members '[TaggedLock, Reader EntryPoint, State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => DependenciesConfig -> Sem r () registerDependencies' conf = do @@ -186,7 +187,7 @@ registerDependencies' conf = do addRootDependency :: forall r. - (Members '[State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => + (Members '[TaggedLock, State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => DependenciesConfig -> EntryPoint -> Path Abs Dir -> @@ -207,7 +208,7 @@ addRootDependency conf e root = do addDependency :: forall r. - (Members '[State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => + (Members '[TaggedLock, State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => Maybe EntryPoint -> PackageDependencyInfo -> Sem r LockfileDependency @@ -224,7 +225,7 @@ addDependency me d = do addDependency' :: forall r. - (Members '[State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => + (Members '[TaggedLock, State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => Package -> Maybe EntryPoint -> ResolvedDependency -> @@ -314,7 +315,7 @@ expectedPath' actualPath m = do re :: forall r a. - (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => + (Members '[TaggedLock, Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => Sem (PathResolver ': r) a -> Sem (Reader ResolverEnv ': State ResolverState ': r) a re = reinterpret2H helper @@ -337,13 +338,13 @@ re = reinterpret2H helper Right (r, _) -> r raise (evalPathResolver' st' root' (a' x')) -evalPathResolver' :: (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a +evalPathResolver' :: (Members '[TaggedLock, Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a evalPathResolver' st root = fmap snd . runPathResolver' st root -runPathResolver :: (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolver :: (Members '[TaggedLock, Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolver = runPathResolver' iniResolverState -runPathResolver' :: (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolver' :: (Members '[TaggedLock, Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone, EvalFileEff] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolver' st root x = do e <- ask let _envSingleFile :: Maybe (Path Abs File) @@ -359,15 +360,15 @@ runPathResolver' st root x = do } runState st (runReader env (re x)) -runPathResolverPipe' :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => ResolverState -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolverPipe' :: (Members '[TaggedLock, Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => ResolverState -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolverPipe' iniState a = do r <- asks (^. entryPointResolverRoot) runPathResolver' iniState r a -runPathResolverPipe :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolverPipe :: (Members '[TaggedLock, Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolverPipe a = do r <- asks (^. entryPointResolverRoot) runPathResolver r a -evalPathResolverPipe :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Sem (PathResolver ': r) a -> Sem r a +evalPathResolverPipe :: (Members '[TaggedLock, Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Sem (PathResolver ': r) a -> Sem r a evalPathResolverPipe = fmap snd . runPathResolverPipe diff --git a/src/Juvix/Compiler/Pipeline.hs b/src/Juvix/Compiler/Pipeline.hs index 44eb7ea653..619d9fb24b 100644 --- a/src/Juvix/Compiler/Pipeline.hs +++ b/src/Juvix/Compiler/Pipeline.hs @@ -37,11 +37,12 @@ import Juvix.Compiler.Reg.Data.InfoTable qualified as Reg import Juvix.Compiler.Reg.Translation.FromAsm qualified as Reg import Juvix.Data.Effect.Git import Juvix.Data.Effect.Process +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude -type PipelineEff = '[PathResolver, EvalFileEff, Error PackageLoaderError, Error DependencyError, GitClone, Error GitProcessError, Process, Log, Reader EntryPoint, Files, NameIdGen, Builtins, Error JuvixError, HighlightBuilder, Internet, Embed IO] +type PipelineEff = '[PathResolver, EvalFileEff, Error PackageLoaderError, Error DependencyError, GitClone, Error GitProcessError, Process, Log, TaggedLock, Reader EntryPoint, Files, NameIdGen, Builtins, Error JuvixError, HighlightBuilder, Internet, Embed IO, Resource, Final IO] -type TopPipelineEff = '[PathResolver, EvalFileEff, Error PackageLoaderError, Error DependencyError, GitClone, Error GitProcessError, Process, Log, Reader EntryPoint, Files, NameIdGen, Builtins, State Artifacts, Error JuvixError, HighlightBuilder, Embed IO] +type TopPipelineEff = '[PathResolver, EvalFileEff, Error PackageLoaderError, Error DependencyError, GitClone, Error GitProcessError, Process, Log, TaggedLock, Reader EntryPoint, Files, NameIdGen, Builtins, State Artifacts, Error JuvixError, HighlightBuilder, Embed IO, Resource, Final IO] -------------------------------------------------------------------------------- -- Workflows diff --git a/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs b/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs index 018b3439b0..3e32ab02c9 100644 --- a/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs +++ b/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs @@ -6,10 +6,11 @@ import Juvix.Compiler.Pipeline.EntryPoint import Juvix.Compiler.Pipeline.Package.Loader.EvalEff import Juvix.Compiler.Pipeline.Package.Loader.PathResolver import Juvix.Data.Effect.Git +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude -runPathResolverArtifacts :: (Members '[Files, Reader EntryPoint, State Artifacts, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Sem (PathResolver ': r) a -> Sem r a +runPathResolverArtifacts :: (Members '[TaggedLock, Files, Reader EntryPoint, State Artifacts, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Sem (PathResolver ': r) a -> Sem r a runPathResolverArtifacts = runStateLikeArtifacts runPathResolverPipe' artifactResolver -runPackagePathResolverArtifacts :: (Members '[Files, Reader EntryPoint, State Artifacts, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a +runPackagePathResolverArtifacts :: (Members '[TaggedLock, Files, Reader EntryPoint, State Artifacts, Error DependencyError, GitClone, Error JuvixError, EvalFileEff] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a runPackagePathResolverArtifacts root = runStateLikeArtifacts (runPackagePathResolver'' root) artifactResolver diff --git a/src/Juvix/Compiler/Pipeline/EntryPoint/IO.hs b/src/Juvix/Compiler/Pipeline/EntryPoint/IO.hs index f400b9943f..9babe49707 100644 --- a/src/Juvix/Compiler/Pipeline/EntryPoint/IO.hs +++ b/src/Juvix/Compiler/Pipeline/EntryPoint/IO.hs @@ -2,16 +2,21 @@ module Juvix.Compiler.Pipeline.EntryPoint.IO where import Juvix.Compiler.Pipeline.EntryPoint import Juvix.Compiler.Pipeline.Root +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude -defaultEntryPointCwdIO :: Path Abs File -> IO EntryPoint -defaultEntryPointCwdIO mainFile = do - cwd <- getCurrentDir - root <- findRootAndChangeDir (Just (parent mainFile)) Nothing cwd +defaultEntryPointIO :: Path Abs Dir -> Path Abs File -> IO EntryPoint +defaultEntryPointIO = defaultEntryPointIO' LockModePermissive + +defaultEntryPointIO' :: LockMode -> Path Abs Dir -> Path Abs File -> IO EntryPoint +defaultEntryPointIO' lockMode cwd mainFile = do + root <- findRootAndChangeDir lockMode (Just (parent mainFile)) Nothing cwd return (defaultEntryPoint root mainFile) -defaultEntryPointNoFileCwdIO :: IO EntryPoint -defaultEntryPointNoFileCwdIO = do - cwd <- getCurrentDir - root <- findRootAndChangeDir Nothing Nothing cwd +defaultEntryPointNoFileIO :: Path Abs Dir -> IO EntryPoint +defaultEntryPointNoFileIO = defaultEntryPointNoFileIO' LockModePermissive + +defaultEntryPointNoFileIO' :: LockMode -> Path Abs Dir -> IO EntryPoint +defaultEntryPointNoFileIO' lockMode cwd = do + root <- findRootAndChangeDir lockMode Nothing Nothing cwd return (defaultEntryPointNoFile root) diff --git a/src/Juvix/Compiler/Pipeline/Package.hs b/src/Juvix/Compiler/Pipeline/Package.hs index 6697347535..764e2e6705 100644 --- a/src/Juvix/Compiler/Pipeline/Package.hs +++ b/src/Juvix/Compiler/Pipeline/Package.hs @@ -18,6 +18,7 @@ import Juvix.Compiler.Pipeline.Package.Loader import Juvix.Compiler.Pipeline.Package.Loader.Error import Juvix.Compiler.Pipeline.Package.Loader.EvalEff import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.Paths import Juvix.Prelude @@ -124,35 +125,41 @@ readPackageFile root buildDir f = mapError (JuvixError @PackageLoaderError) $ do checkNoDuplicateDepNames f (pkg ^. packageDependencies) return (pkg {_packageLockfile = mLockfile}) -loadPackageFileIO :: (Members '[Error JuvixError, Embed IO] r) => Path Abs Dir -> BuildDir -> Sem r Package +loadPackageFileIO :: (Members '[TaggedLock, Error JuvixError, Embed IO] r) => Path Abs Dir -> BuildDir -> Sem r Package loadPackageFileIO root buildDir = runFilesIO . mapError (JuvixError @PackageLoaderError) . runEvalFileEffIO $ loadPackage buildDir (mkPackagePath root) -readPackageIO :: Path Abs Dir -> BuildDir -> IO Package -readPackageIO root buildDir = - runM +readPackageIO :: LockMode -> Path Abs Dir -> BuildDir -> IO Package +readPackageIO lockMode root buildDir = + runFinal + . resourceToIOFinal + . embedToFinal @IO . runFilesIO . runErrorIO' @JuvixError . mapError (JuvixError @PackageLoaderError) + . runTaggedLock lockMode . runEvalFileEffIO $ readPackage root buildDir -readGlobalPackageIO :: IO Package -readGlobalPackageIO = - runM +readGlobalPackageIO :: LockMode -> IO Package +readGlobalPackageIO lockMode = + runFinal + . resourceToIOFinal + . embedToFinal @IO . runFilesIO . runErrorIO' @JuvixError . mapError (JuvixError @PackageLoaderError) + . runTaggedLock lockMode . runEvalFileEffIO $ readGlobalPackage -readGlobalPackage :: (Members '[Error JuvixError, EvalFileEff, Files] r) => Sem r Package +readGlobalPackage :: (Members '[TaggedLock, Error JuvixError, EvalFileEff, Files] r) => Sem r Package readGlobalPackage = do packagePath <- globalPackageJuvix - unlessM (fileExists' packagePath) writeGlobalPackage + withTaggedLockDir (parent packagePath) (unlessM (fileExists' packagePath) writeGlobalPackage) readPackage (parent packagePath) DefaultBuildDir writeGlobalPackage :: (Members '[Files] r) => Sem r () diff --git a/src/Juvix/Compiler/Pipeline/Package/Loader/EvalEff/IO.hs b/src/Juvix/Compiler/Pipeline/Package/Loader/EvalEff/IO.hs index 67ed4acfac..327c64d97a 100644 --- a/src/Juvix/Compiler/Pipeline/Package/Loader/EvalEff/IO.hs +++ b/src/Juvix/Compiler/Pipeline/Package/Loader/EvalEff/IO.hs @@ -18,6 +18,7 @@ import Juvix.Compiler.Pipeline.Package.Loader.EvalEff import Juvix.Compiler.Pipeline.Package.Loader.PathResolver import Juvix.Data.Effect.Git import Juvix.Data.Effect.Process +import Juvix.Data.Effect.TaggedLock data LoaderResource = LoaderResource { _loaderResourceResult :: CoreResult, @@ -26,7 +27,7 @@ data LoaderResource = LoaderResource makeLenses ''LoaderResource -runEvalFileEffIO :: forall r a. (Members '[Embed IO, Error PackageLoaderError] r) => Sem (EvalFileEff ': r) a -> Sem r a +runEvalFileEffIO :: forall r a. (Members '[TaggedLock, Embed IO, Error PackageLoaderError] r) => Sem (EvalFileEff ': r) a -> Sem r a runEvalFileEffIO = interpretScopedAs allocator handler where allocator :: Path Abs File -> Sem r LoaderResource @@ -114,7 +115,7 @@ runEvalFileEffIO = interpretScopedAs allocator handler Just l -> l ^. intervalFile == f Nothing -> False -loadPackage' :: (Members '[Embed IO, Error PackageLoaderError] r) => Path Abs File -> Sem r CoreResult +loadPackage' :: (Members '[TaggedLock, Embed IO, Error PackageLoaderError] r) => Path Abs File -> Sem r CoreResult loadPackage' packagePath = do ( mapError ( \e -> diff --git a/src/Juvix/Compiler/Pipeline/Package/Loader/PathResolver.hs b/src/Juvix/Compiler/Pipeline/Package/Loader/PathResolver.hs index b9bb686474..052355d208 100644 --- a/src/Juvix/Compiler/Pipeline/Package/Loader/PathResolver.hs +++ b/src/Juvix/Compiler/Pipeline/Package/Loader/PathResolver.hs @@ -7,13 +7,14 @@ import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.PathResolver.Data import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.PathResolver.Error import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.PathResolver.Paths import Juvix.Compiler.Core.Language +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.PackageFiles import Juvix.Extra.Paths import Juvix.Extra.Stdlib -- | A PackageResolver interpreter intended to be used to load a Package file. -- It aggregates files at `rootPath` and files from the global package stdlib. -runPackagePathResolver :: forall r a. (Members '[Files] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a +runPackagePathResolver :: forall r a. (Members '[TaggedLock, Files] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a runPackagePathResolver rootPath sem = do globalStdlib <- juvixStdlibDir . rootBuildDir <$> globalRoot globalPackageDir <- globalPackageDescriptionRoot @@ -43,10 +44,10 @@ runPackagePathResolver rootPath sem = do | relPath == packageFilePath = Just rootPath | otherwise = Nothing -runPackagePathResolver' :: (Members '[Files] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPackagePathResolver' :: (Members '[TaggedLock, Files] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPackagePathResolver' root eff = do res <- runPackagePathResolver root eff return (iniResolverState, res) -runPackagePathResolver'' :: (Members '[Files] r) => Path Abs Dir -> ResolverState -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPackagePathResolver'' :: (Members '[TaggedLock, Files] r) => Path Abs Dir -> ResolverState -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPackagePathResolver'' root _ eff = runPackagePathResolver' root eff diff --git a/src/Juvix/Compiler/Pipeline/Repl.hs b/src/Juvix/Compiler/Pipeline/Repl.hs index 55827d44da..cdfece57df 100644 --- a/src/Juvix/Compiler/Pipeline/Repl.hs +++ b/src/Juvix/Compiler/Pipeline/Repl.hs @@ -20,6 +20,7 @@ import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO import Juvix.Data.Effect.Git.Process import Juvix.Data.Effect.Git.Process.Error import Juvix.Data.Effect.Process (runProcessIO) +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude arityCheckExpression :: @@ -196,6 +197,7 @@ compileReplInputIO fp txt = do hasInternet <- not <$> asks (^. entryPointOffline) runError . evalInternet hasInternet + . runTaggedLockPermissive . runLogIO . runFilesIO . mapError (JuvixError @GitProcessError) diff --git a/src/Juvix/Compiler/Pipeline/Root.hs b/src/Juvix/Compiler/Pipeline/Root.hs index 6a09b262be..4beaea06e4 100644 --- a/src/Juvix/Compiler/Pipeline/Root.hs +++ b/src/Juvix/Compiler/Pipeline/Root.hs @@ -7,16 +7,17 @@ where import Control.Exception qualified as IO import Juvix.Compiler.Pipeline.Package import Juvix.Compiler.Pipeline.Root.Base +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.Paths qualified as Paths import Juvix.Prelude findRootAndChangeDir :: + LockMode -> Maybe (Path Abs Dir) -> Maybe (Path Abs Dir) -> Path Abs Dir -> IO Root -findRootAndChangeDir minputFileDir mbuildDir _rootInvokeDir = do - whenJust minputFileDir setCurrentDir +findRootAndChangeDir lockMode minputFileDir mbuildDir _rootInvokeDir = do r <- IO.try go case r of Left (err :: IO.SomeException) -> do @@ -30,8 +31,8 @@ findRootAndChangeDir minputFileDir mbuildDir _rootInvokeDir = do findPackageFile :: IO (Maybe (Path Abs File)) findPackageFile = do - cwd <- getCurrentDir - let findPackageFile' = findFile (possiblePaths cwd) + let cwd = fromMaybe _rootInvokeDir minputFileDir + findPackageFile' = findFile (possiblePaths cwd) yamlFile <- findPackageFile' Paths.juvixYamlFile pFile <- findPackageFile' Paths.packageFilePath return (pFile <|> yamlFile) @@ -41,7 +42,7 @@ findRootAndChangeDir minputFileDir mbuildDir _rootInvokeDir = do l <- findPackageFile case l of Nothing -> do - _rootPackage <- readGlobalPackageIO + _rootPackage <- readGlobalPackageIO lockMode _rootRootDir <- runM (runFilesIO globalRoot) let _rootPackageGlobal = True _rootBuildDir = getBuildDir mbuildDir @@ -50,7 +51,7 @@ findRootAndChangeDir minputFileDir mbuildDir _rootInvokeDir = do let _rootRootDir = parent yamlPath _rootPackageGlobal = False _rootBuildDir = getBuildDir mbuildDir - _rootPackage <- readPackageIO _rootRootDir _rootBuildDir + _rootPackage <- readPackageIO lockMode _rootRootDir _rootBuildDir return Root {..} getBuildDir :: Maybe (Path Abs Dir) -> BuildDir diff --git a/src/Juvix/Compiler/Pipeline/Run.hs b/src/Juvix/Compiler/Pipeline/Run.hs index 9a19d3698d..d542967ff8 100644 --- a/src/Juvix/Compiler/Pipeline/Run.hs +++ b/src/Juvix/Compiler/Pipeline/Run.hs @@ -25,26 +25,38 @@ import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO import Juvix.Compiler.Pipeline.Package.Loader.PathResolver import Juvix.Data.Effect.Git import Juvix.Data.Effect.Process +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude -- | It returns `ResolverState` so that we can retrieve the `juvix.yaml` files, -- which we require for `Scope` tests. runIOEither :: forall a. EntryPoint -> Sem PipelineEff a -> IO (Either JuvixError (ResolverState, a)) -runIOEither entry = fmap snd . runIOEitherHelper entry +runIOEither = runIOEither' LockModePermissive + +runIOEither' :: forall a. LockMode -> EntryPoint -> Sem PipelineEff a -> IO (Either JuvixError (ResolverState, a)) +runIOEither' lockMode entry = fmap snd . runIOEitherHelper' lockMode entry runIOEitherTermination :: forall a. EntryPoint -> Sem (Termination ': PipelineEff) a -> IO (Either JuvixError (ResolverState, a)) -runIOEitherTermination entry = fmap snd . runIOEitherHelper entry . evalTermination iniTerminationState +runIOEitherTermination = runIOEitherTermination' LockModePermissive + +runIOEitherTermination' :: forall a. LockMode -> EntryPoint -> Sem (Termination ': PipelineEff) a -> IO (Either JuvixError (ResolverState, a)) +runIOEitherTermination' lockMode entry = fmap snd . runIOEitherHelper' lockMode entry . evalTermination iniTerminationState runPipelineHighlight :: forall a. EntryPoint -> Sem PipelineEff a -> IO HighlightInput runPipelineHighlight entry = fmap fst . runIOEitherHelper entry runIOEitherHelper :: forall a. EntryPoint -> Sem PipelineEff a -> IO (HighlightInput, (Either JuvixError (ResolverState, a))) -runIOEitherHelper entry = do +runIOEitherHelper = runIOEitherHelper' LockModePermissive + +runIOEitherHelper' :: forall a. LockMode -> EntryPoint -> Sem PipelineEff a -> IO (HighlightInput, (Either JuvixError (ResolverState, a))) +runIOEitherHelper' lockMode entry = do let hasInternet = not (entry ^. entryPointOffline) runPathResolver' | mainIsPackageFile entry = runPackagePathResolver' (entry ^. entryPointResolverRoot) | otherwise = runPathResolverPipe - runM + runFinal + . resourceToIOFinal + . embedToFinal @IO . evalInternet hasInternet . runHighlightBuilder . runJuvixError @@ -52,6 +64,7 @@ runIOEitherHelper entry = do . evalTopNameIdGen . runFilesIO . runReader entry + . runTaggedLock lockMode . runLogIO . runProcessIO . mapError (JuvixError @GitProcessError) @@ -66,6 +79,14 @@ mainIsPackageFile entry = case entry ^? entryPointModulePaths . _head of Just p -> p == mkPackagePath (entry ^. entryPointResolverRoot) Nothing -> False +runIOLockMode :: LockMode -> GenericOptions -> EntryPoint -> Sem PipelineEff a -> IO (ResolverState, a) +runIOLockMode lockMode opts entry = runIOEither' lockMode entry >=> mayThrow + where + mayThrow :: Either JuvixError r -> IO r + mayThrow = \case + Left err -> runM . runReader opts $ printErrorAnsiSafe err >> embed exitFailure + Right r -> return r + runIO :: GenericOptions -> EntryPoint -> Sem PipelineEff a -> IO (ResolverState, a) runIO opts entry = runIOEither entry >=> mayThrow where @@ -74,8 +95,8 @@ runIO opts entry = runIOEither entry >=> mayThrow Left err -> runM . runReader opts $ printErrorAnsiSafe err >> embed exitFailure Right r -> return r -runIO' :: EntryPoint -> Sem PipelineEff a -> IO (ResolverState, a) -runIO' = runIO defaultGenericOptions +runIOExclusive :: EntryPoint -> Sem PipelineEff a -> IO (ResolverState, a) +runIOExclusive = runIOLockMode LockModeExclusive defaultGenericOptions corePipelineIO' :: EntryPoint -> IO Artifacts corePipelineIO' = corePipelineIO defaultGenericOptions @@ -91,13 +112,21 @@ corePipelineIO opts entry = corePipelineIOEither entry >>= mayThrow corePipelineIOEither :: EntryPoint -> IO (Either JuvixError Artifacts) -corePipelineIOEither entry = do +corePipelineIOEither = corePipelineIOEither' LockModePermissive + +corePipelineIOEither' :: + LockMode -> + EntryPoint -> + IO (Either JuvixError Artifacts) +corePipelineIOEither' lockMode entry = do let hasInternet = not (entry ^. entryPointOffline) runPathResolver' | mainIsPackageFile entry = runPackagePathResolverArtifacts (entry ^. entryPointResolverRoot) | otherwise = runPathResolverArtifacts eith <- - runM + runFinal + . resourceToIOFinal + . embedToFinal @IO . evalInternet hasInternet . ignoreHighlightBuilder . runError @@ -106,6 +135,7 @@ corePipelineIOEither entry = do . runNameIdGenArtifacts . runFilesIO . runReader entry + . runTaggedLock lockMode . runLogIO . mapError (JuvixError @GitProcessError) . runProcessIO diff --git a/src/Juvix/Data/Effect/FileLock.hs b/src/Juvix/Data/Effect/FileLock.hs new file mode 100644 index 0000000000..54d5ab630f --- /dev/null +++ b/src/Juvix/Data/Effect/FileLock.hs @@ -0,0 +1,10 @@ +module Juvix.Data.Effect.FileLock + ( module Juvix.Data.Effect.FileLock.Base, + module Juvix.Data.Effect.FileLock.IO, + module Juvix.Data.Effect.FileLock.Permissive, + ) +where + +import Juvix.Data.Effect.FileLock.Base +import Juvix.Data.Effect.FileLock.IO +import Juvix.Data.Effect.FileLock.Permissive diff --git a/src/Juvix/Data/Effect/FileLock/Base.hs b/src/Juvix/Data/Effect/FileLock/Base.hs new file mode 100644 index 0000000000..ec6b976aa5 --- /dev/null +++ b/src/Juvix/Data/Effect/FileLock/Base.hs @@ -0,0 +1,9 @@ +module Juvix.Data.Effect.FileLock.Base where + +import Juvix.Prelude + +-- | An effect for wrapping an action in file lock +data FileLock m a where + WithFileLock' :: Path Abs File -> m a -> FileLock m a + +makeSem ''FileLock diff --git a/src/Juvix/Data/Effect/FileLock/IO.hs b/src/Juvix/Data/Effect/FileLock/IO.hs new file mode 100644 index 0000000000..039bf6ed16 --- /dev/null +++ b/src/Juvix/Data/Effect/FileLock/IO.hs @@ -0,0 +1,10 @@ +module Juvix.Data.Effect.FileLock.IO where + +import Juvix.Data.Effect.FileLock.Base +import Juvix.Prelude +import System.FileLock hiding (FileLock) + +-- | Interpret `FileLock` using `System.FileLock` +runFileLockIO :: (Members '[Resource, Embed IO] r) => Sem (FileLock ': r) a -> Sem r a +runFileLockIO = interpretH $ \case + WithFileLock' p ma -> bracket (embed $ lockFile (toFilePath p) Exclusive) (embed . unlockFile) (const (runTSimple ma)) diff --git a/src/Juvix/Data/Effect/FileLock/Permissive.hs b/src/Juvix/Data/Effect/FileLock/Permissive.hs new file mode 100644 index 0000000000..a5712e4dcb --- /dev/null +++ b/src/Juvix/Data/Effect/FileLock/Permissive.hs @@ -0,0 +1,9 @@ +module Juvix.Data.Effect.FileLock.Permissive where + +import Juvix.Data.Effect.FileLock.Base +import Juvix.Prelude + +-- | Interpret `FileLock` by executing all actions unconditionally +runFileLockPermissive :: Sem (FileLock ': r) a -> Sem r a +runFileLockPermissive = interpretH $ \case + WithFileLock' _ ma -> runTSimple ma diff --git a/src/Juvix/Data/Effect/Files/Base.hs b/src/Juvix/Data/Effect/Files/Base.hs index e4d946bbcb..cefbe74b2d 100644 --- a/src/Juvix/Data/Effect/Files/Base.hs +++ b/src/Juvix/Data/Effect/Files/Base.hs @@ -44,5 +44,6 @@ data Files m a where JuvixConfigDir :: Files m (Path Abs Dir) CanonicalDir :: Path Abs Dir -> Prepath Dir -> Files m (Path Abs Dir) NormalizeDir :: Path b Dir -> Files m (Path Abs Dir) + NormalizeFile :: Path b File -> Files m (Path Abs File) makeSem ''Files diff --git a/src/Juvix/Data/Effect/Files/IO.hs b/src/Juvix/Data/Effect/Files/IO.hs index ff03a3517f..340c2adf30 100644 --- a/src/Juvix/Data/Effect/Files/IO.hs +++ b/src/Juvix/Data/Effect/Files/IO.hs @@ -50,6 +50,7 @@ runFilesIO = interpret helper JuvixConfigDir -> juvixConfigDirIO CanonicalDir root d -> prepathToAbsDir root d NormalizeDir p -> canonicalizePath p + NormalizeFile b -> canonicalizePath b juvixConfigDirIO :: IO (Path Abs Dir) juvixConfigDirIO = ( versionDir) . absDir <$> getUserConfigDir "juvix" diff --git a/src/Juvix/Data/Effect/Files/Pure.hs b/src/Juvix/Data/Effect/Files/Pure.hs index 00434c8872..b63c26037b 100644 --- a/src/Juvix/Data/Effect/Files/Pure.hs +++ b/src/Juvix/Data/Effect/Files/Pure.hs @@ -86,6 +86,7 @@ re cwd = reinterpret $ \case JuvixConfigDir -> return juvixConfigDirPure CanonicalDir root d -> return (canonicalDirPure root d) NormalizeDir p -> return (absDir (cwd' toFilePath p)) + NormalizeFile p -> return (absFile (cwd' toFilePath p)) where cwd' :: FilePath cwd' = toFilePath cwd diff --git a/src/Juvix/Data/Effect/Git/Process.hs b/src/Juvix/Data/Effect/Git/Process.hs index 5790be3713..c85dfeebac 100644 --- a/src/Juvix/Data/Effect/Git/Process.hs +++ b/src/Juvix/Data/Effect/Git/Process.hs @@ -4,6 +4,7 @@ import Data.Text qualified as T import Juvix.Data.Effect.Git.Base import Juvix.Data.Effect.Git.Process.Error import Juvix.Data.Effect.Process +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude import Polysemy.Opaque @@ -62,15 +63,15 @@ gitHeadRef :: (Members '[Process, Error GitProcessError, Reader CloneEnv] r) => gitHeadRef = gitNormalizeRef "HEAD" -- | Checkout the clone at a particular ref -gitCheckout :: (Members '[Process, Error GitProcessError, Reader CloneEnv] r) => Text -> Sem r () -gitCheckout ref = void (runGitCmdInDir ["checkout", ref]) +gitCheckout :: (Members '[TaggedLock, Process, Error GitProcessError, Reader CloneEnv] r) => Text -> Sem r () +gitCheckout ref = withTaggedLockDir' (void (runGitCmdInDir ["checkout", ref])) -- | Fetch in the clone -gitFetch :: (Members '[Process, Error GitProcessError, Reader CloneEnv, Internet] r) => Sem r () +gitFetch :: (Members '[TaggedLock, Process, Error GitProcessError, Reader CloneEnv, Internet] r) => Sem r () gitFetch = whenHasInternet gitFetchOnline -gitFetchOnline :: (Members '[Reader CloneEnv, Error GitProcessError, Process, Online] r) => Sem r () -gitFetchOnline = void (runGitCmdInDir ["fetch"]) +gitFetchOnline :: (Members '[TaggedLock, Reader CloneEnv, Error GitProcessError, Process, Online] r) => Sem r () +gitFetchOnline = withTaggedLockDir' (void (runGitCmdInDir ["fetch"])) gitCloneOnline :: (Members '[Log, Error GitProcessError, Process, Online, Reader CloneEnv] r) => Text -> Sem r () gitCloneOnline url = do @@ -81,10 +82,10 @@ gitCloneOnline url = do cloneGitRepo :: (Members '[Log, Files, Process, Error GitProcessError, Reader CloneEnv, Internet] r) => Text -> Sem r () cloneGitRepo = whenHasInternet . gitCloneOnline -initGitRepo :: (Members '[Log, Files, Process, Error GitProcessError, Reader CloneEnv, Internet] r) => Text -> Sem r (Path Abs Dir) +initGitRepo :: (Members '[TaggedLock, Log, Files, Process, Error GitProcessError, Reader CloneEnv, Internet] r) => Text -> Sem r (Path Abs Dir) initGitRepo url = do p <- asks (^. cloneEnvDir) - unlessM (directoryExists' p) (cloneGitRepo url) + withTaggedLockDir' (unlessM (directoryExists' p) (cloneGitRepo url)) return p handleNotACloneError :: (Member (Error GitProcessError) r, Monad m) => (GitError -> m x) -> Tactical e m r x -> Tactical e m r x @@ -97,9 +98,14 @@ handleNormalizeRefError errorHandler ref eff = catch @GitProcessError eff $ \cas GitCmdError GitCmdErrorDetails {_gitCmdErrorDetailsExitCode = ExitFailure 128} -> runTSimple (return (NoSuchRef ref)) >>= bindTSimple errorHandler e -> throw e +withTaggedLockDir' :: (Members '[TaggedLock, Reader CloneEnv] r) => Sem r a -> Sem r a +withTaggedLockDir' ma = do + p <- asks (^. cloneEnvDir) + withTaggedLockDir p ma + runGitProcess :: forall r a. - (Members '[Log, Files, Process, Error GitProcessError, Internet] r) => + (Members '[TaggedLock, Log, Files, Process, Error GitProcessError, Internet] r) => Sem (Scoped CloneArgs Git ': r) a -> Sem r a runGitProcess = interpretScopedH allocator handler diff --git a/src/Juvix/Data/Effect/TaggedLock.hs b/src/Juvix/Data/Effect/TaggedLock.hs new file mode 100644 index 0000000000..81a8204e34 --- /dev/null +++ b/src/Juvix/Data/Effect/TaggedLock.hs @@ -0,0 +1,39 @@ +module Juvix.Data.Effect.TaggedLock + ( module Juvix.Data.Effect.TaggedLock, + module Juvix.Data.Effect.TaggedLock.Base, + module Juvix.Data.Effect.TaggedLock.Permissive, + module Juvix.Data.Effect.TaggedLock.IO, + ) +where + +import Juvix.Data.Effect.TaggedLock.Base +import Juvix.Data.Effect.TaggedLock.IO +import Juvix.Data.Effect.TaggedLock.Permissive +import Juvix.Prelude + +-- | A variant of `withTaggedLock` that accepts an absolute directory as a tag. +-- +-- The absolute path does not need to exist in the filesystem. +-- +-- Example: +-- +-- @ +-- runFinal +-- . resourceToIOFinal +-- . embedToFinal @IO +-- . runFilesIO +-- . runTaggedLockIO +-- $ withTaggedLockDir $(mkAbsDir "/a/b/c") (embed (putStrLn "Hello" >> hFlush stdout)) +-- @ +withTaggedLockDir :: (Member TaggedLock r) => Path Abs Dir -> Sem r a -> Sem r a +withTaggedLockDir d = do + let lockFile = $(mkRelFile ".lock") + p = maybe lockFile ( lockFile) (dropDrive d) + withTaggedLock p + +data LockMode = LockModePermissive | LockModeExclusive + +runTaggedLock :: (Members '[Resource, Embed IO] r) => LockMode -> Sem (TaggedLock ': r) a -> Sem r a +runTaggedLock = \case + LockModePermissive -> runTaggedLockPermissive + LockModeExclusive -> runTaggedLockIO diff --git a/src/Juvix/Data/Effect/TaggedLock/Base.hs b/src/Juvix/Data/Effect/TaggedLock/Base.hs new file mode 100644 index 0000000000..ad73b62a22 --- /dev/null +++ b/src/Juvix/Data/Effect/TaggedLock/Base.hs @@ -0,0 +1,12 @@ +module Juvix.Data.Effect.TaggedLock.Base where + +import Juvix.Prelude + +-- | An effect that wraps an action with a lock that is tagged with a relative +-- path. +-- +-- The relative path does not need to exist in the filesystem. +data TaggedLock m a where + WithTaggedLock :: Path Rel File -> m a -> TaggedLock m a + +makeSem ''TaggedLock diff --git a/src/Juvix/Data/Effect/TaggedLock/IO.hs b/src/Juvix/Data/Effect/TaggedLock/IO.hs new file mode 100644 index 0000000000..f1891cb55a --- /dev/null +++ b/src/Juvix/Data/Effect/TaggedLock/IO.hs @@ -0,0 +1,21 @@ +module Juvix.Data.Effect.TaggedLock.IO where + +import Juvix.Data.Effect.FileLock +import Juvix.Data.Effect.TaggedLock.Base +import Juvix.Prelude + +-- | Interpret `TaggedLock` using `FileLock`. +-- +-- When multiple processes or threads call `withTaggedLock` with the same tag, +-- then only one of them can perform the action at a time. +runTaggedLockIO :: forall r a. (Members '[Resource, Embed IO] r) => Sem (TaggedLock ': r) a -> Sem r a +runTaggedLockIO sem = do + rootLockPath <- ( $(mkRelDir "juvix-file-locks")) <$> getTempDir + runFileLockIO (runFilesIO (go rootLockPath sem)) + where + go :: Path Abs Dir -> Sem (TaggedLock ': r) a -> Sem (Files ': FileLock ': r) a + go r = reinterpret2H $ \case + WithTaggedLock t ma -> do + p <- normalizeFile (r t) + ensureDir' (parent p) + withFileLock' p (runTSimple ma) diff --git a/src/Juvix/Data/Effect/TaggedLock/Permissive.hs b/src/Juvix/Data/Effect/TaggedLock/Permissive.hs new file mode 100644 index 0000000000..1360596b19 --- /dev/null +++ b/src/Juvix/Data/Effect/TaggedLock/Permissive.hs @@ -0,0 +1,8 @@ +module Juvix.Data.Effect.TaggedLock.Permissive where + +import Juvix.Data.Effect.TaggedLock.Base +import Juvix.Prelude + +runTaggedLockPermissive :: Sem (TaggedLock ': r) a -> Sem r a +runTaggedLockPermissive = interpretH $ \case + WithTaggedLock _ ma -> runTSimple ma diff --git a/src/Juvix/Extra/Files.hs b/src/Juvix/Extra/Files.hs index 026282beaa..8e1c81b84d 100644 --- a/src/Juvix/Extra/Files.hs +++ b/src/Juvix/Extra/Files.hs @@ -1,6 +1,7 @@ module Juvix.Extra.Files where import Juvix.Data.Effect.Files +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.Paths import Juvix.Extra.Version import Juvix.Prelude @@ -38,23 +39,27 @@ versionFile :: (Member (Reader OutputRoot) r) => Sem r (Path Abs File) versionFile = ( $(mkRelFile ".version")) <$> ask writeVersion :: forall r. (Members '[Reader OutputRoot, Files] r) => Sem r () -writeVersion = versionFile >>= flip writeFile' versionTag +writeVersion = do + vf <- versionFile + ensureDir' (parent vf) + writeFile' vf versionTag readVersion :: (Members '[Reader OutputRoot, Files] r) => Sem r (Maybe Text) readVersion = do vf <- versionFile whenMaybeM (fileExists' vf) (readFile' vf) -updateFiles :: forall r. (Members '[Reader OutputRoot, Files] r) => Sem r () -> Sem r () -updateFiles action = - whenM shouldUpdate $ do +updateFiles :: (Members '[TaggedLock, Reader OutputRoot, Files] r) => (forall r0. (Members '[Files, Reader OutputRoot] r0) => Sem r0 ()) -> Sem r () +updateFiles action = do + root <- ask @OutputRoot + withTaggedLockDir root . whenM shouldUpdate $ do whenM - (ask @OutputRoot >>= directoryExists') - (ask @OutputRoot >>= removeDirectoryRecursive') - action + (directoryExists' root) + (removeDirectoryRecursive' root) writeVersion + action where - shouldUpdate :: Sem r Bool + shouldUpdate :: (Members '[Files, Reader OutputRoot] r) => Sem r Bool shouldUpdate = orM [ not <$> (ask @OutputRoot >>= directoryExists'), diff --git a/src/Juvix/Extra/PackageFiles.hs b/src/Juvix/Extra/PackageFiles.hs index 6458f178dc..a8ef2546ea 100644 --- a/src/Juvix/Extra/PackageFiles.hs +++ b/src/Juvix/Extra/PackageFiles.hs @@ -1,6 +1,7 @@ module Juvix.Extra.PackageFiles where import Juvix.Data.Effect.Files +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.Files import Juvix.Extra.Paths import Juvix.Prelude @@ -11,5 +12,5 @@ packageFiles = juvixFiles $(packageDescriptionDirContents) writePackageFiles :: forall r. (Members '[Reader OutputRoot, Files] r) => Sem r () writePackageFiles = writeFiles packageFiles -updatePackageFiles :: (Members '[Reader OutputRoot, Files] r) => Sem r () +updatePackageFiles :: (Members '[TaggedLock, Reader OutputRoot, Files] r) => Sem r () updatePackageFiles = updateFiles writePackageFiles diff --git a/src/Juvix/Extra/Stdlib.hs b/src/Juvix/Extra/Stdlib.hs index 3eef31304c..432fb65e6c 100644 --- a/src/Juvix/Extra/Stdlib.hs +++ b/src/Juvix/Extra/Stdlib.hs @@ -2,6 +2,7 @@ module Juvix.Extra.Stdlib where import Juvix.Compiler.Pipeline.Package.Dependency import Juvix.Data.Effect.Files +import Juvix.Data.Effect.TaggedLock import Juvix.Extra.Files import Juvix.Extra.Paths import Juvix.Prelude @@ -9,7 +10,7 @@ import Juvix.Prelude stdlibFiles :: [(Path Rel File, ByteString)] stdlibFiles = juvixFiles $(stdlibDir) -ensureStdlib :: (Members '[Files] r) => Path Abs Dir -> Path Abs Dir -> [Dependency] -> Sem r () +ensureStdlib :: (Members '[TaggedLock, Files] r) => Path Abs Dir -> Path Abs Dir -> [Dependency] -> Sem r () ensureStdlib rootDir buildDir deps = whenJustM (packageStdlib rootDir buildDir deps) $ \stdlibRoot -> runReader stdlibRoot updateStdlib @@ -35,5 +36,5 @@ packageStdlib rootDir buildDir = firstJustM isStdLib writeStdlib :: forall r. (Members '[Reader OutputRoot, Files] r) => Sem r () writeStdlib = writeFiles stdlibFiles -updateStdlib :: (Members '[Reader OutputRoot, Files] r) => Sem r () +updateStdlib :: (Members '[TaggedLock, Reader OutputRoot, Files] r) => Sem r () updateStdlib = updateFiles writeStdlib diff --git a/src/Juvix/Prelude/Base.hs b/src/Juvix/Prelude/Base.hs index b83147f5df..1e302690a5 100644 --- a/src/Juvix/Prelude/Base.hs +++ b/src/Juvix/Prelude/Base.hs @@ -163,7 +163,7 @@ import GHC.Stack.Types import Language.Haskell.TH.Syntax (Lift) import Lens.Micro.Platform import Path -import Path.IO qualified as Path +import Path.IO qualified as Path hiding (getCurrentDir, setCurrentDir, withCurrentDir) import Polysemy import Polysemy.Embed import Polysemy.Error hiding (fromEither) diff --git a/src/Juvix/Prelude/Path.hs b/src/Juvix/Prelude/Path.hs index 42b6d67da2..0d0bdfa1d3 100644 --- a/src/Juvix/Prelude/Path.hs +++ b/src/Juvix/Prelude/Path.hs @@ -14,6 +14,7 @@ import Path hiding ((<.>), ()) import Path qualified import Path.IO hiding (listDirRel, walkDirRel) import Path.Internal +import System.FilePath qualified as FilePath data FileOrDir @@ -122,3 +123,20 @@ withTempDir' = withSystemTempDir "tmp" -- | 'pure True' if the file exists and is executable, 'pure False' otherwise isExecutable :: (MonadIO m) => Path b File -> m Bool isExecutable f = doesFileExist f &&^ (executable <$> getPermissions f) + +-- | Split an absolute path into a drive and, perhaps, a path. On POSIX, @/@ is +-- a drive. +-- Remove when we upgrade to path-0.9.5 +splitDrive :: Path Abs t -> (Path Abs Dir, Maybe (Path Rel t)) +splitDrive (Path fp) = + let (d, rest) = FilePath.splitDrive fp + mRest = if null rest then Nothing else Just (Path rest) + in (Path d, mRest) + +-- | Drop the drive from an absolute path. May result in 'Nothing' if the path +-- is just a drive. +-- +-- > dropDrive x = snd (splitDrive x) +-- Remove when we upgrade to path-0.9.5 +dropDrive :: Path Abs t -> Maybe (Path Rel t) +dropDrive = snd . splitDrive diff --git a/src/Juvix/Prelude/Prepath.hs b/src/Juvix/Prelude/Prepath.hs index b57ccd687b..24dafb3d9d 100644 --- a/src/Juvix/Prelude/Prepath.hs +++ b/src/Juvix/Prelude/Prepath.hs @@ -111,9 +111,9 @@ prepathToAbsDir :: Path Abs Dir -> Prepath Dir -> IO (Path Abs Dir) prepathToAbsDir root = fmap absDir . prepathToFilePath root prepathToFilePath :: Path Abs Dir -> Prepath d -> IO FilePath -prepathToFilePath root pre = - withCurrentDir root $ - expandPrepath pre >>= System.canonicalizePath +prepathToFilePath root pre = do + expandedPre <- expandPrepath pre + System.canonicalizePath (toFilePath root expandedPre) fromPreFileOrDir :: Path Abs Dir -> Prepath FileOrDir -> IO (Either (Path Abs File) (Path Abs Dir)) fromPreFileOrDir cwd fp = do diff --git a/test/Arity/Negative.hs b/test/Arity/Negative.hs index 93043b5232..775d69a6ef 100644 --- a/test/Arity/Negative.hs +++ b/test/Arity/Negative.hs @@ -2,6 +2,7 @@ module Arity.Negative (allTests) where import Base import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.ArityChecking.Error +import Juvix.Data.Effect.TaggedLock type FailMsg = String @@ -20,7 +21,7 @@ testDescr NegTest {..} = { _testName = _name, _testRoot = tRoot, _testAssertion = Single $ do - entryPoint <- defaultEntryPointCwdIO file' + entryPoint <- defaultEntryPointIO' LockModeExclusive tRoot file' result <- runIOEitherTermination entryPoint upToInternalArity case mapLeft fromJuvixError result of Left (Just tyError) -> whenJust (_checkErr tyError) assertFailure diff --git a/test/BackendGeb/Compilation/Base.hs b/test/BackendGeb/Compilation/Base.hs index 4a5b3de706..fe8116965c 100644 --- a/test/BackendGeb/Compilation/Base.hs +++ b/test/BackendGeb/Compilation/Base.hs @@ -4,14 +4,16 @@ import BackendGeb.FromCore.Base import Base import Juvix.Compiler.Backend (Target (TargetGeb)) import Juvix.Compiler.Core qualified as Core +import Juvix.Data.Effect.TaggedLock gebCompilationAssertion :: + Path Abs Dir -> Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion -gebCompilationAssertion mainFile expectedFile step = do +gebCompilationAssertion root mainFile expectedFile step = do step "Translate to JuvixCore" - entryPoint <- set entryPointTarget TargetGeb <$> defaultEntryPointCwdIO mainFile - tab <- (^. Core.coreResultTable) . snd <$> runIO' entryPoint upToCore + entryPoint <- set entryPointTarget TargetGeb <$> defaultEntryPointIO' LockModeExclusive root mainFile + tab <- (^. Core.coreResultTable) . snd <$> runIOExclusive entryPoint upToCore coreToGebTranslationAssertion' tab entryPoint expectedFile step diff --git a/test/BackendGeb/Compilation/Positive.hs b/test/BackendGeb/Compilation/Positive.hs index 553c96eb51..418f87dd2b 100644 --- a/test/BackendGeb/Compilation/Positive.hs +++ b/test/BackendGeb/Compilation/Positive.hs @@ -23,7 +23,7 @@ testDescr PosTest {..} = _testRoot = tRoot, _testAssertion = Steps $ - gebCompilationAssertion file' expected' + gebCompilationAssertion tRoot file' expected' } allTests :: TestTree diff --git a/test/BackendGeb/FromCore/Base.hs b/test/BackendGeb/FromCore/Base.hs index 0af71b399c..d20a9f4433 100644 --- a/test/BackendGeb/FromCore/Base.hs +++ b/test/BackendGeb/FromCore/Base.hs @@ -7,17 +7,19 @@ import Juvix.Compiler.Backend (Target (TargetGeb)) import Juvix.Compiler.Backend.Geb qualified as Geb import Juvix.Compiler.Core qualified as Core import Juvix.Compiler.Core.Pretty qualified as Core +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude.Pretty coreToGebTranslationAssertion :: + Path Abs Dir -> Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion -coreToGebTranslationAssertion mainFile expectedFile step = do +coreToGebTranslationAssertion root mainFile expectedFile step = do step "Parse Juvix Core file" input <- readFile . toFilePath $ mainFile - entryPoint <- set entryPointTarget TargetGeb <$> defaultEntryPointCwdIO mainFile + entryPoint <- set entryPointTarget TargetGeb <$> defaultEntryPointIO' LockModeExclusive root mainFile case Core.runParserMain mainFile Core.emptyInfoTable input of Left err -> assertFailure . show . pretty $ err Right coreInfoTable -> coreToGebTranslationAssertion' coreInfoTable entryPoint expectedFile step diff --git a/test/BackendGeb/FromCore/Positive.hs b/test/BackendGeb/FromCore/Positive.hs index c259dea5fb..f64e3031d0 100644 --- a/test/BackendGeb/FromCore/Positive.hs +++ b/test/BackendGeb/FromCore/Positive.hs @@ -23,7 +23,7 @@ testDescr PosTest {..} = _testRoot = tRoot, _testAssertion = Steps $ - coreToGebTranslationAssertion file' expected' + coreToGebTranslationAssertion tRoot file' expected' } filterOutTests :: [String] -> [PosTest] -> [PosTest] diff --git a/test/BackendMarkdown/Negative.hs b/test/BackendMarkdown/Negative.hs index c396658522..dc6e3341a0 100644 --- a/test/BackendMarkdown/Negative.hs +++ b/test/BackendMarkdown/Negative.hs @@ -2,6 +2,7 @@ module BackendMarkdown.Negative where import Base import Juvix.Compiler.Backend.Markdown.Error +import Juvix.Data.Effect.TaggedLock import Juvix.Parser.Error type FailMsg = String @@ -21,8 +22,8 @@ testDescr NegTest {..} = { _testName = _name, _testRoot = tRoot, _testAssertion = Single $ do - entryPoint <- defaultEntryPointCwdIO file' - result <- runIOEither entryPoint upToParsing + entryPoint <- defaultEntryPointIO' LockModeExclusive tRoot file' + result <- runIOEither' LockModeExclusive entryPoint upToParsing case mapLeft fromJuvixError result of Left (Just err) -> whenJust (_checkErr err) assertFailure Right _ -> assertFailure "Unexpected success." diff --git a/test/BackendMarkdown/Positive.hs b/test/BackendMarkdown/Positive.hs index 9508fc7891..7edc8349d5 100644 --- a/test/BackendMarkdown/Positive.hs +++ b/test/BackendMarkdown/Positive.hs @@ -6,6 +6,7 @@ import Juvix.Compiler.Concrete qualified as Concrete import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping qualified as Scoper import Juvix.Compiler.Concrete.Translation.FromSource qualified as Parser import Juvix.Compiler.Pipeline.Setup +import Juvix.Data.Effect.TaggedLock data PosTest = PosTest { _name :: String, @@ -35,13 +36,13 @@ testDescr PosTest {..} = { _testName = _name, _testRoot = _dir, _testAssertion = Steps $ \step -> do - entryPoint <- defaultEntryPointCwdIO _file + entryPoint <- defaultEntryPointIO' LockModeExclusive _dir _file step "Parsing" - p :: Parser.ParserResult <- snd <$> runIO' entryPoint upToParsing + p :: Parser.ParserResult <- snd <$> runIOExclusive entryPoint upToParsing step "Scoping" s :: Scoper.ScoperResult <- snd - <$> runIO' + <$> runIOExclusive entryPoint ( do void (entrySetup defaultDependenciesConfig) diff --git a/test/Compilation/Base.hs b/test/Compilation/Base.hs index 70b47ba58a..60cc0215dd 100644 --- a/test/Compilation/Base.hs +++ b/test/Compilation/Base.hs @@ -4,6 +4,7 @@ import Base import Core.Compile.Base import Core.Eval.Base import Juvix.Compiler.Core qualified as Core +import Juvix.Data.Effect.TaggedLock import Juvix.Data.PPOutput data CompileAssertionMode @@ -13,16 +14,17 @@ data CompileAssertionMode | EvalAndCompile compileAssertion :: + Path Abs Dir -> Int -> CompileAssertionMode -> Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion -compileAssertion optLevel mode mainFile expectedFile step = do +compileAssertion root' optLevel mode mainFile expectedFile step = do step "Translate to JuvixCore" - entryPoint <- defaultEntryPointCwdIO mainFile - tab <- (^. Core.coreResultTable) . snd <$> runIO' entryPoint upToCore + entryPoint <- defaultEntryPointIO' LockModeExclusive root' mainFile + tab <- (^. Core.coreResultTable) . snd <$> runIOExclusive entryPoint upToCore case run $ runReader Core.defaultCoreOptions $ runError $ Core.toEval' tab of Left err -> assertFailure (show (pretty (fromJuvixError @GenericError err))) Right tab' -> do @@ -34,13 +36,14 @@ compileAssertion optLevel mode mainFile expectedFile step = do EvalAndCompile -> evalAssertion >> compileAssertion' "" compileErrorAssertion :: + Path Abs Dir -> Path Abs File -> (String -> IO ()) -> Assertion -compileErrorAssertion mainFile step = do +compileErrorAssertion root' mainFile step = do step "Translate to JuvixCore" - entryPoint <- defaultEntryPointCwdIO mainFile - tab <- (^. Core.coreResultTable) . snd <$> runIO' entryPoint upToCore + entryPoint <- defaultEntryPointIO' LockModeExclusive root' mainFile + tab <- (^. Core.coreResultTable) . snd <$> runIOExclusive entryPoint upToCore case run $ runReader Core.defaultCoreOptions $ runError @JuvixError $ Core.toStripped' tab of Left _ -> assertBool "" True Right _ -> assertFailure "no error" diff --git a/test/Compilation/Negative.hs b/test/Compilation/Negative.hs index 6198dc6fa6..3656fabd88 100644 --- a/test/Compilation/Negative.hs +++ b/test/Compilation/Negative.hs @@ -19,7 +19,7 @@ testDescr NegTest {..} = in TestDescr { _testName = _name, _testRoot = tRoot, - _testAssertion = Steps $ compileErrorAssertion file' + _testAssertion = Steps $ compileErrorAssertion tRoot file' } allTests :: TestTree diff --git a/test/Compilation/Positive.hs b/test/Compilation/Positive.hs index 4d1ca6dc30..87b5d0ebc7 100644 --- a/test/Compilation/Positive.hs +++ b/test/Compilation/Positive.hs @@ -24,7 +24,7 @@ toTestDescr optLevel PosTest {..} = in TestDescr { _testName = _name, _testRoot = tRoot, - _testAssertion = Steps $ compileAssertion optLevel _assertionMode file' expected' + _testAssertion = Steps $ compileAssertion _dir optLevel _assertionMode file' expected' } allTests :: TestTree diff --git a/test/Examples/Positive.hs b/test/Examples/Positive.hs index 1d5eec14a5..9d66eceea6 100644 --- a/test/Examples/Positive.hs +++ b/test/Examples/Positive.hs @@ -24,7 +24,7 @@ toTestDescr PosTest {..} = TestDescr { _testRoot = _dir, _testName = _name, - _testAssertion = Steps $ compileAssertion 3 (CompileOnly _stdin) _file _expectedFile + _testAssertion = Steps $ compileAssertion _dir 3 (CompileOnly _stdin) _file _expectedFile } allTests :: TestTree diff --git a/test/Format.hs b/test/Format.hs index c70e91b31d..b4821bd482 100644 --- a/test/Format.hs +++ b/test/Format.hs @@ -5,6 +5,7 @@ import Juvix.Compiler.Concrete qualified as Concrete import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping qualified as Scoper import Juvix.Compiler.Concrete.Translation.FromSource qualified as Parser import Juvix.Compiler.Pipeline.Setup +import Juvix.Data.Effect.TaggedLock import Juvix.Formatter data PosTest = PosTest @@ -33,19 +34,19 @@ testDescr PosTest {..} = { _testName = _name, _testRoot = _dir, _testAssertion = Steps $ \step -> do - entryPoint <- defaultEntryPointCwdIO _file + entryPoint <- defaultEntryPointIO' LockModeExclusive _dir _file let maybeFile = entryPoint ^? entryPointModulePaths . _head f <- fromMaybeM (assertFailure "Not a module") (return maybeFile) original :: Text <- readFile (toFilePath f) step "Parsing" - p :: Parser.ParserResult <- snd <$> runIO' entryPoint upToParsing + p :: Parser.ParserResult <- snd <$> runIOExclusive entryPoint upToParsing step "Scoping" s :: Scoper.ScoperResult <- snd - <$> runIO' + <$> runIOExclusive entryPoint ( do void (entrySetup defaultDependenciesConfig) diff --git a/test/Formatter/Positive.hs b/test/Formatter/Positive.hs index b91898b25b..be6ac18648 100644 --- a/test/Formatter/Positive.hs +++ b/test/Formatter/Positive.hs @@ -1,18 +1,19 @@ module Formatter.Positive where import Base +import Juvix.Data.Effect.TaggedLock import Juvix.Formatter import Scope.Positive qualified import Scope.Positive qualified as Scope -runScopeEffIO :: (Member (Embed IO) r) => Sem (ScopeEff ': r) a -> Sem r a -runScopeEffIO = interpret $ \case +runScopeEffIO :: (Member (Embed IO) r) => Path Abs Dir -> Sem (ScopeEff ': r) a -> Sem r a +runScopeEffIO root = interpret $ \case ScopeFile p -> do - entry <- embed (defaultEntryPointCwdIO p) - embed (snd <$> runIO' entry upToScoping) + entry <- embed (defaultEntryPointIO' LockModeExclusive root p) + embed (snd <$> runIOExclusive entry upToScoping) ScopeStdin -> do - entry <- embed defaultEntryPointNoFileCwdIO - embed (snd <$> runIO' entry upToScoping) + entry <- embed (defaultEntryPointNoFileIO' LockModeExclusive root) + embed (snd <$> runIOExclusive entry upToScoping) makeFormatTest' :: Scope.PosTest -> TestDescr makeFormatTest' Scope.PosTest {..} = @@ -26,7 +27,7 @@ makeFormatTest' Scope.PosTest {..} = runM . runError . runOutputList @FormattedFileInfo - . runScopeEffIO + . runScopeEffIO tRoot . runFilesIO $ format file' case d of diff --git a/test/Internal/Eval/Base.hs b/test/Internal/Eval/Base.hs index 684d45d73b..92baf8c2be 100644 --- a/test/Internal/Eval/Base.hs +++ b/test/Internal/Eval/Base.hs @@ -11,12 +11,13 @@ import Juvix.Compiler.Core.Info.NoDisplayInfo import Juvix.Compiler.Core.Pretty import Juvix.Compiler.Core.Transformation (etaExpansionApps) import Juvix.Compiler.Core.Translation.FromInternal.Data as Core +import Juvix.Data.Effect.TaggedLock -internalCoreAssertion :: Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion -internalCoreAssertion mainFile expectedFile step = do +internalCoreAssertion :: Path Abs Dir -> Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion +internalCoreAssertion root' mainFile expectedFile step = do step "Translate to Core" - entryPoint <- defaultEntryPointCwdIO mainFile - tab0 <- (^. Core.coreResultTable) . snd <$> runIO' entryPoint upToCore + entryPoint <- defaultEntryPointIO' LockModeExclusive root' mainFile + tab0 <- (^. Core.coreResultTable) . snd <$> runIOExclusive entryPoint upToCore let tab = etaExpansionApps tab0 case (tab ^. infoMain) >>= ((tab ^. identContext) HashMap.!?) of Just node -> do diff --git a/test/Internal/Eval/Positive.hs b/test/Internal/Eval/Positive.hs index 039cd97415..933797673f 100644 --- a/test/Internal/Eval/Positive.hs +++ b/test/Internal/Eval/Positive.hs @@ -24,7 +24,7 @@ testDescr r PosTest {..} = in TestDescr { _testName = _name, _testRoot = tRoot, - _testAssertion = Steps $ internalCoreAssertion file' expected' + _testAssertion = Steps $ internalCoreAssertion tRoot file' expected' } allTests :: TestTree diff --git a/test/Main.hs b/test/Main.hs index c3391e26a0..787cf6a612 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -51,5 +51,4 @@ fastTests = ] main :: IO () -main = do - defaultMain (testGroup "Juvix tests" [fastTests, slowTests]) +main = defaultMain (testGroup "Juvix tests" [fastTests, slowTests]) diff --git a/test/Package/Negative.hs b/test/Package/Negative.hs index 9470c07abf..c6ab250dd4 100644 --- a/test/Package/Negative.hs +++ b/test/Package/Negative.hs @@ -3,6 +3,7 @@ module Package.Negative where import Base import Juvix.Compiler.Pipeline.Package import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO +import Juvix.Data.Effect.TaggedLock type FailMsg = String @@ -24,10 +25,13 @@ testDescr NegTest {..} = _testAssertion = Single $ do res <- withTempDir' - ( runM + ( runFinal + . resourceToIOFinal + . embedToFinal @IO . runError . runFilesIO . mapError (JuvixError @PackageLoaderError) + . runTaggedLock LockModeExclusive . runEvalFileEffIO . readPackage tRoot . CustomBuildDir diff --git a/test/Package/Positive.hs b/test/Package/Positive.hs index 23684ba2d5..f06db36560 100644 --- a/test/Package/Positive.hs +++ b/test/Package/Positive.hs @@ -3,6 +3,7 @@ module Package.Positive where import Base import Juvix.Compiler.Pipeline.Package import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO +import Juvix.Data.Effect.TaggedLock type FailMsg = String @@ -25,10 +26,13 @@ testDescr PosTest {..} = withTempDir' $ \d -> do let buildDir = CustomBuildDir (Abs d) res <- - runM + runFinal + . resourceToIOFinal + . embedToFinal @IO . runError @JuvixError . runFilesIO . mapError (JuvixError @PackageLoaderError) + . runTaggedLock LockModeExclusive . runEvalFileEffIO . readPackage tRoot $ buildDir diff --git a/test/Parsing/Negative.hs b/test/Parsing/Negative.hs index e408c9cb68..09aa054812 100644 --- a/test/Parsing/Negative.hs +++ b/test/Parsing/Negative.hs @@ -2,6 +2,7 @@ module Parsing.Negative where import Base import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.PathResolver.Error +import Juvix.Data.Effect.TaggedLock import Juvix.Parser.Error root :: Path Abs Dir @@ -23,8 +24,8 @@ testDescr NegTest {..} = { _testName = _name, _testRoot = tRoot, _testAssertion = Single $ do - entryPoint <- defaultEntryPointCwdIO _file - res <- runIOEither entryPoint upToParsing + entryPoint <- defaultEntryPointIO' LockModeExclusive tRoot _file + res <- runIOEither' LockModeExclusive entryPoint upToParsing case mapLeft fromJuvixError res of Left (Just parErr) -> whenJust (_checkErr parErr) assertFailure Left Nothing -> assertFailure "An error ocurred but it was not in the parser." diff --git a/test/Reachability/Positive.hs b/test/Reachability/Positive.hs index c387140b68..5eba6a3478 100644 --- a/test/Reachability/Positive.hs +++ b/test/Reachability/Positive.hs @@ -4,6 +4,7 @@ import Base import Data.HashSet qualified as HashSet import Juvix.Compiler.Internal.Language qualified as Internal import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.TypeChecking.Data.Context qualified as Internal +import Juvix.Data.Effect.TaggedLock data PosTest = PosTest { _name :: String, @@ -29,10 +30,10 @@ testDescr PosTest {..} = let noStdlib = _stdlibMode == StdlibExclude entryPoint <- set entryPointNoStdlib noStdlib - <$> defaultEntryPointCwdIO file' + <$> defaultEntryPointIO' LockModeExclusive tRoot file' step "Pipeline up to reachability" - p :: Internal.InternalTypedResult <- snd <$> runIO' entryPoint upToInternalReachability + p :: Internal.InternalTypedResult <- snd <$> runIOExclusive entryPoint upToInternalReachability step "Check reachability results" let names = concatMap getNames (p ^. Internal.resultModules) diff --git a/test/Scope/Negative.hs b/test/Scope/Negative.hs index 093ad2df07..a437def225 100644 --- a/test/Scope/Negative.hs +++ b/test/Scope/Negative.hs @@ -2,6 +2,7 @@ module Scope.Negative (allTests) where import Base import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping.Error +import Juvix.Data.Effect.TaggedLock type FailMsg = String @@ -23,8 +24,8 @@ testDescr NegTest {..} = { _testName = _name, _testRoot = tRoot, _testAssertion = Single $ do - entryPoint <- defaultEntryPointCwdIO file' - res <- runIOEitherTermination entryPoint upToInternal + entryPoint <- defaultEntryPointIO' LockModeExclusive tRoot file' + res <- runIOEitherTermination' LockModeExclusive entryPoint upToInternal case mapLeft fromJuvixError res of Left (Just err) -> whenJust (_checkErr err) assertFailure Left Nothing -> assertFailure "An error ocurred but it was not in the scoper." diff --git a/test/Scope/Positive.hs b/test/Scope/Positive.hs index edabd64311..0a62627948 100644 --- a/test/Scope/Positive.hs +++ b/test/Scope/Positive.hs @@ -16,6 +16,7 @@ import Juvix.Compiler.Pipeline.Package.Loader.PathResolver import Juvix.Compiler.Pipeline.Setup import Juvix.Data.Effect.Git import Juvix.Data.Effect.Process +import Juvix.Data.Effect.TaggedLock import Juvix.Prelude.Aeson import Juvix.Prelude.Pretty @@ -53,13 +54,15 @@ testDescr PosTest {..} = helper renderCodeNew { _testName = _name, _testRoot = tRoot, _testAssertion = Steps $ \step -> do - entryPoint <- defaultEntryPointCwdIO file' + entryPoint <- defaultEntryPointIO' LockModeExclusive tRoot file' let runHelper :: HashMap (Path Abs File) Text -> Sem PipelineEff a -> IO (ResolverState, a) runHelper files = do let runPathResolver' = case _pathResolverMode of FullPathResolver -> runPathResolverPipe PackagePathResolver -> runPackagePathResolver' (entryPoint ^. entryPointResolverRoot) - runM + runFinal + . resourceToIOFinal + . embedToFinal @IO . evalInternetOffline . ignoreHighlightBuilder . runErrorIO' @JuvixError @@ -67,6 +70,7 @@ testDescr PosTest {..} = helper renderCodeNew . evalTopNameIdGen . runFilesPure files tRoot . runReader entryPoint + . runTaggedLock LockModeExclusive . ignoreLog . runProcessIO . mapError (JuvixError @GitProcessError) @@ -79,11 +83,11 @@ testDescr PosTest {..} = helper renderCodeNew evalHelper files = fmap snd . runHelper files step "Parsing" - p :: Parser.ParserResult <- snd <$> runIO' entryPoint upToParsing + p :: Parser.ParserResult <- snd <$> runIOExclusive entryPoint upToParsing step "Scoping" (resolverState :: ResolverState, s :: Scoper.ScoperResult) <- - runIO' + runIOExclusive entryPoint ( do void (entrySetup defaultDependenciesConfig) diff --git a/test/Termination/Negative.hs b/test/Termination/Negative.hs index 9324a8fa9a..dce990ff0b 100644 --- a/test/Termination/Negative.hs +++ b/test/Termination/Negative.hs @@ -2,6 +2,7 @@ module Termination.Negative (module Termination.Negative) where import Base import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.Termination +import Juvix.Data.Effect.TaggedLock type FailMsg = String @@ -20,8 +21,8 @@ testDescr NegTest {..} = { _testName = _name, _testRoot = tRoot, _testAssertion = Single $ do - entryPoint <- set entryPointNoStdlib True <$> defaultEntryPointCwdIO file' - result <- runIOEither entryPoint upToInternalTyped + entryPoint <- set entryPointNoStdlib True <$> defaultEntryPointIO' LockModeExclusive tRoot file' + result <- runIOEither' LockModeExclusive entryPoint upToInternalTyped case mapLeft fromJuvixError result of Left (Just lexError) -> whenJust (_checkErr lexError) assertFailure Left Nothing -> assertFailure "The termination checker did not find an error." diff --git a/test/Termination/Positive.hs b/test/Termination/Positive.hs index 9e9a96e401..c9cc023f2a 100644 --- a/test/Termination/Positive.hs +++ b/test/Termination/Positive.hs @@ -1,6 +1,7 @@ module Termination.Positive where import Base +import Juvix.Data.Effect.TaggedLock (LockMode (LockModeExclusive)) import Termination.Negative qualified as N data PosTest = PosTest @@ -20,8 +21,8 @@ testDescr PosTest {..} = { _testName = _name, _testRoot = tRoot, _testAssertion = Single $ do - entryPoint <- set entryPointNoStdlib True <$> defaultEntryPointCwdIO file' - (void . runIO' entryPoint) upToInternalTyped + entryPoint <- set entryPointNoStdlib True <$> defaultEntryPointIO' LockModeExclusive tRoot file' + (void . runIOExclusive entryPoint) upToInternalTyped } -------------------------------------------------------------------------------- @@ -42,8 +43,8 @@ testDescrFlag N.NegTest {..} = entryPoint <- set entryPointNoTermination True . set entryPointNoStdlib True - <$> defaultEntryPointCwdIO file' - (void . runIO' entryPoint) upToInternalTyped + <$> defaultEntryPointIO' LockModeExclusive tRoot file' + (void . runIOExclusive entryPoint) upToInternalTyped } tests :: [PosTest] @@ -88,7 +89,7 @@ negTests = N.tests allTests :: TestTree allTests = testGroup - "Positive tests" + "Termination positive tests" [ testGroup "Well-known terminating functions" (map (mkTest . testDescr) tests), diff --git a/test/Typecheck/Negative.hs b/test/Typecheck/Negative.hs index 7ba459b90d..45f3c56809 100644 --- a/test/Typecheck/Negative.hs +++ b/test/Typecheck/Negative.hs @@ -2,6 +2,7 @@ module Typecheck.Negative where import Base import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.TypeChecking.Error +import Juvix.Data.Effect.TaggedLock type FailMsg = String @@ -20,8 +21,8 @@ testDescr NegTest {..} = { _testName = _name, _testRoot = tRoot, _testAssertion = Single $ do - entryPoint <- defaultEntryPointCwdIO file' - result <- runIOEither entryPoint upToInternalTyped + entryPoint <- defaultEntryPointIO' LockModeExclusive tRoot file' + result <- runIOEither' LockModeExclusive entryPoint upToInternalTyped case mapLeft fromJuvixError result of Left (Just tyError) -> whenJust (_checkErr tyError) assertFailure Left Nothing -> assertFailure "An error ocurred but it was not in the type checker." diff --git a/test/Typecheck/Positive.hs b/test/Typecheck/Positive.hs index de24d59efe..b1abaaefea 100644 --- a/test/Typecheck/Positive.hs +++ b/test/Typecheck/Positive.hs @@ -2,6 +2,7 @@ module Typecheck.Positive where import Base import Compilation.Positive qualified as Compilation +import Juvix.Data.Effect.TaggedLock import Typecheck.Negative qualified as N data PosTest = PosTest @@ -27,8 +28,8 @@ testDescr PosTest {..} = { _testName = _name, _testRoot = _dir, _testAssertion = Single $ do - entryPoint <- defaultEntryPointCwdIO _file - (void . runIO' entryPoint) upToInternalTyped + entryPoint <- defaultEntryPointIO' LockModeExclusive _dir _file + (void . runIOExclusive entryPoint) upToInternalTyped } rootNegTests :: Path Abs Dir @@ -45,8 +46,8 @@ testNoPositivityFlag N.NegTest {..} = _testAssertion = Single $ do entryPoint <- set entryPointNoPositivity True - <$> defaultEntryPointCwdIO file' - (void . runIO' entryPoint) upToInternalTyped + <$> defaultEntryPointIO' LockModeExclusive tRoot file' + (void . runIOExclusive entryPoint) upToInternalTyped } negPositivityTests :: [N.NegTest] diff --git a/test/Typecheck/PositiveNew.hs b/test/Typecheck/PositiveNew.hs index 637b016fdc..9ac651b98f 100644 --- a/test/Typecheck/PositiveNew.hs +++ b/test/Typecheck/PositiveNew.hs @@ -2,6 +2,7 @@ module Typecheck.PositiveNew where import Base import Data.HashSet qualified as HashSet +import Juvix.Data.Effect.TaggedLock import Typecheck.Positive qualified as Old root :: Path Abs Dir @@ -19,8 +20,8 @@ testDescr Old.PosTest {..} = { _testName = _name, _testRoot = _dir, _testAssertion = Single $ do - entryPoint <- set entryPointNewTypeCheckingAlgorithm True <$> defaultEntryPointCwdIO _file - (void . runIO' entryPoint) upToInternalTyped + entryPoint <- set entryPointNewTypeCheckingAlgorithm True <$> defaultEntryPointIO' LockModeExclusive _dir _file + (void . runIOExclusive entryPoint) upToInternalTyped } allTests :: TestTree diff --git a/test/VampIR/Compilation/Base.hs b/test/VampIR/Compilation/Base.hs index 72b6665c27..17688a007f 100644 --- a/test/VampIR/Compilation/Base.hs +++ b/test/VampIR/Compilation/Base.hs @@ -4,23 +4,25 @@ import Base import Core.VampIR.Base (coreVampIRAssertion') import Juvix.Compiler.Core import Juvix.Compiler.Core.Data.TransformationId +import Juvix.Data.Effect.TaggedLock import VampIR.Core.Base (VampirBackend (..), vampirAssertion') -vampirCompileAssertion :: Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion -vampirCompileAssertion mainFile dataFile step = do +vampirCompileAssertion :: Path Abs Dir -> Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion +vampirCompileAssertion root' mainFile dataFile step = do step "Translate to JuvixCore" - entryPoint <- defaultEntryPointCwdIO mainFile - tab <- (^. coreResultTable) . snd <$> runIO' entryPoint upToCore + entryPoint <- defaultEntryPointIO' LockModeExclusive root' mainFile + tab <- (^. coreResultTable) . snd <$> runIOExclusive entryPoint upToCore coreVampIRAssertion' tab toVampIRTransformations mainFile dataFile step vampirAssertion' VampirHalo2 tab dataFile step vampirCompileErrorAssertion :: + Path Abs Dir -> Path Abs File -> (String -> IO ()) -> Assertion -vampirCompileErrorAssertion mainFile step = do +vampirCompileErrorAssertion root' mainFile step = do step "Translate to JuvixCore" - entryPoint <- defaultEntryPointCwdIO mainFile + entryPoint <- defaultEntryPointIO' LockModeExclusive root' mainFile r <- runIOEither entryPoint upToCore case r of Left _ -> return () diff --git a/test/VampIR/Compilation/Negative.hs b/test/VampIR/Compilation/Negative.hs index cb729b214b..d190945479 100644 --- a/test/VampIR/Compilation/Negative.hs +++ b/test/VampIR/Compilation/Negative.hs @@ -19,7 +19,7 @@ testDescr NegTest {..} = in TestDescr { _testName = _name, _testRoot = tRoot, - _testAssertion = Steps $ vampirCompileErrorAssertion file' + _testAssertion = Steps $ vampirCompileErrorAssertion tRoot file' } allTests :: TestTree diff --git a/test/VampIR/Compilation/Positive.hs b/test/VampIR/Compilation/Positive.hs index 686944a29a..8543214630 100644 --- a/test/VampIR/Compilation/Positive.hs +++ b/test/VampIR/Compilation/Positive.hs @@ -26,7 +26,7 @@ toTestDescr PosTest {..} = in TestDescr { _testName = _name, _testRoot = tRoot, - _testAssertion = Steps $ vampirCompileAssertion file' data' + _testAssertion = Steps $ vampirCompileAssertion _dir file' data' } allTests :: TestTree