on master help sign in
Detect native files, build into resulting programs

The “native-modules” field in elm_dependencies.json is now read and used when generating JS. Whitelisted native code can be used now.

commit31ca14d950f2b3d237b05a9b06010161111acc71 authorEvan Czaplicki <info@elm-lang.org> dateSat 10 May 2014, 19:22 (12 years ago) parent985fec0
4 files changed+86−63
compiler/Build/Dependencies.hs+64−42 compiler/Build/Utils.hs+9 compiler/Compiler.hs+9−7 compiler/Elm/Internal/Dependencies.hs+4−14
compiler/Build/Dependencies.hs+64−42
@@ -1,55 +1,57 @@
11{-# OPTIONS_GHC -W #-}
2−module Build.Dependencies (getSortedDependencies, readDeps) where
2+module Build.Dependencies (Recipe(..), getBuildRecipe, readDeps) where
33
4−import Control.Applicative
54import Control.Monad.Error
65import qualified Control.Monad.State as State
7−import qualified Data.Aeson as Json
8−import qualified Data.ByteString.Lazy.Char8 as BSC
96import qualified Data.Graph as Graph
107import qualified Data.List as List
118import qualified Data.Map as Map
12−import qualified Data.Maybe as Maybe
139import qualified Data.Set as Set
1410import System.Directory
1511import System.FilePath as FP
1612
17−import Build.Print (failure)
18−
1913import qualified SourceSyntax.Module as Module
2014import qualified Parse.Parse as Parse
2115import qualified Elm.Internal.Paths as Path
2216import qualified Elm.Internal.Name as N
17+import qualified Elm.Internal.Version as V
2318import qualified Elm.Internal.Dependencies as Deps
2419
25−getSortedDependencies :: [FilePath] -> Module.Interfaces -> FilePath -> IO [String]
26−getSortedDependencies srcDirs builtIns root =
27− do extras <- extraDependencies
28− let allSrcDirs = srcDirs ++ Maybe.fromMaybe [] extras
29− result <- runErrorT $ readAllDeps allSrcDirs builtIns root
30− case result of
31− Right deps -> sortDeps deps
32− Left err -> failure err
33−
34−extraDependencies :: IO (Maybe [FilePath])
35−extraDependencies =
36− do exists <- doesFileExist Path.dependencyFile
37− if not exists then return Nothing else Just <$> getPaths
20+data Recipe = Recipe
21+ { _elmFiles :: [FilePath]
22+ , _jsFiles :: [FilePath]
23+ }
24+
25+getBuildRecipe :: [FilePath] -> Module.Interfaces -> FilePath -> ErrorT String IO Recipe
26+getBuildRecipe srcDirs builtIns root =
27+ do directories <- getDependencies
28+ jsFiles <- nativeFiles ("." : directories)
29+ let allSrcDirs = srcDirs ++ directories
30+ nodes <- collectDependencies allSrcDirs builtIns root
31+ elmFiles <- sortElmFiles nodes
32+ return (Recipe elmFiles jsFiles)
33+
34+-- | Based on the projects elm_dependencies.json, find all of the paths and
35+-- dependency information we might need.
36+getDependencies :: ErrorT String IO [FilePath]
37+getDependencies =
38+ do exists <- liftIO $ doesFileExist Path.dependencyFile
39+ if not exists then return [] else getPaths
3840 where
39− getPaths = do
40− raw <- BSC.readFile Path.dependencyFile
41− case Json.eitherDecode raw of
42− Right (Deps.Mini deps) -> mapM validate deps
43− Left err ->
44− failure $ "Error reading the " ++ Path.dependencyFile ++ " file:\n" ++ err
45−
46− validate (name,version) = do
47− let path = Path.dependencyDirectory </> toPath name version
48− exists <- doesDirectoryExist path
49− if exists then return path else failure (notFound name version)
50−
51− toPath name version = N.toFilePath name </> show version
52−
41+ getPaths :: ErrorT String IO [FilePath]
42+ getPaths =
43+ Deps.withDeps Path.dependencyFile $ \deps ->
44+ mapM getPath (Deps.dependencies deps)
45+
46+ getPath :: (N.Name, V.Version) -> ErrorT String IO FilePath
47+ getPath (name,version) = do
48+ let path = Path.dependencyDirectory </> N.toFilePath name </> show version
49+ exists <- liftIO $ doesDirectoryExist path
50+ if exists
51+ then return path
52+ else throwError (notFound name version)
53+
54+ notFound :: N.Name -> V.Version -> String
5355 notFound name version =
5456 unlines
5557 [ "Your " ++ Path.dependencyFile ++ " file says you depend on library"
@@ -58,27 +60,47 @@ extraDependencies =
5860 , ""
5961 , " elm-get install " ++ show name ++ " " ++ show version ]
6062
61−type Deps = (FilePath, String, [String])
63+nativeFiles :: [FilePath] -> ErrorT String IO [FilePath]
64+nativeFiles directories =
65+ concat `fmap` mapM getNativeFiles directories
66+ where
67+ getNativeFiles dir =
68+ Deps.withDeps (dir </> Path.dependencyFile) $ \deps ->
69+ return (map (toPath dir) (Deps.native deps))
70+
71+ toPath dir moduleName =
72+ dir </> joinPath (split moduleName) <.> "js"
73+
74+split :: String -> [String]
75+split moduleName = go [] moduleName
76+ where
77+ go paths str =
78+ case break (=='.') str of
79+ (path, _:rest) -> go (paths ++ [path]) rest
80+ (path, []) -> paths ++ [path]
81+
82+type DependencyNode = (FilePath, String, [String])
6283
63−sortDeps :: [Deps] -> IO [String]
64−sortDeps depends =
84+sortElmFiles :: [DependencyNode] -> ErrorT String IO [FilePath]
85+sortElmFiles depends =
6586 if null mistakes
6687 then return (concat sccs)
67− else failure $ msg ++ unlines (map show mistakes)
88+ else throwError $ msg ++ unlines (map show mistakes)
6889 where
6990 sccs = map Graph.flattenSCC $ Graph.stronglyConnComp depends
7091
7192 mistakes = filter (\scc -> length scc > 1) sccs
7293 msg = "A cyclical module dependency or was detected in:\n"
7394
74−readAllDeps :: [FilePath] -> Module.Interfaces -> FilePath -> ErrorT String IO [Deps]
75−readAllDeps srcDirs rawBuiltIns filePath =
95+collectDependencies :: [FilePath] -> Module.Interfaces -> FilePath
96+ -> ErrorT String IO [DependencyNode]
97+collectDependencies srcDirs rawBuiltIns filePath =
7698 State.evalStateT (go Nothing filePath) Set.empty
7799 where
78100 builtIns :: Set.Set String
79101 builtIns = Set.fromList $ Map.keys rawBuiltIns
80102
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]
82104 go parentModuleName filePath = do
83105 filePath' <- lift $ findSrcFile parentModuleName srcDirs filePath
84106 (moduleName, deps) <- lift $ readDeps filePath'
@@ -93,9 +115,9 @@ readDeps :: FilePath -> ErrorT String IO (String, [String])
93115readDeps path = do
94116 txt <- lift $ readFile path
95117 case Parse.dependencies txt of
118+ Right o -> return o
96119 Left err -> throwError $ msg ++ show err
97120 where msg = "Error resolving dependencies in " ++ path ++ ":\n"
98− Right o -> return o
99121
100122findSrcFile :: Maybe String -> [FilePath] -> FilePath -> ErrorT String IO FilePath
101123findSrcFile parentModuleName dirs path =
compiler/Build/Utils.hs+9
@@ -1,10 +1,12 @@
11{-# OPTIONS_GHC -W #-}
22module Build.Utils where
33
4+import Control.Monad.Error
45import System.Directory (doesFileExist)
56import System.Environment (getEnv)
67import System.FilePath ((</>), replaceExtension)
78import qualified Build.Flags as Flag
9+import qualified Build.Print as Print
810import qualified Paths_Elm as This
911
1012buildPath :: Flag.Flags -> FilePath -> String -> FilePath
@@ -33,3 +35,10 @@ getDataFile name = do
3335 else do
3436 env <- getEnv "ELM_HOME"
3537 return (env </> name)
38+
39+run :: ErrorT String IO a -> IO a
40+run command = do
41+ either <- runErrorT command
42+ case either of
43+ Left err -> Print.failure err
44+ Right result -> return result
compiler/Compiler.hs+9−7
@@ -10,7 +10,7 @@ import System.Directory
1010import System.FilePath
1111import GHC.Conc
1212
13−import Build.Dependencies (getSortedDependencies)
13+import Build.Dependencies (getBuildRecipe, Recipe(..))
1414import qualified Generate.Html as Html
1515import qualified Metadata.Prelude as Prelude
1616import qualified Build.Utils as Utils
@@ -33,13 +33,15 @@ build flags rootFile =
3333 do let noPrelude = Flag.no_prelude flags
3434 builtIns <- Prelude.interfaces noPrelude
3535
36− files <- if Flag.make flags
37− then getSortedDependencies (Flag.src_dir flags) builtIns rootFile
38− else return [rootFile]
36+ (Recipe elmFiles jsFiles) <-
37+ if Flag.make flags
38+ then Utils.run (getBuildRecipe (Flag.src_dir flags) builtIns rootFile)
39+ else return (Recipe [rootFile] [])
3940
40− moduleName <- File.build flags builtIns files
41+ moduleName <- File.build flags builtIns elmFiles
4142
42− js <- foldM appendToOutput BS.empty files
43+ let elmos = map (Utils.elmo flags) elmFiles
44+ js <- foldM appendToOutput BS.empty (elmos ++ jsFiles)
4345
4446 (extension, code) <-
4547 if Flag.only_js flags
@@ -56,7 +58,7 @@ build flags rootFile =
5658 where
5759 appendToOutput :: BS.ByteString -> FilePath -> IO BS.ByteString
5860 appendToOutput js filePath = do
59− src <- BS.readFile (Utils.elmo flags filePath)
61+ src <- BS.readFile filePath
6062 return (BS.append src js)
6163
6264 sources js = map Html.Link (Flag.scripts flags) ++ [ Html.Source js ]
compiler/Elm/Internal/Dependencies.hs+4−14
@@ -31,16 +31,6 @@ data Deps = Deps
3131 , dependencies :: [(N.Name,V.Version)]
3232 } deriving (Show, Eq, Ord)
3333
34−data MiniDeps = Mini [(N.Name,V.Version)]
35− deriving (Show, Eq, Ord)
36−
37−instance ToJSON MiniDeps where
38− toJSON (Mini m) = toJSON m
39−
40−instance FromJSON MiniDeps where
41− parseJSON (Object obj) = Mini <$> getDependencies obj
42− parseJSON _ = mzero
43−
4434instance ToJSON Deps where
4535 toJSON d =
4636 object $
@@ -128,12 +118,12 @@ repoToName repo
128118 \like <https://github.com/USER/PROJECT.git> where USER is your GitHub name\n\
129119 \and PROJECT is the repo you want to upload."
130120
131−withDeps :: (Deps -> a) -> FilePath -> ErrorT String IO a
132−withDeps handle path =
121+withDeps :: FilePath -> (Deps -> ErrorT String IO a) -> ErrorT String IO a
122+withDeps path handle =
133123 do json <- readPath
134124 case eitherDecode json of
135125 Left err -> throwError $ "Error reading file " ++ path ++ ":\n " ++ err
136− Right ds -> return (handle ds)
126+ Right ds -> handle ds
137127 where
138128 readPath :: ErrorT String IO BS.ByteString
139129 readPath = do
@@ -147,7 +137,7 @@ withDeps handle path =
147137 " <https://github.com/evancz/automaton/blob/master/elm_dependencies.json>"
148138
149139depsAt :: FilePath -> ErrorT String IO Deps
150−depsAt = withDeps id
140+depsAt filePath = withDeps filePath return
151141
152142-- | Encode dependencies in a canonical JSON format
153143prettyJSON :: Deps -> BS.ByteString