Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changes/root-source-dir-rebuild.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Stop `rescript` from rebuilding the modules of a `"."` or `"./src"` source
dir, and their dependents, on every build when nothing changed.
9 changes: 6 additions & 3 deletions rewatch-ocaml/build_artifacts.ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ let relative_output_directory path (spec : Config.package_spec) =

let generated_js_path (config : Config.t) path (spec : Config.package_spec) =
let output_dir = relative_output_directory path spec in
(* The output of a module of the package root is "A.js" or "lib/es6/A.js",
as the output inventory spells it, not "./A.js" or "lib/es6/./A.js". *)
Filename.concat config.root
(Filename.concat output_dir
(Filename.remove_extension (Filename.basename path)
^ Config.package_spec_suffix config spec))
(Source.without_current_dir_segments
(Filename.concat output_dir
(Filename.remove_extension (Filename.basename path)
^ Config.package_spec_suffix config spec)))

let published_ast_path ~ocaml_dir source =
Filename.concat ocaml_dir (Filename.basename (Source.ast_path source))
Expand Down
26 changes: 24 additions & 2 deletions rewatch-ocaml/source.ml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@ let interface_mismatch_error implementation interface =
different path names or different cases: `%s` vs `%s`"
implementation interface)

(* [path] without its "." segments, so a file found below source dir "." or
"./src" has the path bsc records for it, "A.res" or "src/A.res". Cleanup and
freshness match modules to their ASTs and outputs by these paths: a
"./A.res" matched no AST, and a "./ui/U.js" in the inventory of "." matched
no module's output, so each build removed them and rebuilt the module. *)
let rec without_current_dir_segments path =
let directory = Filename.dirname path in
let base = Filename.basename path in
if directory = path || path = Filename.current_dir_name then path
else if base = Filename.current_dir_name then
without_current_dir_segments directory
else
match without_current_dir_segments directory with
| directory when directory = Filename.current_dir_name -> base
| directory -> Filename.concat directory base

(* A package source tree has three consumers with deliberately different
recursion rules. Compilation follows directory links and honors source
activation/subdirs; cleanup inventories every real descendant but treats
Expand Down Expand Up @@ -134,7 +150,11 @@ let scan_source ~root (source : Config.source) ~discover_modules ~on_missing
| Some kind ->
candidates :=
{
file = {path = relative_path; modified = metadata.Unix.st_mtime};
file =
{
path = without_current_dir_segments relative_path;
modified = metadata.Unix.st_mtime;
};
kind;
is_dev = source.is_dev;
}
Expand All @@ -143,7 +163,9 @@ let scan_source ~root (source : Config.source) ~discover_modules ~on_missing
List.iter
(fun name ->
let relative_path = Filename.concat relative name in
let absolute_path = Filename.concat root relative_path in
let absolute_path =
Filename.concat root (without_current_dir_segments relative_path)
in
match Unix.lstat absolute_path with
| metadata -> (
match metadata.Unix.st_kind with
Expand Down
4 changes: 4 additions & 0 deletions rewatch-ocaml/source.mli
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ type source_kind = Implementation | Interface

val source_kind : string -> source_kind option
val module_name : string -> string

val without_current_dir_segments : string -> string
(** [path] without its "." segments: "./src/./A.res" is "src/A.res". *)

val namespace_members : entry:string option -> module_ list -> module_ list

val duplicate_error :
Expand Down
57 changes: 57 additions & 0 deletions rewatch/tests/compile/23-root-source-dir-rebuild.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash

cd $(dirname $0)
source "../utils.sh"

bold "Test: Modules of source dir \".\" are not rebuilt when nothing changed"

fixtures=$(mktemp -d 2>/dev/null || mktemp -d -t rewatch-root-source-dir)
trap "rm -rf '$fixtures'" EXIT

# "." spells its modules "./Root.res" unless the build system drops the
# segment, while bsc records "Root.res" in the AST and the output inventory
# finds "Root.mjs" or "lib/es6/Root.mjs". A mismatch makes every build clean
# those artifacts.
check_layout() {
local name=$1 in_source=$2 root_output=$3 ui_output=$4
local fixture="$fixtures/$name"
mkdir -p "$fixture/ui"

cat > "$fixture/rescript.json" <<JSON
{
"name": "rewatch-root-source-dir-$name",
"sources": [{ "dir": "." }, { "dir": "ui" }],
"package-specs": { "module": "esmodule", "in-source": $in_source },
"suffix": ".mjs"
}
JSON

printf 'let value = 1\n' > "$fixture/Root.res"
printf 'let value = Root.value + 1\n' > "$fixture/ui/Component.res"

if ! compiler_output=$(cd "$fixture" && "$REWATCH_EXECUTABLE" build 2>&1); then
error "Initial $name fixture build failed"
printf "%s\n" "$compiler_output" >&2
return 1
fi

# Several unchanged builds, since a removed output only shows on the next one.
for attempt in 1 2 3; do
compiler_output=$(cd "$fixture" && "$REWATCH_EXECUTABLE" build 2>&1)
if ! printf '%s\n' "$compiler_output" | grep -q "Compiled 0 modules"; then
error "Unchanged $name build $attempt recompiled modules"
printf "%s\n" "$compiler_output" >&2
return 1
fi
done

if [ ! -f "$fixture/$root_output" ] || [ ! -f "$fixture/$ui_output" ]; then
error "Unchanged $name builds removed generated outputs"
return 1
fi
}

check_layout in-source true Root.mjs ui/Component.mjs || exit 1
check_layout out-of-source false lib/es6/Root.mjs lib/es6/ui/Component.mjs || exit 1

success "Modules of source dir \".\" are not rebuilt when nothing changed"
1 change: 1 addition & 0 deletions rewatch/tests/suite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ fi
./compile/20-schedule-independent-modules-after-error.sh &&
./compile/21-rebuild-dependency-for-package-specs.sh &&
./compile/22-qualified-own-namespace-dependency.sh &&
./compile/23-root-source-dir-rebuild.sh &&
./compile/14-no-testrepo-changes.sh &&
./compile/15-no-new-files.sh &&
./compile/16-snapshots-unchanged.sh &&
Expand Down
Loading