# How can I be sure that a C++20 module is not re-compiled after a non-module code change?

**URL:** https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535
**Category:** Code
**Created:** [March 2, 2026, 12:59pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535 "2026-03-02T12:59:33Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![sebjames](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/f1d935/32.png) [@sebjames](https://discourse.cmake.org/u/sebjames)
#### Post date: [March 2, 2026, 12:59pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/1 "2026-03-02T12:59:33Z")

</div>

Hi Cmakers,

This is a question about compiling C++ code with modules.

I’ve built an example that is based on some existing C++ code I have. The class is called `sm::range` . I have made a CMakeLists to compile an executable target, `use_range`. The executable uses `sm::range` compiled as a module.

Here’s the CMakeLists.txt:

```cmake
cmake_minimum_required(VERSION 3.28)

project(modules_test1 LANGUAGES CXX)

set(CMAKE_CXX_FLAGS “-g -Wfatal-errors”)

include_directories(BEFORE ${PROJECT_SOURCE_DIR})

set_source_files_properties (sm/range PROPERTIES LANGUAGE CXX)

add_executable(use_range use_range.cpp)
target_sources(use_range PUBLIC FILE_SET CXX_MODULES FILES sm/range)
target_compile_features(use_range PUBLIC cxx_std_20)

```

Find this at [GitHub - sebsjames/modules\_test1: Testing C++20 standard modules](https://github.com/sebsjames/modules_test1)

I have adapted my C++ header file `sm/range` ([original non-module code](https://github.com/sebsjames/maths/blob/main/sm/range)) with the necessary `module;` and `export` keywords.

The project compiles (after not a little work)!

However, I am not sure whether or not I am seeing the promised benefit of C++ modules in terms of build times, because if I trivially change the use\_range.cpp code, and recompile, the recompilation seems to take as long as the initial compilation.

Here’s the process. Clean:

`[12:40:13 build] cmake --build . --target clean [1/1] Cleaning all built files... Cleaning... 12 files.`

Build:

```bash
[12:41:14 build] time cmake --build . --target all --verbose
Change Dir: '/home/seb/src/modules_test1/build'

Run Build Command(s): /usr/bin/ninja -v all
[1/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -E -x c++ /home/seb/src/modules_test1/use_range.cpp -MT CMakeFiles/use_range.dir/use_range.cpp.o.ddi -MD -MF CMakeFiles/use_range.dir/use_range.cpp.o.ddi.d -fmodules-ts -fdeps-file=CMakeFiles/use_range.dir/use_range.cpp.o.ddi -fdeps-target=CMakeFiles/use_range.dir/use_range.cpp.o -fdeps-format=p1689r5 -o CMakeFiles/use_range.dir/use_range.cpp.o.ddi.i
[2/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -x c++ -g -Wfatal-errors -std=gnu++20 -E -x c++ /home/seb/src/modules_test1/sm/range -MT CMakeFiles/use_range.dir/sm/range.o.ddi -MD -MF CMakeFiles/use_range.dir/sm/range.o.ddi.d -fmodules-ts -fdeps-file=CMakeFiles/use_range.dir/sm/range.o.ddi -fdeps-target=CMakeFiles/use_range.dir/sm/range.o -fdeps-format=p1689r5 -o CMakeFiles/use_range.dir/sm/range.o.ddi.i
[3/6] /usr/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/use_range.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc --dd=CMakeFiles/use_range.dir/CXX.dd @CMakeFiles/use_range.dir/CXX.dd.rsp
[4/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -x c++ -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/sm/range.o -MF CMakeFiles/use_range.dir/sm/range.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/sm/range.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/sm/range.o -c /home/seb/src/modules_test1/sm/range
[5/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/use_range.cpp.o -MF CMakeFiles/use_range.dir/use_range.cpp.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/use_range.cpp.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/use_range.cpp.o -c /home/seb/src/modules_test1/use_range.cpp
[6/6] : && /usr/bin/c++ -g -Wfatal-errors CMakeFiles/use_range.dir/use_range.cpp.o CMakeFiles/use_range.dir/sm/range.o -o use_range && :

real 0m0.641s
user 0m0.567s
sys 0m0.110s

```

Run the program:  
`[12:41:18 build] ./use_range`  
`Range is [-1, 50]`  
`[-1, 50]`

Edit the program (changing a constant value 50.0f to 100.0f):

`[12:41:21 build] emacs ../use_range.cpp`

Re-build:

```bash
[12:41:35 build] time cmake --build . --target all --verbose
Change Dir: '/home/seb/src/modules_test1/build'

Run Build Command(s): /usr/bin/ninja -v all
[1/5] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -E -x c++ /home/seb/src/modules_test1/use_range.cpp -MT CMakeFiles/use_range.dir/use_range.cpp.o.ddi -MD -MF CMakeFiles/use_range.dir/use_range.cpp.o.ddi.d -fmodules-ts -fdeps-file=CMakeFiles/use_range.dir/use_range.cpp.o.ddi -fdeps-target=CMakeFiles/use_range.dir/use_range.cpp.o -fdeps-format=p1689r5 -o CMakeFiles/use_range.dir/use_range.cpp.o.ddi.i
[2/5] /usr/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/use_range.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc --dd=CMakeFiles/use_range.dir/CXX.dd @CMakeFiles/use_range.dir/CXX.dd.rsp
[3/5] /usr/bin/c++ -I/home/seb/src/modules_test1 -x c++ -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/sm/range.o -MF CMakeFiles/use_range.dir/sm/range.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/sm/range.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/sm/range.o -c /home/seb/src/modules_test1/sm/range
[4/5] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/use_range.cpp.o -MF CMakeFiles/use_range.dir/use_range.cpp.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/use_range.cpp.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/use_range.cpp.o -c /home/seb/src/modules_test1/use_range.cpp
[5/5] : && /usr/bin/c++ -g -Wfatal-errors CMakeFiles/use_range.dir/use_range.cpp.o CMakeFiles/use_range.dir/sm/range.o -o use_range && :

real 0m0.617s
user 0m0.532s
sys 0m0.083s

```

Re-run to verify the program changed

`[12:41:41 build] ./use_range Range is [-1, 100] [-1, 100]`

Note 1: I see that there are 6 processes in the first build output and only 5 in the second. The process that does not appear in the second build is this one:

`/usr/bin/c++ -I/home/seb/src/modules_test1 -x c++ -g -Wfatal-errors -std=gnu++20 -E -x c++ /home/seb/src/modules_test1/sm/range -MT CMakeFiles/use_range.dir/sm/range.o.ddi -MD -MF CMakeFiles/use_range.dir/sm/range.o.ddi.d -fmodules-ts -fdeps-file=CMakeFiles/use_range.dir/sm/range.o.ddi -fdeps-target=CMakeFiles/use_range.dir/sm/range.o -fdeps-format=p1689r5 -o CMakeFiles/use_range.dir/sm/range.o.ddi.i`

Which as far as I can tell is a preprocessing step that creates a preprocessed version of the code for the module.

I timed each of the 6 build steps and this is not one that takes a long time; it’s not an actual compilation.

Note 2: I wonder if I need to separate my sm/range into two files, say sm/range with the original C++ code implementation and sm/range.ixx (or .cppm) with the module interface?

So: Is there a reason that a recompile after editing use\_range.cpp is not significantly faster than the clean-build?

Thanks for reading such a long post and best wishes,

Seb James

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 2, 2026, 2:08pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/2 "2026-03-02T14:08:52Z")

</div>

The command that isn’t run is the scan of the `cm/range` file (to get its module dependencies). Can you run `ninja -d explain` to see why it thinks it needs to recompile the `sm/range` file?

---

<div class="post-metadata">

### Author: ![sebjames](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/f1d935/32.png) [@sebjames](https://discourse.cmake.org/u/sebjames)
#### Post date: [March 2, 2026, 2:20pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/3 "2026-03-02T14:20:58Z")

</div>

Thanks for the reply!

Here’s the same process using ninja -d explain --verbose:

`[14:22:11 build] cmake --build . --target clean`  
`[1/1] Cleaning all built files…`  
`Cleaning… 12 files.`

`[14:23:20 build] ninja -d explain --verbose`  
`ninja explain: depfile ‘CMakeFiles/use_range.dir/use_range.cpp.o.ddi.d’ is missing`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o.ddi is dirty`  
`ninja explain: depfile ‘CMakeFiles/use_range.dir/sm/range.o.ddi.d’ is missing`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o.ddi is dirty`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o.modmap is dirty`  
`ninja explain: output CMakeFiles/use_range.dir/sm.range.gcm of phony edge with no inputs doesn’t exist`  
`ninja explain: CMakeFiles/use_range.dir/sm.range.gcm is dirty`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o.modmap is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o is dirty`  
`ninja explain: use_range is dirty`  
`[1/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -E -x c++ /home/seb/src/modules_test1/use_range.cpp -MT CMakeFiles/use_range.dir/use_range.cpp.o.ddi -MD -MF CMakeFiles/use_range.dir/use_range.cpp.o.ddi.d -fmodules-ts -fdeps-file=CMakeFiles/use_range.dir/use_range.cpp.o.ddi -fdeps-target=CMakeFiles/use_range.dir/use_range.cpp.o -fdeps-format=p1689r5 -o CMakeFiles/use_range.dir/use_range.cpp.o.ddi.i`  
`[2/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -x c++ -g -Wfatal-errors -std=gnu++20 -E -x c++ /home/seb/src/modules_test1/sm/range -MT CMakeFiles/use_range.dir/sm/range.o.ddi -MD -MF CMakeFiles/use_range.dir/sm/range.o.ddi.d -fmodules-ts -fdeps-file=CMakeFiles/use_range.dir/sm/range.o.ddi -fdeps-target=CMakeFiles/use_range.dir/sm/range.o -fdeps-format=p1689r5 -o CMakeFiles/use_range.dir/sm/range.o.ddi.i`  
`[3/6] /usr/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/use_range.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc --dd=CMakeFiles/use_range.dir/CXX.dd @CMakeFiles/use_range.dir/CXX.dd.rsp`  
`ninja explain: loading dyndep file ‘CMakeFiles/use_range.dir/CXX.dd’`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o.modmap is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o.modmap is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm.range.gcm is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm.range.gcm is dirty`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o is dirty`  
`ninja explain: use_range is dirty`  
`[4/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -x c++ -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/sm/range.o -MF CMakeFiles/use_range.dir/sm/range.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/sm/range.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/sm/range.o -c /home/seb/src/modules_test1/sm/range`  
`[5/6] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/use_range.cpp.o -MF CMakeFiles/use_range.dir/use_range.cpp.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/use_range.cpp.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/use_range.cpp.o -c /home/seb/src/modules_test1/use_range.cpp`  
`[6/6] : && /usr/bin/c++ -g -Wfatal-errors CMakeFiles/use_range.dir/use_range.cpp.o CMakeFiles/use_range.dir/sm/range.o -o use_range && :`

`[14:23:39 build] ./use_range`  
`Range is [-1, 100]`  
`[-1, 100]`

`[14:23:26 build] emacs ../use_range.cpp`

`[14:23:38 build] ninja -d explain --verbose`  
`ninja explain: output CMakeFiles/use_range.dir/use_range.cpp.o.ddi older than most recent input /home/seb/src/modules_test1/use_range.cpp (1772461406087494134 vs 1772461417336117667)`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o.ddi is dirty`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o.modmap is dirty`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o.modmap is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o is dirty`  
`ninja explain: use_range is dirty`  
`[1/5] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -E -x c++ /home/seb/src/modules_test1/use_range.cpp -MT CMakeFiles/use_range.dir/use_range.cpp.o.ddi -MD -MF CMakeFiles/use_range.dir/use_range.cpp.o.ddi.d -fmodules-ts -fdeps-file=CMakeFiles/use_range.dir/use_range.cpp.o.ddi -fdeps-target=CMakeFiles/use_range.dir/use_range.cpp.o -fdeps-format=p1689r5 -o CMakeFiles/use_range.dir/use_range.cpp.o.ddi.i`  
`[2/5] /usr/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/use_range.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc --dd=CMakeFiles/use_range.dir/CXX.dd @CMakeFiles/use_range.dir/CXX.dd.rsp`  
`ninja explain: loading dyndep file ‘CMakeFiles/use_range.dir/CXX.dd’`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o.modmap is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o.modmap is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm.range.gcm is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm.range.gcm is dirty`  
`ninja explain: CMakeFiles/use_range.dir/use_range.cpp.o is dirty`  
`ninja explain: CMakeFiles/use_range.dir/sm/range.o is dirty`  
`ninja explain: use_range is dirty`  
`[3/5] /usr/bin/c++ -I/home/seb/src/modules_test1 -x c++ -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/sm/range.o -MF CMakeFiles/use_range.dir/sm/range.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/sm/range.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/sm/range.o -c /home/seb/src/modules_test1/sm/range`  
`[4/5] /usr/bin/c++ -I/home/seb/src/modules_test1 -g -Wfatal-errors -std=gnu++20 -MD -MT CMakeFiles/use_range.dir/use_range.cpp.o -MF CMakeFiles/use_range.dir/use_range.cpp.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/use_range.dir/use_range.cpp.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/use_range.dir/use_range.cpp.o -c /home/seb/src/modules_test1/use_range.cpp`  
`[5/5] : && /usr/bin/c++ -g -Wfatal-errors CMakeFiles/use_range.dir/use_range.cpp.o CMakeFiles/use_range.dir/sm/range.o -o use_range && :`

`[14:23:41 build] ./use_range`  
`Range is [-1, 50]`  
`[-1, 50]`

(edited to replace with `ninja -d explain --verbose`)

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 2, 2026, 4:43pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/4 "2026-03-02T16:43:16Z")

</div>

Strange. So yes, the scanning bits make sense. What doesn’t make sense is that `sm/range.o.modmap` is dirty after scanning to make `use_range.cpp.o.ddi`. The `.modmap` files are outputs of the `cmake -E cmake_ninja_dyndep` command which uses `restat = 1` and should only update the outputs if they have different contents. Can you confirm that the `_DYNDEP_` rule for `use_range` has `restat = 1` on it (see `CMakeFiles/rules.ninja`). If so, can you compare the `.modmap` contents before and after the rebuild? Also its `mtime` if it is unchanged.

---

<div class="post-metadata">

### Author: ![sebjames](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/f1d935/32.png) [@sebjames](https://discourse.cmake.org/u/sebjames)
#### Post date: [March 2, 2026, 4:53pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/5 "2026-03-02T16:53:24Z")

</div>

Thanks - I’ll do what you’ve asked and come back with results.

Independently, I was experimenting with an alternative compiler. I used gcc-14 in all the output that I have previously posted. I just tried clang-18 and this appears to compile in half the time after the ‘trivial edit’ and only carries out 4 steps. Could this be a compiler issue?

---

<div class="post-metadata">

### Author: ![sebjames](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/f1d935/32.png) [@sebjames](https://discourse.cmake.org/u/sebjames)
#### Post date: [March 2, 2026, 5:33pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/6 "2026-03-02T17:33:58Z")

</div>

Here’s the CMakeFiles/rules.ninja:

```auto
# CMAKE generated file: DO NOT EDIT!
# Generated by "Ninja" Generator, CMake Version 3.28

# This file contains all the rules used to get the outputs files
# built from the input files.
# It is included in the main 'build.ninja'.

# =============================================================================
# Project: modules_test1
# Configurations: 
# =============================================================================
# =============================================================================

#############################################
# Rule for generating CXX dependencies.

rule CXX_SCAN__use_range_
  depfile = $DEP_FILE
  command = /usr/bin/c++ $DEFINES $INCLUDES $FLAGS -E -x c++ $in -MT $DYNDEP_INTERMEDIATE_FILE -MD -MF $DEP_FILE -fmodules-ts -fdeps-file=$DYNDEP_INTERMEDIATE_FILE -fdeps-target=$OBJ_FILE -fdeps-format=p1689r5 -o $PREPROCESSED_OUTPUT_FILE
  description = Scanning $in for CXX dependencies

#############################################
# Rule to generate ninja dyndep files for CXX.

rule CXX_DYNDEP__use_range_
  command = /usr/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/use_range.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc --dd=$out @$out.rsp
  description = Generating CXX dyndep file $out
  rspfile = $out.rsp
  rspfile_content = $in

#############################################
# Rule for compiling CXX files.

rule CXX_COMPILER__use_range_scanned_
  depfile = $DEP_FILE
  deps = gcc
  command = ${LAUNCHER}${CODE_CHECK}/usr/bin/c++ $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -fmodules-ts -fmodule-mapper=$DYNDEP_MODULE_MAP_FILE -MD -fdeps-format=p1689r5 -x c++ -o $out -c $in
  description = Building CXX object $out

#############################################
# Rule for compiling CXX files.

rule CXX_COMPILER__use_range_unscanned_
  depfile = $DEP_FILE
  deps = gcc
  command = ${LAUNCHER}${CODE_CHECK}/usr/bin/c++ $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -o $out -c $in
  description = Building CXX object $out

#############################################
# Rule for linking CXX executable.

rule CXX_EXECUTABLE_LINKER__use_range_
  command = $PRE_LINK && /usr/bin/c++ $FLAGS $LINK_FLAGS $in -o $TARGET_FILE $LINK_PATH $LINK_LIBRARIES && $POST_BUILD
  description = Linking CXX executable $TARGET_FILE
  restat = $RESTAT

#############################################
# Rule for running custom commands.

rule CUSTOM_COMMAND
  command = $COMMAND
  description = $DESC

#############################################
# Rule for re-running cmake.

rule RERUN_CMAKE
  command = /usr/bin/cmake --regenerate-during-build -S/home/seb/src/modules_test1 -B/home/seb/src/modules_test1/bgcc14
  description = Re-running CMake...
  generator = 1

#############################################
# Rule for cleaning all built files.

rule CLEAN
  command = /usr/bin/ninja $FILE_ARG -t clean $TARGETS
  description = Cleaning all built files...

#############################################
# Rule for printing all primary targets available.

rule HELP
  command = /usr/bin/ninja -t targets
  description = All primary targets available:

```

The only reference to restat appears to be to place the value of the variable RESTAT into restat, but there’s no other reference to RESTAT. But then there’s a file CMakeFiles/use\_range.dir/CXX.dd which contains:

```auto
ninja_dyndep_version = 1.0
build CMakeFiles/use_range.dir/use_range.cpp.o: dyndep | CMakeFiles/use_range.dir/sm.range.gcm

build CMakeFiles/use_range.dir/sm/range.o | CMakeFiles/use_range.dir/sm.range.gcm: dyndep
restat = 1

```

Here’s the modmap changes:

After build, but before rebuild:

```auto
[17:30:36 use_range.dir] cat use_range.cpp.o.modmap
$root .
sm.range CMakeFiles/use_range.dir/sm.range.gcm

[17:31:07 use_range.dir] ls -l use_range.cpp.o.modmap
-rw-rw-r-- 1 seb seb 55 Mar 2 17:25 use_range.cpp.o.modmap

```

After an edit to use\_range.cpp and a rebuild:

`[17:32:48 use_range.dir] ls -l use_range.cpp.o.modmap`  
`-rw-rw-r-- 1 seb seb 55 Mar 2 17:32 use_range.cpp.o.modmap`  
`[17:32:53 use_range.dir] cat use_range.cpp.o.modmap`  
`$root .`  
`sm.range CMakeFiles/use_range.dir/sm.range.gcm`

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 2, 2026, 5:40pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/7 "2026-03-02T17:40:04Z")

</div>

Yes, the `CXX_DYNDEP__use_range_` rule is missing `restat = 1`. What version of CMake is this?

---

<div class="post-metadata">

### Author: ![sebjames](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/f1d935/32.png) [@sebjames](https://discourse.cmake.org/u/sebjames)
#### Post date: [March 2, 2026, 5:40pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/8 "2026-03-02T17:40:52Z")

</div>

I’m using the one packaged on Ubuntu 24.04

```auto
[17:40:03 use_range.dir] cmake --version
cmake version 3.28.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).

```

```auto
[17:40:16 use_range.dir] apt show cmake Package: cmake Version: 3.28.3-1build7 Priority: optional Section: devel Origin: Ubuntu Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> Original-Maintainer: Debian CMake Team <pkg-cmake-team@lists.alioth.debian.org> Bugs: https://bugs.launchpad.net/ubuntu/+filebug Installed-Size: 37.4 MB Depends: libarchive13t64 (>= 3.3.3), libc6 (>= 2.38), libcurl4t64 (>= 7.16.2), libexpat1 (>= 2.0.1), libgcc-s1 (>= 3.0), libjsoncpp25 (>= 1.9.5), librhash0 (>= 1.2.6), libstdc++6 (>= 13.1), libuv1t64 (>= 1.38.0), zlib1g (>= 1:1.1.4), cmake-data (= 3.28.3-1build7), procps

```

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 2, 2026, 5:42pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/9 "2026-03-02T17:42:55Z")

