haskell: cd command does not work in shake/command library - haskell

For some reason I cannot make cd command work in shake/command Haskell library. It thinks directory I called with cd does not exist even though it is present in the filesystem.
Here is an excerpt of my code:
dataDir = "data"
data Settings = Settings {
url :: String
} deriving (Eq, Show, Generic, JSON.ToJSON, JSON.FromJSON)
settings :: String -> Handler Settings
settings subfolder = let
gitPath = dataDir ++ "/" ++ subfolder ++ "/git/.git"
in do
pathExists <- liftIO $ doesPathExist gitPath
-- Stdout pwdOut <- liftIO $ cmd ("pwd" :: String)
-- liftIO $ putStrLn $ pwdOut
if not pathExists
then do
liftIO $ (cmd_ ("mkdir -p" :: String) [gitPath] :: IO ())
liftIO $ (cmd_ ("cd" :: String) [gitPath] :: IO ())
liftIO $ (cmd_ ("git init" :: String) :: IO ())
return $ Settings { url = ""}
else do
liftIO $ (cmd_ (Cwd ".") ("cd" :: String) [gitPath] :: IO ())
Stdout out <- liftIO $ (cmd ("git config --get remote.origin.url" :: String))
return $ Settings {url = out}
It fails with an error cd: createProcess: runInteractiveProcess: exec: does not exist (No such file or directory) in both cases: if dir exists and when mkdir command is executed.
Cannot wrap my head around it. But before I submit a bug to the shake's github page, I want to make sure with you I am not doing anything stupid that might cause this kind of behavior.
Thanks in advance for help.

As described in the other answer, cd is not an executable, so if you wanted to run it, you would have to pass Shell to cmd.
However, it is almost certainly the case that you don't want to call cd in a command, as it does not change the directory for any subsequent command. Each cmd is a separate process, with a separate environment, so the subsequent command will be in a fresh environment, and the same working directory as before the cd. The solution is to pass (Cwd gitPath) to each command you want to operate with the given directory.

Shake's Haddock page describes cmd_, and links to its source. There we can see that cmd_ eventually calls commandExplicitIO, which constructs a ProcessOpts with RawCommand and passes it to process. process then takes that ProcessOpts, pattern-matches it as a RawCommand (via cmdSpec), and calls proc. We have now entered the well-documented zone: you must give proc an executable, and cd is not an executable. (Why? Since processes cannot change the working directory of their parent, cd must be a shell builtin.)

Related

How does optparse-applicative bash autocompletion work?

I'm building a brainfuck compiler. The executable accepts two commands $ brainfuck compile ... and $ brainfuck run. I want the executable to auto complete when pressing tab. E.g. writing $ brainfuck com and then pressing tab should generate $ brainfuck compile.
data Command = Compile CompileArgs | Run RunArgs
deriving (Show)
main :: IO ()
main = execute =<< execParser opts
where
opts = info (helper <*> argsParser) fullDesc
execute :: Command -> IO ()
execute (Compile args) = compile args
execute (Run args) = run args
argsParser :: Parser Command
argsParser = subparser (compileCommand <> runCommand)
where
compileCommand = command "compile" $ info compileOptions $ progDesc "Compile brainfuck to an executable"
runCommand = command "run" $ info runOptions $ progDesc "Execute brainfuck code"
There is a section on optparse's github page here, but I don't really understand it.
The function completeWith :: Options.Applicative.Builder.Internal.HasCompleter f => [String] -> Mod f a looks quite similar to command :: String -> ParserInfo a -> Mod CommandFields a which I'm already using. So I figured I could use it and just combine them with <> but it turns out that CommandFields is not an instance of HasCompleter.
How are you supposed to get the auto completion to work?
After RTFM'ing a bit I found out how to configure the auto completion.
completeWith is applied when constructing the parsers for the individual arguments.
Like so:
data CompileArgs = CompileArgs
{
debug :: Bool,
optimizations :: OptimizationLevel,
file :: String
}
deriving (Show, Read)
compileArgsParser :: Parser CompileArgs
compileArgsParser = CompileArgs
<$> switch (
long "debug" <>
help "Outputs object and assembly files")
<*> option auto (
long "optimization-level" <>
value All <>
metavar "LEVEL" <>
help "all | none, default: all" <>
completeWith ["all", "none"])
<*> argument str (
metavar "FILE" <>
help "brainfuck source code" <>
action "file")
<**> helper
action is an instruction to bash on how auto complete. "file" means auto complete with any file or directory. See this page for more info.
In order for these auto completions to kick in you need to generate a script and make sure that script is sourced. By convention it's placed under /etc/bash_completion.d/ when using bash.
brainfuck --bash-completion-script `which brainfuck` | sudo tee /etc/bash_completion.d/brainfuck
in my case where my program is called brainfuck.
I have not tested this, but after reading the documentation, it seems to me that by calling execParser in main, your program automatically supports the required options for command complete. You just need to run your program with --bash-completion-script as documented to generate a shell script, and then load that script to bash.

