Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why overwrite CMAKE_MSVC_RUNTIME_LIBRARY #1699

Open
ghuser404 opened this issue Feb 2, 2025 · 19 comments
Open

Why overwrite CMAKE_MSVC_RUNTIME_LIBRARY #1699

ghuser404 opened this issue Feb 2, 2025 · 19 comments

Comments

@ghuser404
Copy link

Godot version

N/A

godot-cpp version

master

System information

N/A

Issue description

I've noticed there has been recently some acitivity regarding MSVC runtime in CMake. With the latest changes I see that windows.cmake overwrites CMAKE_MSVC_RUNTIME_LIBRARY to empty. It feels quite intrusive A project may be compiling other things separate from the app (like dlls or other exes) that rely on this variable.

Steps to reproduce

N/A

Minimal reproduction project

N/A

@enetheru
Copy link
Contributor

enetheru commented Feb 4, 2025

yeah, I made this, trying to match the scons solution in terms of options, but more things are coming out of the woodwork that tells me it wasnt the best solution to the problem and its probably best I leave that upto consumers entirely.

Can I please get your feedback for an ideal situation? how woud you like linking to the msvc managed?

@enetheru
Copy link
Contributor

enetheru commented Feb 4, 2025

In the cmake/windows.cmake file there is a description at the top as to why.

Just thinking out loud here: The crux to the situation is that the MSVC_RUNTIME_LIBRARY target property isn't transient, meaning it does not flow to consuming libraries, so whatever is in the CMAKE_MSVC_RUNTIME_LIBRARY gets added to targets, and that by default conflicts with the build flags.

The only solution I have is to clobber the CMAKE_MSVC_RUNTIME_LIBRARY so in the simple case targets that rely on godot-cpp will have the right variables set. More advanced users like yourself can reset the contents of CMAKE_MSVC_RUNTIME_LIBRARY before your targets are defined that need something else.

Looking over the usecase and code again I can't see an alternative solution.

@enetheru
Copy link
Contributor

enetheru commented Feb 4, 2025

@Naros this issue might interest you as you also had questions about the MSVC runtime.

@ghuser404
Copy link
Author

@enetheru We can just set CMAKE_MSVC_RUNTIME_LIBRARY to the proper value using GODOT_USE_STATIC_CPP and GODOT_DEBUG_CRT accordingly. And then notify that it was changed (I see that Godot is also forcing MDd instead of MTd).

I believe, this way you don't have to propagate these flags into the parent project, or even set them in the first place with target_compile_options.

I strongly suggest looking at other open-source projects to get an idea how it's usually done. And I don't mean to say that you didn't. One of the examples is GLFW: https://github.com/glfw/glfw/blob/master/CMakeLists.txt

#--------------------------------------------------------------------
# Apply Microsoft C runtime library option
# This is here because it also applies to tests and examples
#--------------------------------------------------------------------
if (MSVC AND NOT USE_MSVC_RUNTIME_LIBRARY_DLL)
    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()

@enetheru
Copy link
Contributor

enetheru commented Feb 5, 2025

This amounts to the same thing no? We would still be overwriting the value of CMAKE_MSVC_RUNTIME_LIBRARY, so it would be set when godot-cpp is included as an submodule either through fetch make available. Is it the cached part that is the problem?

I can make it available at the top level scope without caching by using PARENT_SCOPE. but only in its current location.

set( CMAKE_MSVC_RUNTIME_LIBRARY "$<1:>" PARENT_SCOPE)

It needs to be set before the targets are defined, that includes godot-cpp and any dependent targets.
If it's going to be used as the means of setting the flags, as a generator expression it needs to be set after any dependent options.
That means it needs to be set after the options() calls. and PARENT_SCOPE only works for one level. so it wouldnt be visible at the top level if it was in a function.

It's just so darn hard to actually use because it doesnt propogate to consumers of the library, where just using the flags does.

in both cases, we would be overwriting the value of CMAKE_MSVC_RUNTIME_LIBRARY and anybody who needs a separate value needs to change that.

