1{-# OPTIONS_GHC -Wno-unused-do-bind #-}
2{-# LANGUAGE BangPatterns, GADTs, OverloadedStrings #-}
18import Control.Concurrent (forkIO)
19import Control.Concurrent.MVar
20import Control.Monad (filterM, mapM_, sequence_)
21import qualified Data.ByteString as B
22import qualified Data.Char as Char
23import qualified Data.Graph as Graph
24import qualified Data.List as List
25import qualified Data.Map.Utils as Map
26import qualified Data.Map.Strict as Map
27import Data.Map.Strict ((!))
28import qualified Data.Maybe as Maybe
29import qualified Data.Name as Name
30import qualified Data.NonEmptyList as NE
31import qualified Data.OneOrMore as OneOrMore
32import qualified Data.Set as Set
33import qualified System.Directory as Dir
34import qualified System.FilePath as FP
35import System.FilePath ((</>), (<.>))
37import qualified AST.Canonical as Can
38import qualified AST.Source as Src
39import qualified AST.Optimized as Opt
40import qualified Compile
41import qualified Elm.Details as Details
42import qualified Elm.Docs as Docs
43import qualified Elm.Interface as I
44import qualified Elm.ModuleName as ModuleName
45import qualified Elm.Outline as Outline
46import qualified Elm.Package as Pkg
48import qualified Json.Encode as E
49import qualified Parse.Module as Parse
50import qualified Reporting
51import qualified Reporting.Annotation as A
52import qualified Reporting.Error as Error
53import qualified Reporting.Error.Syntax as Syntax
54import qualified Reporting.Error.Import as Import
55import qualified Reporting.Exit as Exit
56import qualified Reporting.Render.Type.Localizer as L
66 { _key :: Reporting.BKey
68 , _project :: Parse.ProjectType
69 , _srcDirs :: [AbsoluteSrcDir]
70 , _buildID :: Details.BuildID
71 , _locals :: Map.Map ModuleName.Raw Details.Local
72 , _foreigns :: Map.Map ModuleName.Raw Details.Foreign
76makeEnv :: Reporting.BKey -> FilePath -> Details.Details -> IO Env
77makeEnv key root (Details.Details _ validOutline buildID locals foreigns _) =
79 Details.ValidApp givenSrcDirs ->
80 do srcDirs <- traverse (toAbsoluteSrcDir root) (NE.toList givenSrcDirs)
81 return $ Env key root Parse.Application srcDirs buildID locals foreigns
83 Details.ValidPkg pkg _ _ ->
84 do srcDir <- toAbsoluteSrcDir root (Outline.RelativeSrcDir "src")
85 return $ Env key root (Parse.Package pkg) [srcDir] buildID locals foreigns
92newtype AbsoluteSrcDir =
93 AbsoluteSrcDir FilePath
96toAbsoluteSrcDir :: FilePath -> Outline.SrcDir -> IO AbsoluteSrcDir
97toAbsoluteSrcDir root srcDir =
98 AbsoluteSrcDir <$> Dir.canonicalizePath
101 Outline.AbsoluteSrcDir dir -> dir
102 Outline.RelativeSrcDir dir -> root </> dir
106addRelative :: AbsoluteSrcDir -> FilePath -> FilePath
107addRelative (AbsoluteSrcDir srcDir) path =
115-- PERF try using IORef semephore on file crawl phase?
116-- described in Chapter 13 of Parallel and Concurrent Programming in Haskell by Simon Marlow
117-- https://www.oreilly.com/library/view/parallel-and-concurrent/9781449335939/ch13.html#sec_conc-par-overhead
119fork :: IO a -> IO (MVar a)
121 do mvar <- newEmptyMVar
122 _ <- forkIO $ putMVar mvar =<< work
126{-# INLINE forkWithKey #-}
127forkWithKey :: (k -> a -> IO b) -> Map.Map k a -> IO (Map.Map k (MVar b))
128forkWithKey func dict =
129 Map.traverseWithKey (\k v -> fork (func k v)) dict
136fromExposed :: Reporting.Style -> FilePath -> Details.Details -> DocsGoal docs -> NE.List ModuleName.Raw -> IO (Either Exit.BuildProblem docs)
137fromExposed style root details docsGoal exposed@(NE.List e es) =
138 Reporting.trackBuild style $ \key ->
139 do env <- makeEnv key root details
140 dmvar <- Details.loadInterfaces root details
144 let docsNeed = toDocsNeed docsGoal
145 roots <- Map.fromKeysA (fork . crawlModule env mvar docsNeed) (e:es)
148 statuses <- traverse readMVar =<< readMVar mvar
151 midpoint <- checkMidpoint dmvar statuses
154 return (Left (Exit.BuildProjectProblem problem))
157 do rmvar <- newEmptyMVar
158 resultMVars <- forkWithKey (checkModule env foreigns rmvar) statuses
159 putMVar rmvar resultMVars
160 results <- traverse readMVar resultMVars
161 writeDetails root details results
162 finalizeExposed docsGoal exposed results
172 , _deps :: Dependencies
173 , _mains :: NE.List Main
174 , _modules :: [Module]
179 = Fresh ModuleName.Raw I.Interface Opt.LocalGraph
180 | Cached ModuleName.Raw Bool (MVar CachedInterface)
184 Map.Map ModuleName.Canonical I.DependencyInterface
187fromMains :: Reporting.Style -> FilePath -> Details.Details -> NE.List FilePath -> IO (Either Exit.BuildProblem Artifacts)
188fromMains style root details paths =
189 Reporting.trackBuild style $ \key ->
190 do env <- makeEnv key root details
192 elmains <- findMains env paths
195 return (Left (Exit.BuildProjectProblem problem))
199 dmvar <- Details.loadInterfaces root details
200 smvar <- newMVar Map.empty
201 smainMVars <- traverse (fork . crawlMain env smvar) lmains
202 smains <- traverse readMVar smainMVars
203 statuses <- traverse readMVar =<< readMVar smvar
205 midpoint <- checkMainsMidpoint dmvar statuses smains
208 return (Left (Exit.BuildProjectProblem problem))
212 rmvar <- newEmptyMVar
213 resultsMVars <- forkWithKey (checkModule env foreigns rmvar) statuses
214 putMVar rmvar resultsMVars
215 rmainMVars <- traverse (fork . checkMain env resultsMVars) smains
216 results <- traverse readMVar resultsMVars
217 writeDetails root details results
218 toArtifacts env foreigns results <$> traverse readMVar rmainMVars
225getMainNames :: Artifacts -> NE.List ModuleName.Raw
226getMainNames (Artifacts _ _ mains _) =
227 fmap getMainName mains
230getMainName :: Main -> ModuleName.Raw
234 Outside name _ _ -> name
242 Map.Map ModuleName.Raw (MVar Status)
246 = SCached Details.Local
247 | SChanged Details.Local B.ByteString Src.Module DocsNeed
248 | SBadImport Import.Problem
249 | SBadSyntax FilePath File.Time B.ByteString Syntax.Error
254crawlDeps :: Env -> MVar StatusDict -> [ModuleName.Raw] -> a -> IO a
255crawlDeps env mvar deps blockedValue =
256 do statusDict <- takeMVar mvar
257 let depsDict = Map.fromKeys (\_ -> ()) deps
258 let newsDict = Map.difference depsDict statusDict
259 statuses <- Map.traverseWithKey crawlNew newsDict
260 putMVar mvar (Map.union statuses statusDict)
261 mapM_ readMVar statuses
264 crawlNew name () = fork (crawlModule env mvar (DocsNeed False) name)
267crawlModule :: Env -> MVar StatusDict -> DocsNeed -> ModuleName.Raw -> IO Status
268crawlModule env@(Env _ root projectType srcDirs buildID locals foreigns) mvar docsNeed name =
269 do let fileName = ModuleName.toFilePath name <.> "elm"
271 paths <- filterM File.exists (map (`addRelative` fileName) srcDirs)
275 case Map.lookup name foreigns of
276 Just (Details.Foreign dep deps) ->
277 return $ SBadImport $ Import.Ambiguous path [] dep deps
280 do newTime <- File.getTime path
281 case Map.lookup name locals of
283 crawlFile env mvar docsNeed name path newTime buildID
285 Just local@(Details.Local oldPath oldTime deps _ lastChange _) ->
286 if path /= oldPath || oldTime /= newTime || needsDocs docsNeed
287 then crawlFile env mvar docsNeed name path newTime lastChange
288 else crawlDeps env mvar deps (SCached local)
291 return $ SBadImport $ Import.AmbiguousLocal (FP.makeRelative root p1) (FP.makeRelative root p2) (map (FP.makeRelative root) ps)
294 case Map.lookup name foreigns of
295 Just (Details.Foreign dep deps) ->
298 return $ SForeign dep
301 return $ SBadImport $ Import.AmbiguousForeign dep d ds
304 if Name.isKernel name && Parse.isKernel projectType then
305 do exists <- File.exists ("src" </> ModuleName.toFilePath name <.> "js")
306 return $ if exists then SKernel else SBadImport Import.NotFound
308 return $ SBadImport Import.NotFound
311crawlFile :: Env -> MVar StatusDict -> DocsNeed -> ModuleName.Raw -> FilePath -> File.Time -> Details.BuildID -> IO Status
312crawlFile env@(Env _ root projectType _ buildID _ _) mvar docsNeed expectedName path time lastChange =
313 do source <- File.readUtf8 (root </> path)
315 case Parse.fromByteString projectType source of
317 return $ SBadSyntax path time source err
319 Right modul@(Src.Module maybeActualName _ _ imports values _ _ _ _) ->
320 case maybeActualName of
322 return $ SBadSyntax path time source (Syntax.ModuleNameUnspecified expectedName)
324 Just name@(A.At _ actualName) ->
325 if expectedName == actualName then
327 deps = map Src.getImportName imports
328 local = Details.Local path time deps (any isMain values) lastChange buildID
330 crawlDeps env mvar deps (SChanged local source modul docsNeed)
332 return $ SBadSyntax path time source (Syntax.ModuleNameMismatch expectedName name)
335isMain :: A.Located Src.Value -> Bool
336isMain (A.At _ (Src.Value (A.At _ name) _ _ _)) =
345 Map.Map ModuleName.Raw (MVar Result)
349 = RNew !Details.Local !I.Interface !Opt.LocalGraph !(Maybe Docs.Module)
350 | RSame !Details.Local !I.Interface !Opt.LocalGraph !(Maybe Docs.Module)
351 | RCached Bool Details.BuildID (MVar CachedInterface)
352 | RNotFound Import.Problem
353 | RProblem Error.Module
355 | RForeign I.Interface
365checkModule :: Env -> Dependencies -> MVar ResultDict -> ModuleName.Raw -> Status -> IO Result
366checkModule env@(Env _ root projectType _ _ _ _) foreigns resultsMVar name status =
368 SCached local@(Details.Local path time deps hasMain lastChange lastCompile) ->
369 do results <- readMVar resultsMVar
370 depsStatus <- checkDeps root results deps lastCompile
373 do source <- File.readUtf8 path
374 case Parse.fromByteString projectType source of
375 Right modul -> compile env (DocsNeed False) local source ifaces modul
378 Error.Module name path time source (Error.BadSyntax err)
381 do mvar <- newMVar Unneeded
382 return (RCached hasMain lastChange mvar)
387 DepsNotFound problems ->
388 do source <- File.readUtf8 path
389 return $ RProblem $ Error.Module name path time source $
390 case Parse.fromByteString projectType source of
391 Right (Src.Module _ _ _ imports _ _ _ _ _) ->
392 Error.BadImports (toImportErrors env results imports problems)
397 SChanged local@(Details.Local path time deps _ _ lastCompile) source modul@(Src.Module _ _ _ imports _ _ _ _ _) docsNeed ->
398 do results <- readMVar resultsMVar
399 depsStatus <- checkDeps root results deps lastCompile
402 compile env docsNeed local source ifaces modul
404 DepsSame same cached ->
405 do maybeLoaded <- loadInterfaces root same cached
407 Nothing -> return RBlocked
408 Just ifaces -> compile env docsNeed local source ifaces modul
413 DepsNotFound problems ->
414 return $ RProblem $ Error.Module name path time source $
415 Error.BadImports (toImportErrors env results imports problems)
417 SBadImport importProblem ->
418 return (RNotFound importProblem)
420 SBadSyntax path time source err ->
421 return $ RProblem $ Error.Module name path time source $
425 case foreigns ! ModuleName.Canonical home name of
426 I.Public iface -> return (RForeign iface)
427 I.Private _ _ _ -> error $ "mistakenly seeing private interface for " ++ Pkg.toChars home ++ " " ++ ModuleName.toChars name
438 = DepsChange (Map.Map ModuleName.Raw I.Interface)
439 | DepsSame [Dep] [CDep]
441 | DepsNotFound (NE.List (ModuleName.Raw, Import.Problem))
444checkDeps :: FilePath -> ResultDict -> [ModuleName.Raw] -> Details.BuildID -> IO DepsStatus
445checkDeps root results deps lastCompile =
446 checkDepsHelp root results deps [] [] [] [] False 0 lastCompile
449type Dep = (ModuleName.Raw, I.Interface)
450type CDep = (ModuleName.Raw, MVar CachedInterface)
453checkDepsHelp :: FilePath -> ResultDict -> [ModuleName.Raw] -> [Dep] -> [Dep] -> [CDep] -> [(ModuleName.Raw,Import.Problem)] -> Bool -> Details.BuildID -> Details.BuildID -> IO DepsStatus
454checkDepsHelp root results deps new same cached importProblems isBlocked lastDepChange lastCompile =
457 do result <- readMVar (results ! dep)
459 RNew (Details.Local _ _ _ _ lastChange _) iface _ _ ->
460 checkDepsHelp root results otherDeps ((dep,iface) : new) same cached importProblems isBlocked (max lastChange lastDepChange) lastCompile
462 RSame (Details.Local _ _ _ _ lastChange _) iface _ _ ->
463 checkDepsHelp root results otherDeps new ((dep,iface) : same) cached importProblems isBlocked (max lastChange lastDepChange) lastCompile
465 RCached _ lastChange mvar ->
466 checkDepsHelp root results otherDeps new same ((dep,mvar) : cached) importProblems isBlocked (max lastChange lastDepChange) lastCompile
469 checkDepsHelp root results otherDeps new same cached ((dep,prob) : importProblems) True lastDepChange lastCompile
472 checkDepsHelp root results otherDeps new same cached importProblems True lastDepChange lastCompile
475 checkDepsHelp root results otherDeps new same cached importProblems True lastDepChange lastCompile
478 checkDepsHelp root results otherDeps new ((dep,iface) : same) cached importProblems isBlocked lastDepChange lastCompile
481 checkDepsHelp root results otherDeps new same cached importProblems isBlocked lastDepChange lastCompile
485 case reverse importProblems of
487 return $ DepsNotFound (NE.List p ps)
493 else if null new && lastDepChange <= lastCompile then
494 return $ DepsSame same cached
497 do maybeLoaded <- loadInterfaces root same cached
499 Nothing -> return DepsBlock
500 Just ifaces -> return $ DepsChange $ Map.union (Map.fromList new) ifaces
507toImportErrors :: Env -> ResultDict -> [Src.Import] -> NE.List (ModuleName.Raw, Import.Problem) -> NE.List Import.Error
508toImportErrors (Env _ _ _ _ _ locals foreigns) results imports problems =
512 [ Map.keysSet foreigns
514 , Map.keysSet results
518 Set.difference knownModules (Set.fromList (map Src.getImportName imports))
521 Map.fromList (map (\(Src.Import (A.At region name) _ _) -> (name, region)) imports)
523 toError (name, problem) =
524 Import.Error (regionDict ! name) name unimportedModules problem
526 fmap toError problems
530-- LOAD CACHED INTERFACES
533loadInterfaces :: FilePath -> [Dep] -> [CDep] -> IO (Maybe (Map.Map ModuleName.Raw I.Interface))
534loadInterfaces root same cached =
535 do loading <- traverse (fork . loadInterface root) cached
536 maybeLoaded <- traverse readMVar loading
537 case sequence maybeLoaded of
542 return $ Just $ Map.union (Map.fromList loaded) (Map.fromList same)
545loadInterface :: FilePath -> CDep -> IO (Maybe Dep)
546loadInterface root (name, ciMvar) =
547 do cachedInterface <- takeMVar ciMvar
548 case cachedInterface of
550 do putMVar ciMvar cachedInterface
554 do putMVar ciMvar cachedInterface
555 return (Just (name, iface))
558 do maybeIface <- File.readBinary (Stuff.elmi root name)
561 do putMVar ciMvar Corrupted
565 do putMVar ciMvar (Loaded iface)
566 return (Just (name, iface))
573checkMidpoint :: MVar (Maybe Dependencies) -> Map.Map ModuleName.Raw Status -> IO (Either Exit.BuildProjectProblem Dependencies)
574checkMidpoint dmvar statuses =
575 case checkForCycles statuses of
577 do maybeForeigns <- readMVar dmvar
578 case maybeForeigns of
579 Nothing -> return (Left Exit.BP_CannotLoadDependencies)
580 Just fs -> return (Right fs)
582 Just (NE.List name names) ->
583 do _ <- readMVar dmvar
584 return (Left (Exit.BP_Cycle name names))
587checkMainsMidpoint :: MVar (Maybe Dependencies) -> Map.Map ModuleName.Raw Status -> NE.List MainStatus -> IO (Either Exit.BuildProjectProblem Dependencies)
588checkMainsMidpoint dmvar statuses smains =
589 case checkForCycles statuses of
591 case checkUniqueMains statuses smains of
593 do maybeForeigns <- readMVar dmvar
594 case maybeForeigns of
595 Nothing -> return (Left Exit.BP_CannotLoadDependencies)
596 Just fs -> return (Right fs)
599 do _ <- readMVar dmvar
600 return (Left problem)
602 Just (NE.List name names) ->
603 do _ <- readMVar dmvar
604 return (Left (Exit.BP_Cycle name names))
611checkForCycles :: Map.Map ModuleName.Raw Status -> Maybe (NE.List ModuleName.Raw)
612checkForCycles modules =
614 !graph = Map.foldrWithKey addToGraph [] modules
615 !sccs = Graph.stronglyConnComp graph
617 checkForCyclesHelp sccs
620checkForCyclesHelp :: [Graph.SCC ModuleName.Raw] -> Maybe (NE.List ModuleName.Raw)
621checkForCyclesHelp sccs =
628 Graph.AcyclicSCC _ -> checkForCyclesHelp otherSccs
629 Graph.CyclicSCC [] -> checkForCyclesHelp otherSccs
630 Graph.CyclicSCC (m:ms) -> Just (NE.List m ms)
634 ( ModuleName.Raw, ModuleName.Raw, [ModuleName.Raw] )
637addToGraph :: ModuleName.Raw -> Status -> [Node] -> [Node]
638addToGraph name status graph =
642 SCached (Details.Local _ _ deps _ _ _) -> deps
643 SChanged (Details.Local _ _ deps _ _ _) _ _ _ -> deps
645 SBadSyntax _ _ _ _ -> []
649 (name, name, dependencies) : graph
656checkUniqueMains :: Map.Map ModuleName.Raw Status -> NE.List MainStatus -> Maybe Exit.BuildProjectProblem
657checkUniqueMains insides smains =
660 Map.fromListWith OneOrMore.more (Maybe.mapMaybe mainStatusToNamePathPair (NE.toList smains))
662 case Map.traverseWithKey checkOutside outsidesDict of
667 case sequence_ (Map.intersectionWithKey checkInside outsides insides) of
669 Left problem -> Just problem
672mainStatusToNamePathPair :: MainStatus -> Maybe (ModuleName.Raw, OneOrMore.OneOrMore FilePath)
673mainStatusToNamePathPair smain =
676 SOutsideOk (Details.Local path _ _ _ _ _) _ modul -> Just (Src.getName modul, OneOrMore.one path)
677 SOutsideErr _ -> Nothing
680checkOutside :: ModuleName.Raw -> OneOrMore.OneOrMore FilePath -> Either Exit.BuildProjectProblem FilePath
681checkOutside name paths =
682 case OneOrMore.destruct NE.List paths of
683 NE.List p [] -> Right p
684 NE.List p1 (p2:_) -> Left (Exit.BP_MainNameDuplicate name p1 p2)
687checkInside :: ModuleName.Raw -> FilePath -> Status -> Either Exit.BuildProjectProblem ()
688checkInside name p1 status =
690 SCached (Details.Local p2 _ _ _ _ _) -> Left (Exit.BP_MainNameDuplicate name p1 p2)
691 SChanged (Details.Local p2 _ _ _ _ _) _ _ _ -> Left (Exit.BP_MainNameDuplicate name p1 p2)
692 SBadImport _ -> Right ()
693 SBadSyntax _ _ _ _ -> Right ()
694 SForeign _ -> Right ()
702compile :: Env -> DocsNeed -> Details.Local -> B.ByteString -> Map.Map ModuleName.Raw I.Interface -> Src.Module -> IO Result
703compile (Env key root projectType _ buildID _ _) docsNeed (Details.Local path time deps main lastChange _) source ifaces modul =
705 pkg = projectTypeToPkg projectType
707 case Compile.compile pkg ifaces modul of
708 Right (Compile.Artifacts canonical annotations objects) ->
709 do let name = Src.getName modul
710 let iface = I.fromModule pkg canonical annotations
711 let docs = makeDocs docsNeed canonical
712 let elmi = Stuff.elmi root name
713 File.writeBinary (Stuff.elmo root name) objects
714 maybeOldi <- File.readBinary elmi
716 Just oldi | oldi == iface ->
717 do -- iface should be fully forced by equality check
718 Reporting.report key Reporting.BDone
719 let local = Details.Local path time deps main lastChange buildID
720 return (RSame local iface objects docs)
723 do -- iface may be lazy still
724 File.writeBinary elmi iface
725 Reporting.report key Reporting.BDone
726 let local = Details.Local path time deps main buildID buildID
727 return (RNew local iface objects docs)
731 Error.Module (Src.getName modul) path time source err
734projectTypeToPkg :: Parse.ProjectType -> Pkg.Name
735projectTypeToPkg projectType =
737 Parse.Package pkg -> pkg
738 Parse.Application -> Pkg.dummyName
745writeDetails :: FilePath -> Details.Details -> Map.Map ModuleName.Raw Result -> IO ()
746writeDetails root (Details.Details time outline buildID locals foreigns extras) results =
747 File.writeBinary (Stuff.details root) $
748 Details.Details time outline buildID (Map.foldrWithKey addNewLocal locals results) foreigns extras
751addNewLocal :: ModuleName.Raw -> Result -> Map.Map ModuleName.Raw Details.Local -> Map.Map ModuleName.Raw Details.Local
752addNewLocal name result locals =
754 RNew local _ _ _ -> Map.insert name local locals
755 RSame local _ _ _ -> Map.insert name local locals
756 RCached _ _ _ -> locals
757 RNotFound _ -> locals
768finalizeExposed :: DocsGoal docs -> NE.List ModuleName.Raw -> Map.Map ModuleName.Raw Result -> IO (Either Exit.BuildProblem docs)
769finalizeExposed docsGoal exposed results =
770 case foldr (addImportProblems results) [] (NE.toList exposed) of
772 return $ Left $ Exit.BuildProjectProblem (Exit.BP_MissingExposed (NE.List p ps))
775 case Map.foldr addErrors [] results of
776 [] -> Right <$> finalizeDocs docsGoal results
777 e:es -> return $ Left $ Exit.BuildBadModules e es
780addErrors :: Result -> [Error.Module] -> [Error.Module]
781addErrors result errors =
783 RNew _ _ _ _ -> errors
784 RSame _ _ _ _ -> errors
785 RCached _ _ _ -> errors
786 RNotFound _ -> errors
787 RProblem e -> e:errors
793addImportProblems :: Map.Map ModuleName.Raw Result -> ModuleName.Raw -> [(ModuleName.Raw, Import.Problem)] -> [(ModuleName.Raw, Import.Problem)]
794addImportProblems results name problems =
795 case results ! name of
796 RNew _ _ _ _ -> problems
797 RSame _ _ _ _ -> problems
798 RCached _ _ _ -> problems
799 RNotFound p -> (name, p) : problems
800 RProblem _ -> problems
802 RForeign _ -> problems
811 KeepDocs :: DocsGoal Docs.Documentation
812 WriteDocs :: FilePath -> DocsGoal ()
813 IgnoreDocs :: DocsGoal ()
817 DocsNeed { needsDocs :: Bool }
820toDocsNeed :: DocsGoal a -> DocsNeed
823 IgnoreDocs -> DocsNeed False
824 WriteDocs _ -> DocsNeed True
825 KeepDocs -> DocsNeed True
828makeDocs :: DocsNeed -> Can.Module -> Maybe Docs.Module
829makeDocs (DocsNeed isNeeded) modul =
831 case Docs.fromModule modul of
832 Right docs -> Just docs
838finalizeDocs :: DocsGoal docs -> Map.Map ModuleName.Raw Result -> IO docs
839finalizeDocs goal results =
842 return $ Map.mapMaybe toDocs results
845 E.writeUgly path $ Docs.encode $ Map.mapMaybe toDocs results
851toDocs :: Result -> Maybe Docs.Module
856 RCached _ _ _ -> Nothing
857 RNotFound _ -> Nothing
858 RProblem _ -> Nothing
860 RForeign _ -> Nothing
865--------------------------------------------------------------------------------
866------ NOW FOR SOME REPL STUFF -------------------------------------------------
867--------------------------------------------------------------------------------
875 { _repl_home :: ModuleName.Canonical
876 , _repl_modules :: [Module]
877 , _repl_localizer :: L.Localizer
878 , _repl_annotations :: Map.Map Name.Name Can.Annotation
882fromRepl :: FilePath -> Details.Details -> B.ByteString -> IO (Either Exit.Repl ReplArtifacts)
883fromRepl root details source =
884 do env@(Env _ _ projectType _ _ _ _) <- makeEnv Reporting.ignorer root details
885 case Parse.fromByteString projectType source of
887 return $ Left $ Exit.ReplBadInput source $ Error.BadSyntax syntaxError
889 Right modul@(Src.Module _ _ _ imports _ _ _ _ _) ->
890 do dmvar <- Details.loadInterfaces root details
892 let deps = map Src.getImportName imports
893 mvar <- newMVar Map.empty
894 crawlDeps env mvar deps ()
896 statuses <- traverse readMVar =<< readMVar mvar
897 midpoint <- checkMidpoint dmvar statuses
901 return $ Left $ Exit.ReplProjectProblem problem
904 do rmvar <- newEmptyMVar
905 resultMVars <- forkWithKey (checkModule env foreigns rmvar) statuses
906 putMVar rmvar resultMVars
907 results <- traverse readMVar resultMVars
908 writeDetails root details results
909 depsStatus <- checkDeps root resultMVars deps 0
910 finalizeReplArtifacts env source modul depsStatus resultMVars results
913finalizeReplArtifacts :: Env -> B.ByteString -> Src.Module -> DepsStatus -> ResultDict -> Map.Map ModuleName.Raw Result -> IO (Either Exit.Repl ReplArtifacts)
914finalizeReplArtifacts env@(Env _ root projectType _ _ _ _) source modul@(Src.Module _ _ _ imports _ _ _ _ _) depsStatus resultMVars results =
917 projectTypeToPkg projectType
919 compileInput ifaces =
920 case Compile.compile pkg ifaces modul of
921 Right (Compile.Artifacts canonical annotations objects) ->
923 h = Can._name canonical
924 m = Fresh (Src.getName modul) (I.fromModule pkg canonical annotations) objects
925 ms = Map.foldrWithKey addInside [] results
927 return $ Right $ ReplArtifacts h (m:ms) (L.fromModule modul) annotations
930 return $ Left $ Exit.ReplBadInput source errors
936 DepsSame same cached ->
937 do maybeLoaded <- loadInterfaces root same cached
939 Just ifaces -> compileInput ifaces
940 Nothing -> return $ Left $ Exit.ReplBadCache
943 case Map.foldr addErrors [] results of
944 [] -> return $ Left $ Exit.ReplBlocked
945 e:es -> return $ Left $ Exit.ReplBadLocalDeps e es
947 DepsNotFound problems ->
948 return $ Left $ Exit.ReplBadInput source $ Error.BadImports $
949 toImportErrors env resultMVars imports problems
953--------------------------------------------------------------------------------
954--------------------------------------------------------------------------------
955------ AFTER THIS, EVERYTHING IS ABOUT HANDLING MODULES GIVEN BY FILEPATH ------
956--------------------------------------------------------------------------------
957--------------------------------------------------------------------------------
965 = LInside ModuleName.Raw
969findMains :: Env -> NE.List FilePath -> IO (Either Exit.BuildProjectProblem (NE.List MainLocation))
971 do mvars <- traverse (fork . findLocation env) paths
972 elocs <- traverse readMVar mvars
973 return $ checkLocations =<< sequence elocs
976checkLocations :: NE.List Location -> Either Exit.BuildProjectProblem (NE.List MainLocation)
977checkLocations locations =
979 toOneOrMore loc@(Location absolute _ _) =
980 (absolute, OneOrMore.one loc)
982 fromOneOrMore loc locs =
985 loc2:_ -> Left (Exit.BP_MainPathDuplicate (_relative loc) (_relative loc2))
987 fmap (\_ -> fmap _location locations) $
988 traverse (OneOrMore.destruct fromOneOrMore) $
989 Map.fromListWith OneOrMore.more $ map toOneOrMore (NE.toList locations)
998 { _absolute :: FilePath
999 , _relative :: FilePath
1000 , _location :: MainLocation
1004findLocation :: Env -> FilePath -> IO (Either Exit.BuildProjectProblem Location)
1005findLocation env path =
1006 do exists <- File.exists path
1008 then findLoc env path =<< Dir.canonicalizePath path
1009 else return (Left (Exit.BP_PathUnknown path))
1012findLoc :: Env -> FilePath -> FilePath -> IO (Either Exit.BuildProjectProblem Location)
1013findLoc (Env _ root _ srcDirs _ _ _) path absolutePath =
1015 (dirs, file) = FP.splitFileName absolutePath
1016 (final, ext) = FP.splitExtension file
1020 return $ Left $ Exit.BP_WithBadExtension path
1023 absoluteSegments = FP.splitDirectories dirs ++ [final]
1025 case Maybe.mapMaybe (isInsideSrcDirByPath absoluteSegments) srcDirs of
1027 return $ Right $ Location absolutePath path (LOutside path)
1029 [(_, Right names)] ->
1030 do let name = Name.fromChars (List.intercalate "." names)
1031 matchingDirs <- filterM (isInsideSrcDirByName names) srcDirs
1032 case matchingDirs of
1034 do let p1 = addRelative d1 (FP.joinPath names <.> "elm")
1035 let p2 = addRelative d2 (FP.joinPath names <.> "elm")
1036 return $ Left $ Exit.BP_MainNameDuplicate name p1 p2
1039 return $ Right $ Location absolutePath path (LInside name)
1041 [(s, Left names)] ->
1042 return $ Left $ Exit.BP_MainNameInvalid path s names
1045 return $ Left $ Exit.BP_WithAmbiguousSrcDir path s1 s2
1049isInsideSrcDirByName :: [String] -> AbsoluteSrcDir -> IO Bool
1050isInsideSrcDirByName names srcDir =
1051 File.exists (addRelative srcDir (FP.joinPath names <.> "elm"))
1054isInsideSrcDirByPath :: [String] -> AbsoluteSrcDir -> Maybe (FilePath, Either [String] [String])
1055isInsideSrcDirByPath segments (AbsoluteSrcDir srcDir) =
1056 case dropPrefix (FP.splitDirectories srcDir) segments of
1061 if all isGoodName names
1062 then Just (srcDir, Right names)
1063 else Just (srcDir, Left names)
1066isGoodName :: [Char] -> Bool
1073 Char.isUpper char && all (\c -> Char.isAlphaNum c || c == '_') chars
1076-- INVARIANT: Dir.canonicalizePath has been run on both inputs
1078dropPrefix :: [FilePath] -> [FilePath] -> Maybe [FilePath]
1079dropPrefix roots paths =
1087 p:ps -> if r == p then dropPrefix rs ps else Nothing
1095 = SInside ModuleName.Raw
1096 | SOutsideOk Details.Local B.ByteString Src.Module
1097 | SOutsideErr Error.Module
1100crawlMain :: Env -> MVar StatusDict -> MainLocation -> IO MainStatus
1101crawlMain env@(Env _ _ projectType _ buildID _ _) mvar given =
1104 do statusMVar <- newEmptyMVar
1105 statusDict <- takeMVar mvar
1106 putMVar mvar (Map.insert name statusMVar statusDict)
1107 putMVar statusMVar =<< crawlModule env mvar (DocsNeed False) name
1108 return (SInside name)
1111 do time <- File.getTime path
1112 source <- File.readUtf8 path
1113 case Parse.fromByteString projectType source of
1114 Right modul@(Src.Module _ _ _ imports values _ _ _ _) ->
1115 do let deps = map Src.getImportName imports
1116 let local = Details.Local path time deps (any isMain values) buildID buildID
1117 crawlDeps env mvar deps (SOutsideOk local source modul)
1120 return $ SOutsideErr $
1121 Error.Module "???" path time source (Error.BadSyntax syntaxError)
1129 = RInside ModuleName.Raw
1130 | ROutsideOk ModuleName.Raw I.Interface Opt.LocalGraph
1131 | ROutsideErr Error.Module
1135checkMain :: Env -> ResultDict -> MainStatus -> IO MainResult
1136checkMain env@(Env _ root _ _ _ _ _) results pendingMain =
1139 return (RInside name)
1142 return (ROutsideErr err)
1144 SOutsideOk local@(Details.Local path time deps _ _ lastCompile) source modul@(Src.Module _ _ _ imports _ _ _ _ _) ->
1145 do depsStatus <- checkDeps root results deps lastCompile
1147 DepsChange ifaces ->
1148 compileOutside env local source ifaces modul
1150 DepsSame same cached ->
1151 do maybeLoaded <- loadInterfaces root same cached
1153 Nothing -> return ROutsideBlocked
1154 Just ifaces -> compileOutside env local source ifaces modul
1157 return ROutsideBlocked
1159 DepsNotFound problems ->
1160 return $ ROutsideErr $ Error.Module (Src.getName modul) path time source $
1161 Error.BadImports (toImportErrors env results imports problems)
1164compileOutside :: Env -> Details.Local -> B.ByteString -> Map.Map ModuleName.Raw I.Interface -> Src.Module -> IO MainResult
1165compileOutside (Env key _ projectType _ _ _ _) (Details.Local path time _ _ _ _) source ifaces modul =
1167 pkg = projectTypeToPkg projectType
1168 name = Src.getName modul
1170 case Compile.compile pkg ifaces modul of
1171 Right (Compile.Artifacts canonical annotations objects) ->
1172 do Reporting.report key Reporting.BDone
1173 return $ ROutsideOk name (I.fromModule pkg canonical annotations) objects
1176 return $ ROutsideErr $ Error.Module name path time source errors
1184 = Inside ModuleName.Raw
1185 | Outside ModuleName.Raw I.Interface Opt.LocalGraph
1188toArtifacts :: Env -> Dependencies -> Map.Map ModuleName.Raw Result -> NE.List MainResult -> Either Exit.BuildProblem Artifacts
1189toArtifacts (Env _ _ projectType _ _ _ _) foreigns results mainResults =
1190 case gatherProblemsOrMains results mainResults of
1191 Left (NE.List e es) ->
1192 Left (Exit.BuildBadModules e es)
1195 Right $ Artifacts (projectTypeToPkg projectType) foreigns mains $
1196 Map.foldrWithKey addInside (foldr addOutside [] mainResults) results
1199gatherProblemsOrMains :: Map.Map ModuleName.Raw Result -> NE.List MainResult -> Either (NE.List Error.Module) (NE.List Main)
1200gatherProblemsOrMains results (NE.List mainResult mainResults) =
1202 sortMain result (es, mains) =
1204 RInside n -> ( es, Inside n : mains)
1205 ROutsideOk n i o -> ( es, Outside n i o : mains)
1206 ROutsideErr e -> (e:es, mains)
1207 ROutsideBlocked -> ( es, mains)
1209 errors = Map.foldr addErrors [] results
1211 case (mainResult, foldr sortMain (errors, []) mainResults) of
1212 (RInside n , ( [], ms)) -> Right (NE.List (Inside n) ms)
1213 (RInside _ , (e:es, _ )) -> Left (NE.List e es)
1214 (ROutsideOk n i o, ( [], ms)) -> Right (NE.List (Outside n i o) ms)
1215 (ROutsideOk _ _ _, (e:es, _ )) -> Left (NE.List e es)
1216 (ROutsideErr e , ( es, _ )) -> Left (NE.List e es)
1217 (ROutsideBlocked , ( [], _ )) -> error "seems like elm-stuff/ is corrupted"
1218 (ROutsideBlocked , (e:es, _ )) -> Left (NE.List e es)
1221addInside :: ModuleName.Raw -> Result -> [Module] -> [Module]
1222addInside name result modules =
1224 RNew _ iface objs _ -> Fresh name iface objs : modules
1225 RSame _ iface objs _ -> Fresh name iface objs : modules
1226 RCached main _ mvar -> Cached name main mvar : modules
1227 RNotFound _ -> error (badInside name)
1228 RProblem _ -> error (badInside name)
1229 RBlocked -> error (badInside name)
1230 RForeign _ -> modules
1234badInside :: ModuleName.Raw -> [Char]
1236 "Error from `" ++ Name.toChars name ++ "` should have been reported already."
1239addOutside :: MainResult -> [Module] -> [Module]
1240addOutside main modules =
1242 RInside _ -> modules
1243 ROutsideOk name iface objs -> Fresh name iface objs : modules
1244 ROutsideErr _ -> modules
1245 ROutsideBlocked -> modules
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list