From 84ddb4e2a15a33e550fb01dd9e04871c87977ec6 Mon Sep 17 00:00:00 2001 From: Oleg Grenrus <oleg.grenrus@iki.fi> Date: Fri, 5 Jul 2019 12:13:32 +0300 Subject: [PATCH] cabal run works with Literate Haskell file --- cabal-install/Distribution/Client/CmdRun.hs | 77 +++++++++++++------ .../CmdRun/ScriptLiterate/cabal.project | 1 + .../CmdRun/ScriptLiterate/cabal.test.hs | 5 ++ .../CmdRun/ScriptLiterate/script.cabal | 4 + .../NewBuild/CmdRun/ScriptLiterate/script.lhs | 10 +++ 5 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.project create mode 100644 cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.cabal create mode 100644 cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.lhs diff --git a/cabal-install/Distribution/Client/CmdRun.hs b/cabal-install/Distribution/Client/CmdRun.hs index 6cc183637c..363c932ee8 100644 --- a/cabal-install/Distribution/Client/CmdRun.hs +++ b/cabal-install/Distribution/Client/CmdRun.hs @@ -104,7 +104,7 @@ import qualified Text.Parsec as P import System.Directory ( getTemporaryDirectory, removeDirectoryRecursive, doesFileExist ) import System.FilePath - ( (</>), isValid, isPathSeparator ) + ( (</>), isValid, isPathSeparator, takeExtension ) runCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags, TestFlags) @@ -171,8 +171,10 @@ runAction (configFlags, configExFlags, installFlags, haddockFlags, testFlags) let scriptOrError script err = do exists <- doesFileExist script + let pol | takeExtension script == ".lhs" = LiterateHaskell + | otherwise = PlainHaskell if exists - then BS.readFile script >>= handleScriptCase verbosity baseCtx tempDir + then BS.readFile script >>= handleScriptCase verbosity pol baseCtx tempDir else reportTargetSelectorProblems verbosity err (baseCtx', targetSelectors) <- @@ -339,9 +341,9 @@ parseScriptBlock str = readScriptBlock :: Verbosity -> BS.ByteString -> IO Executable readScriptBlock verbosity = parseString parseScriptBlock verbosity "script block" -readScriptBlockFromScript :: Verbosity -> BS.ByteString -> IO (Executable, BS.ByteString) -readScriptBlockFromScript verbosity str = do - str' <- case extractScriptBlock str of +readScriptBlockFromScript :: Verbosity -> PlainOrLiterate -> BS.ByteString -> IO (Executable, BS.ByteString) +readScriptBlockFromScript verbosity pol str = do + str' <- case extractScriptBlock pol str of Left e -> die' verbosity $ "Failed extracting script block: " ++ e Right x -> return x when (BS.all isSpace str') $ warn verbosity "Empty script block" @@ -356,39 +358,70 @@ readScriptBlockFromScript verbosity str = do -- -- * @-}@ -- +-- or for @.lhs@ files: +-- +-- * @%cabal:@ +-- +-- * @%endcabal@ +-- -- appearing alone on lines (while tolerating trailing whitespace). -- These tokens are not part of the 'Right' result. -- -- In case of missing or unterminated blocks a 'Left'-error is -- returned. -extractScriptBlock :: BS.ByteString -> Either String BS.ByteString -extractScriptBlock str = goPre (BS.lines str) +extractScriptBlock :: PlainOrLiterate -> BS.ByteString -> Either String BS.ByteString +extractScriptBlock pol str = goPre (BS.lines str) where - isStartMarker = (== "{- cabal:") . stripTrailSpace - isEndMarker = (== "-}") . stripTrailSpace + isStartMarker = (== startMarker) . stripTrailSpace + isEndMarker = (== endMarker) . stripTrailSpace stripTrailSpace = fst . BS.spanEnd isSpace -- before start marker goPre ls = case dropWhile (not . isStartMarker) ls of - [] -> Left "`{- cabal:` start marker not found" + [] -> Left $ "`" ++ startMarker ++ "` start marker not found" (_:ls') -> goBody [] ls' - goBody _ [] = Left "`-}` end marker not found" + goBody _ [] = Left $ "`" ++ endMarker ++ "` end marker not found" goBody acc (l:ls) - | isEndMarker l = Right $! BS.unlines $ reverse acc + | isEndMarker l = Right $! BS.unlines $ map postProcessLine $ reverse acc | otherwise = goBody (l:acc) ls - -handleScriptCase :: Verbosity - -> ProjectBaseContext - -> FilePath - -> BS.ByteString - -> IO (ProjectBaseContext, [TargetSelector]) -handleScriptCase verbosity baseCtx tempDir scriptContents = do - (executable, contents') <- readScriptBlockFromScript verbosity scriptContents + startMarker, endMarker :: IsString a => a + startMarker = case pol of + PlainHaskell -> fromString "{- cabal:" + LiterateHaskell -> fromString "%cabal:" + endMarker = case pol of + PlainHaskell -> fromString "-}" + LiterateHaskell -> fromString "%endcabal" + + -- strip leading % in LiterateHaskell format + postProcessLine bs = case pol of + PlainHaskell -> bs + LiterateHaskell -> case BS.uncons bs of + Nothing -> bs + Just ('%', bs') -> bs' + Just _ -> bs + +data PlainOrLiterate + = PlainHaskell + | LiterateHaskell + +handleScriptCase + :: Verbosity + -> PlainOrLiterate + -> ProjectBaseContext + -> FilePath + -> BS.ByteString + -> IO (ProjectBaseContext, [TargetSelector]) +handleScriptCase verbosity pol baseCtx tempDir scriptContents = do + (executable, contents') <- readScriptBlockFromScript verbosity pol scriptContents -- We need to create a dummy package that lives in our dummy project. let + mainName = case pol of + PlainHaskell -> "Main.hs" + LiterateHaskell -> "Main.lhs" + sourcePackage = SourcePackage { packageInfoId = pkgId , SP.packageDescription = genericPackageDescription @@ -400,7 +433,7 @@ handleScriptCase verbosity baseCtx tempDir scriptContents = do , condExecutables = [("script", CondNode executable' targetBuildDepends [])] } executable' = executable - { modulePath = "Main.hs" + { modulePath = mainName , buildInfo = binfo { defaultLanguage = case defaultLanguage of @@ -417,7 +450,7 @@ handleScriptCase verbosity baseCtx tempDir scriptContents = do pkgId = fakePackageId writeGenericPackageDescription (tempDir </> "fake-package.cabal") genericPackageDescription - BS.writeFile (tempDir </> "Main.hs") contents' + BS.writeFile (tempDir </> mainName) contents' let baseCtx' = baseCtx diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.project b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.project new file mode 100644 index 0000000000..5356e76f67 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.project @@ -0,0 +1 @@ +packages: . \ No newline at end of file diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs new file mode 100644 index 0000000000..64c858e8d0 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs @@ -0,0 +1,5 @@ +import Test.Cabal.Prelude + +main = cabalTest $ do + res <- cabal' "v2-run" ["script.lhs"] + assertOutputContains "Hello World" res diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.cabal b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.cabal new file mode 100644 index 0000000000..56b2e9feb6 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.cabal @@ -0,0 +1,4 @@ +name: script +version: 1.0 +build-type: Simple +cabal-version: >= 1.10 diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.lhs b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.lhs new file mode 100644 index 0000000000..678391ab3b --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/script.lhs @@ -0,0 +1,10 @@ +%cabal: +%build-depends: base ^>= 4.0 +%endcabal +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE ScopedTypeVariables #-} + +import Prelude + +main :: IO () +main = putStrLn "Hello World" -- GitLab