casey / just

🤖 Just a command runner
https://just.systems
Creative Commons Zero v1.0 Universal
18.96k stars 430 forks source link

Can't add dependencies to a specific invocation of a recipe with arguments #1357

Open mdbooth opened 1 year ago

mdbooth commented 1 year ago

I'm playing with just for the first time and decided to experiment by rewriting this simple Makefile I'm currently using:

default: ign/cluster-1.ign

ign/cluster-1.ign: ign/system.ign ign/ssh.ign ign/k8s.ign
ign/%.ign: %.butane | igndir
        butane -s $< -d $(@D) > $@

.PHONY: igndir
igndir:
        mkdir -p ign

This generates ignition files from butane files. It also encapsulates the dependency that the cluster-1 ignition imports the system, ssh, and k8s ignitions, so they must be built first. This was my Justfile attempt:

default: (ign cluster-1)

ign cluster-1: (ign system) (ign ssh) (ign k8s)
ign IGN: igndir
        butane -s "{{IGN}}.butane" -d . > "ign/{{IGN}}.ign"

@igndir:
        mkdir -p ign

Unfortunately this dies with:

error: Recipe `ign` first defined on line 3 is redefined on line 4
  |
4 | ign IGN: igndir
  | ^^^

This is idiomatic and very powerful in make, and consequently I use it extensively. I couldn't see an obvious way to do this in just. I don't want a new recipe; I want to declare that when this recipe is called for 'cluster-1' specifically it has some additional dependencies.

Is there some other more idiomatic way of doing this in just?

casey commented 1 year ago

Neat, I've never seen that pattern in make before!

So when you call ign cluster-1, you want to add additional dependencies, but still run the same recipe body?

Does this do what you want:

default: ign-cluster-1

ign-cluster-1: (ign system) (ign ssh) (ign k8s) (ign cluster-1)

ign IGN: igndir
        butane -s "{{IGN}}.butane" -d . > "ign/{{IGN}}.ign"

@igndir:
        mkdir -p ign
mdbooth commented 1 year ago

It would, and I considered custom targets. However, I've been working on this project for an additional 24 hours now and the dependency tree is substantially more complex already 🙄

For example, there are many more butanes, and the top level one is no longer the only one with dependencies. Additionally some of the butanes are now generated by a generic templating rule. Despite this they are all still processed identically.

Unfortunately I don't think a custom target for every one would be very ergonomic.

That said, neither is this 😬:

# Read node variables from nodes.yaml
nodevars := $(shell ../bin/get-node-env ./nodes.yaml $(NODE))
$(foreach var,$(nodevars),$(eval export $(var)))
casey commented 1 year ago

Gotcha. You can use _name if you want to suppress a recipe from being shown in --list, if you have internal-only recipes.

I think it would be pretty confusing if we did this, it's kind of a wacky feature of make, although I'd definitely be open to alternative ways of doing this, like conditional dependencies:

ign cluster: ? if cluster == "1" { extra-deps }

Syntax will likely be heinous, since it's hard to add things to the dependency parser.