Proposal: Platform-aware configuration and generation in llcppg

Cerrado
#1,124 1 comentario 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
30/100
Tipo de issue
Nueva funcionalidad
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
go

Línea de trabajo

Read tool/gen.go to trace configuration selection and generation, then inspect tool.Config.NewPackage and clang/clang.go for target argument propagation. Check xtool/env/cstdlib/stdlib.go for standard library discovery and require explicit Stdlib for recognized platform configurations. Done means ordinary and single-platform inputs retain their behavior, direct platform children generate into per-platform destinations with errors collected across failures, and the specified target and header rules are honored.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

configuration proposal

1. Summary

Introduce a platform-aware configuration and generation model in llcppg for C/C++ libraries whose bindings vary by target. Organize platform configurations in directories named GOOS-GOARCH[-<option>], each containing llcppg.cfg. Use the existing command arguments to select an ordinary configuration, one platform configuration, or a platform collection. A root llcppg.cfg selects single-configuration processing; only its absence triggers discovery in direct platform child directories. llarhub/libc is a concrete use case for this general model.

2. User Stories / Motivation

A binding maintainer may need separate configurations when a C/C++ library exposes different headers, declarations, macro values, or type layouts for different targets.

llarhub/libc illustrates this need: platforms can use different libc implementations, and the same implementation can expose different declarations or macro values under different target macros. For example, headers may select different branches for __x86_64__ and __aarch64__.

The generation input therefore needs both a target platform and matching library and system headers. Clang can parse for another target when given these inputs; a matching host machine is not required.

Maintainers need to select either an entire platform collection or one platform directly. Existing ordinary configurations must remain usable. For the initial libc use case, each platform uses its documented default libc; an optional suffix allows musl or another implementation to be distinguished later.

3. Current Workaround

Separate configurations can already be selected through llcppg's source-directory argument, one invocation at a time. However, the configuration-to-parser path does not currently pass a target to libclang. The proposal adds the association between directory and target, plus automatic discovery of a platform collection.

4. Goals

  • Keep each platform identity, generation configuration, and target header environment together.
  • Support ordinary, single-platform, and automatic platform-collection input through the existing positional arguments.
  • Preserve ordinary generation and the destination behavior of single-configuration invocations.

5. Out of Scope

  • The llcppg -merge algorithm.
  • GCC support and implementation of musl bindings; musl illustrates the naming extension.
  • Automatic Git commits, pushes, or branch switches.

Automatic processing writes each platform to <dest>/<platform>, continues after a platform fails, and returns the collected errors after processing the remaining platforms.

6. Proposal

6.1 Design Rule

The selected configuration provides generation settings and matching headers. When it belongs to a platform directory, that directory provides the target identity. The target and headers determine preprocessing and type parsing independently of the host running llcppg.

Configuration count and target selection are separate decisions: a directly selected platform directory is a single-configuration invocation with an explicit directory-derived target.

6.2 Syntax / API / Interface

Retain the existing command form and positional argument order:

llcppg <dest-gopkg-dir> [<src-header-files-and-cfg-dir>]

The second argument defaults to the current directory. No new argument or flag is introduced.

Configuration selection follows this order:

  1. Look for llcppg.cfg directly in the selected source directory. If present, load that configuration through the existing single-configuration path. Do not scan child directories.
  2. Only if that root configuration does not exist, inspect direct child directories for platform configurations. A child qualifies when its name is one of the initially supported platform names listed in section 6.5 and it contains llcppg.cfg.
  3. If one or more qualifying children exist, enter automatic platform-collection mode and process those configurations.
  4. If neither a root configuration nor qualifying platform configurations exist, report that no configuration was found.

A root configuration read error or invalid configuration is an error, not evidence that the directory is a platform collection.

6.3 Examples as Specification

Use the following example layout. single contains an ordinary configuration; platforms has no root llcppg.cfg and contains two platform configurations.

workspace/
    single/
        llcppg.cfg
    platforms/
        linux-amd64/
            llcppg.cfg
        darwin-arm64/
            llcppg.cfg

All six calls below supply the same destination, workspace/generated. The relative spelling changes with the working directory.

Case Working directory Command Selected input
Ordinary configuration, explicit source workspace/ llcppg ./generated ./single single/llcppg.cfg
Ordinary configuration, source omitted workspace/single/ llcppg ../generated The current directory's llcppg.cfg
Automatic platform collection, explicit source workspace/ llcppg ./generated ./platforms Both direct platform configurations
Automatic platform collection, source omitted workspace/platforms/ llcppg ../generated Both direct platform configurations in the current directory
One platform, explicit source workspace/ llcppg ./generated ./platforms/linux-amd64 Only linux-amd64/llcppg.cfg, with its directory-derived target
One platform, source omitted workspace/platforms/linux-amd64/ llcppg ../../generated The current directory's llcppg.cfg, with the linux-amd64 target

