From 1b00525574c5bd6367490212af5e64280445a564 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 18 Aug 2025 15:56:33 -0700 Subject: [PATCH 1/2] Add test for relative dest-dir --- tests/testsuite/build.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index 6bbf061d..7d02c868 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -66,3 +66,22 @@ fn book_toml_isnt_required() { str![[r##"

Chapter 1

"##]], ); } + +// Dest dir relative path behavior. +#[test] +fn dest_dir_relative_path() { + let mut test = BookTest::from_dir("build/basic_build"); + let current_dir = test.dir.join("work"); + std::fs::create_dir_all(¤t_dir).unwrap(); + test.run("build", |cmd| { + cmd.args(&["--dest-dir", "foo", ".."]) + .current_dir(¤t_dir) + .expect_stderr(str![[r#" +[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started +[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend +[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/work/../foo` + +"#]]); + }); + assert!(test.dir.join("foo/index.html").exists()); +} From c17708110408a350fc17ed40911f91002689a934 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 18 Aug 2025 16:23:18 -0700 Subject: [PATCH 2/2] Change CLI dest-dir to be relative to the current directory This changes the `--dest-dir` flag so that it is relative to the current directory, not the book root. This has been a source of confusion for several people. Fixes https://github.com/rust-lang/mdBook/issues/698 --- crates/mdbook-core/src/config.rs | 2 +- guide/src/cli/build.md | 2 +- guide/src/cli/clean.md | 2 +- guide/src/cli/serve.md | 2 +- guide/src/cli/watch.md | 2 +- src/cmd/build.rs | 5 +---- src/cmd/clean.rs | 4 +++- src/cmd/command_prelude.rs | 12 +++++++++++- src/cmd/serve.rs | 4 +--- src/cmd/watch.rs | 4 +--- tests/testsuite/build.rs | 4 ++-- 11 files changed, 24 insertions(+), 19 deletions(-) diff --git a/crates/mdbook-core/src/config.rs b/crates/mdbook-core/src/config.rs index c53d03a3..77120482 100644 --- a/crates/mdbook-core/src/config.rs +++ b/crates/mdbook-core/src/config.rs @@ -366,7 +366,7 @@ impl TextDirection { #[serde(default, rename_all = "kebab-case", deny_unknown_fields)] #[non_exhaustive] pub struct BuildConfig { - /// Where to put built artefacts relative to the book's root directory. + /// Where to put built artifacts relative to the book's root directory. pub build_dir: PathBuf, /// Should non-existent markdown files specified in `SUMMARY.md` be created /// if they don't exist? diff --git a/guide/src/cli/build.md b/guide/src/cli/build.md index 36e053fd..a3427417 100644 --- a/guide/src/cli/build.md +++ b/guide/src/cli/build.md @@ -30,7 +30,7 @@ your default web browser after building it. #### `--dest-dir` The `--dest-dir` (`-d`) option allows you to change the output directory for the -book. Relative paths are interpreted relative to the book's root directory. If +book. Relative paths are interpreted relative to the current directory. If not specified it will default to the value of the `build.build-dir` key in `book.toml`, or to `./book`. diff --git a/guide/src/cli/clean.md b/guide/src/cli/clean.md index f0932805..a2f94b62 100644 --- a/guide/src/cli/clean.md +++ b/guide/src/cli/clean.md @@ -20,7 +20,7 @@ mdbook clean path/to/book The `--dest-dir` (`-d`) option allows you to override the book's output directory, which will be deleted by this command. Relative paths are interpreted -relative to the book's root directory. If not specified it will default to the +relative to the current directory. If not specified it will default to the value of the `build.build-dir` key in `book.toml`, or to `./book`. ```bash diff --git a/guide/src/cli/serve.md b/guide/src/cli/serve.md index 4603df8e..43aa42ec 100644 --- a/guide/src/cli/serve.md +++ b/guide/src/cli/serve.md @@ -40,7 +40,7 @@ default web browser after starting the server. #### `--dest-dir` The `--dest-dir` (`-d`) option allows you to change the output directory for the -book. Relative paths are interpreted relative to the book's root directory. If +book. Relative paths are interpreted relative to the current directory. If not specified it will default to the value of the `build.build-dir` key in `book.toml`, or to `./book`. diff --git a/guide/src/cli/watch.md b/guide/src/cli/watch.md index be2f5be4..364dd9e5 100644 --- a/guide/src/cli/watch.md +++ b/guide/src/cli/watch.md @@ -23,7 +23,7 @@ your default web browser. #### `--dest-dir` The `--dest-dir` (`-d`) option allows you to change the output directory for the -book. Relative paths are interpreted relative to the book's root directory. If +book. Relative paths are interpreted relative to the current directory. If not specified it will default to the value of the `build.build-dir` key in `book.toml`, or to `./book`. diff --git a/src/cmd/build.rs b/src/cmd/build.rs index a04adc5f..b4ac4857 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -2,7 +2,6 @@ use super::command_prelude::*; use crate::{get_book_dir, open}; use anyhow::Result; use mdbook_driver::MDBook; -use std::path::PathBuf; // Create clap subcommand arguments pub fn make_subcommand() -> Command { @@ -18,9 +17,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let book_dir = get_book_dir(args); let mut book = MDBook::load(book_dir)?; - if let Some(dest_dir) = args.get_one::("dest-dir") { - book.config.build.build_dir = dest_dir.into(); - } + set_dest_dir(args, &mut book); book.build()?; diff --git a/src/cmd/clean.rs b/src/cmd/clean.rs index a07bb2ae..1bea7da0 100644 --- a/src/cmd/clean.rs +++ b/src/cmd/clean.rs @@ -21,7 +21,9 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let book = MDBook::load(book_dir)?; let dir_to_remove = match args.get_one::("dest-dir") { - Some(dest_dir) => dest_dir.into(), + Some(dest_dir) => std::env::current_dir() + .expect("current dir should be valid") + .join(dest_dir), None => book.root.join(&book.config.build.build_dir), }; diff --git a/src/cmd/command_prelude.rs b/src/cmd/command_prelude.rs index d5df3af9..b2a91ae9 100644 --- a/src/cmd/command_prelude.rs +++ b/src/cmd/command_prelude.rs @@ -1,6 +1,7 @@ //! Helpers for building the command-line arguments for commands. pub use clap::{Arg, ArgMatches, Command, arg}; +use mdbook_driver::MDBook; use std::path::PathBuf; pub trait CommandExt: Sized { @@ -15,7 +16,7 @@ pub trait CommandExt: Sized { .value_parser(clap::value_parser!(PathBuf)) .help( "Output directory for the book\n\ - Relative paths are interpreted relative to the book's root directory.\n\ + Relative paths are interpreted relative to the current directory.\n\ If omitted, mdBook uses build.build-dir from book.toml \ or defaults to `./book`.", ), @@ -57,3 +58,12 @@ impl CommandExt for Command { self.arg(arg) } } + +pub fn set_dest_dir(args: &ArgMatches, book: &mut MDBook) { + if let Some(dest_dir) = args.get_one::("dest-dir") { + let build_dir = std::env::current_dir() + .expect("current dir should be valid") + .join(dest_dir); + book.config.build.build_dir = build_dir; + } +} diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 5fc20f9a..6d8a0616 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -62,9 +62,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { book.config .set("output.html.live-reload-endpoint", LIVE_RELOAD_ENDPOINT) .expect("live-reload-endpoint update failed"); - if let Some(dest_dir) = args.get_one::("dest-dir") { - book.config.build.build_dir = dest_dir.into(); - } + set_dest_dir(args, book); // Override site-url for local serving of the 404 file book.config.set("output.html.site-url", "/").unwrap(); }; diff --git a/src/cmd/watch.rs b/src/cmd/watch.rs index 4f637505..bd9c19b2 100644 --- a/src/cmd/watch.rs +++ b/src/cmd/watch.rs @@ -38,9 +38,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let mut book = MDBook::load(&book_dir)?; let update_config = |book: &mut MDBook| { - if let Some(dest_dir) = args.get_one::("dest-dir") { - book.config.build.build_dir = dest_dir.into(); - } + set_dest_dir(args, book); }; update_config(&mut book); diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index 7d02c868..7b6cfc4d 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -79,9 +79,9 @@ fn dest_dir_relative_path() { .expect_stderr(str![[r#" [TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started [TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend -[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/work/../foo` +[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/work/foo` "#]]); }); - assert!(test.dir.join("foo/index.html").exists()); + assert!(current_dir.join("foo/index.html").exists()); }