</div>

Found it. I fixed this in [this MR](https://gitlab.kitware.com/cmake/cmake/-/merge_requests/9259) which landed in 3.29.

---

<div class="post-metadata">

### Author: ![sebjames](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/f1d935/32.png) [@sebjames](https://discourse.cmake.org/u/sebjames)
#### Post date: [March 2, 2026, 5:43pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/10 "2026-03-02T17:43:58Z")

</div>

Great! I can update my minimum version for cmake. Thanks for the help today! I’ll verify 3.29 works and then mark that the solution.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 2, 2026, 5:45pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/11 "2026-03-02T17:45:45Z")

</div>

It was also backported to 3.28.4, so…Canonical really should pick up patch releases on a better schedule.

---

<div class="post-metadata">

### Author: ![sebjames](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/f1d935/32.png) [@sebjames](https://discourse.cmake.org/u/sebjames)
#### Post date: [March 2, 2026, 6:38pm UTC](https://discourse.cmake.org/t/how-can-i-be-sure-that-a-c-20-module-is-not-re-compiled-after-a-non-module-code-change/15535/12 "2026-03-02T18:38:44Z")

</div>

> I can confirm that the correct rebuild behaviour is found in cmake 3.29.0 and in 3.28.5 (3.28.4 had the same behaviour as 3.28.3). Thanks again for the help!