In each omitted-source form, adding . as the second argument selects the same source directory. The destination argument is still required.

The ordinary calls retain existing configuration and Pkgs behavior. The collection calls automatically process both qualifying direct platform children because the selected directory has no root configuration. The single-platform calls find a root configuration in the selected directory and process only that one; its platform name supplies the target.

Single-configuration calls write directly to the supplied destination, retaining existing subpackage behavior. Collection calls write each platform under <dest>/<platform>. For the collection example above:

generated/
    linux-amd64/
        ...generated files
        llcppg.pub
    darwin-arm64/
        ...generated files
        llcppg.pub

To regenerate just the Linux result in that layout, explicitly select its destination:

llcppg ./generated/linux-amd64 ./platforms/linux-amd64

Configured subpackages remain below the platform's destination, for example generated/linux-amd64/adt/. These are filesystem paths; generation does not select, switch, commit, or push Git branches.

6.4 Boundary Cases
Condition Behavior
Root llcppg.cfg and platform children coexist Root configuration takes precedence; children are not automatically processed
Configurations exist only below deeper descendants Do not recursively discover them
Root configuration is unreadable or invalid Report the error; do not switch modes

A child directory qualifies only when both its platform name is recognized and its llcppg.cfg exists.

6.5 Platform Names and Mapping

Use GOOS-GOARCH[-<option>], with Go's platform spellings and an optional target-environment choice. The libc mappings below illustrate the option: linux-amd64-musl identifies Linux, amd64, and musl. Omitting the option uses the documented platform default, independently of the host.

Platform names remain independent of compiler-specific target spelling. The Clang mapping is:

Platform directory Target platform libc selection Clang target
linux-amd64 Linux / amd64 glibc x86_64-unknown-linux-gnu
linux-arm64 Linux / arm64 glibc aarch64-unknown-linux-gnu
darwin-amd64 Darwin / amd64 System default x86_64-apple-darwin
darwin-arm64 Darwin / arm64 System default arm64-apple-darwin
linux-amd64-musl Linux / amd64 musl x86_64-unknown-linux-musl
linux-arm64-musl Linux / arm64 musl aarch64-unknown-linux-musl

The initial implementation recognizes exactly linux-amd64, linux-arm64, darwin-amd64, and darwin-arm64. Linux defaults to glibc. The musl rows illustrate a future naming extension and are not recognized platform configurations in this increment.

6.6 Target Headers and Stdlib

The target selects compiler-provided platform macros and type/ABI interpretation. The headers supply the C/C++ declarations and target-specific definitions being interpreted. For example, a musl target needs musl headers; a target string does not provide them.

Input Responsibility
Platform directory name Target OS, architecture, and optional target-environment choice
Dir / Files Headers whose bindings are generated
Stdlib Matching standard-library include directory needed during parsing

Relative Dir and Stdlib paths remain relative to the selected configuration directory; Files remains relative to Dir. When generating libc itself, these inputs can reference the same prepared header tree.

An empty Stdlib currently triggers local Clang include discovery. A recognized platform configuration must explicitly provide Stdlib; omission returns an error instead of using the host's default header directories. Ordinary configurations retain the existing default discovery behavior. Stdlib remains an include directory, not a sysroot.

6.7 Generation and Module Responsibilities

For a selected darwin-arm64 configuration:

  1. Load its llcppg.cfg.
  2. Map the platform name to arm64-apple-darwin.
  3. Resolve Dir, Files, and Stdlib to the selected headers and matching target environment.
  4. Pass -target arm64-apple-darwin alongside language and include arguments to libclang. Conditional compilation, macros, and types are interpreted for that target before Go generation.
  5. Generate bindings from the resulting translation units.

The existing Index.ParseTranslationUnit forwards arguments to libclang. The integration needed is target propagation through the configuration-driven path. CFlags currently serves wrapper compilation, not header parsing.

The command selects configuration directories and discovers platform collections. tool.Config.NewPackage derives the target from the basename of its existing workDir argument and supplies the corresponding libclang arguments. No Target field, new CLI option, or public method parameter is added. cl consumes translation units without knowing platform directory names or Git branches.

