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

Implemented switch from SCons to Meson to improve Godot's build performance #45093

Closed
wants to merge 5 commits into from

Conversation

marstaik
Copy link
Contributor

@marstaik marstaik commented Jan 11, 2021

Proposal: godotengine/godot-proposals#1797

Meson build for Godot

This is the initial implementation of the meson build system for godot.

Features

  • Out-of-source builds
  • Zero-second null builds on every platform
  • compile_commands.json
    • Use your favorite IDE/editor and get full intellisense/code linting
  • Windows ninja and vcproj file support
  • Thirdparty libraries cleanup
  • Revamped build code
  • Module database
  • Modifying (and in some cases rewritten) python build scripts
  • many other things...

Intro

How is meson different from scons? Scons is a Turing-complete build system built around python. It both controls configuration and building.

Meson, on the other hand, is a buildfile/makefile/ninja/vcproj generator.
It assembles the appropriate files needed to run ninja, Visual Studio, make, and delegates the responsibility of building to system appropriate tooling.

Changes

The port to meson was an opportunity to not only move to a newer more supported build system, but also served as an opportunity to really clean up the build.

Thirdparty libraries are now built into their own static libraries, instead of being directly injected into godot's build files.
Dependencies are now tracked appropriately.

Build system cleanup has occured, centralizing/cleaning up as many #defines as possible.

A ModuleDb file has been created, which holds all enabled modules, dependency information, and additional information.
This can also lead to even more flexibility in the future.

This has set up the future for potential dynamic linking for development speed. Once the main libraries such as core, scene, editor are isolated, we can begin dllspec'ing code and move towards dynamic linking.

Build Instructions

Head over and download meson on your system. You will also need python3.

Generally, from the project root, you can initiate a build structure by typing: meson ./build, substituting build with your desired folder. Then, from your build folder, execute meson compile.

You can change build types by specifying -Dbuildtype=[plain,debug,debugoptimized,release]. The current default build is debugoptimized.

All of the available options can be found in meson_options.txt.

I recommend reading through https://mesonbuild.com/SimpleStart.html for more information.

Here is a quick example block:

~/godot$ meson ./build -Dbuildtype=debug
~/godot$ cd ./build
~/godot/build$ meson compile

Windows

For now, you will need to run meson from an Developer Command Prompt (MSVC) to be able to use the msvc compilers.
There are plans to build in msvc detection for meson.

Due to a bug in meson adding /ZI as a default option for debug, launching the editor/binary on windows debug is excruciatingly slow. I have worked around this for now by making the plain buildtype into debug, so just use plain for now. This should be fixed in meson master soon.
Example: meson ./build -Dbuildtype=plain

Linux to Windows Cross Compile

There is a cross-file specified in cross/ called linux-mingw-win64. Assuming you have mingw64 installed, you simply have to specify the cross file in your meson configuration step.
Example: meson ./build --cross-file cross/linux-mingw-wing64 -Dbuildtype=debug

Things left to do...

  • Support for the other platforms
  • Modules mono and webm setup
  • Fixing the build-ci scripts
    • this should be done using native-files or cross-files.

@marstaik
Copy link
Contributor Author

Reserved.

@AndreaCatania
Copy link
Contributor

Is this feature https://docs.godotengine.org/en/stable/development/cpp/custom_modules_in_cpp.html already supported? How it would work?

@marstaik
Copy link
Contributor Author

marstaik commented Jan 11, 2021

Is this feature https://docs.godotengine.org/en/stable/development/cpp/custom_modules_in_cpp.html already supported? How it would work?

It almost works exactly the same. There is config.py that is almost identical, and instead of providing an SCsub, you provide a meson.build file now.

The docs need to be updated, but there should be tons of examples on how to do it with the existing modules.

@AndreaCatania
Copy link
Contributor

So it's still possible specify modules that are outside the godot directory using custom_modules=../modules (like in scons custom_modules=../modules)?

@marstaik
Copy link
Contributor Author

So it's still possible specify modules that are outside the godot directory using custom_modules=../modules (like in scons custom_modules=../modules)?

Ah, custom directory isn't done yet, but its pretty trivial. I will work on it. @fire keeps mentioning this to me.

@AndreaCatania
Copy link
Contributor

Awesome, thanks!

@AndreaCatania
Copy link
Contributor

Would this be doable to: #42875 ?
Basically, it reads the my_module/config.py and if has_custom_iterator is set to True it defines a macro: CUSTOM_ITERATOR.

@marstaik
Copy link
Contributor Author

marstaik commented Jan 11, 2021

Would this be doable to: #42875 ?
Basically, it reads the my_module/config.py and if has_custom_iterator is set to True it defines a macro: CUSTOM_ITERATOR.

The module_db can be extended for additional functions, so it is possible. The config.py's of modules are checked before any targets in meson have been configured, so it is possible to pass a define. I will note that currently no module needs to do this. There was one module that did it, but it wasn't even used IIRC so I removed it.

