Jbuilder rule with a `cp ${^} .` action

I’m working on a web project. I’m trying to maintain a webstatic directory containing all my html and css, and then websrc holds my *.mls, and finally a web directory containing a jbuilder file that concatenates the two.

websrc/main.ml:

type t = string

websrc/jbuild:

(jbuild_version 1)

(executables
 ((names (main))))

webstatic/index.html:


<html>

<head>

<title>why</title>
<script src="main.bc.js">

</head>

<body>

<p>sup</p>

</body>

</html>

web/jbuild:

; -*- mode: lisp -*-

(jbuild_version 1)

(rule
 ((targets ("index.html"))
  (deps ("../websrc/main.bc.js"
         (files_recursively_in "../webstatic")))
  (action
   (system "echo cp ${^} . && cp ${^} ."))))

My goal is to be able to run jbuilder build web/index.html and have _build/default/web get populated with everything from webstatic alongside the main.bc.js that’s built from websrc.

It works but only once.

In fact, it does something weird, it actually deletes the main.bc.js when I modify the main.ml and rebuild:

$ find
.
./web
./web/jbuild
./webstatic
./webstatic/index.html
./websrc
./websrc/jbuild
./websrc/main.ml
$ jbuilder build web/index.html
          sh web/index.html
cp ../websrc/main.bc.js ../webstatic/index.html .
$ ls -lh _build/default/web
total 16K
-rw-r--r-- 1 brian brian 108 Apr  8 02:27 index.html
-rw-r--r-- 1 brian brian 11K Apr  8 02:27 main.bc.js
$ nano websrc/main.ml 
$ cat websrc/main.ml 
type t = unit
$ jbuilder build web/index.html
$ ls -lh _build/default/web
total 4.0K
-rw-r--r-- 1 brian brian 108 Apr  8 02:27 index.html
$ 

My logic is:

  1. websrc/main.bc.js is a dependency of web/index.html
  2. running jbuilder build web/index.html should notice that websrc/main.bc.js is out of date
  3. thus it should be rebuilt and the rule to generate web/index.html (the cp command) should get rerun

I’m perplexed. Am I missing something obvious? Also, is there an easy way to debug jbuilder dependency trees?

There is. Try $ jbuilder rules -m -r. You may optionally add a target name to this command to decrease of the heavily verbose output.

For the sake of debugging would you mind making your targets field complete? In your example, it only lists index.html as a target, but there’s clearly other targets as well.

Thanks!

Updated:

; -*- mode: lisp -*-

(jbuild_version 1)

(rule
 ((targets ("index.html" "main.bc.js"))
  (deps ("../websrc/main.bc.js"
         (files_recursively_in "../webstatic")))
  (action
   (system "echo cp ${^} . && cp ${^} ."))))

Ultimately, I do intend for the targets field to be left incomplete, but I figured that if I tied the required files to web/index.html as a dependency, and if other jbuilder rules fulfilled those dependencies, it’d all come out in the wash.

Listing all the targets like this didn’t change the behavior? Anyway, at this point I’d like a (preferrably) minimally reproducible example. Would like to try this out myself.

I also opened an issue on github, should we move this conversation there?

Huh. On trying it a second time, it does seem to be keeping the file now. Guess I messed up the other day.

Heh, actually my original was meant to be a small, reproducible example. But I think I can get smaller:

dest/jbuild:

(jbuild_version 1)

(rule
 ((targets ("random.txt" "main.bc.js"))
  (deps ("../src/main.bc.js"))
  (action
   (system "perl -e 'use feature qw(say); say rand();' > random.txt && cp ${^} ."))
 )
)

src/main.ml:

type t = unit

src/jbuild:

(jbuild_version 1)

(executables ((names (main))))

So this should generate random.txt (random float via perl) and main.bc.js (cp from another directory).

$ find
.
./dest
./dest/jbuild
./src
./src/main.ml
./src/jbuild
$ jbuilder build dest/random.txt
$ ls -lh _build/default/dest/
total 16K
-rw-r--r-- 1 brian brian 11K Apr 10 18:55 main.bc.js
-rw-r--r-- 1 brian brian  18 Apr 10 18:55 random.txt
# ^ looks good
$ ls -lh _build/default/src/
total 168K
-rwxr-xr-x 1 brian brian 148K Apr 10 18:55 main.bc
-rw-r--r-- 1 brian brian  11K Apr 10 18:55 main.bc.js
-rw-r--r-- 1 brian brian   16 Apr 10 18:55 main.ml
-rw-r--r-- 1 brian brian   13 Apr 10 18:55 main.ml.d
# ^ build artifacts, built at 18:55
$ nano src/main.ml 
$ cat src/main.ml 
type t = string
# ^ updating src/main.ml
$ jbuilder build dest/random.txt
$ ls -lh _build/default/dest/
total 16K
-rw-r--r-- 1 brian brian 11K Apr 10 18:55 main.bc.js
-rw-r--r-- 1 brian brian  18 Apr 10 18:55 random.txt
# ^ stale results
$ ls -lh _build/default/src/
total 168K
-rwxr-xr-x 1 brian brian 148K Apr 10 18:57 main.bc
-rw-r--r-- 1 brian brian  11K Apr 10 18:57 main.bc.js
-rw-r--r-- 1 brian brian   15 Apr 10 18:57 main.ml
-rw-r--r-- 1 brian brian   13 Apr 10 18:57 main.ml.d
# but the build artifact that dest/main.bc.js is copied from has indeed updated, as seen above