@@ -1,55 +1,57 @@
1{-# OPTIONS_GHC -W #-}
1{-# OPTIONS_GHC -W #-}
2−module Build.Dependencies (getSortedDependencies, readDeps) where
2+module Build.Dependencies (Recipe(..), getBuildRecipe, readDeps) where
4−import Control.Applicative
5import Control.Monad.Error
4import Control.Monad.Error
6import qualified Control.Monad.State as State
5import qualified Control.Monad.State as State
7−import qualified Data.Aeson as Json
8−import qualified Data.ByteString.Lazy.Char8 as BSC
9import qualified Data.Graph as Graph
6import qualified Data.Graph as Graph
10import qualified Data.List as List
7import qualified Data.List as List
11import qualified Data.Map as Map
8import qualified Data.Map as Map
12−import qualified Data.Maybe as Maybe
13import qualified Data.Set as Set
9import qualified Data.Set as Set
14import System.Directory
10import System.Directory
15import System.FilePath as FP
11import System.FilePath as FP
17−import Build.Print (failure)
19import qualified SourceSyntax.Module as Module
13import qualified SourceSyntax.Module as Module
20import qualified Parse.Parse as Parse
14import qualified Parse.Parse as Parse
21import qualified Elm.Internal.Paths as Path
15import qualified Elm.Internal.Paths as Path
22import qualified Elm.Internal.Name as N
16import qualified Elm.Internal.Name as N
17+import qualified Elm.Internal.Version as V
23import qualified Elm.Internal.Dependencies as Deps
18import qualified Elm.Internal.Dependencies as Deps
21+ { _elmFiles :: [FilePath]
22+ , _jsFiles :: [FilePath]
25−getSortedDependencies :: [FilePath] -> Module.Interfaces -> FilePath -> IO [String]
25+getBuildRecipe :: [FilePath] -> Module.Interfaces -> FilePath -> ErrorT String IO Recipe
26−getSortedDependencies srcDirs builtIns root =
26+getBuildRecipe srcDirs builtIns root =
27− do extras <- extraDependencies
27+ do directories <- getDependencies
28+ jsFiles <- nativeFiles ("." : directories)
28− let allSrcDirs = srcDirs ++ Maybe.fromMaybe [] extras
29+ let allSrcDirs = srcDirs ++ directories
29− result <- runErrorT $ readAllDeps allSrcDirs builtIns root
30+ nodes <- collectDependencies allSrcDirs builtIns root
30− case result of
31+ elmFiles <- sortElmFiles nodes
31− Right deps -> sortDeps deps
32+ return (Recipe elmFiles jsFiles)
32− Left err -> failure err
33+
33−
34+-- | Based on the projects elm_dependencies.json, find all of the paths and
34−extraDependencies :: IO (Maybe [FilePath])
35+-- dependency information we might need.
35−extraDependencies =
36+getDependencies :: ErrorT String IO [FilePath]
36− do exists <- doesFileExist Path.dependencyFile
38+ do exists <- liftIO $ doesFileExist Path.dependencyFile
37− if not exists then return Nothing else Just <$> getPaths
39+ if not exists then return [] else getPaths
39− getPaths = do
41+ getPaths :: ErrorT String IO [FilePath]
40− raw <- BSC.readFile Path.dependencyFile
42+ getPaths =
41− case Json.eitherDecode raw of
43+ Deps.withDeps Path.dependencyFile $ \deps ->
42− Right (Deps.Mini deps) -> mapM validate deps
44+ mapM getPath (Deps.dependencies deps)
44− failure $ "Error reading the " ++ Path.dependencyFile ++ " file:\n" ++ err
46+ getPath :: (N.Name, V.Version) -> ErrorT String IO FilePath
46− validate (name,version) = do
47+ getPath (name,version) = do
47− let path = Path.dependencyDirectory </> toPath name version
48+ let path = Path.dependencyDirectory </> N.toFilePath name </> show version
48− exists <- doesDirectoryExist path
49+ exists <- liftIO $ doesDirectoryExist path
49− if exists then return path else failure (notFound name version)
52+ else throwError (notFound name version)
51− toPath name version = N.toFilePath name </> show version
54+ notFound :: N.Name -> V.Version -> String
53 notFound name version =
55 notFound name version =
55 [ "Your " ++ Path.dependencyFile ++ " file says you depend on library"
57 [ "Your " ++ Path.dependencyFile ++ " file says you depend on library"
@@ -58,27 +60,47 @@ extraDependencies =
59 , " elm-get install " ++ show name ++ " " ++ show version ]
61 , " elm-get install " ++ show name ++ " " ++ show version ]
63+nativeFiles :: [FilePath] -> ErrorT String IO [FilePath]
64+nativeFiles directories =
65+ concat `fmap` mapM getNativeFiles directories
68+ Deps.withDeps (dir </> Path.dependencyFile) $ \deps ->
69+ return (map (toPath dir) (Deps.native deps))
71+ toPath dir moduleName =
72+ dir </> joinPath (split moduleName) <.> "js"
74+split :: String -> [String]
75+split moduleName = go [] moduleName
78+ case break (=='.') str of
79+ (path, _:rest) -> go (paths ++ [path]) rest
80+ (path, []) -> paths ++ [path]
61−type Deps = (FilePath, String, [String])
82+type DependencyNode = (FilePath, String, [String])
63−sortDeps :: [Deps] -> IO [String]
84+sortElmFiles :: [DependencyNode] -> ErrorT String IO [FilePath]
64−sortDeps depends =
85+sortElmFiles depends =
65 if null mistakes
86 if null mistakes
66 then return (concat sccs)
87 then return (concat sccs)
67− else failure $ msg ++ unlines (map show mistakes)
88+ else throwError $ msg ++ unlines (map show mistakes)
69 sccs = map Graph.flattenSCC $ Graph.stronglyConnComp depends
90 sccs = map Graph.flattenSCC $ Graph.stronglyConnComp depends
71 mistakes = filter (\scc -> length scc > 1) sccs
92 mistakes = filter (\scc -> length scc > 1) sccs
72 msg = "A cyclical module dependency or was detected in:\n"
93 msg = "A cyclical module dependency or was detected in:\n"
74−readAllDeps :: [FilePath] -> Module.Interfaces -> FilePath -> ErrorT String IO [Deps]
95+collectDependencies :: [FilePath] -> Module.Interfaces -> FilePath
96+ -> ErrorT String IO [DependencyNode]
75−readAllDeps srcDirs rawBuiltIns filePath =
97+collectDependencies srcDirs rawBuiltIns filePath =
76 State.evalStateT (go Nothing filePath) Set.empty
98 State.evalStateT (go Nothing filePath) Set.empty
78 builtIns :: Set.Set String
100 builtIns :: Set.Set String
79 builtIns = Set.fromList $ Map.keys rawBuiltIns
101 builtIns = Set.fromList $ Map.keys rawBuiltIns
81− go :: Maybe String -> FilePath -> State.StateT (Set.Set String) (ErrorT String IO) [Deps]
103+ go :: Maybe String -> FilePath -> State.StateT (Set.Set String) (ErrorT String IO) [DependencyNode]
82 go parentModuleName filePath = do
104 go parentModuleName filePath = do
83 filePath' <- lift $ findSrcFile parentModuleName srcDirs filePath
105 filePath' <- lift $ findSrcFile parentModuleName srcDirs filePath
84 (moduleName, deps) <- lift $ readDeps filePath'
106 (moduleName, deps) <- lift $ readDeps filePath'
@@ -93,9 +115,9 @@ readDeps :: FilePath -> ErrorT String IO (String, [String])
93readDeps path = do
115readDeps path = do
94 txt <- lift $ readFile path
116 txt <- lift $ readFile path
95 case Parse.dependencies txt of
117 case Parse.dependencies txt of
96 Left err -> throwError $ msg ++ show err
119 Left err -> throwError $ msg ++ show err
97 where msg = "Error resolving dependencies in " ++ path ++ ":\n"
120 where msg = "Error resolving dependencies in " ++ path ++ ":\n"
100findSrcFile :: Maybe String -> [FilePath] -> FilePath -> ErrorT String IO FilePath
122findSrcFile :: Maybe String -> [FilePath] -> FilePath -> ErrorT String IO FilePath
101findSrcFile parentModuleName dirs path =
123findSrcFile parentModuleName dirs path =