@Chaosus Chaosus changed the title Meson Implemented switch from SCons to Meson to improve Godot's build performance Jan 11, 2021
@Chaosus Chaosus added this to the 4.0 milestone Jan 11, 2021
@Chaosus
Copy link
Member

Chaosus commented Jan 11, 2021

I suppose the @reduz will be heavily against this change. Just a note from https://docs.godotengine.org/en/latest/development/compiling/introduction_to_the_buildsystem.html

Godot uses SCons to build. We love it, we are not changing it for anything else. We are not even sure other build systems are up to the task of building Godot. We constantly get requests to move the build system to CMake, or Visual Studio, but this is not going to happen. There are many reasons why we have chosen SCons over other alternatives, for example:

Godot can be compiled for a dozen different platforms: all PC platforms, all mobile platforms, many consoles, and WebAssembly.
Developers often need to compile for several of the platforms at the same time, or even different targets of the same platform. They can't afford reconfiguring and rebuilding the project each time. SCons can do this with no sweat, without breaking the builds.
SCons will never break a build no matter how many changes, configurations, additions, removals etc. You have more chances to die struck by lightning than needing to clean and rebuild in SCons.
Godot build process is not simple. Several files are generated by code (binders), others are parsed (shaders), and others need to offer customization (plugins). This requires complex logic which is easier to write in an actual programming language (like Python) rather than using a mostly macro-based language only meant for building.
Godot build process makes heavy use of cross-compiling tools. Each platform has a specific detection process, and all these must be handled as specific cases with special code written for each.

@Chaosus Chaosus modified the milestones: 4.0, 4.1 Jan 11, 2021
@akien-mga
Copy link
Member

akien-mga commented Jan 11, 2021

I suppose the @reduz will be heavily against this change. Just a note from https://docs.godotengine.org/en/latest/development/compiling/introduction_to_the_buildsystem.html

This was discussed on IRC, both @reduz and I are OK with the idea to use Meson, if and only if the conditions that you quoted are fulfilled. Meson ticks some of the boxes (can also run Python scripts), and the rest have to be ensured in this PR and/or in a feature branch if we decide to merge into a branch while contributors keep working on it.

If the end result proves to be not only faster, but also has the same or better capabilities to support all our platforms (including cross-compilation for many of them), and with a clean buildsystem setup that is easy to extend and maintain, then yes, this switch could be done. But it needs to give the same or better output, we don't want to have regressions due to a buildsystem change (one exception being WebM support as the build setup for it was unmaintainable and we plan to move video decoding to first-party plugins).

@akien-mga
Copy link
Member

akien-mga commented Jan 11, 2021

Note: I haven't reviewed any code yet.

I did some testing on Linux, here are some initial results and comments, in no specific order.

System specs:

System:    Host: cauldron Kernel: 5.10.4-desktop-4.mga8 x86_64 bits: 64 Desktop: KDE Plasma 5.20.4 Distro: Mageia 8 mga8 
CPU:       Info: Quad Core model: Intel Core i7-8705G bits: 64 type: MT MCP L2 cache: 8 MiB 
           Speed: 3095 MHz min/max: 800/3100 MHz Core speeds (MHz): 1: 3095 2: 3082 3: 3099 4: 3099 5: 3097 6: 3081 7: 3095 
           8: 3099 
Graphics:  Device-1: Intel HD Graphics 630 driver: i915 v: kernel 
           Device-2: Advanced Micro Devices [AMD/ATI] Polaris 22 XL [Radeon RX Vega M GL] driver: amdgpu v: kernel 
           Device-3: Cheng Uei Precision Industry (Foxlink) HP Wide Vision FHD Camera type: USB driver: uvcvideo 
           Display: x11 server: Mageia X.org 1.20.10 driver: intel,v4l resolution: 1920x1080 
           OpenGL: renderer: Mesa Intel HD Graphics 630 (KBL GT2) v: 4.6 Mesa 20.3.2

Configuration step

The configuration step looks cool and is very fast:

