【问题标题】:Makefile for conditional rendering of Rmarkdown files用于条件渲染 Rmarkdown 文件的 Makefile
【发布时间】:2017-11-09 11:56:55
【问题描述】:

我是 Makefile 新手,我想在 Rmarkdown 文件更改时间戳时使用它来渲染它们,以生成相应的 .pdf 文件。文件data_prep_1.Rmd 被渲染为.pdf 并额外生成输出文件all_sample_data.csv。当data_prep_1.Rmd 被改变(或者它的时间戳被改变)时,它应该被重新渲染,../bioinformatics/Reads_by_sample.Rmddistance_matrices.Rmd 因为它们依赖于.csv 文件。如果../bioinformatics/Reads_by_sample.Rmd 的时间戳相对于它的.pdf 发生了变化,那么只有这个.Rmd 文件应该被重新渲染。

将所有 : 和 .pdf.csv 文件放在第一行将导致两个块在每次调用 make 时运行。
我有:

../../data/all_sample_data.csv : data_prep_1.Rmd
    Rscript -e "rmarkdown::render('data_prep_1.Rmd')"
    Rscript -e "rmarkdown::render('../bioinformatics/Reads_by_sample.Rmd')"
    Rscript -e "rmarkdown::render('distance_matrices.Rmd')"

../bioinformatics/Reads_by_sample.pdf :  ../bioinformatics/Reads_by_sample.Rmd
    Rscript -e "rmarkdown::render('../bioinformatics/Reads_by_sample.Rmd')"

如果我执行touch data_prep_1.Rmd,然后执行make,则第一个块将按需要运行。
但是如果我这样做touch ../bioinformatics/Reads_by_sample.Rmd,那么make

我收到make: '../../data/all_sample_data.csv is up to date'

我知道这是对 Makefile 的笨拙使用,但我怎样才能在单个 Makefile 中获得我想要的行为?

【问题讨论】:

  • 您需要添加一个PHONY 目标,例如all,它具有../../data/all_sample_data.csv../bioinformatics/Reads_by_sample.pdf 作为先决条件,并且是makefile 中的第一个目标。
  • 但是即使只更改了 Reads_by_sample.Rmd,第一个块中的所有命令也不会运行吗?我想在先决条件没有改变时避免渲染。
  • 除非您修改data_prep_1.Rmd,否则第一条规则的配方不会运行。

标签: makefile rstudio r-markdown gnu-make


【解决方案1】:

上面的 cmets 令人困惑,但隐藏了答案。

make 运行时,它不会尝试构建makefile 中的每一个目标。相反,它只尝试在 makefile 中构建 first 目标。在您的 makefile 中,第一个目标是 ../../data/all_sample_data.csv,因此这是 make 将尝试构建的唯一目标。

您可以通过将它们添加到命令行来构建其他目标;例如make ../bioinformatics/Reads_by_sample.pdf

如果您总是想要一组特定的目标,您所做的就是创建一个新目标作为 makefile 中的第一个目标,其中列出了您想要创建的所有其他目标作为先决条件。按照惯例,但只是惯例,该目标通常称为all。所以它看起来像这样:

all: ../../data/all_sample_data.csv ../bioinformatics/Reads_by_sample.pdf
.PHONY: all

../../data/all_sample_data.csv : data_prep_1.Rmd
        Rscript -e "rmarkdown::render('data_prep_1.Rmd')"
        Rscript -e "rmarkdown::render('../bioinformatics/Reads_by_sample.Rmd')"
        Rscript -e "rmarkdown::render('distance_matrices.Rmd')"

../bioinformatics/Reads_by_sample.pdf :  ../bioinformatics/Reads_by_sample.Rmd
        Rscript -e "rmarkdown::render('../bioinformatics/Reads_by_sample.Rmd')"

.PHONY 不是必需的,但很有用,它告诉 make all 不是真正的文件,而只是 makefile 中的一个虚拟目标。

您还应该考虑创建一些 make 变量并使用 automatic variables 来简化您的 makefile,例如:

BIODIR := ../bioinformatics
DATADIR := ../../data

all: $(DATADIR)/all_sample_data.csv $(BIODIR)/Reads_by_sample.pdf
.PHONY: all

$(DATADIR)/all_sample_data.csv : data_prep_1.Rmd
        Rscript -e "rmarkdown::render('$<')"
        Rscript -e "rmarkdown::render('$(BIODIR)/Reads_by_sample.Rmd')"
        Rscript -e "rmarkdown::render('distance_matrices.Rmd')"

$(BIODIR)/Reads_by_sample.pdf :  $(BIODIR)/Reads_by_sample.Rmd
        Rscript -e "rmarkdown::render('$<')"

如果您有很多这些类型的文件,您可以更进一步并使用模式规则等。

另外,看来你的第一个目标不仅应该依赖于data_prep_1.Rmd,还应该依赖于distance_matrices.RmdReads_by_sample.Rmd;如果有任何这些更改,您不想重建吗?

【讨论】:

  • 在你最后的评论中,你的意思是 All 应该有两个 .Rmd 文件作为依赖,对吧?所以一个额外的目标和依赖是: ./geo_distances_populations.csv : distance_matrices.Rmd Rscript -e "rmarkdown::render('distance_matrices.Rmd')" 并且 .csv 文件也会进入所有文件?
  • 您可以根据需要在同一规则行中列出所有先决条件。我不太确定该配方的哪个方面会导致创建输出 CSV 文件。但你可以说:.../all_sample_data.csv : data_prep_1.Rmd distance_matrices.Rmd .../Reads_by_sample.Rmd
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-19
  • 1970-01-01
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多