Haskell: interaction between withCurrentDirectory and runConcurrently

I'm trying to automate some file management in Haskell using System.Directory. My script works synchronously, but in my use case, I have about twenty directories, for each of which I'd like to start a long-running process, so I am also using Control.Concurrent.Async, which seems to be causing problems.
Minimal Example:
#!/usr/bin/env stack
-- stack --resolver lts-10.3 --install-ghc runghc --package async
import Control.Concurrent.Async (Concurrently(..), runConcurrently)
import Control.Monad (filterM)
import System.Directory as Dir
import System.Process (callCommand)
dirs :: IO [FilePath]
dirs = do
prefix <- (++ "/Desktop/dirs/") <$> Dir.getHomeDirectory
paths <- fmap (prefix ++) <$> Dir.listDirectory prefix
filterM Dir.doesDirectoryExist paths
pullDir :: FilePath -> IO ()
pullDir dir = Dir.withCurrentDirectory dir $ callCommand "pwd"
main :: IO ()
main = dirs >>= runConcurrently . traverse (Concurrently . pullDir) >> pure ()
Expected output:
/Users/daniel/Desktop/dirs/1
/Users/daniel/Desktop/dirs/2
/Users/daniel/Desktop/dirs/3
/Users/daniel/Desktop/dirs/4
/Users/daniel/Desktop/dirs/5
Actual output (varies!):
/Users/daniel/Desktop/dirs/3
/Users/daniel/Desktop/dirs/4
/Users/daniel/Desktop/dirs/3
/Users/daniel/Desktop/dirs/5
/Users/daniel/Desktop/dirs/5
We see the actual output runs pwd for the same directory more than once and fails to run pwd for some of the directories entierly. I'm almost positive this has to do with withCurrentDirectory.
How can I implement this correctly while still preserving the concurrency?
This isn't possible with withCurrentDirectory. The current directory is a process-wide setting. Whenever something changes it, it's changed for everything in the process. This isn't a Haskell issue - it's just how the concept of "current directory" works.
To get this to work concurrently, you'll need to use full paths for everything, instead of changing the current directory.

Haskells stack or interact are adding characters

I'm doing my first steps using Haskell. I created a project using stack and changed the Main.hs into
module Main where
my_fkt :: String -> String
my_fkt input = show (length input)
main :: IO ()
main = interact my_fkt
I build the project via stack build, run it via stack exec firststeps-exe, enter "abcd" and finish input via <CTRL>-D. In the console I now see
abcd4%
The %is inverted. If I use a text file containing the "abcd" (without line break) and execute more sample.txt | stack exec firststeps-exe I see
abcd5%
Why do I get one additional character in the second case and what is the inverted percentage sign?
That is because the definition of interact uses putStr instead of putStrLn.
You can take a look at the source code here.
interact :: (String -> String) -> IO ()
interact f = do s <- getContents
putStr (f s)
To remedy your issue I would go on and create a similar function
interact' :: (String -> String) -> IO ()
interact' f = do s <- getContents
putStrLn (f s)
or if you like to mix it up and write a bit terser code
interact' f = putStrLn =<< (f <$> getContents)
I don't know what the % is or why it is showing up, my guess would be that it is the escaped CTRL-D.
With regards to your second question about the additional "non-existing" character, I am also not sure, but here my guess would be that this is the \EOF.
Btw. you can always check using more testinput | wc -c it should yield the same result as your haskell program.

