X Tutup
Skip to content

SCons: Remove transitive includes in libc++#111767

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
Repiteo:scons/libcpp-transitive
Dec 4, 2025
Merged

SCons: Remove transitive includes in libc++#111767
Repiteo merged 1 commit intogodotengine:masterfrom
Repiteo:scons/libcpp-transitive

Conversation

@Repiteo
Copy link
Contributor

@Repiteo Repiteo commented Oct 17, 2025

I really like the idea of the above PR, but the implementation via typedefs.h would be inconsistent. It relies on all stdlib headers being defined after this header, which is unlikely when it's usually sorted so late in header chains & isn't a universal include for our repo. In order to guarantee this is applied for ALL scripts equally, the define is setup via SCons instead.

While this does the trick of catching overlooked cases early, that's proving to be a bit of a double-edged sword. Most notably: some thirdparty headers are caught in the crossfire, which I'll be gradually cleaning up over the next few days. This implementation currently excludes thirdparty compilation files, but could be expanded to include them as well, given we're already having to touch thirdparty code to some extent.

@Repiteo Repiteo added this to the 4.x milestone Oct 17, 2025
@Repiteo Repiteo requested a review from a team as a code owner October 17, 2025 17:25
@Repiteo Repiteo requested a review from a team as a code owner October 17, 2025 17:25
@Repiteo Repiteo marked this pull request as draft October 17, 2025 17:25
@Repiteo Repiteo removed request for a team October 17, 2025 17:26
@Repiteo Repiteo force-pushed the scons/libcpp-transitive branch from 12b1a1b to 97562b1 Compare October 17, 2025 17:32
@Repiteo Repiteo marked this pull request as ready for review October 17, 2025 18:43
@Repiteo Repiteo requested a review from a team as a code owner October 17, 2025 18:43
@Repiteo Repiteo requested review from a team and removed request for a team October 17, 2025 18:44
Copy link
Member

@Ivorforce Ivorforce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When preparing #111557, I initially wanted to go for a SConstruct based approach as well.
However, I got caught up on 2 problems:

  • I don't know an easy way to query the build system whether libc++ is in use.
  • Editing SConstruct also affects thirdparty libraries, some of which break.

It seems like you've run into both as well, and decided to tackle them instead.
Your approach should work everywhere, but ends up a bit more complicated than #111557, and has to make some 'educated guesses' about when libc++ is used.

My approach (changing typedefs.h) is stupid simple. But it doesn't help in all compile units (depending on include order), and comes with a small risk of unexpected missing includes in headers, depending on include order of the compile unit.

I'd be fine with choosing either. Maybe time for more people to weigh in?

Copy link
Member

@Ivorforce Ivorforce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, after thinking about it some more, I think i prefer your approach.
But maybe we can simplify it some more?

Instead of patching 3rd party libraries, can we just selectively disable _LIBCPP_REMOVE_TRANSITIVE_INCLUDES on those that don't support it? That seems easier to maintain.

SConstruct Outdated
env.AppendUnique(CCFLAGS=["-fansi-escape-codes"])

# Reduce transitive includes.
if (methods.using_clang(env) and not env.msvc) or methods.using_emcc(env) or "-stdlib=libc++" in env["CXXFLAGS"]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How sure are we that this works and won't break?
I suppose the worst that happens if it's wrong is that we don't save on compile time, or define a useless macro. But still, it feels a little weird to hardcode this.

Copy link
Contributor Author

@Repiteo Repiteo Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I suppose it isn't an absolute guarantee. The only certainty is msvc will NOT support this.

I'll just add the define universally then

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me. Unless long compile commands are a problem, I don't see an actual downside to this.

methods.py Outdated
Comment on lines +119 to +120
if "_LIBCPP_REMOVE_TRANSITIVE_INCLUDES" in self["CPPDEFINES"]:
self["CPPDEFINES"] = [x for x in self["CPPDEFINES"] if x != "_LIBCPP_REMOVE_TRANSITIVE_INCLUDES"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the flag when warnings are disabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disable_warnings is a bit of a misnomer; it's more "prepare thirdparty libraries". That is: this would disable the define for compiled thirdparty code

@Repiteo Repiteo force-pushed the scons/libcpp-transitive branch from 97562b1 to 5f6082c Compare October 24, 2025 17:35
@Repiteo
Copy link
Contributor Author

Repiteo commented Oct 24, 2025

Got this simplified, albeit in the other direction. That is: instead of directly excluding _LIBCPP_REMOVE_TRANSITIVE_INCLUDES under certain circumstances or trying to include it granularly, I'll just apply it universally. This meant that even more thirdparty code had to be given patches, but it only affected libraries that already recieved patches, so this isn't introducing any "new" friction. The one exception is Jolt, but that's simply integrating an upstream change, so it too isn't adding any new friction

Copy link
Member

@Ivorforce Ivorforce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to disable / remove the macro from the libraries that error instead of adding patches?

@Repiteo
Copy link
Contributor Author

Repiteo commented Oct 24, 2025

Only partially. That's what the disable_warnings() function was doing before. However, anything calling thirdparty headers, espcially in headers, could still need patches. Otherwise we're having the same problem of system headers potentially using transitive includes

@Ivorforce
Copy link
Member

Ivorforce commented Oct 24, 2025

Only partially. That's what the disable_warnings() function was doing before. However, anything calling thirdparty headers, espcially in headers, could still need patches. Otherwise we're having the same problem of system headers potentially using transitive includes

Makes sense.
Then I got a new idea: How about we create our own wrapper headers that fix the header includes for them? Something like this:

// "thirdparty/jolt/wrapper.h

// _LIBCPP_REMOVE_TRANSITIVE_INCLUDES fixes
#include <type_traits>

#include "‎thirdparty/jolt_physics/Jolt/Core/Core.h‎"

We'd include this file instead of Jolt/Core/Core.h, and could disable the _LIBCPP_REMOVE_TRANSITIVE_INCLUDES for the Jolt library (removing the need for the patches).

Sorry about being so picky about this. But I'm thinking, this is 'just' a build performance PR after all. I'd feel a lot more comfortable if we could find a way to introduce the macro without increasing our maintenance cost / risk of future build failures with these libraries.

@Repiteo Repiteo force-pushed the scons/libcpp-transitive branch from 5f6082c to 83f1008 Compare October 24, 2025 22:14
@Repiteo
Copy link
Contributor Author

Repiteo commented Oct 24, 2025

If Jolt in particular is a hangup, we could always hold out for an upstream sync where the change is already in place. Otherwise it'd mean editing a lot of files for a wrapper that'd be redundant not too long after

More likely you're just wanting to cover your bases about not causing potential breakage. To that end, I just added a new SCons flag: limit_transitive_includes. It's enabled by default, but disabling it will exclude the define

@Ivorforce
Copy link
Member

Ivorforce commented Oct 24, 2025

More likely you're just wanting to cover your bases about not causing potential breakage. To that end, I just added a new SCons flag: limit_transitive_includes. It's enabled by default, but disabling it will exclude the define

Yes, I mainly wouldn't want this optimization to cause problems or maintenance effort down the line.
The option to disable this is a great idea. But it won't help people who run into build problems because they won't be able to connect the build problem to the build option on their own. The only really safe option I'd see is disabling it by default, though that would mean nearly nobody would benefit from it...

The Jolt example above was just serving as an example to explain a possible alternative to the patches.
Maybe I'm too hesitant to add in patches? I don't know our policy for them. If it's something we're normally happy to take for this kind of improvement I have no further complaints.

@akien-mga akien-mga self-requested a review October 24, 2025 23:08
@Repiteo Repiteo force-pushed the scons/libcpp-transitive branch 2 times, most recently from 821b5e8 to aa7ffce Compare October 24, 2025 23:41
Copy link
Contributor Author

@Repiteo Repiteo Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out 0005-msvc-include-ctype.patch was excluded from the thirdparty README, likely from sharing the 0005 prefix and being erroneously excluded in a rebase. Given it's already just adding a header, I consolidated it into this new patch file

@Repiteo Repiteo force-pushed the scons/libcpp-transitive branch from aa7ffce to ad02128 Compare November 23, 2025 17:10
Copy link
Member

@Ivorforce Ivorforce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this change and it compiled successfully, ~10% faster than master.
Code looks good to me now. I appreciate simplifying the implementation to this level!

I really hope that this won't cause too many issues down the line (I can imagine there will be a few), but the 10% compile time boost is worth it in my opinion.
If it ends up being too frail in practice, we could switch to transitive libc++ by default, and support non-transitive on a best-effort basis.

(I'm actually surprised at how few patches are required to get this working. I would have fully expected we'd need to disable this optimization for 3rd party libs.)

Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine.

Could you propose these changes upstream to the various libraries patched here? So hopefully they get merged and we can drop our patches eventually.

@Repiteo Repiteo modified the milestones: 4.x, 4.6 Dec 4, 2025
@Repiteo Repiteo merged commit 63e14e1 into godotengine:master Dec 4, 2025
20 checks passed
@Repiteo Repiteo deleted the scons/libcpp-transitive branch December 4, 2025 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

X Tutup