From 41d219a29b03f3114af7a0521c8b2dbbb487c3e1 Mon Sep 17 00:00:00 2001 From: Philip McGrath <philip@philipmcgrath.com> Date: Wed, 13 Apr 2022 18:45:58 -0400 Subject: [PATCH] reactor: look for static files relative to executable Must built with `-DGUIX_REACTOR_STATIC_REL_ROOT="../path/to/reactor"`. This lets us build a version of Elm without the `elm reactor` for bootstrapping, then simply put the files in place in the final package. --- elm.cabal | 2 +- terminal/src/Develop.hs | 32 +++++++++++---- terminal/src/Develop/StaticFiles.hs | 37 ++++++++++------- terminal/src/Develop/StaticFiles/Build.hs | 50 ++++++++++++++--------- 4 files changed, 79 insertions(+), 42 deletions(-) diff --git a/elm.cabal b/elm.cabal index bf1cfcf0..93161072 100644 --- a/elm.cabal +++ b/elm.cabal @@ -50,6 +50,7 @@ Executable elm other-extensions: TemplateHaskell + CPP Main-Is: Main.hs @@ -211,7 +212,6 @@ Executable elm containers >= 0.5.8.2 && < 0.6, directory >= 1.2.3.0 && < 2.0, edit-distance >= 0.2 && < 0.3, - file-embed, filelock, filepath >= 1 && < 2.0, ghc-prim >= 0.5.2, diff --git a/terminal/src/Develop.hs b/terminal/src/Develop.hs index 00339364..6855b03e 100644 --- a/terminal/src/Develop.hs +++ b/terminal/src/Develop.hs @@ -33,6 +33,7 @@ import qualified Reporting.Exit as Exit import qualified Reporting.Task as Task import qualified Stuff +import System.Exit as SysExit -- RUN THE DEV SERVER @@ -45,13 +46,29 @@ data Flags = run :: () -> Flags -> IO () -run () (Flags maybePort) = +run () flags = do + frontEnd <- StaticFiles.prepare + case frontEnd of + Right lookup -> + reallyRun lookup flags + Left missing -> + SysExit.die $ unlines + [ "The `reactor` command is not available." + , "" + , "On Guix, these files are needed for `elm reactor` to work," + , "but they are missing:" + , "" + , unlines (map (\pth -> " " ++ (show pth)) missing) + ] + +reallyRun :: StaticFiles.Lookup -> Flags -> IO () +reallyRun lookup (Flags maybePort) = do let port = maybe 8000 id maybePort putStrLn $ "Go to http://localhost:" ++ show port ++ " to see your project dashboard." httpServe (config port) $ serveFiles <|> serveDirectoryWith directoryConfig "." - <|> serveAssets + <|> serveAssets lookup <|> error404 @@ -169,16 +186,15 @@ compile path = -- SERVE STATIC ASSETS -serveAssets :: Snap () -serveAssets = +serveAssets :: StaticFiles.Lookup -> Snap () +serveAssets lookup = do path <- getSafePath - case StaticFiles.lookup path of + case lookup path of Nothing -> pass - Just (content, mimeType) -> - do modifyResponse (setContentType (mimeType <> ";charset=utf-8")) - writeBS content + Just (fsPath, mimeType) -> + serveFileAs (mimeType <> ";charset=utf-8") fsPath diff --git a/terminal/src/Develop/StaticFiles.hs b/terminal/src/Develop/StaticFiles.hs index 94ee72dc..3227d617 100644 --- a/terminal/src/Develop/StaticFiles.hs +++ b/terminal/src/Develop/StaticFiles.hs @@ -2,7 +2,8 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} module Develop.StaticFiles - ( lookup + ( prepare + , Lookup , cssPath , elmPath , waitingPath @@ -11,9 +12,7 @@ module Develop.StaticFiles import Prelude hiding (lookup) import qualified Data.ByteString as BS -import Data.FileEmbed (bsToExp) import qualified Data.HashMap.Strict as HM -import Language.Haskell.TH (runIO) import System.FilePath ((</>)) import qualified Develop.StaticFiles.Build as Build @@ -26,20 +25,29 @@ import qualified Develop.StaticFiles.Build as Build type MimeType = BS.ByteString +type Lookup = FilePath -> Maybe (FilePath, MimeType) -lookup :: FilePath -> Maybe (BS.ByteString, MimeType) -lookup path = +prepare :: IO (Either [FilePath] Lookup) +prepare = do + found <- Build.findReactorFrontEnd expectedFiles + return $ case found of + Left missing -> + Left missing + Right resolved -> + Right (mkLookup (HM.fromList resolved)) + +mkLookup :: HM.HashMap FilePath (FilePath, MimeType) -> Lookup +mkLookup dict path = HM.lookup path dict -dict :: HM.HashMap FilePath (BS.ByteString, MimeType) -dict = - HM.fromList - [ faviconPath ==> (favicon , "image/x-icon") - , elmPath ==> (elm , "application/javascript") - , cssPath ==> (css , "text/css") - , codeFontPath ==> (codeFont, "font/ttf") - , sansFontPath ==> (sansFont, "font/ttf") +expectedFiles :: [(FilePath, MimeType)] +expectedFiles = + [ faviconPath ==> "image/x-icon" + , elmPath ==> "application/javascript" + , cssPath ==> "text/css" + , codeFontPath ==> "font/ttf" + , sansFontPath ==> "font/ttf" ] @@ -82,7 +90,7 @@ sansFontPath = "_elm" </> "source-sans-pro.ttf" - +{- -- ELM @@ -121,3 +129,4 @@ sansFont = favicon :: BS.ByteString favicon = $(bsToExp =<< runIO (Build.readAsset "favicon.ico")) +-} diff --git a/terminal/src/Develop/StaticFiles/Build.hs b/terminal/src/Develop/StaticFiles/Build.hs index c61fae57..c39b08b0 100644 --- a/terminal/src/Develop/StaticFiles/Build.hs +++ b/terminal/src/Develop/StaticFiles/Build.hs @@ -1,28 +1,39 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE CPP #-} module Develop.StaticFiles.Build - ( readAsset - , buildReactorFrontEnd + ( findReactorFrontEnd ) where - -import qualified Data.ByteString as BS -import qualified Data.ByteString.Builder as B -import qualified Data.ByteString.Lazy as LBS -import qualified Data.NonEmptyList as NE import qualified System.Directory as Dir -import System.FilePath ((</>)) - -import qualified BackgroundWriter as BW -import qualified Build -import qualified Elm.Details as Details -import qualified Generate -import qualified Reporting -import qualified Reporting.Exit as Exit -import qualified Reporting.Task as Task - - - +import System.FilePath ((</>), takeDirectory) +import System.Environment (getExecutablePath) +import Data.Either as Either + +reactorStaticRelRoot :: FilePath +reactorStaticRelRoot = GUIX_REACTOR_STATIC_REL_ROOT + +type Resolved a = (FilePath, (FilePath, a)) + +findReactorFrontEnd :: [(FilePath, a)] -> IO (Either [FilePath] [Resolved a]) +findReactorFrontEnd specs = do + exe <- getExecutablePath + let dir = takeDirectory exe </> reactorStaticRelRoot + dirExists <- Dir.doesDirectoryExist dir + files <- sequence (map (findFile dir) specs) + return $ case Either.lefts files of + [] -> + Right (Either.rights files) + missing -> + Left $ if dirExists then missing else [dir] + +findFile :: FilePath -> (FilePath, a) -> IO (Either FilePath (Resolved a)) +findFile dir (rel, rhs) = do + let abs = dir </> rel + exists <- Dir.doesFileExist abs + return $ if not exists then Left abs else Right (rel, (abs, rhs)) + +{- -- ASSETS @@ -71,3 +82,4 @@ runTaskUnsafe task = \\nCompile with `elm make` directly to figure it out faster\ \\n--------------------------------------------------------\ \\n" +-} -- 2.32.0