How to print paths using Haskell Turtle library?

To learn a bit about Turtle, I thought it would be nice to modify example from the tutorial. I chose to remove the reduntant "FilePath" from each line of the output thinking it would be a simple exercise.
And yet, despite author's efforts into making his library easy to use I nearly failed to use it to solve this simple problem.
I tried everyting I saw that looked like it would allow me to somehow lift >>= from IO into Shell: MonadIO, FoldM, liftIO, _foldIO with no success. I grew frustrated and only through reading Turtle source code I was able to find something that seems to work ("no obvious defects" comes to mind).
Why is this so hard? How does one logically arrive a solution using API of this library?
#!/usr/bin/env stack
-- stack --resolver lts-8.17 --install-ghc runghc --package turtle --package lens
{-# LANGUAGE OverloadedStrings #-}
import Turtle
import Control.Lens
import Control.Foldl as Foldl
import Filesystem.Path.CurrentOS
import Data.Text.IO as T
import Data.Text as T
main = do
homedir <- home
let paths = lstree $ homedir </> "projects"
let t = fmap (Control.Lens.view _Right . toText) paths
customView t
customView s = sh (do
x <- s
liftIO $ T.putStrLn x)
You don't lift >>= from IO into Shell. Shell already has a Monad instance that comes with its own >>= function. Instead you either lift IO actions into Shell with liftIO or run the shell with fold or foldM. Use sh to run the Shell when you don't care about the results.
I believe your example can be simplified to
main = sh $ do
homedir <- home
filepath <- lstree $ homedir </> "projects"
case (toText filepath) of
Right path -> liftIO $ T.putStrLn x
Left approx -> return () -- This shouldn't happen
As for the difficulty with getting a string back from a FilePath, I don't think that can be blamed on the Turtle author. I think it can be simplified to
stringPath :: FilePath -> String
stringPath filepath =
case (toText filePath) of -- try to use the human readable version
Right path -> T.unpack path
Left _ -> encodeString filePath -- fall back on the machine readable one
Combined this would simplify the example to
main = sh $ do
homedir <- home
filepath <- lstree $ homedir </> "projects"
liftIO $ putStrLn (stringPath filepath)
or
main = view $ do
homedir <- home
filepath <- lstree $ homedir </> "projects"
return $ stringPath filepath

Profiling executable with criterion

I need to profile a large number of haskell executables, hopefully in parallel. I was able to get the clock time with measure and measTime from the Criterion library, but couldn't get measCpuTime or any GC report to work (measCpuTime returns a time that's impossibly short). The code looks like:
buildProj :: FilePath -> IO ExitCode
buildProj projDir = system $ "cd " ++ projDir ++ "; cabal sandbox init; cabal configure; cabal build"
-- Time a project
instance NFData ExitCode
where
rnf ExitSuccess = ()
rnf (ExitFailure _) = ()
benchmark :: FilePath -> Int64 -> IO Double
benchmark projDir runs = do
let runProj = "./" ++ projDir ++ "/dist/build/" ++ projDir ++ "/" ++ projDir ++ "> /dev/null"
exit <- timeout 17000000 $ system runProj -- TODO hardcode timeout
case exit of
Just ExitSuccess -> do {(m, _) <- measure (nfIO $ system runProj) runs;
return $! measTime m}
Just (ExitFailure _) -> return 100
Nothing -> return 100
In short, I'm running the executables with System.Process.system as an IO action and I've declared ExitCode as NFData in order to get nfIO to work. What have I done wrong? Are there better tools to do the task?
The file's here if you want to play with it.
I took a look at this SO question and got some ideas. First note that criterion uses cbits to enable system-dependent cpu time functions. Let's pretend you're on unix. The simplest thing to do is to directly read from /proc/PID/stat/cutime at the start and end of your runs and take the difference. Beyond that, you can actually use the c code provided in that question, link it in yourself as a foreign import, and then call that directly from your own code.

Resources