diff --git a/src/Main.hs b/src/Main.hs index 0ccd7b5..b6d32e7 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -36,7 +36,8 @@ data Options = Options { keepGoing :: Bool, jobs :: Int, shuffle :: Bool, - noColor :: Bool + noColor :: Bool, + quiet :: Bool } -- Redo default options: @@ -51,7 +52,8 @@ defaultOptions = Options { keepGoing = False, jobs = 1, shuffle = False, - noColor = False + noColor = False, + quiet = False } -- Define program options: @@ -73,6 +75,7 @@ options = , Option ['k'] ["keep-going"] (NoArg setKeepGoing) "keep building even if some targets fail" , Option ['s'] ["shuffle"] (NoArg setShuffle) "randomize the build order to find dependency bugs" , Option ['n'] ["no-color"] (NoArg setNoColor) "print plain text, disable ANSI terminal color output" + , Option ['q'] ["quiet"] (NoArg setQuiet) "suppress all output except errors" ] -- Helper functions for setting the options: @@ -106,6 +109,9 @@ setShuffle opt = return opt { shuffle = True } setNoColor :: Options -> IO Options setNoColor opt = return opt { noColor = True } +setQuiet :: Options -> IO Options +setQuiet opt = return opt { quiet = True } + -- Print the program version and license information: printVersion :: Options -> IO Options printVersion _ = do putStrLn versionString @@ -152,13 +158,15 @@ main = do keepGoing = keepGoing', jobs = jobs', shuffle = shuffle', - noColor = noColor'} = opts + noColor = noColor', + quiet = quiet'} = opts -- Show help or version information if asked: when help' (printHelp progName options []) when keepGoing' (setEnv "REDO_KEEP_GOING" "TRUE") when shuffle' (setEnv "REDO_SHUFFLE" "TRUE") when noColor' (setEnv "REDO_NO_COLOR" "TRUE") + when quiet' (setEnv "REDO_QUIET" "TRUE") when dashD1' (setEnv "REDO_DEBUG_1" "TRUE") when dashD2' (setEnv "REDO_DEBUG_2" "TRUE") when dashC' (setEnv "REDO_CHECK" "TRUE") diff --git a/src/PrettyPrint.hs b/src/PrettyPrint.hs index f2b3ee2..79cf001 100644 --- a/src/PrettyPrint.hs +++ b/src/PrettyPrint.hs @@ -2,6 +2,7 @@ module PrettyPrint(putRedoUnformatted, putRedoInfo, putRedoWarning, putInfoStrLn, putWarningStrLn, putErrorStrLn, putStatusStrLn, putRedoStatus, putRedoError, putUnformattedStrLn, isColorTerminal, putStrBuffered) where +import Control.Monad (unless) import System.IO (hPutStrLn, stderr, hFlush, hSetBuffering, BufferMode(..), stdout, hIsTerminalDevice) import Data.Maybe (fromJust, isNothing, isJust, fromMaybe) import System.Environment (lookupEnv) @@ -51,13 +52,17 @@ putColorStrLn color string = do putUnformattedStrLn :: String -> IO () putUnformattedStrLn = hPutStrLn stderr putInfoStrLn :: String -> IO () -putInfoStrLn = putColorStrLn green +putInfoStrLn string = do + quiet' <- isQuiet + unless quiet' $ putColorStrLn green string putWarningStrLn :: String -> IO () putWarningStrLn = putColorStrLn yellow putErrorStrLn :: String -> IO () putErrorStrLn = putColorStrLn red putStatusStrLn :: String -> IO () -putStatusStrLn = putColorStrLn cyan +putStatusStrLn string = do + quiet' <- isQuiet + unless quiet' $ putColorStrLn cyan string -- Print a "redo" style string. This includes the target being built, indented -- to the appropriate level, with an optional message ater @@ -92,22 +97,34 @@ useColor = do let noColor = fromMaybe "" noColor' return $ noColor /= "TRUE" +-- Is quiet mode enabled? (suppress info and status output; warnings and errors always print) +isQuiet :: IO Bool +isQuiet = do + quiet' <- lookupEnv "REDO_QUIET" + return $ fromMaybe "" quiet' == "TRUE" + -- Put a "redo" style info string: putRedoInfo :: Target -> IO () -putRedoInfo target = putRedo green target "" +putRedoInfo target = do + quiet' <- isQuiet + unless quiet' $ putRedo green target "" -- Put a "redo" style status string: putRedoStatus :: Target -> String -> IO () -putRedoStatus target string = putRedo cyan target ("- " ++ string) +putRedoStatus target string = do + quiet' <- isQuiet + unless quiet' $ putRedo cyan target ("- " ++ string) --- Put a "redo" style warning string: +-- Put a "redo" style warning string (always printed, even in quiet mode): putRedoWarning :: Target -> String -> IO () putRedoWarning target string = putRedo yellow target ("- " ++ string) --- Put a "redo" style error string: +-- Put a "redo" style error string (always printed, even in quiet mode): putRedoError :: Target -> String -> IO () putRedoError target string = putRedo red target ("- " ++ string) -- Put a "redo" style unformatted string: putRedoUnformatted :: Target -> String -> IO () -putRedoUnformatted target string = putRedo "" target ("- " ++ string) +putRedoUnformatted target string = do + quiet' <- isQuiet + unless quiet' $ putRedo "" target ("- " ++ string) diff --git a/test/150-quiet/all.do b/test/150-quiet/all.do new file mode 100644 index 0000000..638abe7 --- /dev/null +++ b/test/150-quiet/all.do @@ -0,0 +1,139 @@ +exec >&2 +. ../skip-if-minimal-do.sh + +# Test 1: With -q, successful build produces no output +echo "Test 1: quiet mode suppresses output on success" +rm -f target1 +output=$(redo -q target1 2>&1) +if [ -n "$output" ]; then + echo "FAIL: expected no output with -q, got: $output" >&2 + exit 1 +fi +[ -e target1 ] || exit 2 +echo " PASS" + +# Test 2: Without -q, successful build produces output +echo "Test 2: normal mode shows output" +rm -f target1 +output=$(redo target1 2>&1) +if [ -z "$output" ]; then + echo "FAIL: expected output without -q" >&2 + exit 3 +fi +echo " PASS" + +# Test 3: With -q, errors are still printed +echo "Test 3: quiet mode still shows errors" +rm -f failme +set +e +output=$(redo -q failme 2>&1) +rc=$? +set -e +if [ "$rc" -eq 0 ]; then + echo "FAIL: expected non-zero exit" >&2 + exit 4 +fi +if [ -z "$output" ]; then + echo "FAIL: expected error output with -q on failure" >&2 + exit 5 +fi +case "$output" in + *Error*) ;; # good + *) echo "FAIL: expected 'Error' in output, got: $output" >&2; exit 6 ;; +esac +echo " PASS" + +# Test 4: -q propagates to child redo processes (nested builds) +echo "Test 4: quiet mode propagates to nested builds" +rm -f outer inner +output=$(redo -q outer 2>&1) +if [ -n "$output" ]; then + echo "FAIL: expected no output from nested -q build, got: $output" >&2 + exit 7 +fi +[ -e outer ] || exit 8 +[ -e inner ] || exit 9 +echo " PASS" + +# Test 5: --quiet long form works +echo "Test 5: --quiet long form" +rm -f target1 +output=$(redo --quiet target1 2>&1) +if [ -n "$output" ]; then + echo "FAIL: expected no output with --quiet, got: $output" >&2 + exit 10 +fi +echo " PASS" + +# Test 6: -q combined with other flags +echo "Test 6: -q combined with -k (keep-going)" +rm -f target1 +output=$(redo -q -k target1 2>&1) +if [ -n "$output" ]; then + echo "FAIL: expected no output with -q -k, got: $output" >&2 + exit 11 +fi +echo " PASS" + +# Test 7: -q with multiple targets +echo "Test 7: -q with multiple targets" +rm -f target1 inner +output=$(redo -q target1 inner 2>&1) +if [ -n "$output" ]; then + echo "FAIL: expected no output with -q and multiple targets, got: $output" >&2 + exit 12 +fi +[ -e target1 ] || exit 13 +[ -e inner ] || exit 14 +echo " PASS" + +# Test 8: -q doesn't affect build correctness (target content is right) +echo "Test 8: -q doesn't affect build output content" +rm -f target1 +redo -q target1 +content=$(cat target1) +if [ "$content" != "built" ]; then + echo "FAIL: expected 'built' in target1, got: $content" >&2 + exit 15 +fi +echo " PASS" + +# Test 9: Nested error in quiet mode still shows error chain +echo "Test 9: nested error in quiet mode shows error chain" +rm -f outer_fail inner_fail +set +e +output=$(redo -q outer_fail 2>&1) +rc=$? +set -e +if [ "$rc" -eq 0 ]; then + echo "FAIL: expected non-zero exit from outer_fail" >&2 + exit 16 +fi +case "$output" in + *Error*) ;; # good + *) echo "FAIL: expected error output, got: $output" >&2; exit 17 ;; +esac +echo " PASS" + +# Test 10: Warnings are NOT suppressed by -q +echo "Test 10: quiet mode still shows warnings" +rm -f warnme +# First build warnme so redo knows it as a target +redo warnme +# Now remove and create as plain source file +rm -f warnme +echo "i am a source" > warnme +# Trying to redo it should produce a warning (source file exists) +set +e +output=$(redo -q warnme 2>&1) +set -e +if [ -z "$output" ]; then + echo "FAIL: expected warning output with -q, got nothing" >&2 + exit 18 +fi +case "$output" in + *Warning*) ;; # good — warning was printed + *) echo "FAIL: expected 'Warning' in output, got: $output" >&2; exit 19 ;; +esac +rm -f warnme +echo " PASS" diff --git a/test/150-quiet/failme.do b/test/150-quiet/failme.do new file mode 100644 index 0000000..379a4c9 --- /dev/null +++ b/test/150-quiet/failme.do @@ -0,0 +1 @@ +exit 1 diff --git a/test/150-quiet/inner.do b/test/150-quiet/inner.do new file mode 100644 index 0000000..7365022 --- /dev/null +++ b/test/150-quiet/inner.do @@ -0,0 +1 @@ +echo "inner" > "$3" diff --git a/test/150-quiet/inner_fail.do b/test/150-quiet/inner_fail.do new file mode 100644 index 0000000..379a4c9 --- /dev/null +++ b/test/150-quiet/inner_fail.do @@ -0,0 +1 @@ +exit 1 diff --git a/test/150-quiet/outer.do b/test/150-quiet/outer.do new file mode 100644 index 0000000..9b0fdfc --- /dev/null +++ b/test/150-quiet/outer.do @@ -0,0 +1,2 @@ +redo-ifchange inner +echo "outer" > "$3" diff --git a/test/150-quiet/outer_fail.do b/test/150-quiet/outer_fail.do new file mode 100644 index 0000000..e3ecf96 --- /dev/null +++ b/test/150-quiet/outer_fail.do @@ -0,0 +1,2 @@ +redo-ifchange inner_fail +echo "should not reach here" > "$3" diff --git a/test/150-quiet/target1.do b/test/150-quiet/target1.do new file mode 100644 index 0000000..9d24a9f --- /dev/null +++ b/test/150-quiet/target1.do @@ -0,0 +1 @@ +echo "built" > "$3" diff --git a/test/150-quiet/warnme.do b/test/150-quiet/warnme.do new file mode 100644 index 0000000..419ea7a --- /dev/null +++ b/test/150-quiet/warnme.do @@ -0,0 +1,2 @@ +# Build a target, then mark it as source so rebuilding triggers a warning +echo "built" > "$3"