Specify devel port on command line

This commit is contained in:
Michael Snoyman 2012-12-11 22:29:55 +02:00
parent fdeaec8dd4
commit 3e51af2b34
4 changed files with 48 additions and 22 deletions

View File

@ -67,11 +67,14 @@ import GhcBuild (buildPackage,
getBuildFlags) getBuildFlags)
import qualified Config as GHC import qualified Config as GHC
import Data.Conduit.Network (HostPreference (HostIPv4),
bindPort)
import Network (withSocketsDo) import Network (withSocketsDo)
import Network.HTTP.Conduit (def, newManager) import Network.HTTP.Conduit (def, newManager)
import Network.HTTP.ReverseProxy (ProxyDest (ProxyDest), import Network.HTTP.ReverseProxy (ProxyDest (ProxyDest),
waiProxyTo) waiProxyTo)
import Network.HTTP.Types (status200) import Network.HTTP.Types (status200)
import Network.Socket (sClose)
import Network.Wai (responseLBS) import Network.Wai (responseLBS)
import Network.Wai.Handler.Warp (run) import Network.Wai.Handler.Warp (run)
import SrcLoc (Located) import SrcLoc (Located)
@ -99,27 +102,29 @@ data DevelOpts = DevelOpts
, successHook :: Maybe String , successHook :: Maybe String
, failHook :: Maybe String , failHook :: Maybe String
, buildDir :: Maybe String , buildDir :: Maybe String
, develPort :: Int
} deriving (Show, Eq) } deriving (Show, Eq)
getBuildDir :: DevelOpts -> String getBuildDir :: DevelOpts -> String
getBuildDir opts = fromMaybe "dist" (buildDir opts) getBuildDir opts = fromMaybe "dist" (buildDir opts)
defaultDevelOpts :: DevelOpts defaultDevelOpts :: DevelOpts
defaultDevelOpts = DevelOpts False False False (-1) Nothing Nothing Nothing defaultDevelOpts = DevelOpts False False False (-1) Nothing Nothing Nothing 3000
-- | Run a reverse proxy from port 3000 to 3001. If there is no response on -- | Run a reverse proxy from port 3000 to 3001. If there is no response on
-- 3001, give an appropriate message to the user. -- 3001, give an appropriate message to the user.
reverseProxy :: IO () reverseProxy :: DevelOpts -> Int -> IO ()
reverseProxy = withSocketsDo $ do reverseProxy opts appPort = do
manager <- newManager def manager <- newManager def
forever $ do let loop = forever $ do
run 3000 $ waiProxyTo run (develPort opts) $ waiProxyTo
(const $ return $ Right $ ProxyDest "127.0.0.1" 3001) (const $ return $ Right $ ProxyDest "127.0.0.1" appPort)
onExc onExc
manager manager
putStrLn "Reverse proxy stopped, but it shouldn't" putStrLn "Reverse proxy stopped, but it shouldn't"
threadDelay 1000000 threadDelay 1000000
putStrLn "Restarting reverse proxy" putStrLn "Restarting reverse proxy"
loop `Ex.onException` exitFailure
where where
onExc _ _ = return $ responseLBS onExc _ _ = return $ responseLBS
status200 status200
@ -128,9 +133,26 @@ reverseProxy = withSocketsDo $ do
] ]
"<h1>App not ready, please refresh</h1>" "<h1>App not ready, please refresh</h1>"
checkPort :: Int -> IO Bool
checkPort p = do
es <- Ex.try $ bindPort p HostIPv4
case es of
Left (_ :: Ex.IOException) -> return False
Right s -> do
sClose s
return True
getPort :: Int -> IO Int
getPort p = do
avail <- checkPort p
if avail then return p else getPort (succ p)
devel :: DevelOpts -> [String] -> IO () devel :: DevelOpts -> [String] -> IO ()
devel opts passThroughArgs = withManager $ \manager -> do devel opts passThroughArgs = withSocketsDo $ withManager $ \manager -> do
_ <- forkIO reverseProxy avail <- checkPort $ develPort opts
unless avail $ error "devel port unavailable"
appPort <- getPort 17834
_ <- forkIO $ reverseProxy opts appPort
checkDevelFile checkDevelFile
writeLock opts writeLock opts
@ -138,7 +160,7 @@ devel opts passThroughArgs = withManager $ \manager -> do
_ <- forkIO $ do _ <- forkIO $ do
filesModified <- newEmptyMVar filesModified <- newEmptyMVar
watchTree manager "." (const True) (\_ -> void (tryPutMVar filesModified ())) watchTree manager "." (const True) (\_ -> void (tryPutMVar filesModified ()))
evalStateT (mainOuterLoop filesModified) Map.empty evalStateT (mainOuterLoop appPort filesModified) Map.empty
_ <- getLine _ <- getLine
writeLock opts writeLock opts
exitSuccess exitSuccess
@ -146,7 +168,7 @@ devel opts passThroughArgs = withManager $ \manager -> do
bd = getBuildDir opts bd = getBuildDir opts
-- outer loop re-reads the cabal file -- outer loop re-reads the cabal file
mainOuterLoop filesModified = do mainOuterLoop appPort filesModified = do
ghcVer <- liftIO ghcVersion ghcVer <- liftIO ghcVersion
cabal <- liftIO $ D.findPackageDesc "." cabal <- liftIO $ D.findPackageDesc "."
gpd <- liftIO $ D.readPackageDescription D.normal cabal gpd <- liftIO $ D.readPackageDescription D.normal cabal
@ -158,10 +180,10 @@ devel opts passThroughArgs = withManager $ \manager -> do
liftIO $ removeFileIfExists "yesod-devel/arargs.txt" -- the configure step, remove them to force liftIO $ removeFileIfExists "yesod-devel/arargs.txt" -- the configure step, remove them to force
liftIO $ removeFileIfExists "yesod-devel/ldargs.txt" -- a cabal build first liftIO $ removeFileIfExists "yesod-devel/ldargs.txt" -- a cabal build first
rebuild <- liftIO $ mkRebuild gpd ghcVer cabal opts ldar rebuild <- liftIO $ mkRebuild gpd ghcVer cabal opts ldar
mainInnerLoop hsSourceDirs filesModified cabal gpd lib ghcVer rebuild mainInnerLoop appPort hsSourceDirs filesModified cabal gpd lib ghcVer rebuild
-- inner loop rebuilds after files change -- inner loop rebuilds after files change
mainInnerLoop hsSourceDirs filesModified cabal gpd lib ghcVer rebuild = go mainInnerLoop appPort hsSourceDirs filesModified cabal gpd lib ghcVer rebuild = go
where where
go = do go = do
_ <- recompDeps hsSourceDirs _ <- recompDeps hsSourceDirs
@ -185,7 +207,7 @@ devel opts passThroughArgs = withManager $ \manager -> do
else "Starting development server..." else "Starting development server..."
env0 <- liftIO getEnvironment env0 <- liftIO getEnvironment
(_,_,_,ph) <- liftIO $ createProcess (proc "runghc" devArgs) (_,_,_,ph) <- liftIO $ createProcess (proc "runghc" devArgs)
{ env = Just $ ("PORT", "3001") : ("DISPLAY_PORT", "3000") : env0 { env = Just $ ("PORT", show appPort) : ("DISPLAY_PORT", show $ develPort opts) : env0
} }
derefMap <- get derefMap <- get
watchTid <- liftIO . forkIO . try_ $ flip evalStateT derefMap $ do watchTid <- liftIO . forkIO . try_ $ flip evalStateT derefMap $ do
@ -201,7 +223,7 @@ devel opts passThroughArgs = withManager $ \manager -> do
liftIO $ Ex.throwTo watchTid (userError "process finished") liftIO $ Ex.throwTo watchTid (userError "process finished")
loop list loop list
n <- liftIO $ cabal `isNewerThan` (bd </> "setup-config") n <- liftIO $ cabal `isNewerThan` (bd </> "setup-config")
if n then mainOuterLoop filesModified else go if n then mainOuterLoop appPort filesModified else go
runBuildHook :: Maybe String -> IO () runBuildHook :: Maybe String -> IO ()
runBuildHook (Just s) = do runBuildHook (Just s) = do