Automatic processing reuses the existing generation functions in the same process. Each platform receives its own <dest>/<platform> destination. A failed platform contributes an error, while the remaining platforms continue. The existing global ignore-diagnostic settings are saved and restored for each generation call so one configuration cannot silence diagnostics in the next.

6.8 Example: llarhub/libc Repository Workflow

llarhub/libc applies this configuration model to its branch organization:

c branch:
    <platform>/llcppg.cfg
              |
              | generate with that platform's inputs
              v
raw branch:
    <platform>/llcppg.cfg
    <platform>/...generated bindings
              |
              | llcppg -merge (later work)
              v
main branch:
    ...merged bindings

Configurations are maintained on c; each configuration and its generated bindings are committed under the corresponding platform name on raw. The later merge produces main. This repository workflow is external to the generator. The generator writes directories; separate Git operations determine which branch records those files.

7. Error Handling

Condition Behavior
Neither input layout supplies a configuration Report that no configuration was found
Root llcppg.cfg cannot be read or parsed Preserve the error; do not try platform discovery as a fallback
A selected platform configuration cannot be read or parsed Report its configuration error
A recognized platform configuration omits Stdlib Report the missing explicit target-header directory
One platform fails during automatic processing Continue the remaining platforms and return errors labeled with their platform names
FailFast reaches its diagnostic limit Stop the current cl.NewPackage call and return an error instead of exiting the process

8. Compatibility

Keep the positional arguments, ordinary configuration behavior, Pkgs handling, and single-configuration destination semantics. Add automatic discovery only when the root configuration is absent; collection output is placed under <dest>/<platform>.

Existing public function signatures remain unchanged. A FailFast limit now returns an error from cl.NewPackage; a single CLI invocation still reports failure through its existing error path. Unexpected panics remain panics and are not converted into platform errors.

9. Alternatives Considered

Alternative Reason for the directory-based design
Organize libc configurations only by libc implementation The same implementation can expose different interfaces under different target macros
Pass the target separately on the command line Platform directories keep the target identity attached to its configuration
Use compiler triples as directory names Go platform names keep repository organization independent of compiler-specific spelling
Require generation on each target host Target parameters and matching headers allow cross-target parsing

10. Testing Strategy

Case Expected result
Ordinary root configuration, including one with platform children Preserve existing generation and Pkgs; select only the root configuration
Missing root with qualifying direct platform children Process those configurations and write each result under <dest>/<platform>
No qualifying configuration Report a missing configuration
Unreadable or invalid root configuration Report the error without switching modes
Direct platform invocation Select one configuration, derive its target, and preserve the explicit destination
Omitted source argument Apply the same selection rules to the current directory
Deeper nested configurations Do not recursively discover them
Target-specific parsing Check OS/architecture macros, header selection, and pointer size for all four targets in C and C++
Platform configuration without Stdlib Return an error without host-header discovery
Configuration error or FailFast in a collection Return the platform errors and still produce the successful platform's output
Consecutive configurations with different ignore settings Restore diagnostic settings between calls
FailFast during loading or compilation Return an error with no partial package; preserve unrelated panics

Use llgo test for implementation tests. The implementation has passed llgo test -v ./... on macOS arm64 with LLGo v1.0.9 and LLVM 22.1.8, including the six CLI input forms and C/C++ generation fixtures for all four targets. These fixtures validate target behavior with supplied test headers; they do not establish complete libc/SDK binding coverage or execution on a Linux host.

The table's six target strings were checked with Clang 22.1.8 on macOS and produced matching OS/architecture macros. This verifies target selection only; complete libc generation remains unverified. See Clang's cross-compilation documentation.

11. Summary of Changes

Area Change
Input selection Root-configuration precedence; otherwise discover direct platform children
Target inputs Associate each selected platform configuration with its target and matching headers
Parsing integration Carry that target into the existing libclang argument path
llarhub/libc use case Maintain platform configurations on c and corresponding configurations and results on raw
CLI Keep positional arguments; write collection output under <dest>/<platform> and aggregate failures
Failure boundary Return FailFast as a package-generation error and restore per-call diagnostic settings

12. Open Questions

  1. How should target SDK/libc headers be prepared or distributed for real platform configurations? The generator currently requires callers to provide the matching Stdlib directory.
  2. Which additional platform/environment options, such as musl, should be implemented after the initial four targets?
Lenguaje dominante
Go
Estrellas
40
Forks
13
Merge medio
2 h 4 min
PR fusionados (30 d)
143

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de goplus/llcppg

Todos los issues de goplus/llcppg

Issues similares

Más issues de Go

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.