0

I'm writing an R package that generates Makefiles, I need to write a Makefile that calls Rscript before making the targets. A MWE of the problem is below. Make quits in error because the right-hand side of .INIT does not execute. Writing a recipe for file.rds does not suit my needs.

a=1
.INIT=`Rscript -e 'saveRDS('$(a)', "file.rds")'`

all: file2.rds

file2.rds: file.rds
        cp file.rds file2.rds

clean:
        rm file.rds file2.rds

What can I do to fix this Makefile and keep it portable? From the R extensions manual, I can't use $(shell for what I'm trying to accomplish.


EDIT

From @Spacedman's first answer, I learned that .INIT is "expanded"/executed if and only if it is used as a variable somewhere. This is perfect! @Spacedman, I invite you to copy the following Makefile into an answer of your own so I can give you credit.

a=1
.INIT=`Rscript -e 'saveRDS('$(a)', "file.rds")'`

all: file2.rds

file2.rds:
        echo "file.rds should not have been built."

file3.rds:
        echo -n $(.INIT)
        cp file.rds file3.rds

clean:
        rm file.rds file2.rds

The following demonstrates the results I had hoped for.

$ make file2.rds
echo "file.rds should not have been built."
file.rds should not have been built.
$ ls file.rds
ls: cannot access file.rds: No such file or directory
$ make file3.rds
echo -n `Rscript -e 'saveRDS('1', "file.rds")'`
cp file.rds file3.rds
$ ls file.rds
file.rds
landau
  • 5,636
  • 1
  • 22
  • 50
  • 1
    What error? Do you want the Makefile to create `file.rds` via the .INIT before seeing it as a dependency for file2.rds? – Spacedman Feb 14 '17 at 08:06
  • Yes, exactly. Ridiculous, I know, but the actual use case is different enough to make sense. – landau Feb 14 '17 at 12:03
  • I suspect you are stuffed if you can't use `$(shell` and you can't use a recipe. I don't see another way to run an external command. – Spacedman Feb 14 '17 at 19:33
  • What do you mean by *portable*? Does it need to work with BSD `make`? – reinierpost Feb 14 '17 at 22:23
  • BSD `make`, `dmake`, etc. This is for an [R package](https://github.com/wlandau/parallelRemake), so it needs to follow the [Writing R Extensions guidelines](https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Writing-portable-packages). – landau Feb 14 '17 at 23:36
  • Are you planning to put this package on CRAN? – Hong Ooi Feb 17 '17 at 15:25
  • Yes, once its dependencies are also on CRAN. – landau Feb 17 '17 at 15:27

1 Answers1

1

I think you need to use := and $(shell ...) thus:

.INIT := $(shell Rscript -e 'saveRDS('$(a)', "file.rds")')

This makes a simply expanded variable rather than a recursively expanded variable. I think Make doesn't bother even looking at your definition for .INIT because its never used.

Backticks don't work like this in Make, you have to use $(shell ...). Can you really not use $(shell ...) anywhere?

https://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_6.html

Test:

$ rm file.rds  file2.rds 
$ make
cp file.rds file2.rds
$ ls file*rds
file2.rds  file.rds

Which seems to show make has created file.rds via the R script.

If you can put the backquoted string in a recipe, you can make it work (as you discovered!). Note I don't think you need to echo the string, you can just get it expanded and this seems to work:

a=1
.INIT=`Rscript -e 'saveRDS('$(a)', "file.rds")'`

all: file2.rds

file2.rds:
    echo "file.rds should not have been built."

file3.rds:
    $(.INIT)
    cp file.rds file3.rds
Spacedman
  • 92,590
  • 12
  • 140
  • 224
  • Yes, I really can't use `$(shell ...)` anywhere. – landau Feb 14 '17 at 19:28
  • See my updated post. You gave me the idea for the solution. Feel free to make the edit into an answer of your own and I will accept it. – landau Feb 14 '17 at 19:42