@Naros
Copy link
Contributor

Naros commented Feb 5, 2025

My only issue with this is the warning I see inside CLion:

CMake Warning at extern/godot-cpp/cmake/windows.cmake:43 (message):
  setting CMAKE_MSVC_RUNTIME_LIBRARY to "$<1:>"
Call Stack (most recent call first):
  extern/godot-cpp/cmake/godotcpp.cmake:32 (include)
  extern/godot-cpp/CMakeLists.txt:40 (include)

If the build directories do not exist yet, this does not happen but if they do it does repeatedly. This leads to CLion indicating that there is a warning that needs to be reviewed as shown here:

Image

As someone who has worked in other projects that allow this sort of stuff to manifest, it desensitizes people's awareness and it just becomes the defacto, and ultimately leads to the potential for other warnings or issues to happen that won't be spotted or recognized because you assume it's the above warning when it could also be accompanied by something else.

For me, that's why I always look for this, which tells me the reload was successful.

Image

@Naros
Copy link
Contributor

Naros commented Feb 5, 2025

@enetheru I should point out that the CMAKE command ran by CLion is this:

E:\Jetbrains\CLion\bin\cmake\win\x64\bin\cmake.exe -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAKE_PROGRAM=E:/Jetbrains/CLion/bin/ninja/win/x64/ninja.exe -DEXTENDED_TOOLTIPS=TRUE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja -S E:\GitHub\godot-orchestrator -B E:\GitHub\godot-orchestrator\cmake-build-release

I'm not sure whether any of that makes a bit of difference. I suspect not, but thought I'd share it for completeness.

@ghuser404
Copy link
Author

@enetheru Let me correct myself: yes, we'd still be overwriting it, but we'd be using an actually proper value.

Imagine scenario

set(CMAKE_MSVC_RUNTIME_LIBRARY)  # This could be set via an argument to the cmake command

add_subdirectory(godot-cpp)  # Sets CMAKE_MSVC_RUNTIME_LIBRARY to empty.
add_subdirectory(somelib)    # Adds a target somelib (external lib, not our code, so we can't change it).
                             # This project doesn't set any runtime flags and doesn't link to godot-cpp,
                             # and relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable.
                             # As a result, because godot-cpp has overwritten CMAKE_MSVC_RUNTIME_LIBRARY,
                             # the somelib target will link to whatever is default in CMake (which is /MD).

add_library(ourgdext MODULE)
target_link_libraries(ourgdext godot-cpp somelib)  # This will result in conflicts, because
                                                   # godot-cpp may be /MT and somelib is /MD

To solve this, I suggest setting CMAKE_MSVC_RUNTIME_LIBRARY to a proper value. This way, all subsequently added subdirectories will have correct value of CMAKE_MSVC_RUNTIME_LIBRARY. Yes, the order of subdirectories will still make a difference, but at least we don't have to do anything additional for other libraries to work with the same runtime (given they don't do any additional black magic with the CMAKE_MSVC_RUNTIME_LIBRARY).

@ghuser404
Copy link
Author

My original statement of why overwrite CMAKE_MSVC_RUNTIME_LIBRARY was due to the fact that the concept of CMAKE_MSVC_RUNTIME_LIBRARY is quite flaky, and the projects still need to introduce option for setting static/dynamic runtime, because it's MSVC specific.

IMHO, ideally, all projects should expose an option for setting static/dynamic runtime and not rely on the CMAKE_MSVC_RUNTIME_LIBRARY (nor change it), but this is simply not how it is. And because not all projects follow this rule, setting CMAKE_MSVC_RUNTIME_LIBRARY to a proper value can benefit users who use external libraries.

@enetheru
Copy link
Contributor

enetheru commented Feb 5, 2025

I get it, took going through your description line by line, attempting to correct mistakes, for me to see it.

This paraphrased/re-arranged line is the crux of it I believe?

