Skip to content

Instantly share code, notes, and snippets.

@mgsloan
Created June 29, 2015 03:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mgsloan/d944011a659e31616ab3 to your computer and use it in GitHub Desktop.
Save mgsloan/d944011a659e31616ab3 to your computer and use it in GitHub Desktop.
diff --git a/yesod-bin/BuildProgram.hs b/yesod-bin/BuildProgram.hs
new file mode 100644
index 0000000..2bdf11b
--- /dev/null
+++ b/yesod-bin/BuildProgram.hs
@@ -0,0 +1,50 @@
+module BuildProgram where
+
+import System.Exit (ExitCode (..), exitWith)
+import System.Process (ProcessHandle, createProcess, getProcessExitCode, proc)
+import Control.Concurrent (threadDelay)
+
+data BuildProgram
+ = CabalLike String -- ^ String indicates which cabal-like program to use (cabal or cabal-dev)
+ | Stack FilePath -- ^ The FilePath indicates which directory has 'stack.yaml'
+ deriving (Show, Eq)
+
+buildProgramName :: BuildProgram -> String
+buildProgramName (CabalLike program) = program
+buildProgramName (Stack _) = "stack"
+
+runConfigure :: BuildProgram -> IO ()
+runConfigure (Stack fp) = putStrLn "No need to configure with stack."
+runConfigure (CabalLike program) = exitOnFailure =<< runProcess' program ["configure"]
+
+runTest :: BuildProgram -> IO ()
+runTest (Stack fp) = fail "FIXME"
+runTest (CabalLike program) = do
+ exitOnFailure =<< runProcess' program ["configure", "--enable-tests", "-flibrary-only"]
+ exitOnFailure =<< runProcess' program ["build"]
+ exitOnFailure =<< runProcess' program ["test"]
+
+runBuild :: BuildProgram -> Bool -> [String] -> IO ExitCode
+runBuild (Stack fp) silent args =
+ runProcess' "stack" ((if silent then ["--verbosity", "silent", "build"] else ["build"]) ++ args)
+runBuild (CabalLike program) silent args =
+ runProcess' program ((if silent then ["build", "-v0"] else ["build"]) ++ args)
+
+runProcess' :: FilePath -> [String] -> IO ExitCode
+runProcess' program args = do
+ (_,_,_,ph) <- createProcess (proc program args)
+ waitForProcess' ph
+
+exitOnFailure :: ExitCode -> IO ()
+exitOnFailure ExitSuccess = return ()
+exitOnFailure code = exitWith code
+
+-- | nonblocking version of @waitForProcess@
+waitForProcess' :: ProcessHandle -> IO ExitCode
+waitForProcess' pid = go
+ where
+ go = do
+ mec <- getProcessExitCode pid
+ case mec of
+ Just ec -> return ec
+ Nothing -> threadDelay 100000 >> go
diff --git a/yesod-bin/Devel.hs b/yesod-bin/Devel.hs
index 0552e0c..2a4f5f0 100644
--- a/yesod-bin/Devel.hs
+++ b/yesod-bin/Devel.hs
@@ -53,7 +53,6 @@ import System.PosixCompat.Files (getFileStatus,
modificationTime)
import System.Process (ProcessHandle,
createProcess, env,
- getProcessExitCode,
proc, readProcess,
system,
terminateProcess)
@@ -63,6 +62,9 @@ import Build (getDeps, isNewerThan,
recompDeps)
import GhcBuild (buildPackage,
getBuildFlags, getPackageArgs)
+import BuildProgram (BuildProgram(..), buildProgramName,
+ runBuild, waitForProcess',
+ runProcess')
import qualified Config as GHC
import Data.Streaming.Network (bindPortTCP)
@@ -103,7 +105,7 @@ data DevelTermOpt = TerminateOnEnter | TerminateOnlyInterrupt
deriving (Show, Eq)
data DevelOpts = DevelOpts
- { isCabalDev :: Bool
+ { buildProgram :: BuildProgram
, forceCabal :: Bool
, verbose :: Bool
, eventTimeout :: Int -- negative value for no timeout
@@ -122,7 +124,7 @@ getBuildDir opts = fromMaybe "dist" (buildDir opts)
defaultDevelOpts :: DevelOpts
defaultDevelOpts = DevelOpts
- { isCabalDev = False
+ { buildProgram = CabalLike "cabal"
, forceCabal = False
, verbose = False
, eventTimeout = -1
@@ -136,11 +138,6 @@ defaultDevelOpts = DevelOpts
, terminateWith = TerminateOnEnter
}
-cabalProgram :: DevelOpts -> FilePath
-cabalProgram opts
- | isCabalDev opts = "cabal-dev"
- | otherwise = "cabal"
-
-- | Run a reverse proxy from port 3000 to 3001. If there is no response on
-- 3001, give an appropriate message to the user.
reverseProxy :: DevelOpts -> I.IORef Int -> IO ()
@@ -263,8 +260,10 @@ devel opts passThroughArgs = withSocketsDo $ withManager $ \manager -> do
ldar <- liftIO lookupLdAr
(hsSourceDirs, _) <- liftIO $ checkCabalFile gpd
liftIO $ removeFileIfExists (bd </> "setup-config")
- c <- liftIO $ configure opts passThroughArgs
- if c then do
+ ec <- case buildProgram opts of
+ Stack _ -> return ExitSuccess
+ CabalLike program -> liftIO $ cabalConfigure program passThroughArgs
+ if ec == ExitSuccess then do
-- these files contain the wrong data after the configure step,
-- remove them to force a cabal build first
liftIO $ mapM_ removeFileIfExists [ "yesod-devel/ghcargs.txt"
@@ -340,21 +339,20 @@ runBuildHook Nothing = return ()
{-
run `cabal configure' with our wrappers
-}
-configure :: DevelOpts -> [String] -> IO Bool
-configure opts extraArgs =
- checkExit =<< createProcess (proc (cabalProgram opts) $
- [ "configure"
- , "-flibrary-only"
- , "--disable-tests"
- , "--disable-benchmarks"
- , "-fdevel"
- , "--disable-library-profiling"
- , "--with-ld=yesod-ld-wrapper"
- , "--with-ghc=yesod-ghc-wrapper"
- , "--with-ar=yesod-ar-wrapper"
- , "--with-hc-pkg=ghc-pkg"
- ] ++ extraArgs
- )
+cabalConfigure :: FilePath -> [String] -> IO ExitCode
+cabalConfigure program extraArgs =
+ runProcess' program $
+ [ "configure"
+ , "-flibrary-only"
+ , "--disable-tests"
+ , "--disable-benchmarks"
+ , "-fdevel"
+ , "--disable-library-profiling"
+ , "--with-ld=yesod-ld-wrapper"
+ , "--with-ghc=yesod-ghc-wrapper"
+ , "--with-ar=yesod-ar-wrapper"
+ , "--with-hc-pkg=ghc-pkg"
+ ] ++ extraArgs
removeFileIfExists :: FilePath -> IO ()
removeFileIfExists file = removeFile file `Ex.catch` handler
@@ -386,11 +384,8 @@ rebuildGhc bf ld ar = do
rebuildCabal :: DevelOpts -> IO Bool
rebuildCabal opts = do
- putStrLn $ "Rebuilding application... (using " ++ cabalProgram opts ++ ")"
- checkExit =<< createProcess (proc (cabalProgram opts) args)
- where
- args | verbose opts = [ "build" ]
- | otherwise = [ "build", "-v0" ]
+ putStrLn $ "Rebuilding application... (using " ++ buildProgramName (buildProgram opts) ++ ")"
+ (==ExitSuccess) <$> runBuild (buildProgram opts) (not (verbose opts)) []
try_ :: forall a. IO a -> IO ()
try_ x = void (Ex.try x :: IO (Either Ex.SomeException a))
@@ -519,17 +514,3 @@ lookupLdAr' = do
return $ (,) <$> look D.ldProgram pgmc' <*> look D.arProgram pgmc'
where
look pgm pdb = fmap D.programPath (D.lookupProgram pgm pdb)
-
--- | nonblocking version of @waitForProcess@
-waitForProcess' :: ProcessHandle -> IO ExitCode
-waitForProcess' pid = go
- where
- go = do
- mec <- getProcessExitCode pid
- case mec of
- Just ec -> return ec
- Nothing -> threadDelay 100000 >> go
-
--- | wait for process started by @createProcess@, return True for ExitSuccess
-checkExit :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO Bool
-checkExit (_,_,_,h) = (==ExitSuccess) <$> waitForProcess' h
diff --git a/yesod-bin/main.hs b/yesod-bin/main.hs
index 6cd65ec..1ac2bfd 100755
--- a/yesod-bin/main.hs
+++ b/yesod-bin/main.hs
@@ -9,6 +9,7 @@ import System.Exit (ExitCode (ExitSuccess), exitWith)
import System.Process (rawSystem)
import AddHandler (addHandler)
+import BuildProgram (BuildProgram(..), runConfigure, runBuild, runTest, exitOnFailure)
import Devel (DevelOpts (..), devel, DevelTermOpt(..))
import Keter (keter)
import Options (injectDefaults)
@@ -32,15 +33,20 @@ windowsWarning :: String
windowsWarning = " (does not work on Windows)"
#endif
-data CabalPgm = Cabal | CabalDev deriving (Show, Eq)
-
data Options = Options
- { optCabalPgm :: CabalPgm
- , optVerbose :: Bool
- , optCommand :: Command
+ { optCabalDev :: Bool
+ , optStackPath :: Maybe FilePath
+ , optVerbose :: Bool
+ , optCommand :: Command
}
deriving (Show, Eq)
+optBuildProgram :: Options -> BuildProgram
+optBuildProgram opts
+ | Just path <- optStackPath opts = Stack path
+ | optCabalDev opts = CabalLike "cabal-dev"
+ | otherwise = CabalLike "cabal"
+
data Command = Init { _initBare :: Bool, _initName :: Maybe String, _initDatabase :: Maybe String }
| HsFiles
| Configure
@@ -72,12 +78,6 @@ data Command = Init { _initBare :: Bool, _initName :: Maybe String, _initDatabas
| Version
deriving (Show, Eq)
-cabalCommand :: Options -> String
-cabalCommand mopt
- | optCabalPgm mopt == CabalDev = "cabal-dev"
- | otherwise = "cabal"
-
-
main :: IO ()
main = do
o <- execParser =<< injectDefaults "yesod"
@@ -97,19 +97,19 @@ main = do
c -> c
})
] optParser'
- let cabal = rawSystem' (cabalCommand o)
case optCommand o of
Init{..} -> scaffold _initBare _initName _initDatabase
HsFiles -> mkHsFile
- Configure -> cabal ["configure"]
- Build es -> touch' >> cabal ("build":es)
+ Configure -> runConfigure (optBuildProgram o)
+ Build es -> do touch'
+ exitOnFailure =<< runBuild (optBuildProgram o) False es
Touch -> touch'
- Keter{..} -> keter (cabalCommand o) _keterNoRebuild _keterNoCopyTo
+ -- Keter{..} -> keter (cabalCommand o) _keterNoRebuild _keterNoCopyTo
Version -> putStrLn ("yesod-bin version: " ++ showVersion Paths_yesod_bin.version)
AddHandler{..} -> addHandler addHandlerRoute addHandlerPattern addHandlerMethods
- Test -> cabalTest cabal
+ Test -> touch' >> runTest (optBuildProgram o)
Devel{..} -> let develOpts = DevelOpts
- { isCabalDev = optCabalPgm o == CabalDev
+ { buildProgram = optBuildProgram o
, forceCabal = _develDisableApi
, verbose = optVerbose o
, eventTimeout = _develRescan
@@ -123,19 +123,15 @@ main = do
, terminateWith = if _interruptOnly then TerminateOnlyInterrupt else TerminateOnEnter
}
in devel develOpts develExtraArgs
- where
- cabalTest cabal = do touch'
- _ <- cabal ["configure", "--enable-tests", "-flibrary-only"]
- _ <- cabal ["build"]
- cabal ["test"]
optParser' :: ParserInfo Options
optParser' = info (helper <*> optParser) ( fullDesc <> header "Yesod Web Framework command line utility" )
optParser :: Parser Options
optParser = Options
- <$> flag Cabal CabalDev ( long "dev" <> short 'd' <> help "use cabal-dev" )
- <*> switch ( long "verbose" <> short 'v' <> help "More verbose output" )
+ <$> switch ( long "dev" <> short 'd' <> help "use cabal-dev" )
+ <*> optStr ( long "stack" <> short 's' <> help "use stack" )
+ <*> switch ( long "verbose" <> short 'v' <> help "More verbose output" )
<*> subparser ( command "init" (info initOptions
(progDesc "Scaffold a new site"))
<> command "hsfiles" (info (pure HsFiles)
@@ -205,7 +201,7 @@ develOptions = Devel <$> switch ( long "disable-api" <> short 'd'
extraCabalArgs :: Parser [String]
extraCabalArgs = many (strOption ( long "extra-cabal-arg" <> short 'e' <> metavar "ARG"
- <> help "pass extra argument ARG to cabal")
+ <> help "pass extra argument ARG to build program")
)
addHandlerOptions :: Parser Command
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment