# Changes to C++ headers not causing Swift code to recompile

**URL:** <https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248>\
**Category:** C++ interoperability\
**Tags:** cmake, ninja\
**Created:** [March 3, 2025, 5:34pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248 "2025-03-03T17:34:22Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![roopekv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roopekv/32/18084_2.png) [@roopekv](https://forums.swift.org/u/roopekv)\
**Post date:** [March 3, 2025, 5:34pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248/1 "2025-03-03T17:34:22Z")

</div>

Hi,

I am facing an issue where changes to definitions in C++ headers don't cause the Swift code that uses them to recompile when building with CMake using Ninja.

Should CMake/Ninja be able to detect changes to definitions in C++ headers used in Swift and recompile when necessary? What can be done to make the example below work so that changes take effect when doing incremental builds?

# Example

include/module.modulemap

```swift
module Foo {
  header "Foo.hpp"
}

```

include/Foo.hpp

```cpp
#pragma once

inline int hpp() {
  return 1; // <--
}

int cpp();

```

Foo.cpp

```cpp
#include "Foo.hpp"

int cpp() {
  return 1; // <--
}

```

main.swift

```swift
import Foo

print("hpp: \(hpp()), cpp: \(cpp())") // <--

```

CMakeLists.txt

```swift
cmake_minimum_required(VERSION 3.29)
project(example LANGUAGES CXX Swift)

add_executable(example main.swift Foo.cpp)
target_compile_options(example
  PRIVATE
    $<$<COMPILE_LANGUAGE:Swift>:-cxx-interoperability-mode=default>
)
target_include_directories(example PRIVATE include)

```

Output after building for the first time

```swift
./example
hpp: 1, cpp: 1

```

Output after changing both `cpp()` and `foo()` to return 2 instead of 1 and building again without cleanup

```swift
./example
hpp: 1, cpp: 2

```

---

<div class="post-metadata">

**Author:** ![etcwilde](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/etcwilde/32/13687_2.png) [@etcwilde](https://forums.swift.org/u/etcwilde)\
**Post date:** [March 3, 2025, 6:33pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248/2 "2025-03-03T18:33:26Z")

</div>

Oh, this is fun.

So for C/C++, Ninja reads the dependency file emitted by clang and adds it to Ninja's internal dependency graph so that when it sees that file change, it implicitly knows which targets to rebuild. It only accepts one dependency file per target though.

The Swift driver emits one dependency file per frontend invocation, so in most cases, there are multiple dependency files for a Swift target. This isn't fed into Ninja, so Ninja doesn't know about the dependency. This isn't really something that CMake knows about either since it's the driver discovering the dependency.

Hand-writing a quick deps file and feeding it into Ninja seems to support this. Please file a bug report on the Driver asking for single dep-file output per invocation and I can wire that into CMake pretty easily once we have that. Thanks.

CC @ArtemC

---

<div class="post-metadata">

**Author:** ![roopekv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roopekv/32/18084_2.png) [@roopekv](https://forums.swift.org/u/roopekv)\
**Post date:** [March 4, 2025, 10:22pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248/3 "2025-03-04T22:22:46Z")

</div>

Fun indeed... :-/

> [@etcwilde](#):
>
> So for C/C++, Ninja reads the dependency file emitted by clang and adds it to Ninja's internal dependency graph so that when it sees that file change, it implicitly knows which targets to rebuild. It only accepts one dependency file per target though.

So each C++ source file / translation unit is its own Ninja target?

Like:

Target "Foo"  
Foo.cpp + referenced headers -\> clang -\> Foo.cpp.o, Foo.cpp.o.d

Target "Bar"  
Bar.cpp + referenced headers -\> clang -\> Bar.cpp.o, Bar.cpp.o.d

?

> [@etcwilde](#):
>
> The Swift driver emits one dependency file per frontend invocation, so in most cases, there are multiple dependency files for a Swift target.

What does frontend invocation mean exactly? If I pass 2 Swift files in one swiftc invocation, does it count as 2 frontend invocations?

Each Swift file produces 1 object file and 1 dependency file, but as multiple Swift files are passed within a single swiftc invocation, they are treated as parts of the same Ninja target?

Like:

Target "FooBar"  
= (Foo.swift + referenced headers) + (Bar.swift + referenced headers)  
-\> swiftc  
-\> Bar.swift.o, Bar.swift.o.d, Foo.swift.o, Foo.swift.o.d

?

> [@etcwilde](#):
>
> Hand-writing a quick deps file and feeding it into Ninja seems to support this. Please file a bug report on the Driver asking for single dep-file output per invocation and I can wire that into CMake pretty easily once we have that. Thanks.

So Ninja needs a single dependency file (\*.d) that has the combined dependencies of all the Swift files included in the swiftc invocation? And there isn't an option in swiftc to generate that file yet?

---

<div class="post-metadata">

**Author:** ![etcwilde](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/etcwilde/32/13687_2.png) [@etcwilde](https://forums.swift.org/u/etcwilde)\
**Post date:** [March 4, 2025, 11:01pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248/4 "2025-03-04T23:01:26Z")

</div>

> [@roopekv](#):
>
> So each C++ source file / translation unit is its own Ninja target?
> 
> Like:
> 
> Target "Foo"  
> Foo.cpp + referenced headers -\> clang -\> Foo.cpp.o, Foo.cpp.o.d
> 
> Target "Bar"  
> Bar.cpp + referenced headers -\> clang -\> Bar.cpp.o, Bar.cpp.o.d

Yeah

> [@roopekv](#):
>
> What does frontend invocation mean exactly? If I pass 2 Swift files in one swiftc invocation, does it count as 2 frontend invocations?

Yep, though it would be `Bar.swift.o`, `Bar.swift.o.d`, `Foo.swift.o` and `Foo.swift.o.d`.

Ninja needs one per target, which in this case is at the module-level so that swiftc can manage things like incremental builds within the sources.

> [@roopekv](#):
>
> So Ninja needs a single dependency file (\*.d) that has the combined dependencies of all the Swift files included in the swiftc invocation? And there isn't an option in swiftc to generate that file yet?

Yes, exactly.

---

<div class="post-metadata">

**Author:** ![roopekv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roopekv/32/18084_2.png) [@roopekv](https://forums.swift.org/u/roopekv)\
**Post date:** [March 4, 2025, 11:29pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248/5 "2025-03-04T23:29:30Z")

</div>

Thanks for the clarification! I can file an issue tomorrow on the swift-driver repository if someone hasn't already done so by then.

Out of curiosity, how are projects that make use of Swift C++ interoperability with CMake handling this issue currently? I feel like this could cause some major problems during development, and I find it odd that it hasn't been resolved yet. I assume that the fix will take some time to appear in official releases of Swift and CMake, so I wonder what would be the best solution to keep the headers in check in the meantime?

> [@etcwilde](#):
>
> Yep, though it would be `Bar.swift.o`, `Bar.swift.o.d`, `Foo.swift.o` and `Foo.swift.o.d`.

Oops, typo.

---

<div class="post-metadata">

**Author:** ![etcwilde](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/etcwilde/32/13687_2.png) [@etcwilde](https://forums.swift.org/u/etcwilde)\
**Post date:** [March 4, 2025, 11:50pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248/6 "2025-03-04T23:50:04Z")

</div>

> [@roopekv](#):
>
> Thanks for the clarification! I can file an issue tomorrow on the swift-driver repository if someone hasn't already done so by then.

👍

> [@roopekv](#):
>
> Out of curiosity, how are projects that make use of Swift C++ interoperability with CMake handling this issue currently?

I don't know.

> [@roopekv](#):
>
> I assume that the fix will take some time to appear in official releases of Swift and CMake, so I wonder what would be the best solution to keep the headers in check in the meantime?

Yeah, I don't know when it will land. If you notice it, you can probably `touch` one of the Swift sources in the target importing the C/C++ header. Ninja will see that the Swift source is dirty and rebuild that target.

If you're feeling adventurous, you could scoop up the generated `*.swift.o.d` for the target, merge them, and feed that into Ninja. Ninja will consume that file unless you pass `-d keepdepfile`. The hand-crafted deps file won't stay in sync with the Swift source or the module maps, so if you add an `import` or modify the module map that the target is importing, you'll need to update it to reflect the dependencies.

---

<div class="post-metadata">

**Author:** ![roopekv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roopekv/32/18084_2.png) [@roopekv](https://forums.swift.org/u/roopekv)\
**Post date:** [March 5, 2025, 7:50pm UTC](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248/7 "2025-03-05T19:50:00Z")

</div>

An issue has been filed:

> <https://github.com/swiftlang/swift-driver/issues/1831>
>
> It seems that there isn't currently a proper way to emit a single Make-compatibl…e dependencies file (\*.d) for the entire build. This would be a useful feature for integration with build systems. 
> 
> Example of a problem this would help to solve:
> \[https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248\](https://forums.swift.org/t/changes-to-c-headers-not-causing-swift-code-to-recompile/78248)
> 
> There has been some demand for this feature in the past as well:
> \[https://forums.swift.org/t/single-depfile-for-swiftpm-target/30169\](https://forums.swift.org/t/single-depfile-for-swiftpm-target/30169)
