stack build --file-watch-post=<command>

Created on 1 Jul 2015  Â·  20Comments  Â·  Source: commercialhaskell/stack

So stack build --file-watch is an awesome feature, something I've always wanted for about half my projects. However, often if I'm working on an executable I want my executable to fire off as well on a rebuild to regenerate the output. I'd like any feedback on adding this as a feature, I was thinking something along the lines of a --file-watch-post=<command>, where command is anything. Might as well tack on a --file-watch-pre=<command> that executes a command _before_ rebuilding, e.g. stack clean.

discuss

Most helpful comment

I was hoping for a built-in solution here, but seems there is none so I made a workaround, posting here for future searchers.

If you're on Linux, you can install inotify-tools, run stack build --file-watch in one terminal and

(
  declare -i p
  trap 'kill "$p"' EXIT
  while true; do
    stack exec server arg1 arg2  & p=$!
    inotifywait -q -e create .stack-work/install/*/*/*/bin/
    sleep 1
    kill "$p"
  done
)

in another (assuming your server executable is named server).

This'll restart server whenever a new file is created in the .stack-work bin folder. Of course it's even simpler if you're testing a one-shot command :)

(The sleep is there so it doesn't restart umpteen times while the file is in the process of being copied.)

All 20 comments

If this isn't implemented, may I suggest using already existing tools? entr, for example, solves this. Also, simple scripting solves this. entr also removes the need for --file-watch, but we already have that, so.

Would it make more sense to simply add pre and post build hooks in general as opposed to specifically for the file watch case? Perhaps a concrete example of where this would be used could help.

General pre and post hooks don't really appeal to me because I can just use my shell, i.e. stack clean && stack build && stack install. However when file-watch is running it is hijacking any control because it is running in a inner loop.

That answer doesn't change my thoughts on generalizing the feature at all. Just because your use case only requires it work in one instance doesn't mean we shouldn't make the feature more general.

So currently I am thinking to add flags like,

stack build --pre-build-cmd which we be equivalent to stack <command> && stack build. Is that what everyone else had in mind? The && and behavior for the --pre-build-cmd means that any error from the pre-build-cmd would abort the build.

All the commands that share the build command arguments install, bench, test, etc... would also inherit this flag.

What sounds like good expected behavior for stack install --post-build-cmd test. Would the stack test run before or after the stack install, and if before would an error in the test abort the install?

There are a lot of different usecases and I'm trying to think of the way that makes sense to capture most of them and still not overlap too much with shell functionality.

If we consider stack install --post-build-cmd test to be equivalent to stack install && stack test (which would probably surprise me the least), then running after the install would make sense.

Or does having the word "build" in --post-build-cmd imply that the pre/post commands wrap only the build step, rather than the whole "outer" command? Could we also have, say, --pre-install-cmd and --post-install-cmd?

I am inclined to implement this feature as originally specified for this release, which seems straight forward. Then we can open another issue for the general case.

I've had a look at the code yesterday and actually will be cleaner to solve my issue by implementing the in the general case. This is because file-watch is a set a callback for any function that runs in IO, so the smart thing to do would be to implement a stack build --post-build-cmd option. I'm not clear we have worked out what the general case should be, but the whole idea of chaining commands could be generalized, in fact I believe there may be a name for this pattern. :)

At this point I'm thinking extensions to stack.yaml giving directives for different commands and the possibility to specify your own. E.g.

build:
  pre: ["clean"]

install:
  post: ["test", "haddock"]
  flags:
    - local-bin-path: "."

start-webserver:
   file-watch: True
   commands: ["install", "exec ./webserver"]

So currently:

  • We'd need to make all the options that I've started refactoring into Stack.Options yaml readable.
  • And then figure out the syntax for stack commands.
  • Change Main.hs to interprete the sequence of stack commands.

This has the large side effect of making stack much more like a make like tool, but we we would just be specifying the ordering the actions. Perhaps we should close and move this to a new issue?

I'm not sure if we want to go this far. As @DanBurton has pointed out elsewhere, Setup.hs already supports doing things like this already, so we may want to just use that for the moment. Ultimately having a full blown generic build system inside stack could make sense, but I don't want to jump into that right now. (Others should feel free to jump in on this.)

Okay, let's see if we can limit the scope of this as I agree the full blown generic build system is a little overblown. I would suggest that we could somehow beef up the command datatype: currently in Stack.Options:

data Command
    = Build
    | Test
    | Haddock
    | Bench
    deriving (Eq)

with something like a:

  • Options included as well
  • newtype Commands = [Command]
  • Other missing cli commands into there, i.e. Exec.

The new syntax for a cli invocation would have to pull out all of the global options and apply it to all commands in the chain, and you could "cons" commands together with a : syntax, like so:

stack --file-watch --local-bin-path ./ clean : install : exec ./webserver

@manny-fp suggested the much nicer and easier to parse syntax:

stack watch --cmd build --cmd 'exec foo'

with watch as a new stack command. I'm happy with syntax for the next milestone because it doesn't have the chance to change the behavior of other commands. I'll have some time next week to start implementing.

Just want to add I'm not really thrilled with the name --cmd, or that you'd need it in the case of just one command, I just couldn't think of something better in the moment when we were talking. For the "only one command" case I think it should probably support something like stack watch build, and maybe just need the --cmd-style option in the case of multiples. Another possibility could be to support stack watch build : exec foo for multiple commands (a bit like @drwebb's earlier idea).

I still like your suggestion that it's going to easy to parse. : is already used for specifying intra-package targets, so I'm against overloading that. Though I think good short argument name argument may be -c which is pretty close to bash if I'm not mistaken.

@borsboom Was this superseded by a new approach you proposed elsewhere?

I belive this is superseded by https://github.com/commercialhaskell/stack/issues/651#issuecomment-125996680.

I was hoping for a built-in solution here, but seems there is none so I made a workaround, posting here for future searchers.

If you're on Linux, you can install inotify-tools, run stack build --file-watch in one terminal and

(
  declare -i p
  trap 'kill "$p"' EXIT
  while true; do
    stack exec server arg1 arg2  & p=$!
    inotifywait -q -e create .stack-work/install/*/*/*/bin/
    sleep 1
    kill "$p"
  done
)

in another (assuming your server executable is named server).

This'll restart server whenever a new file is created in the .stack-work bin folder. Of course it's even simpler if you're testing a one-shot command :)

(The sleep is there so it doesn't restart umpteen times while the file is in the process of being copied.)

For use with external tools like entr, it would be useful if stack could provide a command to output the list of the files that --file-watch would watch. So you could then run something like

stack watchable-files | entr -d -c stack test --fast --haddock

May I suggest just adding a '--exit-on-success" flag, instead of pre-post hooks? Then you could just do something like this

while true; do
    stack install --file-watch --exit-on-success
    doYourThing
done

My solution to the problem:

find src/ -type f | entr -r -c stack build --exec $project_name

you could use an alias

alias stack-watch="find src/ -type f | entr -r -c stack build --exec"

__Note__: I use -r because I am working on a yesod project and the server blocks rebuilding for both entr without -r and --file-watch

Thanks for reporting, @noxecane — I'm sure entr users will find that helpful.

There is now direct support using e.g. ghcid.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rrnewton picture rrnewton  Â·  4Comments

abhinav picture abhinav  Â·  4Comments

Toxaris picture Toxaris  Â·  4Comments

srghma picture srghma  Â·  3Comments

sjakobi picture sjakobi  Â·  4Comments