richfitz / remake

Make-like declarative workflows in R
Other
340 stars 32 forks source link

Auto detect file dependencies in latex or knitr docs #130

Open dfalster opened 8 years ago

dfalster commented 8 years ago

I use remake a lot, including to run knitr reports or compile latex documents. Often these documents require an external file, e.g. a figure. Currently, this requires that we list filenames twice, once in remake, and again within the knitr or latex document. That's entirely workable, but it does often happen that errors arise from forgetting to update names in two places.

An ideal situation would be if remake built a list of file dependencies from examining the document.

I know, this is asking a lot!

dfalster commented 8 years ago

Alternatively, could we somehow ask remake to depend on everything in a certain directory? So if any file in that directory is added or changed, it triggers a rebuild? That would save listing every file twice.

krlmlr commented 8 years ago

I'm using the following helper:

knit_with_deps <- function(target_name, source_name, ..., envir = parent.frame()) {
  dir.create(dirname(target_name), recursive = TRUE, showWarnings = FALSE)

  dep_names <- as.character(unlist(substitute(list(...))[-1]))
  deps <- list(...)
  names(deps) <- dep_names
  deps <- deps[unique(names(deps))]

  dep_env <- as.environment(deps)

  parent.env(dep_env) <- envir

  rmarkdown::render(input = source_name, output_file = basename(target_name), envir = dep_env)
}

remake.yml usage:

  report.html:
    command: knit_with_deps(target_name,
      "report.Rmd", dep1, dep2, dep3, ...)

You could use this and then pass your .bib file as dependency:

  report.html:
    command: knit_with_deps(target_name,
      "report.Rmd", bib_file = "my.bib")

In the .Rmd:

---
bibliography: `r bib_file`
---

Not tested, but might work actually ;-)

dfalster commented 7 years ago

Thanks @krlmlr for the suggestion. I can see that could help with knitting Rmd files, less so with rendering latex. In anycase, for Rmd is your proposed solution different to doing something like:

  report.md:
    knitr:
      input: report.Rmd
    depends:
      - dep1
      - dep2
  report.html:
    command: render(report.md, I("html_document"), quiet=TRUE)