A dependency( independent to godot-cpp ) that doesn't set any runtime flags, which relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable will very likely not have the correct msvc runtime flags set.

Which is very enlightening thank you.

Looks like I have a bunch of testing to do.

@ghuser404
Copy link
Author

@enetheru Sounds about right. I think I'd suggest that godot-cpp should only care about its own sub-directories (sub-project) and not force specific CMAKE_MSVC_RUNTIME_LIBRARY onto the parent. Parent project shall configure its sub-directories (sub-projects) on their own, by setting the correct CMAKE_MSVC_RUNTIME_LIBRARY to propagate further. Please think about this and let me know what you think.

@enetheru
Copy link
Contributor

enetheru commented Feb 5, 2025

@enetheru Sounds about right. I think I'd suggest that godot-cpp should only care about its own sub-directories (sub-project) and not force specific CMAKE_MSVC_RUNTIME_LIBRARY onto the parent. Parent project shall configure its sub-directories (sub-projects) on their own, by setting the correct CMAKE_MSVC_RUNTIME_LIBRARY to propagate further. Please think about this and let me know what you think.

I wish it were that simple, the thing is that we have to match the actual godot executable, if the exe and our dll dont match i believe we have problems. So the CMAKE_MSVC_RUNTIME_LIBRARY settings need to mirror the configuration options of godot and godot-cpp SCons. We can't create a situation where using equivalent options creates incompatible libraries because you werent aware of what exactly to make the CMAKE_MSVC_RUNTIME_LIBRARY or what to put into target_compile_options. The least we can do is warn users, or document things, but I want to do better than that.

I'm working on something that might work. i'll submit a PR soon and see if it satisfies.

@ghuser404
Copy link
Author

Godot executable is compiled completely separately, I don't think it has to do anything with the runtime of godot-cpp. We compile a dll and statically link to godot-cpp. This is complete dll, everything within it must use the same runtime flag. What happens next doesn't matter. I believe this is how it works.

But yes, let's see what you'll come up with.

enetheru added a commit to enetheru/godot-cpp that referenced this issue Feb 5, 2025
Previously I eschewed the use of CMAKE_MSVC_RUNTIME_LIBRARY in favour of setting the flags using target_compile_options so that they would propagate to consumers.

However, it has been raised that a dependency( independent to godot-cpp ) that doesn't set any runtime flags, which relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable will very likely not have the correct msvc runtime flags set.

Where MSVC documentation states "All modules passed to a given invocation of the linker must have been compiled with the same runtime library compiler option (/MD, /MT, /LD)."

It was also mentioned that messaging as a warning  that we are setting the option was not ideal.

So I have updated the cmake code to use CMAKE_MSVC_RUNTIME_LIBRARY  over target-compile_options. And set it as a CACHE STRING variable so that it can be overridden if desired. We still message consumers, but as a NOTICE.

While performing this work I noticed that the various options code being inside functions was unnecessary, and less flexible.
@enetheru
Copy link
Contributor

enetheru commented Feb 5, 2025

Try this PR, it still sets as a cache variable, but at least it doesnt clobber it like it did before.
Sorry if its a mess, I moved all the options out of functions because it was unnecessary.

The only code thats really changed is in windows.cmake

enetheru added a commit to enetheru/godot-cpp that referenced this issue Feb 5, 2025
Previously I eschewed the use of CMAKE_MSVC_RUNTIME_LIBRARY in favour of setting the flags using target_compile_options so that they would propagate to consumers.

However, it has been raised that a dependency( independent to godot-cpp ) that doesn't set any runtime flags, which relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable will very likely not have the correct msvc runtime flags set.

Where MSVC documentation states "All modules passed to a given invocation of the linker must have been compiled with the same runtime library compiler option (/MD, /MT, /LD)."

It was also mentioned that message type WARNING is not ideal for notifying consumers.

So I have updated the cmake code to use CMAKE_MSVC_RUNTIME_LIBRARY  over target-compile_options. And set it as a CACHE STRING variable so that it can be overridden if desired. We still message consumers, but as a NOTICE.

