Browse Source

[Scons] Use builder to track bindings regeneration.

Using a scons Builder we now regenerate the bindings automatically
when the Godot API json or header has changed.

The option to force bindings regeneration (generate_bindings=yes) is
retained.
Fabio Alessandrelli 3 years ago
parent
commit
fa698ddd12
2 changed files with 40 additions and 38 deletions
  1. 13 29
      SConstruct
  2. 27 9
      binding_generator.py

+ 13 - 29
SConstruct

@@ -3,6 +3,7 @@
 import os
 import sys
 import subprocess
+from binding_generator import scons_generate_bindings, scons_emit_files
 
 if sys.version_info < (3,):
 
@@ -112,13 +113,7 @@ opts.Add(
 )
 opts.Add(PathVariable("custom_api_file", "Path to a custom JSON API file", None, PathVariable.PathIsFile))
 opts.Add(
-    EnumVariable(
-        "generate_bindings",
-        "Generate GDNative API bindings",
-        "auto",
-        allowed_values=["yes", "no", "auto", "true"],
-        ignorecase=2,
-    )
+    BoolVariable("generate_bindings", "Force GDExtension API bindings generation. Auto-detected by default.", False)
 )
 opts.Add(EnumVariable("android_arch", "Target Android architecture", "armv7", ["armv7", "arm64v8", "x86", "x86_64"]))
 opts.Add("macos_deployment_target", "macOS deployment target", "default")
@@ -442,16 +437,8 @@ elif env["platform"] == "javascript":
     elif env["target"] == "release":
         env.Append(CCFLAGS=["-O3"])
 
-env.Append(
-    CPPPATH=[
-        ".",
-        env["headers_dir"],
-        "#include",
-        "#gen/include",
-    ]
-)
-
-# Generate bindings?
+# Generate bindings
+env.Append(BUILDERS={"GenerateBindings": Builder(action=scons_generate_bindings, emitter=scons_emit_files)})
 json_api_file = ""
 
 if "custom_api_file" in env:
@@ -459,17 +446,16 @@ if "custom_api_file" in env:
 else:
     json_api_file = os.path.join(os.getcwd(), env["headers_dir"], "extension_api.json")
 
-if env["generate_bindings"] == "auto":
-    # Check if generated files exist
-    should_generate_bindings = not os.path.isfile(os.path.join(os.getcwd(), "gen", "src", "classes", "object.cpp"))
-else:
-    should_generate_bindings = env["generate_bindings"] in ["yes", "true"]
+bindings = env.GenerateBindings(
+    env.Dir("."), [json_api_file, os.path.join(env["headers_dir"], "godot", "gdnative_interface.h")]
+)
 
-if should_generate_bindings:
-    # Actually create the bindings here
-    import binding_generator
+# Forces bindings regeneration.
+if env["generate_bindings"]:
+    AlwaysBuild(bindings)
 
-    binding_generator.generate_bindings(json_api_file, env["generate_template_get_node"])
+# Includes
+env.Append(CPPPATH=[[env.Dir(d) for d in [env["headers_dir"], "include", os.path.join("gen", "include")]]])
 
 # Sources to compile
 sources = []
@@ -477,8 +463,7 @@ add_sources(sources, "src", "cpp")
 add_sources(sources, "src/classes", "cpp")
 add_sources(sources, "src/core", "cpp")
 add_sources(sources, "src/variant", "cpp")
-add_sources(sources, "gen/src/variant", "cpp")
-add_sources(sources, "gen/src/classes", "cpp")
+sources.extend([f for f in bindings if str(f).endswith(".cpp")])
 
 env["arch_suffix"] = env["bits"]
 if env["platform"] == "android":
@@ -500,7 +485,6 @@ if env["build_library"]:
     library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources)
     Default(library)
 
-env.Append(CPPPATH=[env.Dir(f) for f in ["gen/include", "include", "godot-headers"]])
 env.Append(LIBPATH=[env.Dir("bin")])
 env.Append(LIBS=library_name)
 Return("env")

+ 27 - 9
binding_generator.py

@@ -6,9 +6,9 @@ import shutil
 from pathlib import Path
 
 
-def print_file_list(api_filepath, output_dir, headers=False, sources=False):
+def get_file_list(api_filepath, output_dir, headers=False, sources=False):
     api = {}
-    end = ";"
+    files = []
     with open(api_filepath) as api_file:
         api = json.load(api_file)
 
@@ -25,9 +25,9 @@ def print_file_list(api_filepath, output_dir, headers=False, sources=False):
         header_filename = include_gen_folder / "variant" / (camel_to_snake(builtin_class["name"]) + ".hpp")
         source_filename = source_gen_folder / "variant" / (camel_to_snake(builtin_class["name"]) + ".cpp")
         if headers:
-            print(str(header_filename.as_posix()), end=end)
+            files.append(str(header_filename.as_posix()))
         if sources:
-            print(str(source_filename.as_posix()), end=end)
+            files.append(str(source_filename.as_posix()))
 
     for engine_class in api["classes"]:
         # TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
@@ -36,18 +36,36 @@ def print_file_list(api_filepath, output_dir, headers=False, sources=False):
         header_filename = include_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".hpp")
         source_filename = source_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".cpp")
         if headers:
-            print(str(header_filename.as_posix()), end=end)
+            files.append(str(header_filename.as_posix()))
         if sources:
-            print(str(source_filename.as_posix()), end=end)
+            files.append(str(source_filename.as_posix()))
 
     utility_functions_header_path = include_gen_folder / "variant" / "utility_functions.hpp"
     utility_functions_source_path = source_gen_folder / "variant" / "utility_functions.cpp"
     global_constants_header_path = include_gen_folder / "classes" / "global_constants.hpp"
     if headers:
-        print(str(utility_functions_header_path.as_posix()), end=end)
-        print(str(global_constants_header_path.as_posix()), end=end)
+        files.append(str(utility_functions_header_path.as_posix()))
+        files.append(str(global_constants_header_path.as_posix()))
     if sources:
-        print(str(utility_functions_source_path.as_posix()), end=end)
+        files.append(str(utility_functions_source_path.as_posix()))
+    return files
+
+
+def print_file_list(api_filepath, output_dir, headers=False, sources=False):
+    end = ";"
+    for f in get_file_list(api_filepath, output_dir, headers, sources):
+        print(f, end=end)
+
+
+def scons_emit_files(target, source, env):
+    files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True)]
+    env.Clean(files, target)
+    return [target[0]] + files, source
+
+
+def scons_generate_bindings(target, source, env):
+    generate_bindings(str(source[0]), env["generate_template_get_node"], target[0].abspath)
+    return None
 
 
 def generate_bindings(api_filepath, use_template_get_node, output_dir="."):