You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
llgo get should use LLAR to install a requested native module and use llcppg to generate its LLGo binding locally.
The core pipeline is:
flowchart LR
A["llgo get madler/zlib@v1.2.1"]
B["llar install madler/zlib@v1.2.1 --json"]
C["Read module, dependencies, and pkg-config metadata"]
D["Generate dependency bindings"]
E["Run llcppg for madler/zlib"]
F["LLGoCacheDir/madler/zlib@v1.2.1"]
A --> B --> C --> D --> E --> F
Loading
The selected LLAR native-module version is also the cache version of the generated binding. LLGo does not publish or maintain a second, independently assigned wrapper version.
2. Background
The previous llpkg design stored pre-generated wrappers in a dedicated project and maintained a mapping between native-library versions and Go wrapper versions.
That design also acted as a fallback for llcppg instability. This proposal uses a different boundary: LLGo controls the generation pipeline, so llcppg failures can be detected and fixed directly. A separately published wrapper repository is no longer needed as a stability layer.
The resulting ownership is:
Component
Responsibility
LLAR
Select and install the requested native module version and its dependencies.
LLAR metadata
Provide complete pkg-config information for the installed artifact.
LLGo
Orchestrate installation, dependency generation, and package discovery.
llcppg
Generate Go bindings and use declared dependencies to load exported type mappings and emit Go imports.
3. Goals
Support llgo get <module>@<version> for LLAR modules.
Pass the same module and version to llar install.
Generate bindings into {{LLGoCacheDir}}/{{module}}@{{version}}.
Generate dependency bindings before the requested binding.
Pass versioned dependency coordinates from LLAR's structured JSON result to llcppg so it can load dependency types and generate imports.
Use complete pkg-config data instead of an llpkg-specific binary format.
Remove the need for published and independently versioned wrapper modules.
4. Proposed Workflow
4.1 Module without Dependencies
For:
llgo get madler/zlib@v1.2.1
LLGo performs the following logical steps:
Run llar install madler/zlib@v1.2.1 --json.
Read the resolved module version and complete pkg-config metadata.
Run llcppg against the installed headers and libraries.
Place the generated module at:
{{LLGoCacheDir}}/madler/zlib@v1.2.1
The generated module keeps the LLAR module name:
module madler/zlib
The generated cache entry reuses the native input version (v1.2.1). It does not have a separate wrapper release version or tag.
4.2 Import and Use the Generated Package
After:
llgo get madler/zlib@v1.2.1
an LLGo program imports the generated module by its LLAR module name:
package main
import (
"github.com/goplus/lib/c""madler/zlib"
)
funcmain() {
println(c.GoString(zlib.Version()))
}
The program can then be run normally:
llgo run .
The import path is madler/zlib, not madler/zlib@v1.2.1. The version in the llgo get command selects the native artifact from which this package is generated and its versioned cache entry; it is not part of the generated Go module name or source import path.
4.3 Module with Dependencies and Generated Imports
Assume example/reader@v2.0.0 depends on madler/zlib@v1.2.1.
flowchart TD
A["llgo get example/reader@v2.0.0"]
B["llar install example/reader@v2.0.0 --json"]
C["madler/zlib@v1.2.1"]
D["Generate LLGoCacheDir/madler/zlib@v1.2.1"]
E["Set llcppg deps to madler/zlib@v1.2.1"]
F["Generate LLGoCacheDir/example/reader@v2.0.0"]
A --> B
B --> C --> D --> E --> F
Loading
LLGo supplies the LLAR dependency coordinate to llcppg without dropping its version:
{
"deps": ["madler/zlib@v1.2.1"]
}
llcppg uses the versioned coordinate to load the generated dependency and its exported type mappings. When the generated consumer refers to a dependency type, llcppg emits the normal Go import path without @version:
import"madler/zlib"
This is why llcppg needs deps: LLAR describes which native artifacts were resolved, while llcppg needs the corresponding generated packages in order to produce cross-package type references and imports. The version belongs to dependency resolution and the cache coordinate; it never appears in Go import syntax.
4.4 Dependency Graph Rule
LLGo treats LLAR JSON as the source of the native dependency graph.
Dependencies are generated before their consumers.
The same resolved module version is generated once within one llgo get operation.
Diamond dependencies reuse the same generated module-and-version cache entry.
Direct LLAR dependency coordinates are passed to llcppg deps with their versions.
llcppg loads each generated dependency and emits source imports using module names without @version.
LLAR should expose direct dependencies when the result is used to populate llcppg deps. A flattened transitive closure is sufficient for installation, but does not describe the direct generated-package relationships precisely.
5. Cache and Version Model
For the coordinate madler/zlib@v1.2.1, the generated binding occupies:
{{LLGoCacheDir}}/madler/zlib@v1.2.1
This follows the Go module cache shape:
{{LLGoCacheDir}}/{{module}}@{{version}}
The version is part of the cache coordinate, but not the module directive or source import path:
Generate the sibling entry LLGoCacheDir/madler/zlib@v1.3.1; keep v1.2.1.
llcppg behavior changes
Regenerate the affected cache entry rather than assign a new wrapper release version.
The important distinction is:
cache identity = LLAR module path + LLAR native version
independent wrapper release version = none
6. pkg-config Contract
LLAR metadata should evolve from link flags such as -lz into complete pkg-config information. LLGo then gives llcppg one standard description for both header discovery and linking.
For example, an installed artifact may provide a zlib.pc equivalent to:
The exact LLAR JSON representation of the .pc content is a separate LLAR design decision. This proposal requires only that LLGo can obtain the root pkg-config identity and make all dependency pkg-config entries available.
7. Module Naming
The LLAR module name is also the generated Go module name and forms the path part of the versioned cache coordinate:
Meaning
Example
LLAR module
madler/zlib
Generated Go module
madler/zlib
Versioned cache directory
{{LLGoCacheDir}}/madler/zlib@v1.2.1
llcppg dependency
madler/zlib@v1.2.1
Generated source import
madler/zlib
Module names must be safe relative paths and must not contain empty, . or .. components. Cache paths should reuse Go module path and version escaping rules rather than define another filesystem encoding. The precise package-name normalization rule is left to a follow-up proposal.
LLGo is responsible for making these generated modules discoverable during generation and compilation. The specific package-query implementation is not part of this design.
8. Key Tradeoffs and Boundary Cases
8.1 Multiple Cached Versions and One Resolved Version
Different versions of madler/zlib can coexist in the cache. For example, both of these directories may exist:
Storage does not decide which version a build imports. LLGo uses the version resolved in LLAR's dependency result when making madler/zlib discoverable to that build. Version-conflict resolution remains an LLAR dependency-graph responsibility.
8.2 Multiple pkg-config Entries
One LLAR module may install multiple .pc files. LLAR metadata must identify which pkg-config package or packages form the public binding input. The module name alone is not enough to infer this.
8.3 Native Dependencies and Binding Dependencies
A pkg-config Requires entry describes a native compilation and link relationship. An llcppg deps entry lets llcppg load another generated package's exported type mappings and emit a Go import when those types appear in the generated API.
For example, if example/reader exposes a zlib type in its public headers, LLGo passes madler/zlib@v1.2.1 to llcppg and the generated source imports madler/zlib. If a native dependency is link-only and none of its types crosses the public binding boundary, pkg-config still carries the native dependency even though the generated source may not need an import. The first implementation may conservatively pass all direct LLAR dependencies to llcppg; narrowing that set is a later improvement.
8.4 Generator Policy beyond pkg-config
pkg-config describes compilation and linking, but it does not describe llcppg-specific policy such as C++ mode, selected public headers, renamed symbols, or type mappings. This proposal does not decide where that controlled policy is stored.
8.5 Target Selection
The installed LLAR artifact and the generated binding must describe the same target. Cross-target generation should be specified separately.
9. Out of Scope
The exact LLAR metadata schema for complete .pc files.
The package-loader or workspace implementation used to find cached modules.
Cache locking, temporary directories, and update mechanics.
The storage format for generation provenance.
Cross-compilation behavior.
Automatic inference of all llcppg-specific policy.
10. Alternatives Considered
Keep llpkg as a Versioned Wrapper Repository
Rejected. It creates an additional package, version, publication, and mapping lifecycle around generated source. LLGo can instead regenerate and validate bindings directly.
Use an Unversioned Cache Path
Rejected. {{LLGoCacheDir}}/madler/zlib would make a request for one native version overwrite the binding generated for another. A Go module cache-style {{module}}@{{version}} coordinate allows both bindings to coexist without introducing an independent wrapper version.
Pass Raw Link Flags Directly to llcppg
Rejected. Link flags alone do not provide the header and compile information needed by llcppg. Complete pkg-config information supplies both through an existing standard.
11. Open Questions
How does LLAR identify the root .pc entry when an artifact contains more than one?
Does LLAR JSON expose direct dependencies or only the full artifact closure?
Do LLAR version strings already satisfy Go module version escaping rules, or does LLAR need a canonical cache encoding for additional version forms?
Where should controlled per-module llcppg policy live?
Proposal: Generate LLAR Bindings with
llgo getStatus: Draft
1. Summary
llgo getshould use LLAR to install a requested native module and use llcppg to generate its LLGo binding locally.The core pipeline is:
flowchart LR A["llgo get madler/zlib@v1.2.1"] B["llar install madler/zlib@v1.2.1 --json"] C["Read module, dependencies, and pkg-config metadata"] D["Generate dependency bindings"] E["Run llcppg for madler/zlib"] F["LLGoCacheDir/madler/zlib@v1.2.1"] A --> B --> C --> D --> E --> FThe selected LLAR native-module version is also the cache version of the generated binding. LLGo does not publish or maintain a second, independently assigned wrapper version.
2. Background
The previous llpkg design stored pre-generated wrappers in a dedicated project and maintained a mapping between native-library versions and Go wrapper versions.
That design also acted as a fallback for llcppg instability. This proposal uses a different boundary: LLGo controls the generation pipeline, so llcppg failures can be detected and fixed directly. A separately published wrapper repository is no longer needed as a stability layer.
The resulting ownership is:
3. Goals
llgo get <module>@<version>for LLAR modules.llar install.{{LLGoCacheDir}}/{{module}}@{{version}}.4. Proposed Workflow
4.1 Module without Dependencies
For:
LLGo performs the following logical steps:
llar install madler/zlib@v1.2.1 --json.The generated module keeps the LLAR module name:
The generated cache entry reuses the native input version (
v1.2.1). It does not have a separate wrapper release version or tag.4.2 Import and Use the Generated Package
After:
an LLGo program imports the generated module by its LLAR module name:
The program can then be run normally:
The import path is
madler/zlib, notmadler/zlib@v1.2.1. The version in thellgo getcommand selects the native artifact from which this package is generated and its versioned cache entry; it is not part of the generated Go module name or source import path.4.3 Module with Dependencies and Generated Imports
Assume
example/reader@v2.0.0depends onmadler/zlib@v1.2.1.An illustrative LLAR JSON result is:
{ "path": "example/reader", "version": "v2.0.0", "deps": [ { "path": "madler/zlib", "version": "v1.2.1" } ] }LLGo generates the dependency first:
flowchart TD A["llgo get example/reader@v2.0.0"] B["llar install example/reader@v2.0.0 --json"] C["madler/zlib@v1.2.1"] D["Generate LLGoCacheDir/madler/zlib@v1.2.1"] E["Set llcppg deps to madler/zlib@v1.2.1"] F["Generate LLGoCacheDir/example/reader@v2.0.0"] A --> B B --> C --> D --> E --> FLLGo supplies the LLAR dependency coordinate to llcppg without dropping its version:
{ "deps": ["madler/zlib@v1.2.1"] }llcppg uses the versioned coordinate to load the generated dependency and its exported type mappings. When the generated consumer refers to a dependency type, llcppg emits the normal Go import path without
@version:This is why llcppg needs
deps: LLAR describes which native artifacts were resolved, while llcppg needs the corresponding generated packages in order to produce cross-package type references and imports. The version belongs to dependency resolution and the cache coordinate; it never appears in Go import syntax.4.4 Dependency Graph Rule
LLGo treats LLAR JSON as the source of the native dependency graph.
llgo getoperation.depswith their versions.@version.LLAR should expose direct dependencies when the result is used to populate llcppg
deps. A flattened transitive closure is sufficient for installation, but does not describe the direct generated-package relationships precisely.5. Cache and Version Model
For the coordinate
madler/zlib@v1.2.1, the generated binding occupies:This follows the Go module cache shape:
The version is part of the cache coordinate, but not the module directive or source import path:
madler/zlib@v1.2.1LLGoCacheDir/madler/zlib@v1.2.1.madler/zlib@v1.3.1LLGoCacheDir/madler/zlib@v1.3.1; keepv1.2.1.The important distinction is:
6. pkg-config Contract
LLAR metadata should evolve from link flags such as
-lzinto complete pkg-config information. LLGo then gives llcppg one standard description for both header discovery and linking.For example, an installed artifact may provide a
zlib.pcequivalent to:For a module with native dependencies,
Requirescarries the native link relationship while LLAR JSON carries the generated-module relationship:The exact LLAR JSON representation of the
.pccontent is a separate LLAR design decision. This proposal requires only that LLGo can obtain the root pkg-config identity and make all dependency pkg-config entries available.7. Module Naming
The LLAR module name is also the generated Go module name and forms the path part of the versioned cache coordinate:
madler/zlibmadler/zlib{{LLGoCacheDir}}/madler/zlib@v1.2.1madler/zlib@v1.2.1madler/zlibModule names must be safe relative paths and must not contain empty,
.or..components. Cache paths should reuse Go module path and version escaping rules rather than define another filesystem encoding. The precise package-name normalization rule is left to a follow-up proposal.LLGo is responsible for making these generated modules discoverable during generation and compilation. The specific package-query implementation is not part of this design.
8. Key Tradeoffs and Boundary Cases
8.1 Multiple Cached Versions and One Resolved Version
Different versions of
madler/zlibcan coexist in the cache. For example, both of these directories may exist:Storage does not decide which version a build imports. LLGo uses the version resolved in LLAR's dependency result when making
madler/zlibdiscoverable to that build. Version-conflict resolution remains an LLAR dependency-graph responsibility.8.2 Multiple pkg-config Entries
One LLAR module may install multiple
.pcfiles. LLAR metadata must identify which pkg-config package or packages form the public binding input. The module name alone is not enough to infer this.8.3 Native Dependencies and Binding Dependencies
A pkg-config
Requiresentry describes a native compilation and link relationship. An llcppgdepsentry lets llcppg load another generated package's exported type mappings and emit a Go import when those types appear in the generated API.For example, if
example/readerexposes a zlib type in its public headers, LLGo passesmadler/zlib@v1.2.1to llcppg and the generated source importsmadler/zlib. If a native dependency is link-only and none of its types crosses the public binding boundary, pkg-config still carries the native dependency even though the generated source may not need an import. The first implementation may conservatively pass all direct LLAR dependencies to llcppg; narrowing that set is a later improvement.8.4 Generator Policy beyond pkg-config
pkg-config describes compilation and linking, but it does not describe llcppg-specific policy such as C++ mode, selected public headers, renamed symbols, or type mappings. This proposal does not decide where that controlled policy is stored.
8.5 Target Selection
The installed LLAR artifact and the generated binding must describe the same target. Cross-target generation should be specified separately.
9. Out of Scope
.pcfiles.10. Alternatives Considered
Keep llpkg as a Versioned Wrapper Repository
Rejected. It creates an additional package, version, publication, and mapping lifecycle around generated source. LLGo can instead regenerate and validate bindings directly.
Use an Unversioned Cache Path
Rejected.
{{LLGoCacheDir}}/madler/zlibwould make a request for one native version overwrite the binding generated for another. A Go module cache-style{{module}}@{{version}}coordinate allows both bindings to coexist without introducing an independent wrapper version.Pass Raw Link Flags Directly to llcppg
Rejected. Link flags alone do not provide the header and compile information needed by llcppg. Complete pkg-config information supplies both through an existing standard.
11. Open Questions
.pcentry when an artifact contains more than one?