While performing this work I noticed that the various options code being inside functions was unnecessary, and less flexible.
enetheru added a commit to enetheru/godot-cpp that referenced this issue Feb 5, 2025
Previously I eschewed the use of CMAKE_MSVC_RUNTIME_LIBRARY in favour of setting the flags using target_compile_options so that they would propagate to consumers.

However, it has been raised that a dependency( independent to godot-cpp ) that doesn't set any runtime flags, which relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable will very likely not have the correct msvc runtime flags set.

Where MSVC documentation states "All modules passed to a given invocation of the linker must have been compiled with the same runtime library compiler option (/MD, /MT, /LD)."

It was also mentioned that message type WARNING is not ideal for notifying consumers.

So I have updated the cmake code to use CMAKE_MSVC_RUNTIME_LIBRARY  over target-compile_options. And set it as a CACHE STRING variable so that it can be overridden if desired. We still message consumers, but as a NOTICE.
@enetheru
Copy link
Contributor

enetheru commented Feb 5, 2025

I cleaned up the commit to remove all the extra stuff, it can wait for a cleanup PR.

@ghuser404
Copy link
Author

@enetheru Your changes work very well!

A few notes:

  • In the comments you mention PARENT_SCOPE which you don't use.
  • Were options supposed to move out of windows_options? I guess those were your planned cleanup changes.
  • There is a concept of normal vs cached variables in CMake. So if someone runs set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebugDLL"), this value will be used instead of anything that godot-cpp sets in the set(... CACHE STRING ""), which is actually very good; this allows users to have full control and not bother with GODOT_USE_STATIC_CPP and GODOT_DEBUG_CRT. However, since godot-cpp doesn't like being compiled with /MTd, I'd suggest issuing a waning in case user set sCMAKE_MSVC_RUNTIME_LIBRARY to MultiThreadedDebugDLL. This check should come before godot-cpp sets the cached variable.

@enetheru
Copy link
Contributor

enetheru commented Feb 5, 2025

@enetheru Your changes work very well!

Thank you very much 😄 I Appreciate you lodging this in the first place and the ongoing interactions here, it makes validating the work so much easier.

In the comments you mention PARENT_SCOPE which you don't use.

I changed back to CACHE STRING late in the writing, I thought it best as then it can be overridden as you say, forgot to update the comment. I might have also botched it when I reverted all the confiuration changes.

Were options supposed to move out of windows_options? I guess those were your planned cleanup changes.

While writing i noticed that the order of how things are included meant that the options functions were unnecessary abstraction that just got in the way, so I plan a follow up that removes them all entirely. Since I'm using the cache variable again I will put it back.

  • There is a concept of normal vs cached variables in CMake. So if someone runs set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebugDLL"), this value will be used instead of anything that godot-cpp sets in the set(... CACHE STRING ""), which is actually very good; this allows users to have full control and not bother with GODOT_USE_STATIC_CPP and GODOT_DEBUG_CRT.

I did initally think a normal variable was better, but then thought how annoying it might get being unable to override it, or having it only able to propagate up one level from PARENT_SCOPE, and decided that a CACHE STRING is best so that consumer CMake code can be as weird as it likes and they dont have to work to pass around the CMAKE_MSVC_RUNTIME_LIBRARY result from godot-cpp.

However, since godot-cpp doesn't like being compiled with /MTd, I'd suggest issuing a waning in case user set sCMAKE_MSVC_RUNTIME_LIBRARY to MultiThreadedDebugDLL. This check should come before godot-cpp sets the cached variable.

If you can figure out some if condition that can detect its way through generator expressions then I can add it, but I cant think of one, so a simple STATUS message might be all that gets made.

enetheru added a commit to enetheru/godot-cpp that referenced this issue Feb 5, 2025
Previously I eschewed the use of CMAKE_MSVC_RUNTIME_LIBRARY in favour of setting the flags using target_compile_options so that they would propagate to consumers.

