@@ -1,71 +1,435 @@
1+{-# LANGUAGE BangPatterns, OverloadedStrings #-}
2 ( Flags(..)
3 ( Flags(..)
4 , ReportType(..)
5 , ReportType(..)
5 , reportType
7 , reportType
13+import Control.Concurrent (MVar, forkIO, newEmptyMVar, newMVar, putMVar, readMVar)
14+import Control.Monad (liftM2)
15+import qualified Data.ByteString.Builder as B
16+import qualified Data.Map as Map
17+import qualified Data.Maybe as Maybe
18+import qualified Data.NonEmptyList as NE
11import qualified System.FilePath as FP
19import qualified System.FilePath as FP
13−import qualified Elm.Project as Project
21+import qualified AST.Optimized as Opt
22+import qualified Build
23+import qualified Elm.Compiler.Type.Extract as Extract
24+import qualified Elm.Details as Details
25+import qualified Elm.Interface as I
26+import qualified Elm.ModuleName as ModuleName
27+import qualified Elm.Outline as Outline
28+import qualified Elm.Package as Pkg
14−import qualified Generate.Output as Output
29+import qualified File
30+import qualified Generate.Html as Html
31+import qualified Generate.JavaScript as Generate
32+import qualified Generate.Mode as Mode
33+import qualified Nitpick.Debug as Nitpick
34+import qualified Reporting
15import qualified Reporting.Exit as Exit
35import qualified Reporting.Exit as Exit
16−import qualified Reporting.Exit.Make as E
17import qualified Reporting.Task as Task
36import qualified Reporting.Task as Task
18−import qualified Reporting.Progress as Progress
37+import qualified Stuff
19−import qualified Reporting.Progress.Json as Json
20−import qualified Reporting.Progress.Terminal as Terminal
21import Terminal.Args (Parser(..))
38import Terminal.Args (Parser(..))
28data Flags =
45data Flags =
30 { _debug :: Bool
47 { _debug :: Bool
31 , _optimize :: Bool
48 , _optimize :: Bool
32− , _output :: Maybe Output.Output
49+ , _output :: Maybe Output
33 , _report :: Maybe ReportType
50 , _report :: Maybe ReportType
34− , _docs :: Maybe FilePath
68+type Task a = Task.Task Exit.Make a
38run :: [FilePath] -> Flags -> IO ()
71run :: [FilePath] -> Flags -> IO ()
39−run paths (Flags debug optimize output report _docs) =
72+run paths (Flags debug optimize maybeOutput report) =
40− do reporter <- toReporter report
73+ do style <- getStyle report
41− Task.run reporter $
74+ Reporting.attemptWithStyle style Exit.makeToReport $ Task.run $
42− do mode <- toMode debug optimize
43− summary <- Project.getRoot
75+ do root <- getRoot
44− Project.compile mode Output.Client output summary paths
76+ desiredMode <- getMode debug optimize
77+ details <- getDetails style root
80+ do exposed <- getExposed details
81+ _ <- buildExposed style root details exposed
85+ do artifacts <- buildPaths style root details (NE.List p ps)
88+ case getMains artifacts of
47−toMode :: Bool -> Bool -> Task.Task Output.Mode
92+ [name] ->
48−toMode debug optimize =
93+ do builder <- toBuilder root details desiredMode artifacts
49− case (debug, optimize) of
94+ generate style "index.html" (Html.sandwich name builder) (NE.List name [])
50− (True , True ) -> Task.throw $ Exit.Make E.CannotOptimizeAndDebug
95+
51− (False, True ) -> return Output.Prod
96+ name:names ->
52− (False, False) -> return Output.Dev
97+ do builder <- toBuilder root details desiredMode artifacts
53− (True , False) -> return Output.Debug
98+ generate style "elm.js" builder (NE.List name names)
104+ case getNoMains artifacts of
106+ do builder <- toBuilder root details desiredMode artifacts
107+ generate style target builder (getMainNames artifacts)
110+ Task.throw (Exit.MakeNonMainFilesIntoJavaScript name names)
112+ Just (Html target) ->
113+ case hasOneMain artifacts of
115+ do builder <- toBuilder root details desiredMode artifacts
116+ generate style target (Html.sandwich name builder) (NE.List name [])
119+ Task.throw Exit.MakeMultipleFilesIntoHtml
56−toReporter :: Maybe ReportType -> IO Progress.Reporter
126+getStyle :: Maybe ReportType -> IO Reporting.Style
57−toReporter report =
127+getStyle report =
58 case report of
128 case report of
59− Nothing -> Terminal.create
129+ Nothing -> Reporting.terminal
60− Just Json -> return Json.reporter
130+ Just Json -> return Reporting.json
133+getRoot :: Task FilePath
135+ do maybeRoot <- Task.io Stuff.findRoot
137+ Nothing -> Task.throw Exit.MakeNeedsOutline
138+ Just root -> return root
141+getMode :: Bool -> Bool -> Task DesiredMode
142+getMode debug optimize =
143+ case (debug, optimize) of
144+ (True , True ) -> Task.throw Exit.MakeCannotOptimizeAndDebug
145+ (True , False) -> return Debug
146+ (False, False) -> return Dev
147+ (False, True ) -> return Prod
150+getDetails :: Reporting.Style -> FilePath -> Task Details.Details
151+getDetails style root =
152+ Task.eio Exit.MakeBlockedByDetailsProblem (Details.load style root)
155+getExposed :: Details.Details -> Task (NE.List ModuleName.Raw)
156+getExposed (Details.Details _ validOutline _ _ _) =
157+ case validOutline of
158+ Details.ValidApp _ ->
159+ Task.throw Exit.MakeAppNeedsFileNames
161+ Details.ValidPkg (Outline.PkgOutline _ _ _ _ exposed _ _ _) _ ->
162+ case Outline.flattenExposed exposed of
163+ [] -> Task.throw Exit.MakePkgNeedsExposing
164+ m:ms -> return (NE.List m ms)
171+buildExposed :: Reporting.Style -> FilePath -> Details.Details -> NE.List ModuleName.Raw -> Task ()
172+buildExposed style root details exposed =
173+ Task.eio Exit.MakeCannotBuild $
174+ Build.fromExposed style root details exposed
177+buildPaths :: Reporting.Style -> FilePath -> Details.Details -> NE.List FilePath -> Task Build.Artifacts
178+buildPaths style root details paths =
179+ Task.eio Exit.MakeCannotBuild $
180+ Build.fromMains style root details paths
187+getMains :: Build.Artifacts -> [ModuleName.Raw]
188+getMains (Build.Artifacts _ _ mains modules) =
189+ Maybe.mapMaybe (getMain modules) (NE.toList mains)
192+getMain :: [Build.Module] -> Build.Main -> Maybe ModuleName.Raw
193+getMain modules main =
195+ Build.Inside name ->
196+ if any (isMain name) modules
200+ Build.Outside name _ (Opt.LocalGraph maybeMain _ _) ->
206+isMain :: ModuleName.Raw -> Build.Module -> Bool
207+isMain targetName modul =
209+ Build.Fresh name _ (Opt.LocalGraph maybeMain _ _) ->
210+ Maybe.isJust maybeMain && name == targetName
212+ Build.Cached name mainIsDefined _ ->
213+ mainIsDefined && name == targetName
220+hasOneMain :: Build.Artifacts -> Maybe ModuleName.Raw
221+hasOneMain (Build.Artifacts _ _ mains modules) =
223+ NE.List main [] -> getMain modules main
224+ NE.List _ (_:_) -> Nothing
231+getNoMains :: Build.Artifacts -> [ModuleName.Raw]
232+getNoMains (Build.Artifacts _ _ mains modules) =
233+ Maybe.mapMaybe (getNoMain modules) (NE.toList mains)
236+getNoMain :: [Build.Module] -> Build.Main -> Maybe ModuleName.Raw
237+getNoMain modules main =
239+ Build.Inside name ->
240+ if any (isMain name) modules
244+ Build.Outside name _ (Opt.LocalGraph maybeMain _ _) ->
247+ Nothing -> Just name
68− = Json
254+getMainNames :: Build.Artifacts -> NE.List ModuleName.Raw
255+getMainNames (Build.Artifacts _ _ mains _) =
256+ fmap getMainName mains
259+getMainName :: Build.Main -> ModuleName.Raw
262+ Build.Inside name -> name
263+ Build.Outside name _ _ -> name
270+generate :: Reporting.Style -> FilePath -> B.Builder -> NE.List ModuleName.Raw -> Task ()
271+generate style target builder names =
273+ do File.writeBuilder target builder
274+ Reporting.reportGenerate style names target
281+data DesiredMode = Debug | Dev | Prod
284+toBuilder :: FilePath -> Details.Details -> DesiredMode -> Build.Artifacts -> Task B.Builder
285+toBuilder root details desiredMode (Build.Artifacts pkg ifaces roots modules) =
288+ do loading <- loadObjects root details modules
289+ types <- loadTypes root ifaces modules
290+ objects <- finalizeObjects loading
291+ let mode = Mode.Dev (Just types)
292+ let graph = toGlobalGraph objects
293+ let mains = gatherMains pkg objects roots
294+ return $ Generate.generate mode graph mains
297+ do objects <- finalizeObjects =<< loadObjects root details modules
298+ let mode = Mode.Dev Nothing
299+ let graph = toGlobalGraph objects
300+ let mains = gatherMains pkg objects roots
301+ return $ Generate.generate mode graph mains
304+ do objects <- finalizeObjects =<< loadObjects root details modules
305+ checkForDebugUses objects
306+ let graph = toGlobalGraph objects
307+ let mode = Mode.Prod (Mode.shortenFieldNames graph)
308+ let mains = gatherMains pkg objects roots
309+ return $ Generate.generate mode graph mains
312+checkForDebugUses :: LoadedObjects -> Task ()
313+checkForDebugUses (LoadedObjects _ locals) =
314+ case Map.keys (Map.filter Nitpick.hasDebugUses locals) of
316+ m:ms -> Task.throw (Exit.MakeCannotOptimizeDebugValues m ms)
319+toGlobalGraph :: LoadedObjects -> Opt.GlobalGraph
320+toGlobalGraph (LoadedObjects globals locals) =
321+ foldr Opt.addLocalGraph globals locals
328+data LoadingObjects =
330+ { _foreign_mvar :: MVar (Maybe Opt.GlobalGraph)
331+ , _local_mvars :: Map.Map ModuleName.Raw (MVar (Maybe Opt.LocalGraph))
335+loadObjects :: FilePath -> Details.Details -> [Build.Module] -> Task LoadingObjects
336+loadObjects root details modules =
338+ do mvar <- Details.loadObjects root details
339+ mvars <- traverse (loadObject root) modules
340+ return $ LoadingObjects mvar (Map.fromList mvars)
343+loadObject :: FilePath -> Build.Module -> IO (ModuleName.Raw, MVar (Maybe Opt.LocalGraph))
344+loadObject root modul =
346+ Build.Fresh name _ graph ->
347+ do mvar <- newMVar (Just graph)
350+ Build.Cached name _ _ ->
351+ do mvar <- newEmptyMVar
352+ _ <- forkIO $ putMVar mvar =<< File.readBinary (Stuff.elmo root name)
358+ { _foreign :: Opt.GlobalGraph
359+ , _locals :: Map.Map ModuleName.Raw Opt.LocalGraph
363+finalizeObjects :: LoadingObjects -> Task LoadedObjects
364+finalizeObjects (LoadingObjects mvar mvars) =
366+ do result <- readMVar mvar
367+ results <- traverse readMVar mvars
368+ case liftM2 LoadedObjects result (sequence results) of
369+ Just loaded -> return (Right loaded)
370+ Nothing -> return (Left Exit.MakeCannotLoadArtifacts)
377+loadTypes :: FilePath -> Map.Map ModuleName.Canonical I.DependencyInterface -> [Build.Module] -> Task Extract.Types
378+loadTypes root ifaces modules =
380+ do mvars <- traverse (loadTypesHelp root) modules
381+ let !foreigns = Extract.mergeMany (Map.elems (Map.mapWithKey Extract.fromDependencyInterface ifaces))
382+ results <- traverse readMVar mvars
383+ case sequence results of
384+ Just ts -> return (Right (Extract.merge foreigns (Extract.mergeMany ts)))
385+ Nothing -> return (Left Exit.MakeCannotLoadArtifacts)
388+loadTypesHelp :: FilePath -> Build.Module -> IO (MVar (Maybe Extract.Types))
389+loadTypesHelp root modul =
391+ Build.Fresh name iface _ ->
392+ newMVar (Just (Extract.fromInterface name iface))
394+ Build.Cached name _ ciMVar ->
395+ do cachedInterface <- readMVar ciMVar
396+ case cachedInterface of
398+ do mvar <- newEmptyMVar
400+ do maybeIface <- File.readBinary (Stuff.elmo root name)
401+ putMVar mvar (Extract.fromInterface name <$> maybeIface)
404+ Build.Loaded iface ->
405+ newMVar (Just (Extract.fromInterface name iface))
415+gatherMains :: Pkg.Name -> LoadedObjects -> NE.List Build.Main -> Map.Map ModuleName.Canonical Opt.Main
416+gatherMains pkg (LoadedObjects _ locals) buildMains =
417+ Map.fromList $ Maybe.mapMaybe (lookupMain pkg locals) (NE.toList buildMains)
420+lookupMain :: Pkg.Name -> Map.Map ModuleName.Raw Opt.LocalGraph -> Build.Main -> Maybe (ModuleName.Canonical, Opt.Main)
421+lookupMain pkg locals buildMain =
423+ toPair name (Opt.LocalGraph maybeMain _ _) =
424+ (,) (ModuleName.Canonical pkg name) <$> maybeMain
427+ Build.Inside name -> toPair name =<< Map.lookup name locals
428+ Build.Outside name _ g -> toPair name g
71reportType :: Parser ReportType
435reportType :: Parser ReportType
@@ -79,16 +443,32 @@ reportType =
82−
446+output :: Parser Output
86−docsFile :: Parser FilePath
89− { _singular = "json file"
449+ { _singular = "output file"
90− , _plural = "json files"
450+ , _plural = "output files"
91− , _parser = \string -> if FP.takeExtension string == ".json" then Just string else Nothing
451+ , _parser = parseOutput
92 , _suggest = \_ -> return []
452 , _suggest = \_ -> return []
93− , _examples = \_ -> return ["docs.json","documentation.json"]
453+ , _examples = exampleOutput
457+parseOutput :: String -> Maybe Output
459+ if string == "/dev/null" || string == "NUL" || string == "$null" then
462+ else if FP.takeExtension string == ".html" && length string > 5 then
465+ else if FP.takeExtension string == ".js" && length string > 3 then
472+exampleOutput :: String -> IO [String]
474+ return [ "elm.js", "index.html", "/dev/null" ]