meson build3
The Meson build system
Version: 0.56.0
Source dir: /home/akien/Projects/godot/godot.git
Build dir: /home/akien/Projects/godot/godot.git/build3
Build type: native build
Project name: godot
Project version: 4.0.0
C compiler for the host machine: cc (gcc 10.2.1 "cc (Mageia 10.2.1-0.20210109.1.mga8) 10.2.1 20210109")
C linker for the host machine: cc ld.bfd 2.35.1
C++ compiler for the host machine: c++ (gcc 10.2.1 "c++ (Mageia 10.2.1-0.20210109.1.mga8) 10.2.1 20210109")
C++ linker for the host machine: c++ ld.bfd 2.35.1
Host machine cpu family: x86_64
Host machine cpu: x86_64
Message: debugoptimized
Program authors.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/authors.py)
Program certs.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/certs.py)
Program default_controller_mappings.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/default_controller_mappings.py)
Program docs.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/docs.py)
Program donors.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/donors.py)
Program editor_icons.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/editor_icons.py)
Program encryption_key.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/encryption_key.py)
Program fonts.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/fonts.py)
Program glsl.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/glsl.py)
Program license.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/license.py)
Program module_db.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/module_db.py)
Program modules_gen.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/modules_gen.py)
Program platform_logo.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/platform_logo.py)
Program register_exporters.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/register_exporters.py)
Program register_platform_apis.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/register_platform_apis.py)
Program splash_generator.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/splash_generator.py)
Program theme_data.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/theme_data.py)
Program translations.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/translations.py)
Program version.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/scripts/version.py)
Message: Modules Enabled -  ['basis_universal', 'bmp', 'bullet', 'csg', 'cvtt', 'dds', 'denoise', 'enet', 'etc', 'fbx', 'gdnative', 'gdnavigation', 'gdscript', 'glslang', 'gltf', 'gridmap', 'hdr', 'jpg', 'jsonrpc', 'lightmapper_rd', 'mbedtls', 'meshoptimizer', 'minimp3', 'mobile_vr', 'opensimplex', 'pvr', 'regex', 'squish', 'stb_vorbis', 'svg', 'text_server_adv', 'tga', 'theora', 'tinyexr', 'upnp', 'vhacd', 'visual_script', 'webp', 'webrtc', 'websocket', 'webxr', 'xatlas_unwrap']
Message: Modules Disabled -  ['arkit', 'camera', 'camera_iphone', 'gamecenter', 'icloud', 'inappstore', 'mono', 'text_server_fb', 'webm']
Program resource_to_cpp.py found: YES (/usr/bin/env python /home/akien/Projects/godot/godot.git/thirdparty/oidn/weights/resource_to_cpp.py)
Library dl found: YES
Run-time dependency threads found: YES
Found pkg-config: /usr/bin/pkg-config (1.7.3)
Run-time dependency x11 found: YES 1.7.0
Run-time dependency xcursor found: YES 1.2.0
Run-time dependency xinerama found: YES 1.1.4
Run-time dependency xext found: YES 1.3.4
Run-time dependency xrandr found: YES 1.5.2
Run-time dependency xrender found: YES 0.9.10
Run-time dependency xi found: YES 1.7.10
Program scripts/gdnative_api_struct_gen.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/modules/gdnative/scripts/gdnative_api_struct_gen.py)
Program ../scripts/icu_data.py found: YES (/usr/bin/python3 /home/akien/Projects/godot/godot.git/modules/text_server_adv/icu_data/../scripts/icu_data.py)
Build targets in project: 164

Found ninja-1.10.2 at /usr/bin/ninja

(Takes 2 sec.)

I haven't looked into the code yet to see how the dependency relationships are tracked, though I noticed that reconfiguring a finalized build with -Dbuiltin_mbedtls=true (which should be the default) triggered a rebuild of 50% of the files, which seems weird.

Build tests

  • SCons clean build with scons LINKFLAGS='-fuse-ld=gold' -j7 p=linuxbsd verbose=yes warnings=extra werror=yes tests=yes (flags which I use in my dev workflow): 10 min 14 s
  • Meson clean build with meson build && ninja -C build: 15 min 47 s - that's a lot of extra build time for a clean build :| Might be related to different build options, will need to be compared further with same build flags (more on that below).

