I've got some protocol buffers definitions, in proto/*.proto
. My current Makefile-based build system compiles each of them into an Erlang module and header, and into a self-descriptor (as a .pb
file) and then embeds the descriptor as a BEAM module:
foo.proto -> foo_pb.erl, foo_pb.hrl
-> foo.pb -> foo_pb_desc.beam
I've implemented a mix_protobuffs
task to do the first step, and the foo.proto -> foo.pb
part-step.
I've also got the relevant code for embedding an arbitrary file into an Erlang module -- inspired by this answer -- but I'm wondering about the recommended way of integrating it into my mix-based build process.
Similarly, I've got some other files that need to be processed and then embedded, which suggests that I shouldn't make the mix_protobuffs
task know about embedding.
So I guess I'm asking: is there a way to have mix run arbitrary commands pre/post another task?
The common way would be to define your own compiler. For that, you are to create a module in Mix.Task.Compiler
namespace and implement Mix.Task.Compiler
behaviour. Also the project/0
function of your mix.exs
should be updated to return your new compiler. Somewhat like below would do.
defmodule Mix.Tasks.Compile.Protobufs do
use Mix.Task.Compiler
@recursive true
@impl Mix.Task.Compiler
def run(argv) do
# do your compilation, possibly with System.cmd/3
:ok
end
end
And in your project file:
def project do
[
compilers: Mix.compilers() ++ [:protobufs],
# ...
]
end
You also can call any existing mix
task from your own task, like Mix.Tasks.Run.run(args)
.