Reusing logic in recipes
Hi,
Last week I found myself writing a justfile that used a pattern like this:
main: (sub1 "123") (sub2 "456")
sub1 NUM:
#!/bin/sh
X="hello/{{NUM}}"
echo $X
sub2 NUM:
#!/bin/sh
X="hello/{{NUM}}"
file $X
Here, the env var X is computed in the same way, but is duplicated. It would be nice if there was a way to share a single definition of the computation of X between recipes somehow.
Is there currently a way to do this? If not, would it be something nice to add?
I can imagine syntax that expands to the stdout of another recipe, e.g.:
main: (sub1 "123") (sub2 "456")
_compute_x NUM:
echo "hello/{{NUM}}"
sub1 NUM:
echo {%_compute_x NUM%}
sub2 NUM:
file {%(_compute_x NUM)%}
Maybe not the best syntax, but you get the idea?
FWIW, this is the best I could come up with using the current feature set:
main: (sub1 "123") (sub2 "456")
_compute_x NUM:
echo "hello/{{NUM}}"
sub1 NUM:
#!/bin/sh
X=$(just -f {{justfile()}} _compute_x {{NUM}})
echo $X
sub2 NUM:
#!/bin/sh
X=$(just -f {{justfile()}} _compute_x {{NUM}})
file $X
I think it's kinda related to #1906 , no?
Maybe loosely related.
Caching would complement the "replace this bit with the result of running another recipe" idea -- since often the output won't change. But caching on its own doesn't solve the problem i outlined, right?
(Caching could also optimise the "recursive call to just" hack, but ideally that hack wouldn't be necessary in the first place)
Another idea that just occurred to me: allow definitions of user-defined functions.
(just already has the notion of functions, just there's no way to create your own yet)
For now I do this nightmare. At least I'm not aware of a more readable way to sneak "later expantion" in strings. Well. It's readable after you see "{}" that is being replaced by a proper "runtime" $(target) inside rules. It has no input laundering though. So " and ' are prohibited. But that shouldn't be a problem. Hopefully.
test_directory := justfile_directory() / "build/debug/test"
test_executable := test_directory / "{}"
coverage_profraw := test_directory / "{}-coverage.profraw"
coverage_profdata := test_directory / "{}-coverage.profdata"
coverage_html := test_directory / "{}-coverage.html"
coverage_uri := "file://" + coverage_html
test_default_target := "my_test_cmake_target_name"
test target=test_default_target:
{{replace("" \
+ "LLVM_PROFILE_FILE=" + coverage_profraw + " " \
+ test_executable, \
"{}", target)}}
{{replace("" \
+ "llvm-profdata merge " \
+ "-sparse " + coverage_profraw + " " \
+ "-o " + coverage_profdata, \
"{}", target)}}
coverage_write_as_html target=test_default_target:
{{replace("" \
+ "fd . source " \
+ "| fzf --multi --bind " \
+ quote("enter:become(" \
+ "llvm-cov show " \
+ "-format=html " \
+ "-instr-profile=" + coverage_profdata + " " \
+ test_executable + " " \
+ "-sources {+}" \
+ ")") + " " \
+ "> " + coverage_html, \
"{}", target)}}
coverage_open_in_browser target=test_default_target:
{{replace("" \
+ "python " \
+ "-m webbrowser " \
+ "-t " + coverage_uri, \
"{}", target)}}
Another idea that just occurred to me: allow definitions of user-defined functions.
(
justalready has the notion of functions, just there's no way to create your own yet)
It kinda sounds like... a plugin system to me. Since recipes is already function-like and quite encapsulated, implementing those user-defined function is more of a scripting task.
As now my workaround is letting things being written in filesystem directly and read from there. introduce some delay here and there to make things more predictable..