Those were not thorough tests as I did not specify the exact same configuration (I don't know how to get the exact same config with Meson yet) and I was using Firefox with a number of open tabs in parallel (and occasionally a Godot 3.2 instance), so some CPU and RAM taken.

CPU perf limited at 3.1 GHz for both (80% capacity) as otherwise my CPU overheats and tends to throttles.

Then for null builds, Meson+Ninja obviously wins: SCons takes ~9 seconds, Ninja takes ~0 seconds.

For small incremental/leaf builds (e.g. adding an empty line to main/main.cpp), I get 24 s for SCons and 23 s for Ninja. No huge win here.

Build flags

To properly assess this buildsystem change, we need to ensure that we compare things which are comparable, and as such we need to make sure to replicate the build flags used by SCons for the different target, tools, and platform configurations.

Currently, SCons defaults to target=debug while Meson defaults to -Dbuildtype=debugoptimized. The Meson build types listed above do not match what we used to have in SCons 1:1, and I don't know if the actual compiler flags that they enable are the same or not.

It's important that we start from a setup where the build flags are the same, so that we can be sure that the resulting binaries will be the same / very similar. Our current build flags (to be clear I mean stuff like this: https://github.com/godotengine/godot/blob/master/platform/linuxbsd/detect.py#L89-L111) are used in production and are thus heavily tested.

While it might be relevant to review build flags and change some eventually, this should not be done as part of this conversion work, but in future follow-up PRs. They need to be addressed independently of the buildsystem technology, as they're not linked to it (they're linked to compilers, and we're not changing those).

For example, the discrepancy in current build flags might explain why a clean build is much slower with Meson than SCons target=debug for me. It might also explain why my SCons debug build is 500 MiB, but my Meson debugoptimized is 1 GiB (!) (which doesn't match SCons release_debug either as those are smaller than debug builds, even with debug_symbols=yes which is the default).

Another gain from ensuring that current SCons and Meson setups use the same flags and config is that it makes reviewing the PR and learning how Meson works easier for existing contributors, as we can compare how existing SCons code was converted to Meson for a given component or platform. We should definitely use this opportunity to re-evaluate some of our build flags and options as well as fix bugs, but this should ideally be done in follow-up PRs to avoid having to review both tooling and workflow changes on one hand, and build configuration and binary size/performance on the other hand.

Build naming

The current SCons buildsystem gives each object a suffix that loosely matches the build configuration, which more or less allows to have several build configs concurrently in the same Git clone, and the possibility to do incremental builds for each of them with little conflicts (it's not perfect as some files are still shared, but it works somewhat OK). It's convenient when you build e.g. Mono and non-Mono versions and use both, or debug and release_debug versions, or different platforms.

The Meson setup seems not to reproduce this, and generates a binary named godot in the output folder. The "parallel configs" feature is still available thanks to Meson's out of source configuration, but the workflow is somewhat different, e.g.:

meson ./build-linux
meson ./build-windows --cross-file cross/linux-mingw-wing64

This will create build-linux and build-windows folders for each build, so they're fully separate, which is cool. However it means that you need to remember what config options you passed, or hardcode them yourself in the folder name, as the resulting binary will not tell you much: build-linux/godot and build-windows/godot.exe, versus bin/godot.linuxbsd.tools.64 (I know it's a Linux 64-bit tools=yes target=debug build from its name - debug because it doesn't feature .opt suffix).

That's not necessarily a big issue, but that's something worth noting.

I also planned to implement something like godotengine/godot-proposals#1416 at some point to have generated files even better identified, and I'm not sure how/whether it should interface with the proposed Meson setup for now.

Build accuracy

While SCons takes a while to parse its config, a big plus of using it is that (unless we mess up in our Depends()), we can be nearly sure that any change to the codebase will lead to a rebuild of the relevant files, so the binary you test properly matches the code you pull or modified.

With separate configure (Meson) and build (Ninja), you need to remember to manually reconfigure your build folder if anything changed that requires it - and there's a number of things which do:

  • Adding new files
  • Modifying files which are not directly compiled but converted to code by a script, e.g. doc/classes/*.xml

For a fast moving target like Godot's master branch, this basically means that every single time you pull from upstream, you should likely remember to reconfigure your build folders manually, otherwise you might unknowingly end up with a broken build as you'll be missing some content (which may break compilation, like missing .cpp files leading to missing symbols on linking, or may still build successfully and you'll have e.g. outdated documentation, translations, fonts, icons, shaders, etc.).

I'm not sure what's the proper way to handle this in the Meson world, but to get back the safety of SCons we'd probably have to tell users to always reconfigure before running Ninja (maybe Meson can be configured so that the Ninja build files are actually configured to force a reconfigure). The configure step is fast (less than 2 s for me on Linux) so that's not a big deal. Not sure how that plays with e.g. VS as a backend though.

Cross-compilation

Being able to cross-compile from one platform to another is a core feature of Godot's buildsystem and something we will not compromise on. For example, the official Godot buildsystem compiles everything from Linux: Linux binaries on Ubuntu 14.04, all other binaries on Fedora 32 (Android with the Linux SDK/NDK, macOS and iOS with OSXCross, Windows with MinGW-w64/GCC, JavaScript with Emscripten for Linux, UWP with Visual Studio via WINE).

There is a cross-file specified in cross/ called linux-mingw-win64. Assuming you have mingw64 installed, you simply have to specify the cross file in your meson configuration step.
Example: meson ./build --cross-file cross/linux-mingw-win64 -Dbuildtype=debug

I'm a bit concerned about this as we lose some of the SCons buildsystem's flexibility around the platform parameter:

  • We now need to define a custom cross file for every cross-compilation path that we want to support. So we have linux-mingw-win64 but we'd also need macos-mingw-win64, and possibly windows-mingw-win64. Possibly double that if we want to support MinGW-w64/LLVM and not just GCC, and maybe more if different files are needed to support different MinGW distribs (MSYS2, Cygwin, MacPorts, etc.).
    Being explicit like this is not bad per se, but that's definitely a change compared to scons p=windows and the platform/windows/detect.py script being in charged of setting things up correctly based on your detected OS and toolchain arugments.
  • A nice feature of Godot's buildsystem is that many components are self-contained, including (to some extent) platform code. All the information needed to build the Windows platform is in platform/windows. You can have platform ports which are not supported officially by just cloning their Git repo under platform, e.g. the Haiku port (outdated) or proprietary console ports. If those require placing their cross-compilation configuration in the top-level cross folder, that breaks this encapsulation.

That's it for some initial thoughts, will add more as I play more with it and review the code.

@jordo
Copy link
Contributor

jordo commented Jan 12, 2021

agree with @akien-mga . We should be able to manually specify our own flags for debug, release_debug, etc (actually my preference would be -Og for debug and -Os for release). Not sure what upstream defaults are, but we should be able to explicitly define what we would like to see. I imagine it should be be easy to match these 1:1 to what's currently defined in scons.

@Faless
Copy link
Collaborator

Faless commented Jan 12, 2021

We should be able to manually specify our own flags for debug, release_debug, etc (actually my preference would be -Og for debug and -Os for release). Not sure what upstream defaults are

In Godot most platforms uses O3 for release, some uses O2 (when O3 is known to cause problems, namely iphone), you can specify to build with Os by specifying the optimize=size scons flag.
JavaScript always build with Os instead of O3/O2 because the binary size is much more important on the web.

EDIT: You can see the flag used in platform/PLATFORM/detect.py.

@jordo
Copy link
Contributor

jordo commented Jan 12, 2021

In Godot most platforms uses O3 for release, some uses O2 (when O3 is known to cause problems, namely iphone), you can specify to build with Os by specifying the optimize=size scons flag.
JavaScript always build with Os instead of O3/O2 because the binary size is much more important on the web.

EDIT: You can see the flag used in platform/PLATFORM/detect.py

Sure ya, I just mean O3 or O2 or Os or whatever flags godot's targets and platforms standardize on should be reproducible 1:1 in meson compared to current scons flags that are setup in detect.py.

@Riteo
Copy link
Contributor

Riteo commented Jan 12, 2021

I'm getting:

Found runner: ['/usr/bin/ninja']
ninja: Entering directory `.'
[904/2119] Compiling C++ object modules/gdnavigation/libmodule_gdnavigation.a.p/navigation_mesh_generator.cpp.o
FAILED: modules/gdnavigation/libmodule_gdnavigation.a.p/navigation_mesh_generator.cpp.o 
c++ -Imodules/gdnavigation/libmodule_gdnavigation.a.p -Imodules/gdnavigation -I../modules/gdnavigation -I. -I.. -Ithirdparty -I../thirdparty -Iplatform/linuxbsd -I../platform/linuxbsd -I../thirdparty/recastnavigation/Recast/Include -Ithirdparty/rvo2 -I../thirdparty/rvo2 -fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -std=c++17 -g -D_DEBUG -fPIC -Wno-cpp -DDEBUG_ENABLED -DDISABLE_FORCED_INLINE -DTOOLS_ENABLED -DUNIX_ENABLED -DVULKAN_ENABLED -DX11_ENABLED -DHAVE_MNTENT -MD -MQ modules/gdnavigation/libmodule_gdnavigation.a.p/navigation_mesh_generator.cpp.o -MF modules/gdnavigation/libmodule_gdnavigation.a.p/navigation_mesh_generator.cpp.o.d -o modules/gdnavigation/libmodule_gdnavigation.a.p/navigation_mesh_generator.cpp.o -c ../modules/gdnavigation/navigation_mesh_generator.cpp
../modules/gdnavigation/navigation_mesh_generator.cpp:50:10: fatal error: modules/modules_enabled.gen.h: No such file or directory
   50 | #include "modules/modules_enabled.gen.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
[905/2119] Compiling C++ object modules/mbedtls/libmodule_mbedtls.a.p/crypto_mbedtls.cpp.o
FAILED: modules/mbedtls/libmodule_mbedtls.a.p/crypto_mbedtls.cpp.o 
c++ -Imodules/mbedtls/libmodule_mbedtls.a.p -Imodules/mbedtls -I../modules/mbedtls -I. -I.. -Ithirdparty -I../thirdparty -Iplatform/linuxbsd -I../platform/linuxbsd -I../thirdparty/mbedtls/include -fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -std=c++17 -g -D_DEBUG -fPIC -Wno-cpp -DDEBUG_ENABLED -DDISABLE_FORCED_INLINE -DTOOLS_ENABLED -DUNIX_ENABLED -DVULKAN_ENABLED -DX11_ENABLED -DHAVE_MNTENT -MD -MQ modules/mbedtls/libmodule_mbedtls.a.p/crypto_mbedtls.cpp.o -MF modules/mbedtls/libmodule_mbedtls.a.p/crypto_mbedtls.cpp.o.d -o modules/mbedtls/libmodule_mbedtls.a.p/crypto_mbedtls.cpp.o -c ../modules/mbedtls/crypto_mbedtls.cpp
../modules/mbedtls/crypto_mbedtls.cpp:37:10: fatal error: core/io/certs_compressed.gen.h: No such file or directory
   37 | #include "core/io/certs_compressed.gen.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
[913/2119] Compiling C++ object modules/gdnavigation/libmodule_gdnavigation.a.p/navigation_mesh_editor_plugin.cpp.o
ninja: build stopped: subcommand failed.

Maybe the generators are not working?
OS: Ubuntu 20.04.1 LTS
Python: Python 3.8.5
Meson: 0.56.2 (had to install with pip, the distro version 0.53 was not supported)
Ninja: 1.10.0

Same thing on Arch Linux with meson 5.10.4 and ninja 1.10.2.

EDIT: Looks like setting buildtype to plain makes it compile succesfully. Weird.

@marstaik
Copy link
Contributor Author

marstaik commented Jan 13, 2021

@akien-mga

Build tests & Build flags

By default I have set the current base configuration of meson to debugoptimized, so it would explain differences in build time.
When it gets fixed upstream and debug doesnt invoke a ZI flag, I can revert the default back to debug.

However,

In terms of build flags, the only flags that are not mirrored exactly are the debug/optimization flags.

I am going to argue here that debug level and optimization flags should not be specified be hardcoded into any build files.
They are completely irrelevant to how something should be built, and instead specify the information/optimization/speed that you, as the developer or package manager, want out of the build.

The meson devs have also advised against using buildtype, as it is basically a matrix table that combines the parameters debug {true, false} and optimization {0, g, 1, 2, 3, s}, and it will be deprecated in the future.
You can view the built-in options here: https://mesonbuild.com/Builtin-options.html#core-options

The idea of buildtype which refers to performance and debug level, should not be confused with whether or not you package the editor tools or not. Currently in Scons, you cant even build target release (with optimization and no debug symbols) with the tools (this is due to an abuse of the define DEBUG_ENABLED, which for some reason tool-critical code is hidden behind).

What is the purpose behind having set optimization and debug flags? The only viable reason I can think of is to ensure package releases for different platforms are optimal and consistent, and use well-defined options. I would argue that using a native or cross file, where you can fully control optimization level and debug level, is better suited!

  • If you need a specific configured build for linux, save a build file like so: build_files/release/linux.ini
  • If you need a specific configured build for javascript, save a build file like so: build_files/release/javascript.ini
  • If you want additional build files for ci testing linux, save a build file like so: build_files/ci/linux64_debug.ini

You can then quickly configure a build for ci, release, testing, etc, simply by calling meson ./builddir --native-file somefile.ini

I have to say that I spend a lot of time ensuring that the windows and linux platform flags that matter, aside from debug and optimization are precise. Please compare meson.build in platform/windows/ and platform/linux/ and compare the complexity, and what they achieve.

If you still decide against it, there are ways to do it by disabling optimization and debug flags, and setting them all manually, but then you take flexibility away from developers.

Also, we can also always add an option outside of buildtype that holds some pre-configured configuration options per platform, but I dont think they should always be auto-detected and hardcoded.

Build Naming

The build naming can be easily solved in multiple ways, by combining platform name, tools enabled, bits on the cpu, etc.
Or, another way I would recommend, is directly setting the name you would like in a native/cross file to control the name of the exe.

  • You can set build_files/release/linux.ini to produce executable godot_linux_release.exe
  • You can set build_files/ci/linux64_debug.ini to produce executable godot_ci_linux_64_debug.exe

etc, etc.

Either way not a hard issue to solve :)

Build Accuracy

With separate configure (Meson) and build (Ninja), you need to remember to manually reconfigure your build folder if anything changed that requires it - and there's a number of things which do:

* Adding new files

* Modifying files which are not directly compiled but converted to code by a script, e.g. `doc/classes/*.xml`

Meson and ninja perfectly track adding new files to the meson.build and will reconfigure themselves when you simply compile.

The doc classes confuse me, as you already have to bootstrap godot to generate new document files. At that point, you should also be reconfiguring anyways. I get that they are picked up by scons because it fully configures itself every time, but these xml files are not produced by the buildsystem, and they cannot be tracked yet.

A simple solution to that is to make doctool touch a .md5 file, or a timestamp file, and simply have meson listen to changes in that file to reconfigure itself.

For a fast moving target like Godot's master branch, this basically means that every single time you pull from upstream, you should likely remember to reconfigure your build folders manually, otherwise you might unknowingly end up with a broken build as you'll be missing some content (which may break compilation, like missing .cpp files leading to missing symbols on linking, or may still build successfully and you'll have e.g. outdated documentation, translations, fonts, icons, shaders, etc.).

This is completely incorrect. When you pull changes and the sources referenced by the meson build file changes, it automatically reconfigures itself.

Cross-compilation

The platform flag has not dissappeared, it is still there and does exactly what you would like.

I think one of the concepts that people may be missing is that a native build file and cross file allow you to override/specify/add additional parameters to a build.

The primary purpose of linux-mingw-win64 is to specify to specifically use the mingw-gcc binaries, and set the platform parameter to linuxbsd.

This makes it so you dont have to ensure your CXX compiler and CC, and OBJC, and OBJC++ paths are correct before you run meson or cmake.

For example on windows, I have a personal native build file that looks like this: win-msvc-lld.ini

[binaries]
c = 'cl'
c_ld = 'lld-link'
cpp = 'cl'
cpp_ld = 'lld-link'

[properties]
platform = 'windows'

All I am doing here is making it really easy for myself to specifically use msvc's cl.exe, and for linking use llvm's lld-link.exe, without having to set my environment variables before hand.

Also, I specify the platform manually, but its not needed if I load that file on windows anyways.

Notes

I think it may be best to set up a meeting and communicate how these things can be done effectively and cleanly.

If you consider that windows, linux, linux-cross, and macos can be implemented so easily, with much cleaner meson.build files in platform/ compared to the detect.py's, I think this is really good progress forward.

@Faless
Copy link
Collaborator

Faless commented Jan 13, 2021

The meson devs have also advised against using buildtype, as it is basically a matrix table that combines the parameters debug {true, false} and optimization {0, g, 1, 2, 3, s}, and it will be deprecated in the future.
Also, we can also always add an option outside of buildtype that holds some pre-configured configuration options per platform, but I dont think they should always be auto-detected and hardcoded.

Yeah, I think we should have a custom target option like in scons as a start so that we can better compare builds.

you cant even build target release (with optimization and no debug symbols) with the tools (this is due to an abuse of the define DEBUG_ENABLED, which for some reason tool-critical code is hidden behind).

To my knowledge tools depends on extra checks and info that would make release builds slower and more memory-hungry.
A tool+release build, would (assuming you manage to build it), an editor release that crashes on some errors (instead of reporting them to the user to be fixed before release), or unable to profile some functions from the debugger,
Maybe we can do better than that, but in most cases release_debug is just release + DEBUG_ENABLED.

@Faless
Copy link
Collaborator

Faless commented Jan 13, 2021

EDIT: Looks like setting buildtype to plain makes it compile succesfully. Weird.

I get the same error with plain builds. Maybe it's a matter of order and concurrency?

@marstaik
Copy link
Contributor Author

marstaik commented Jan 17, 2021

Build notes

We are only mapping the compiler flags that should have nothing to do with cpp-defines necessary for code building.

Windows

Optimization/Debug table

release (scons) meson equivalent release_debug (scons) meson equivalent debug (scons) meson equivalent
speed=true /02 optimization=2 debug=false /O2 (?debug_symbols /Z7 ) optimization=2 debug=true/false /Od /Ehsc /Z7 optimization=0 debug=true + /Ehsc
speed=false /O1 optimization=s debug=false /O1 (?debug_symbols /Z7 ) optimization=s debug=true/false

Notes

  • /ENTRY:mainCRTStartup only specified on release.
    • Really need to be consistent here.
    • This should NOT be different across release/release_debug/debug.
    • Very very questionable
  • /MT /MD controlled by 'use_static_cpp'. Meson has a built in options for this, and you can even specify the debug types if you desire.
  • /OPT:REF can be added to optimization() > 0/1
  • subsystem console/gui is handled natively by native meson option win_subsystem, so it just isn't needed
  • /Gd, /GR, /nologo are added to every build
    • We can just always add these in meson also
    • /Gd /GR are optimization flags, question if we should manually add them
    • /nologo is trivial, whether or not to show the msvc prompt...lol
  • /TP assume all sources are C++ - meson handles compiling c/c++ files separately perfectly, and as the thirdparties have been fixed as static_libraries() this is 99.9% unnecessary.
  • Meson uses Zi instead of Z7. They are both debug symbols, but Zi stores the symbols separately in a pdb versus Z7 which stores it in the object file. Separate pdb increases build speed. No practical difference.

LinuxBSD

Optimization/Debug table

release (scons) meson equivalent release_debug (scons) meson equivalent debug (scons) meson equivalent
speed=true -O3 optimization=3 debug=false -O2 (?debug_symbols -g2) optimization=2 debug=true/false -g3 optimization=0 debug=true
speed=false -Os optimization=s debug=false -Os (?debug_symbols -g2) optimization=s debug=true/false

Notes

  • sanitization flags have a meson equivalent b_sanitize built in.
  • coverage reports builtin to meson https://mesonbuild.com/howtox.html#producing-a-coverage-report
  • lto builtin to meson. Probably handles more compilers than we ever will with scons.
  • pie builtin to meson. b_pie
  • -rdynamic - This is added to debug, but we cant even build as a shared library properly yet. :/

Conclusion

As you can see from the tables, all of the flags have direct equivalents controllable by users in meson, without needing to add them in manually - the other platforms are also the same way.

Optimization and Debug can already be controlled by the builtin flags, and allows users to customize optimization level even more, either by using the builtin options (which meson synchronizes to behave the same across different platforms (MSVC, gcc, Intel, llvm-clang, etc)) or specifying them manually/through a build file if they want to save a build state for themselves.

Meson already does everything scons does better, more consistently, and with less thinking/overhead on our end.

Finished all modules on except for mono and webm.
Windows compilation with MSVC (LLVM-mingw kind of)
Linux compilation with GCC
Linux cross compile to windows with gcc-mingw-64
@vnen
Copy link
Member

vnen commented Feb 8, 2021

Taking a quick glance at this and I do like how it turned out. I have zero experience with Meson but it didn't seem hard to understand what's going own in the build files.

Still, there are some things I'm still in doubt

  • I took a look at the GDScript module, since it's the one I'm most familiar with, and I noticed a line like this:

    _module_gdscript_cpp_args += ['-DGDSCRIPT_NO_LSP']
    

Which seems to add a compiler flag directly. Won't this be a problem when using a different compiler (like MSVC?).

  • Haven't looked thoroughly but I think some modules/platforms add new options to SCons, which doesn't seem to happen here. So is there a way for a module/platform to define new options? As an example, Android might need an option to define the API level. Also to define different defaults for the common options.

  • It seems dependencies between modules are somewhat manual based on ifs (which is fine, still better than SCons) but there are some decisions that might lead to unintended consequences. For instance, in GDScript if you disable jsonrpc or websocket modules it also disables the LSP. It makes sense that is not supported since there is a dependency, but an user might disable those because they're not using that and not realize that the LSP won't work.
    So, IMO mismatch in dependencies should cause a build failure. Assuming we can add a custom message we can tell what is the issue and make the user decide whether they want to really disable LSP (with another build option) or re-enable the needed modules

If those are things you already planned to do later, that's great. I'm just mentioning since I didn't see any comment about those.

I still think it's a great step up. Thanks for putting effort into this change.

@Faless
Copy link
Collaborator

Faless commented Feb 10, 2021

I've worked a bit on the javascript platform, and got a build (no ZIP bundling yet) at this branch:
https://github.com/Faless/godot/tree/js/meson

With the following option:
meson --reconfigure ./build-js --cross-file cross/emscripten -Dbuildtype=plain -Dbuiltin_pcre2_with_jit=false -Dtools=false -Dplatform=javascript

Many issues to fix:

  • Threads build: should be easy, will need custom option
  • GDNative build: this is tricky, it will need to build all the godot stuff with a different extension (.wasm instead of .js) and build a new target of just one file (and the js-libraries) as a .js file.
  • Closure compiler: Will need custom commands, and adding environment variables to the compiler commands (can we do this?).
  • Tools: haven't tried, will need to add an extra library, but should be fairly easy.

@@ -108,6 +108,7 @@ logs/
[Rr]elease/
x64/
build/
build*/
Copy link
Contributor

Choose a reason for hiding this comment

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

This adds modules/mono/build_scripts/ to the ignore list. I'm going to rename it to modules/mono/meson_scripts, but adding gitignore entries like is to ask for trouble IMO.

Copy link

@jpakkane jpakkane Feb 28, 2021

Choose a reason for hiding this comment

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

If you start this with a slash, Git will only ignore it in the top source directory. So /build* would match a top level build-linux but not modules/mono/build_scripts.

http://git-scm.com/docs/gitignore

@neikeq
Copy link
Contributor

neikeq commented Feb 20, 2021

The mono module port to Meson is mostly done: https://github.com/neikeq/godot/commits/mono-meson
Check the commit message for a brief of what's missing: neikeq/godot@778402a

Since empty builds are very slow with MSBuild I had to make Meson track the input files of the C# projects to only run MSBuild when there are changes, otherwise it would kill all the speed benefits. I also had to drop wildcards in C# projects as Meson doesn't support that. Overall it works pretty well in the end.

A few issues that need to be addressed:

  • I removed the mono module from disabled_modules in meson_options.txt but it should be disabled by default. It should be possible to enable it with an option like -Dmodule_mono_enabled=true but I'm not familiar with the enabling/disabling of Godot modules with Meson. EDIT: I didn't realize this initially as I wasn't familiar with meson, but now I see I can just pass -Ddisabled_modules=text_server_fb without mono in it.

  • As @vnen said, it would be great if we could declare Meson options in subdirectories, e.g.: modules/mono/meson_options.txt (right now mono modules options are in the root meson_options.txt), as modules are meant to be self-contained. Sadly, I think Meson doesn't allow this.

  • It seems there are two important limitation with the output of custom_target:

    1. It must be a file. It can't be a path to a file in a subdirectory. This is bad as I don't want to replicate the output directory structure (e.g.: GodotSharp/Tools/nupkgs or GodotSharp/Mono/lib/mono/4.5/Facades) in the source tree...
    2. The output goes to the builddir in the same subdirectory as the meson.build that declares the target. So the output of targets that I declare for the mono module go into builddir/modules/mono/. I can't make them go into builddir/ which is what I need.

    Right now, as a workaround, I use a stamp as the output file and pass meson.project_build_root() to the scripts to write the output files. This is far from ideal as Meson has no knowledge of the real output files.

@jpakkane
Copy link

it would be great if we could declare Meson options in subdirectories, e.g.: modules/mono/meson_options.txt (right now mono modules options are in the root meson_options.txt), as modules are meant to be self-contained. Sadly, I think Meson doesn't allow this

This is one of those things that we have considered adding every now and then, but since there has not been a strong need for it it has not been implemented (limited resources and all that).

@fire
Copy link
Member

fire commented Aug 23, 2021

Is superseded by #51153.

@Calinou
Copy link
Member

Calinou commented Aug 23, 2021

Superseded by #51153. Thanks for the contribution nonetheless, it's been a very helpful base so far 🙂

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.