just icon indicating copy to clipboard operation
just copied to clipboard

Reusing logic in recipes

Open vext01 opened this issue 4 months ago • 4 comments

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?

vext01 avatar Aug 18 '25 09:08 vext01

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

vext01 avatar Aug 18 '25 14:08 vext01

I think it's kinda related to #1906 , no?

groutoutlook avatar Aug 25 '25 07:08 groutoutlook

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)

vext01 avatar Aug 31 '25 10:08 vext01

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)

vext01 avatar Sep 11 '25 10:09 vext01

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)}}

weirdo-neutrino avatar Dec 03 '25 19:12 weirdo-neutrino

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)

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..

groutoutlook avatar Dec 04 '25 04:12 groutoutlook