View File

@ -62,8 +62,8 @@ getBuildFlags = do
return argv2 return argv2
buildPackage :: [Located String] -> FilePath -> FilePath -> IO Bool buildPackage :: [Located String] -> FilePath -> FilePath -> IO Bool
buildPackage a ld ar = buildPackage' a ld ar `Ex.catch` \(e::Ex.SomeException) -> do buildPackage a ld ar = buildPackage' a ld ar `Ex.catch` \e -> do
putStrLn ("exception building package: " ++ show e) putStrLn ("exception building package: " ++ show (e :: Ex.SomeException))
return False return False
buildPackage' :: [Located String] -> FilePath -> FilePath -> IO Bool buildPackage' :: [Located String] -> FilePath -> FilePath -> IO Bool

View File

@ -53,6 +53,7 @@ data Command = Init
, _develBuildDir :: Maybe String , _develBuildDir :: Maybe String
, develIgnore :: [String] , develIgnore :: [String]
, develExtraArgs :: [String] , develExtraArgs :: [String]
, _develPort :: Int
} }
| Test | Test
| AddHandler | AddHandler
@ -90,7 +91,7 @@ main = do
Configure -> cabal ["configure"] Configure -> cabal ["configure"]
Build es -> touch' >> cabal ("build":es) Build es -> touch' >> cabal ("build":es)
Touch -> touch' Touch -> touch'
Devel da s f r b _ig es -> devel (DevelOpts (optCabalPgm o == CabalDev) da (optVerbose o) r s f b) es Devel da s f r b _ig es p -> devel (DevelOpts (optCabalPgm o == CabalDev) da (optVerbose o) r s f b p) es
Keter noRebuild -> keter (cabalCommand o) noRebuild Keter noRebuild -> keter (cabalCommand o) noRebuild
Version -> do putStrLn ("yesod-core version:" ++ yesodVersion) Version -> do putStrLn ("yesod-core version:" ++ yesodVersion)
putStrLn ("yesod version:" ++ showVersion Paths_yesod.version) putStrLn ("yesod version:" ++ showVersion Paths_yesod.version)
@ -145,6 +146,8 @@ develOptions = Devel <$> switch ( long "disable-api" <> short 'd'
<> help "ignore file changes in DIR" ) <> help "ignore file changes in DIR" )
) )
<*> extraCabalArgs <*> extraCabalArgs
<*> option ( long "port" <> short 'p' <> value 3000 <> metavar "N"
<> help "Devel server listening port" )
extraCabalArgs :: Parser [String] extraCabalArgs :: Parser [String]
extraCabalArgs = many (strOption ( long "extra-cabal-arg" <> short 'e' <> metavar "ARG" extraCabalArgs = many (strOption ( long "extra-cabal-arg" <> short 'e' <> metavar "ARG"

View File

@ -108,6 +108,7 @@ executable yesod
, http-reverse-proxy >= 0.1.0.4 , http-reverse-proxy >= 0.1.0.4
, network , network
, http-conduit , http-conduit
, network-conduit
, project-template >= 0.1.1 , project-template >= 0.1.1
ghc-options: -Wall -threaded ghc-options: -Wall -threaded