godotengine / godot

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

How to compile Swift with Scons? #92421

Closed BraveEvidence closed 5 months ago

BraveEvidence commented 5 months ago

Tested versions

Godot 4.2.2

System information

Godot v4.2.2.stable - macOS 14.5.0 - Vulkan (Mobile) - integrated Apple M1 - Apple M1 (8 Threads)

Issue description

I am trying to create an iOS plugin for Godot Game Engine. For that I need to create a static library. I am trying to call my swift code from objective-c++. Here is the source code but it results in an error when I run scons target=release_debug arch=arm64 plugin=arithematic version=4.0 saying

arithematic/arithematic.mm:10:9:{10:9-10:30}: fatal error: 'arithematic-Swift.h' file not found [1]
 #import "arithematic-Swift.h"
         ^~~~~~~~~~~~~~~~~~~~~
1 error generated.
scons: *** [arithematic/arithematic.o] Error 1
scons: building terminated because of errors. 

I have tried asking it on Stackoverflow , Swift Forums and GodotForums but have not received any solution yet

Steps to reproduce

I am trying to call my swift code from arithematic.mm

I have created the Bridging Header for my swift file and included it in arithematic.mm

#import "arithematic-Swift.h"

Now Godot requires scons to build the '.a' file from a static library, to do that we need to run scons target=release_debug arch=arm64 plugin=arithematic version=4.0but as soon as I run it I get error saying

arithematic/arithematic.mm:10:9:{10:9-10:30}: fatal error: 'arithematic-Swift.h' file not found [1]
 #import "arithematic-Swift.h"
         ^~~~~~~~~~~~~~~~~~~~~
1 error generated.
scons: *** [arithematic/arithematic.o] Error 1
scons: building terminated because of errors. 

Now I also have a SConstruct file which I am guessing is causing the issue

#!/usr/bin/env python
import os
import sys
import subprocess

if sys.version_info < (3,):
    def decode_utf8(x):
        return x
else:
    import codecs
    def decode_utf8(x):
        return codecs.utf_8_decode(x)[0]

# Most of the settings are taken from https://github.com/BastiaanOlij/gdnative_cpp_example

opts = Variables([], ARGUMENTS)

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

# Define our options
opts.Add(EnumVariable('target', "Compilation target", 'debug', ['debug', 'release', "release_debug"]))
opts.Add(EnumVariable('arch', "Compilation Architecture", '', ['', 'arm64', 'armv7', 'x86_64']))
opts.Add(BoolVariable('simulator', "Compilation platform", 'no'))
opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no'))
opts.Add(PathVariable('target_path', 'The path where the lib is installed.', 'bin/'))
opts.Add(EnumVariable('plugin', 'Plugin to build', '', ['', 'arithematic']))
opts.Add(EnumVariable('version', 'Godot version to target', '', ['', '3.x', '4.0']))

# 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['arch'] == '':
    print("No valid arch selected.")
    quit();

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

if env['version'] == '':
    print("No valid Godot version 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

# Enable Obj-C modules
env.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])

if env['simulator']:
    sdk_name = 'iphonesimulator'
    env.Append(CCFLAGS=['-mios-simulator-version-min=16.0'])
    env.Append(LINKFLAGS=["-mios-simulator-version-min=16.0"])
else:
    sdk_name = 'iphoneos'
    env.Append(CCFLAGS=['-miphoneos-version-min=16.0'])
    env.Append(LINKFLAGS=["-miphoneos-version-min=16.0"])

try:
    sdk_path = decode_utf8(subprocess.check_output(['xcrun', '--sdk', sdk_name, '--show-sdk-path']).strip())
