Gluecodium

repository·master·Indexed 42 days ago

https://github.com/heremaps/gluecodium

A cross-platform code generation tool that bridges C++ with high-level languages including Java, Kotlin, Swift, and Dart. It automates the creation of bindings for Android, iOS, and Flutter development using Lime IDL files and a dedicated CMake toolchain.

Tokens
27.3K
Snippets
64
Records
129
Agent score
52%

What's inside Gluecodium

  1. Overview of Gluecodium

    master

    Gluecodium is a code generation tool that produces C++, Java, Kotlin, Swift, and Dart code. Its primary use case is generating C++ interfaces and corresponding bindings for Android (Java/Kotlin), iOS (Swift), and Flutter (Dart).

    Key capabilities:

    • Eliminates manual writing of conversions and bindings once C++ logic is implemented.
    • Supports macOS and Linux.
    • Can generate interfaces/protocols that allow C++ to call implementations written in Java, Kotlin, Swift, or Dart.
  2. Understand the Gluecodium repository structure

    master

    The Gluecodium repository is organized into three main Gradle projects for source code, a dedicated functional testing directory, and various integration tools.

    Source Code Projects

    The core logic is split into three Gradle projects, each containing its own unit tests:

    • gluecodium: Main project source.
    • lime-loader: Loader-related logic.
    • lime-runtime: Runtime-related logic.

    Functional Tests

    Functional tests are located in the functional-tests directory and are separated from unit tests:

    • functional-tests/functional: Primary functional tests for all four output languages.
    • functional-tests/namerules: Tests for 'custom name rules' functionality (Java, Swift, and C++).
    • functional-tests/scripts: Bash scripts used to execute these test suites.

    Integration Tools

    Additional directories provide support for different build systems and IDEs:

    • cmake: A CMake-based toolchain for integrating Gluecodium into CMake build scripts.
    • gluecodium-gradle: A Gradle plugin for integrating Gluecodium into Gradle build scripts.
    • tools: Miscellaneous utilities, including a minimal launcher and IntelliJ IDEA settings for LIME IDL syntax highlighting.
  3. What is LimeIDL?

    master
    LimeIDL (Language-Independent Model Interface Definition Language) is the input language for Gluecodium. It is used to define programming interfaces (classes, interfaces, structs, etc.) which Gluecodium then uses to generate implementation code in target languages like C++, Java, Kotlin, Swift, and Dart. The syntax is inspired by modern languages like Kotlin and Swift, designed to be compact and easy to read.
  4. Understand semantic validation stages

    master

    Semantic validation checks the LIME model for general correctness (independent of specific target languages). It is performed by the Gluecodium class after the model tree is loaded. The process follows three distinct steps:

    1. Type reference validation: The LimeTypeRefsValidator attempts to resolve every late-bound type reference. Failures here are reported as errors.
    2. Type-reference-dependent validation: A group of validators that require all type references to be resolved correctly. These only run if Step 1 succeeds.
    3. Independent validation: A group of validators that do not access type references and can run regardless of the results of Step 1.
  5. Documentation processors

    master

    Documentation processors handle the conversion of LIME documentation comments into target-language-specific documentation.

    Key responsibilities include:

    • Markdown Processing: LIME uses Markdown for documentation. Processors use the Flexmark library to parse and adapt this Markdown to the target language's specific documentation tags and syntax (noting that Java has different requirements than other target languages).
    • Link Resolution: Converting LIME-style documentation links (e.g., [core.MyClass.NestedInterface]) into the correct syntax and names for the target language.
  6. How Gluecodium generators work

    master

    Gluecodium generators follow a consistent high-level structure. They take a LIME model (consisting of a model tree and a reference map) as input and produce in-memory code files as output.

    For most generators (excluding C++), the output is split into two distinct sets:

    1. API files: Code written in the target languages (e.g., Kotlin, Swift, Java).
    2. Bindings files: C/C++ code used to bridge the target language with C/C++.

    Before code generation begins, every generator performs two mandatory steps:

    • Filtering: The model is filtered based on @Skip and @EnableIf generator-specific attributes.
    • Validation: The generator performs specific validation checks on the model.
  7. How Gluecodium handles language boundaries

    master

    Gluecodium generates a C++ API with language bindings for Java, Kotlin, Swift, or Dart.

    • C++: Generates declarations only. You must implement the application logic manually in C++.
    • Platform Code (Java, Kotlin, Swift, Dart): Bindings are fully generated and forward all calls to the C++ implementation.

    Key Interaction Rules:

    • Platform to C++: Platform code can call both static and non-static member functions in C++.
    • C++ to Platform: C++ can only call into platform code if an object was first passed from the platform to C++.
    • Loading: In Java, Kotlin, and Dart, you must manually load the native library before making any calls to C++.

    Mental Model for Objects:

    • Classes: Generated as abstract classes in C++ and wrapper classes in platform code. Logic resides in C++. New instances are created via C++ functions returning a subclass, which the platform code then automatically wraps.
    • Interfaces: Can be implemented in either C++ or platform code. If implemented in platform code, Gluecodium creates a proxy object in C++ so the C++ side can interact with platform services (like positioning or notifications) transparently.
  8. How reference resolution works in the LIME model

    master

    The LIME model represents IDL definitions as immutable tree elements. Because the creation order of these elements is not guaranteed and cyclic references (e.g., A refers to B, B refers to A) are possible, textual references to elements are resolved via a post-creation process.

    References are primarily represented by descendants of the LimeTypeRef abstract class or the LimeElementRef class.

    There are two main categories of references:

    1. Direct references: References to built-in types (like String, Int, Date) or generic types (List, Set, Map). These do not require delayed resolution.
    2. Late-bound references: References that use the Kotlin by lazy mechanism to delay resolution until the first read access. These are used for self-references, ambiguous imports, or positional references in initializer literals.
  9. Understand the Swift binding architecture

    master

    Swift bindings in Gluecodium are structured in two layers to bridge the gap between Swift and C++. Because Swift only supports C calling conventions, a translation layer called C-Bridge is used to facilitate communication between the Swift code and the underlying C++ implementation.

    Key components include:

    • Swift Layer: The high-level code used by developers.
    • C-Bridge: A pure C interface that acts as the intermediary.
    • C++ Layer: The original implementation.
  10. Manage Gluecodium source sets (Main vs Common)

    master

    Gluecodium uses two source sets:

    • main: Depends on the input Lime IDL files.
    • common: Depends only on Gluecodium options (e.g., internal namespace).

    Single Target Use Case

    If you only have one target with generated code, you do not need to set a separate common code directory. The common code will be combined with the main code automatically.

    Multiple Target Use Case

    If code is generated for multiple targets that need to share code, you must separate the common code to avoid duplication and compilation errors:

    1. Set a separate common output directory for apigen_generate.
    2. Add the generated common source to only one target.
    3. Link all other targets against that primary target.

    To support this, add_target_sources and add_target_link_directories provide MAIN and COMMON options.

  11. Understand pointer equality behavior in C++

    master

    Pointer equality is preserved within C++ for two scenarios:

    1. C++ Objects: A C++ object wrapped in Java/Kotlin/Swift is never copied by the bridging code, so its pointer value remains constant.
    2. Interface Implementations: When a platform-side interface implementation is passed to C++, it is wrapped in a std::shared_ptr. If the same platform object is passed to C++ multiple times, the bridge reuses the same wrapper, ensuring pointer equality is maintained in the C++ layer.
  12. Resolve nesting relationships using the reference map

    master
    The reference map can be used to traverse the model tree upwards. Given an element's path (e.g., core.MyClass.doSomething), you can find its "outer" (parent) element by omitting the last section of the path (core.MyClass) and performing a lookup in the reference map using that truncated path. This is commonly used in generator algorithms for import/include resolution and fully qualified name resolution.