However, it has been raised that a dependency( independent to godot-cpp ) that doesn't set any runtime flags, which relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable will very likely not have the correct msvc runtime flags set.

Where MSVC documentation states "All modules passed to a given invocation of the linker must have been compiled with the same runtime library compiler option (/MD, /MT, /LD)."

It was also mentioned that message type WARNING is not ideal for notifying consumers.

So I have updated the cmake code to use CMAKE_MSVC_RUNTIME_LIBRARY  over target-compile_options. And set it as a CACHE STRING variable so that it can be overridden if desired. We still message consumers, but as a NOTICE.
@ghuser404
Copy link
Author

You're right, the generator expressions ^^ I guess, this could be documented somehow in the getting-started docs.

Then I would go back to how it was, but put the whole thing into if(NOT CMAKE_MSVC_RUNTIME_LIBRARY), because if it's already set with a normal variable, it won't make a difference, so issuing the message may be redundant. Unless, of course, the variable is cached, which we can still overwrite... What do you think?

if(NOT CMAKE_MSVC_RUNTIME_LIBRARY)
    message(NOTICE "Setting CMAKE_MSVC_RUNTIME_LIBRARY. For more information please read godot-cpp/cmake/windows.cmake")
    set(CMAKE_MSVC_RUNTIME_LIBRARY
        "MultiThreaded$<IF:$<BOOL:${GODOT_DEBUG_CRT}>,DebugDLL,$<$<NOT:$<BOOL:${GODOT_USE_STATIC_CPP}>>:DLL>>"
        CACHE STRING "Select the MSVC runtime library for use by compilers targeting the MSVC ABI.")
endif()

@enetheru
Copy link
Contributor

enetheru commented Feb 5, 2025

What do you think?

I think that no matter how its sliced, messaging the user during configure with something, and pointing them to the windows.cmake to discover more information is best. So the if statement becomes unnecessary, the current state of the PR does this so given that you have already verified that the changes were OK, I will mark it ready for review.

enetheru added a commit to enetheru/godot-cpp that referenced this issue Feb 8, 2025
Previously I eschewed the use of CMAKE_MSVC_RUNTIME_LIBRARY in favour of setting the flags using target_compile_options so that they would propagate to consumers.

However, it has been raised that a dependency( independent to godot-cpp ) that doesn't set any runtime flags, which relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable will very likely not have the correct msvc runtime flags set.

Where MSVC documentation states "All modules passed to a given invocation of the linker must have been compiled with the same runtime library compiler option (/MD, /MT, /LD)."

It was also mentioned that message type WARNING is not ideal for notifying consumers.

So I have updated the cmake code to use CMAKE_MSVC_RUNTIME_LIBRARY  over target-compile_options. And set it as a CACHE STRING variable so that it can be overridden if desired. We still message consumers, but as a NOTICE.
enetheru added a commit to enetheru/godot-cpp that referenced this issue Feb 15, 2025
Previously I eschewed the use of CMAKE_MSVC_RUNTIME_LIBRARY in favour of setting the flags using target_compile_options so that they would propagate to consumers.

However, it has been raised that a dependency( independent to godot-cpp ) that doesn't set any runtime flags, which relies purely on the CMAKE_MSVC_RUNTIME_LIBRARY variable will very likely not have the correct msvc runtime flags set.

Where MSVC documentation states "All modules passed to a given invocation of the linker must have been compiled with the same runtime library compiler option (/MD, /MT, /LD)."

It was also mentioned that message type WARNING is not ideal for notifying consumers.

So I have updated the cmake code to use CMAKE_MSVC_RUNTIME_LIBRARY  over target-compile_options. And set it as a CACHE STRING variable so that it can be overridden if desired. We still message consumers, but as a NOTICE.
dsnopek added a commit that referenced this issue Feb 17, 2025
CMake: Fix for #1699 msvc runtime selection issues
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants