haskellshake-build-system

A phony target for sending a file via e-mail?


I would like to execute a command that takes a specific file in the project (building it as necessary) and sends it somewhere externally. For example, it may be a command that uploads a web page, or sends an e-mail. It may even write some additional files, such as a log, but that is not the point of calling it.

In other words, this action is evoked by naming its source, rather than the target — either because there is no tangible target, or it is insignificant and the action is primarily wanted for its side effect.

I see it that an extra command line argument will have to be provided, like this:

% BuildSystem send pizza.box

The command above should be equivalent to the following:

% BuildSystem pizza.box
% send pizza.box

Can (and "should") this be performed with the shake build system?


P.S. As Daniel suggests in an answer, I can extend shake's argument parser. But I am not sure if this is the best practice for a case like this. It seems a little at odds with the way shake behaves, treating every single command line argument as a self-contained goal. It is also a whole new logic for the operator to design, so much overhead for such a menial task.

It might be more intuitive to request a receipt file for each box that is sent. For instance:

% BuildSystem pizza.receipt

— would then be equivalent to:

% BuildSystem pizza.box
% send pizza.box >pizza.receipt

On the downside, as I understand from an official answer to a question nearby,we cannot have a pseudo-target like pizza.send that does not actually result in a file pizza.send being created. So I am not sure, again, if this is the right way.

P.S. 2 It would be even better if we could replace the default "file exists" success verifier with a custom code. For instance, instead of verifying that a file pizza.receipt (that we otherwise have no need for) was indeed created, we may telephone the customer and ask them if they enjoyed the lunch. If we can arrange that, we can then invoke the corresponding rule with a "pseudo-file" target pizza.send. In general, build artifacts need not reside on the local file system at all, insofar as the code that can verify and retrieve them is given.


Solution

  • The answer depends on whether you want to send the email once, or repeatedly, even if pizza.box doesn't change. In both cases let's imagine you want to write BuildSystem send.pizza.box to cause the email to be sent.

    Send every time

    If you want to send a copy of pizza.box every time you can use a phony rule:

    phony "send.pizza.box" $ do
        need ["pizza.box"]
        cmd_ "send-email" "pizza.box"
    

    If you want that to work for all files, you can generalise to:

    phonys $ \s -> case stripPrefix "send." s of
        Nothing -> Nothing
        Just file -> Just $ do
            need [file]
            cmd_ "send-email" [file]
    

    Send once

    If you only want one copy of each changed pizza.box to be sent then you need to record evidence of that locally, to stop sending successive copies. The easiest way to do that is actually create a file send.pizza.box:

    "send.*" %> \out -> do
        let src = drop 5 out
        need [src]
        cmd_ "send-email" [src]
        writeFile' out ""
    

    If you strongly want to avoid writing the send.pizza.box file you can use the phony technique from above combined with addOracle (but it's unlikely to be worth the additional hassle).