except (subprocess.CalledProcessError, OSError):
    raise ValueError("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))

env.Append(CCFLAGS=[
    '-fobjc-arc', 
    '-fmessage-length=0', '-fno-strict-aliasing', '-fdiagnostics-print-source-range-info', 
    '-fdiagnostics-show-category=id', '-fdiagnostics-parseable-fixits', '-fpascal-strings', 
    '-fblocks', '-fvisibility=hidden', '-MMD', '-MT', 'dependencies', '-fno-exceptions', 
    '-Wno-ambiguous-macro', 
    '-Wall', '-Werror=return-type',
    # '-Wextra',
])

env.Append(CCFLAGS=['-arch', env['arch'], "-isysroot", "$IOS_SDK_PATH", "-stdlib=libc++", '-isysroot', sdk_path])
env.Append(CCFLAGS=['-DPTRCALL_ENABLED'])
env.Prepend(CXXFLAGS=[
    '-DNEED_LONG_INT', '-DLIBYUV_DISABLE_NEON', 
    '-DIOS_ENABLED', '-DUNIX_ENABLED', '-DCOREAUDIO_ENABLED'
])
env.Append(LINKFLAGS=["-arch", env['arch'], '-isysroot', sdk_path, '-F' + sdk_path])

if env['arch'] == 'armv7':
    env.Prepend(CXXFLAGS=['-fno-aligned-allocation'])

if env['version'] == '3.x':
    env.Append(CCFLAGS=["$IPHONESDK"])
    env.Prepend(CXXFLAGS=['-DIPHONE_ENABLED'])
    env.Prepend(CXXFLAGS=['-DVERSION_3_X'])

    env.Prepend(CFLAGS=['-std=gnu11'])
    env.Prepend(CXXFLAGS=['-DGLES_ENABLED', '-std=gnu++14'])

    if env['target'] == 'debug':
        env.Prepend(CXXFLAGS=[
            '-gdwarf-2', '-O0', 
            '-DDEBUG_MEMORY_ALLOC', '-DDISABLE_FORCED_INLINE', 
            '-D_DEBUG', '-DDEBUG=1', '-DDEBUG_ENABLED',
            '-DPTRCALL_ENABLED',
        ])
    elif env['target'] == 'release_debug':
        env.Prepend(CXXFLAGS=['-O2', '-ftree-vectorize',
            '-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1', '-DDEBUG_ENABLED', 
            '-DPTRCALL_ENABLED',
        ])

        if env['arch'] != 'armv7':
            env.Prepend(CXXFLAGS=['-fomit-frame-pointer'])
    else:
        env.Prepend(CXXFLAGS=[
            '-O2', '-ftree-vectorize',
            '-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1', 
            '-DPTRCALL_ENABLED',
        ])

        if env['arch'] != 'armv7':
            env.Prepend(CXXFLAGS=['-fomit-frame-pointer'])
elif env['version'] == '4.0':
    env.Append(CCFLAGS=["$IOS_SDK_PATH"])
    env.Prepend(CXXFLAGS=['-DIOS_ENABLED'])
    env.Prepend(CXXFLAGS=['-DVERSION_4_0'])

    env.Prepend(CFLAGS=['-std=gnu11'])
    env.Prepend(CXXFLAGS=['-DVULKAN_ENABLED', '-std=gnu++17'])

    if env['target'] == 'debug':
        env.Prepend(CXXFLAGS=[
            '-gdwarf-2', '-O0', 
            '-DDEBUG_MEMORY_ALLOC', '-DDISABLE_FORCED_INLINE', 
            '-D_DEBUG', '-DDEBUG=1', '-DDEBUG_ENABLED', 
        ])
    elif env['target'] == 'release_debug':
        env.Prepend(CXXFLAGS=[
            '-O2', '-ftree-vectorize',
            '-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1', '-DDEBUG_ENABLED',
        ])

        if env['arch'] != 'armv7':
            env.Prepend(CXXFLAGS=['-fomit-frame-pointer'])
    else:
        env.Prepend(CXXFLAGS=[
            '-O2', '-ftree-vectorize',
            '-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1',
        ])

        if env['arch'] != 'armv7':
            env.Prepend(CXXFLAGS=['-fomit-frame-pointer'])            
else:
    print("No valid version to set flags for.")
    quit();

# Adding header files
if env['version'] == '3.x':
    env.Append(CPPPATH=[
        '.', 
        'godot', 
        'godot/platform/iphone',
    ])
else:
       env.Append(CPPPATH=[
        '.', 
        'godot', 
        'godot/platform/ios',
    ])

# tweak this if you want to use different folders, or more folders, to store your source code in.
# sources = Glob('plugin/' + env['plugin'] + '/*.mm')
# sources.append(Glob('plugin/' + env['plugin'] + '/*.mm'))
# sources.append(Glob('plugin/' + env['plugin'] + '/*.m'))

sources = Glob('arithematic/*.cpp')
sources.append(Glob('arithematic/*.mm'))
sources.append(Glob('arithematic/*.m'))

# lib<plugin>.<arch>-<simulator|ios>.<release|debug|release_debug>.a
# library_platform = env["arch"] + "-" + ("simulator" if env["simulator"] else ("iphone" if env['version'] == '3.x' else "ios"))
# library_name = env['plugin'] + "." + library_platform + "." + env["target"] + ".a"
# library = env.StaticLibrary(target=env['target_path'] + library_name, source=sources)

library_platform = env["arch"] + "-" + ("simulator" if env["simulator"] else "iphone")
library_name = env['plugin'] + "." + library_platform + "." + env["target"] + ".a"
library = env.StaticLibrary(target=env['target_path'] + library_name, source=sources)

Default(library)

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

As you can see above it does not know how to compile the Bridging-Header and swift file

Here is what I tried adding in the above SConstruct file

# Adding header files
if env['version'] == '3.x':
    env.Append(CPPPATH=['.', 'godot', 'godot/platform/iphone', ])
else:
    env.Append(CPPPATH=['.', 'godot', 'godot/platform/ios', ])

# ...

sources = Glob('arithematic/*.cpp')
sources.append(Glob('arithematic/*.h'))
sources.append(Glob('arithematic/*.mm'))
sources.append(Glob('arithematic/*.m'))
sources.append(Glob('arithematic/*.swift'))

# ...

env.Append(CCFLAGS=['-swiftc', '-import-objc-header', 'arithematic-Swift.h'])
env.Append(LINKFLAGS=['-swiftc', '-import-objc-header', 'arithematic-Swift.h'])
env['SWIFTCOM'] = 'swiftc'

# ...

It results in even more errors as I don't have enough experience in scons

If I create a simple iOS project with objective-c++ and remove godot specific code and just add a swift file and build the project from Xcode it works perfectly fine so the error is most probably from SConstruct file which I have created

I tried adding this path /Users/Desktop/samples/Godot/mytrial/work/plugin/arithematic to CPPPATH in SConstruct but same issue. Also tried /Users/Desktop/samples/Godot/mytrial/work/plugin/arithematic/arithematic-Swift.h and /Users/Desktop/samples/Godot/mytrial/work/plugin/arithematic/arithematic-Bridging-Header.h

Minimal reproduction project (MRP)

https://github.com/BraveEvidence/GodotSampleStatic/tree/main

AThousandShips commented 5 months ago

This is not for asking support questions, what bug in the engine are you experiencing? This is not for any official part of the engine it looks like

BraveEvidence commented 5 months ago

@AThousandShips Yes I understand but there is literally no help on all other forums

AThousandShips commented 5 months ago

That doesn't mean this is the appropriate forum, this is for bug reporting, not support, especially not for things that aren't part of the engine

I wish you good luck in finding a solution, but closing this as this isn't the right place for this