godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.23k stars 21.04k forks source link

Godot can't load GDNative compiled by MinGW on Windows. #84160

Closed KirillGrigoriev closed 11 months ago

KirillGrigoriev commented 11 months ago

Godot version

v3.5.3.stable.official [6c814135b]

System information

v3.5.3.stable.official - Windows 10.0.19045 - GLES2 - dedicated NVIDIA GeForce GTX 650 (NVIDIA; 30.0.14.7430) - 13th Gen Intel(R) Core(TM) i5-13400 (16 Threads)

Issue description

Godot can't load GDNative compiled by MinGW (LLVM-MinGW-Clang) with error:

Godot Engine v3.5.3.stable.official (c) 2007-2022 Juan Linietsky, Ariel Manzur & Godot Contributors.
--- GDScript language server started ---
 Can't open dynamic library: .../GDProject/demo/bin/win64/libgdexample.dll, error: Error 126: The specified module could not be found.
.
 modules/gdnative/gdnative.cpp:510 - No valid library handle, can't get symbol from GDNative object
 modules/gdnative/nativescript/nativescript.cpp:1503 - No nativescript_init in "res://bin/win64/libgdexample.dll" found

Also tested on v3.6.beta.custom_build [fe7ed984b] compiled by MinGW (LLVM-MinGW-Clang). «nw» can see «nativescript_init»:

...
18000cc90 T fprintf
1801269c0 T free
180126840 T fwrite
180175140 D gdnlib
180001600 T godot_gdnative_init
180001610 T godot_gdnative_terminate
180001620 T godot_nativescript_init
180175058 d initial_daylight
180175048 d initial_timezone
180175098 d initial_tzname0
18017509c d initial_tzname1
180175030 d initial_tznames
...

But «dumpbin» not, if it's important:

Microsoft (R) COFF/PE Dumper Version 14.37.32825.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file libgdexample.dll

File Type: DLL

  Summary

        1000 .00cfg
        1000 .buildid
       14000 .data
        3000 .debug_abbrev
        1000 .debug_aranges
        E000 .debug_info
        3000 .debug_line
        4000 .debug_loc
        1000 .debug_ranges
       11000 .debug_str
       20000 .pdata
       4D000 .rdata
        1000 .reloc
      126000 .text
        1000 .tls

Godot 4 load GDExtension compiled by MinGW (LLVM-MinGW-Clang) without any issues. For compiling used:

clang version 17.0.3 (https://github.com/llvm/llvm-project.git 888437e1b60011b8a375dd30928ec925b448da57)
Target: x86_64-w64-windows-gnu
Thread model: posix

Testing with UCRT and MSVCRT.

Steps to reproduce

  1. Go throw steps from official docs to create example project: https://docs.godotengine.org/en/3.5/tutorials/scripting/gdnative/gdnative_cpp_example.html.
  2. «godot-cpp» compiles with «use_mingw=yes» argument without any issues.
  3. GDNative's SConstruct don't have a «use_mingw» key, so it should be modify to:
    
    #!python
    import os

opts = Variables([], ARGUMENTS)

Gets the standard flags CC, CCX, etc.

env = DefaultEnvironment()

Define our options

opts.Add(EnumVariable('target', "Compilation target", 'debug', ['d', 'debug', 'r', 'release'])) opts.Add(EnumVariable('platform', "Compilation platform", '', ['', 'windows', 'x11', 'linux', 'osx'])) opts.Add(EnumVariable('p', "Compilation target, alias for 'platform'", '', ['', 'windows', 'x11', 'linux', 'osx'])) opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no')) opts.Add(PathVariable('target_path', 'The path where the lib is installed.', 'demo/bin/')) opts.Add(PathVariable('target_name', 'The library name.', 'libgdexample', PathVariable.PathAccept)) opts.Add(BoolVariable('use_mingw', "Use MinGW", 'no'))

Local dependency paths, adapt them to your setup

godot_headers_path = "godot-cpp/godot-headers/" cpp_bindings_path = "godot-cpp/" cpp_library = "libgodot-cpp"

only support 64 at this time..

bits = 64

Updates the environment with the option variables.

opts.Update(env)

Process some arguments

if env['use_llvm']: env['CC'] = 'clang' env['CXX'] = 'clang++'

if env['p'] != '': env['platform'] = env['p']

if env['platform'] == '': print("No valid target platform selected.") quit();

For the reference:

- CCFLAGS are compilation flags shared between C and C++

- CFLAGS are for C-specific compilation flags

- CXXFLAGS are for C++-specific compilation flags

- CPPFLAGS are for pre-processor flags

- CPPDEFINES are for pre-processor defines

- LINKFLAGS are for linking flags

Check our platform specifics

if env['platform'] == "osx": env['target_path'] += 'osx/' cpp_library += '.osx' env.Append(CCFLAGS=['-arch', 'x86_64']) env.Append(CXXFLAGS=['-std=c++17']) env.Append(LINKFLAGS=['-arch', 'x86_64']) if env['target'] in ('debug', 'd'): env.Append(CCFLAGS=['-g', '-O2']) else: env.Append(CCFLAGS=['-g', '-O3'])

elif env['platform'] in ('x11', 'linux'): env['target_path'] += 'x11/' cpp_library += '.linux' env.Append(CCFLAGS=['-fPIC']) env.Append(CXXFLAGS=['-std=c++17']) if env['target'] in ('debug', 'd'): env.Append(CCFLAGS=['-g3', '-Og']) else: env.Append(CCFLAGS=['-g', '-O3'])

elif env['platform'] == "windows": env['target_path'] += 'win64/' cpp_library += '.windows'

This makes sure to keep the session environment variables on windows,

# that way you can run scons in a vs 2017 prompt and it will find all the required tools
env.Append(ENV=os.environ)

if env['use_mingw']:
    env.Append(CCFLAGS=['-fPIC'])
    env.Append(CXXFLAGS=['-std=c++17'])
    if env['target'] in ('debug', 'd'):
        env.Append(CCFLAGS=['-g3', '-Og'])
    else:
        env.Append(CCFLAGS=['-g', '-O3'])
else:
    env.Append(CPPDEFINES=['WIN32', '_WIN32', '_WINDOWS', '_CRT_SECURE_NO_WARNINGS'])
    env.Append(CCFLAGS=['-W3', '-GR'])
    env.Append(CXXFLAGS='/std:c++17')
    if env['target'] in ('debug', 'd'):
        env.Append(CPPDEFINES=['_DEBUG'])
        env.Append(CCFLAGS=['-EHsc', '-MDd', '-ZI'])
        env.Append(LINKFLAGS=['-DEBUG'])
    else:
        env.Append(CPPDEFINES=['NDEBUG'])
        env.Append(CCFLAGS=['-O2', '-EHsc', '-MD'])

if env['target'] in ('debug', 'd'): cpp_library += '.debug' else: cpp_library += '.release'

cpp_library += '.' + str(bits)

make sure our binding library is properly includes

env.Append(CPPPATH=['.', godot_headers_path, cpp_bindings_path + 'include/', cpp_bindings_path + 'include/core/', cpp_bindings_path + 'include/gen/']) env.Append(LIBPATH=[cpp_bindings_path + 'bin/']) env.Append(LIBS=[cpp_library])

tweak this if you want to use different folders, or more folders, to store your source code in.

env.Append(CPPPATH=['src/']) sources = Glob('src/*.cpp')

library = env.SharedLibrary(target=env['target_path'] + env['target_name'] , source=sources)

Default(library)

Generates help for the -h scons option.

Help(opts.GenerateHelpText(env))


But, I'm not sure about I did it right. But with this GDNative compiling without issues.

### Minimal reproduction project

Archive include compiled GNative:
[GDNativeExample.zip](https://github.com/godotengine/godot/files/13199038/GDNativeExample.zip)
bruvzg commented 11 months ago

But «dumpbin» not, if it's important

Default DUMPBIN summary only list sections, not symbols. Try adding /SYMBOLS to the DUMPBIN command.

error: Error 126: The specified module could not be found.

Usually it's missing dependencies. Seems like build script does not have -static, -static-libgcc, -static-libstdc++ link flags like 4.x have, so it probably depends on libc++.dll, libunwind.dll, and other runtime stuff that's missing.

KirillGrigoriev commented 11 months ago

Oh, big thanks, it's actually work. Final SConstruct, if someone would will need it in future:

#!python
import os

opts = Variables([], ARGUMENTS)

# Gets the standard flags CC, CCX, etc.
env = DefaultEnvironment()

# Define our options
opts.Add(EnumVariable('target', "Compilation target", 'debug', ['d', 'debug', 'r', 'release']))
opts.Add(EnumVariable('platform', "Compilation platform", '', ['', 'windows', 'x11', 'linux', 'osx']))
opts.Add(EnumVariable('p', "Compilation target, alias for 'platform'", '', ['', 'windows', 'x11', 'linux', 'osx']))
opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no'))
opts.Add(PathVariable('target_path', 'The path where the lib is installed.', 'demo/bin/'))
opts.Add(PathVariable('target_name', 'The library name.', 'libgdexample', PathVariable.PathAccept))
opts.Add(BoolVariable('use_mingw', "Use MinGW", 'no'))

# Local dependency paths, adapt them to your setup
godot_headers_path = "godot-cpp/godot-headers/"
cpp_bindings_path = "godot-cpp/"
cpp_library = "libgodot-cpp"

# only support 64 at this time..
bits = 64

# Updates the environment with the option variables.
opts.Update(env)

# Process some arguments
if env['use_llvm']:
    env['CC'] = 'clang'
    env['CXX'] = 'clang++'

if env['p'] != '':
    env['platform'] = env['p']

if env['platform'] == '':
    print("No valid target platform selected.")
    quit();

# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
# - CXXFLAGS are for C++-specific compilation flags
# - CPPFLAGS are for pre-processor flags
# - CPPDEFINES are for pre-processor defines
# - LINKFLAGS are for linking flags

# Check our platform specifics
if env['platform'] == "osx":
    env['target_path'] += 'osx/'
    cpp_library += '.osx'
    env.Append(CCFLAGS=['-arch', 'x86_64'])
    env.Append(CXXFLAGS=['-std=c++17'])
    env.Append(LINKFLAGS=['-arch', 'x86_64'])
    if env['target'] in ('debug', 'd'):
        env.Append(CCFLAGS=['-g', '-O2'])
    else:
        env.Append(CCFLAGS=['-g', '-O3'])

elif env['platform'] in ('x11', 'linux'):
    env['target_path'] += 'x11/'
    cpp_library += '.linux'
    env.Append(CCFLAGS=['-fPIC'])
    env.Append(CXXFLAGS=['-std=c++17'])
    if env['target'] in ('debug', 'd'):
        env.Append(CCFLAGS=['-g3', '-Og'])
    else:
        env.Append(CCFLAGS=['-g', '-O3'])

elif env['platform'] == "windows":
    env['target_path'] += 'win64/'
    cpp_library += '.windows'
    # This makes sure to keep the session environment variables on windows,
    # that way you can run scons in a vs 2017 prompt and it will find all the required tools
    env.Append(ENV=os.environ)

    if env['use_mingw']:
        env.Append(CCFLAGS=['-fPIC'])
        env.Append(CXXFLAGS=['-std=c++17'])
        env.Append(LINKFLAGS=['-static', '-static-libgcc', '-static-libstdc++'])
        if env['target'] in ('debug', 'd'):
            env.Append(CCFLAGS=['-g3', '-Og'])
        else:
            env.Append(CCFLAGS=['-g', '-O3'])
    else:
        env.Append(CPPDEFINES=['WIN32', '_WIN32', '_WINDOWS', '_CRT_SECURE_NO_WARNINGS'])
        env.Append(CCFLAGS=['-W3', '-GR'])
        env.Append(CXXFLAGS='/std:c++17')
        if env['target'] in ('debug', 'd'):
            env.Append(CPPDEFINES=['_DEBUG'])
            env.Append(CCFLAGS=['-EHsc', '-MDd', '-ZI'])
            env.Append(LINKFLAGS=['-DEBUG'])
        else:
            env.Append(CPPDEFINES=['NDEBUG'])
            env.Append(CCFLAGS=['-O2', '-EHsc', '-MD'])

if env['target'] in ('debug', 'd'):
    cpp_library += '.debug'
else:
    cpp_library += '.release'

cpp_library += '.' + str(bits)

# make sure our binding library is properly includes
env.Append(CPPPATH=['.', godot_headers_path, cpp_bindings_path + 'include/', cpp_bindings_path + 'include/core/', cpp_bindings_path + 'include/gen/'])
env.Append(LIBPATH=[cpp_bindings_path + 'bin/'])
env.Append(LIBS=[cpp_library])

# tweak this if you want to use different folders, or more folders, to store your source code in.
env.Append(CPPPATH=['src/'])
sources = Glob('src/*.cpp')

library = env.SharedLibrary(target=env['target_path'] + env['target_name'] , source=sources)

Default(library)

# Generates help for the -h scons option.
Help(opts.GenerateHelpText(env))