Why doesn't knitr's `root.dir` option apply to relative `child` document paths?

Consider the following reprex:

writeLines(text = "blablabla",
           con = "child.Rmd")

writeLines(text = c("# Minimal child document example",
                    "",
                    "``` {r, child = 'child.Rmd'}",
                    "```"),
           con = "main.Rmd")

tmp_file <- tempfile(fileext = ".Rmd")

readLines(con = "main.Rmd") |>
    stringr::str_replace(pattern = "(# Minimal )",
                         replacement = "\\1R Markdown ") |>
    writeLines(con = tmp_file)

# this renders fine (child document found)
rmarkdown::render(input = "main.Rmd",
                  output_format = rmarkdown::github_document(),
                  quiet = TRUE)

# but this doesn't (child document NOT found)
rmarkdown::render(input = tmp_file,
                  output_format = rmarkdown::github_document(),
                  knit_root_dir = getwd(),
                  quiet = TRUE)
#> Warning in call_block(x): The chunk 'unnamed-chunk-1' has the 'child' option,
#> and this code chunk must be empty. Its code will be ignored.
#> Warning in file(con, "r"): cannot open file './child.Rmd': No such file or
#> directory
#> Quitting from lines 4-4 (./child.Rmd)
#> Error in file(con, "r"): cannot open the connection

Created on 2021-10-09 by the reprex package (v2.0.1)

Obviously, knitr's root.dir option (set via rmarkdown::render(knit_root_dir = getwd())) is ignored for resolving relative child document paths and only respected for code evaluation inside code chunks.

Now I wonder: Why?

And more importantly: How can I change the root directory for resolving relative child document paths? I couldn't find anything about this so far...

(The warning saying The chunk 'unnamed-chunk-1' has the 'child' option and this code chunk must be empty only occurs inside the reprex, and I have no clue why.)

This is possibly an issue or a missing piece.
Or maybe there is a reason because of the order in which things are processed for child document.

@yihui if I may ping you here, is this a known 'non feature' ?

I may be missing something, but it does not feel right.

However, usually I try to never mess with knit_root_dir or knitr option root.dir. :man_shrugging:

I've never heard this issue reported before. I'm not totally sure if it's a bug or feature. To me, root.dir defines the working directory for the code inside code chunks. For package options such as child, I'm not sure if that working directory also applies. Anyway, I just changed the behavior via https://github.com/yihui/knitr/pull/2059 and you may try the dev version of knitr now:

remotes::install_github('yihui/knitr')

Thanks!

1 Like

Thank you very much! I can confirm the child option now respects the root.dir option.

Another question that came up when skimming the linked PR: Are there any (remaining) differences between setting opts_knit$set(root.dir = getwd()) and opts_knit$set(knitr.use.cwd = TRUE)?

They have the same effect when root.dir = getwd(). It's just that the former has precedence over the latter, and the latter is probably easier to set. The former option can be used to specify any directory, not limited to getwd(), and the latter is a boolean option.

BTW, it's options(knitr.use.cwd) instead of opts_knit$set(knitr.use.cwd).

BTW, it's options(knitr.use.cwd) instead of opts_knit$set(knitr.use.cwd) .

Oh yeah, of course. That's what I meant, sorry. :grinning_face_with_smiling_eyes:

They have the same effect when root.dir = getwd() . It's just that the former has precedence over the latter, and the latter is probably easier to set. The former option can be used to specify any directory, not limited to getwd() , and the latter is a boolean option.

Ok, understood. So you might wanna consider deprecating the knitr.use.cwd option to simplify the whole knitr API (to remain backwards compatible, you could internally transform knitr.use.cwd = TRUE into opts_knit$set(root.dir = getwd()) I guess). :upside_down_face:

Thanks! That's a good suggestion!