Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Proposal: `Define` — inject constants into the generated package

Đang mở
#1,067 2 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Một pull request liên quan đã được merge.

  • #1070 của @fennoai — đã merge

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
25/100
Loại issue
Tính năng
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Đình trệ
Công nghệ
go, json
Lĩnh vực
build-system, cli, tooling

Hướng nghiên cứu

Start with llcppg’s command-line flag parsing, llcppg.cfg loading, and the cl package phase between loading and package writing described in the issue. Trace how converted constants receive final Go names, then cover parsing, typing, interpolation, cycles, conflicts, precedence, and deterministic emission. Done means both configuration and -d inputs behave as specified, invalid cases report the relevant source and entry, references resolve to generated constants, and generated packages contain an ordered const block without entries in llcppg.pub.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

configuration feature proposal

Summary

Add a way to make llcppg inject user-specified constants into the generated Go package. It is exposed in two forms:

  • A repeatable command-line flag: llcppg -d NAME1=VALUE1 -d NAME2=VALUE2 ...
  • A field in llcppg.cfg:
"Define": [
    "NAME1=VALUE1",
    "NAME2=VALUE2"
]

Each entry adds a Go const named NAME with the given value to the target package. Both forms accept literal values (integers, floats, strings). Only the Define field in llcppg.cfg additionally lets a value reference constants already defined in the package with $(name), which makes it possible to derive new constants (for example a library version string) from what the converted C headers already provide. The -d flag does not support references: its values are always literal.

Motivation

Generated bindings often need a few constants that do not exist in the C headers in the form we want:

  • The version of the C library the binding was generated against, e.g. Python's headers define VERSION = "3.14.8" and we want a LLGoLibVersion = "v3.14.8" constant in the package so that tooling can tell which library version a binding targets.
  • Build-time or packaging metadata that is conceptually part of the package but has no C counterpart.
  • Small conveniences, such as exposing a numeric macro as a string, or giving a stable alias to a constant whose C name is awkward.

Today the only workaround is to hand-edit generated files, which is lost on the next regeneration. Making this a first-class llcppg feature keeps the output fully reproducible.

Proposal

Syntax

An entry has the form NAME=VALUE:

  • NAME must be a valid Go identifier.
  • The entry is split at the first =; VALUE may itself contain =.
  • Leading and trailing whitespace around NAME and around the whole VALUE is trimmed.

On the command line, each -d argument carries exactly one entry:

llcppg -d NAME1=VALUE1 -d NAME2=VALUE2 ...

-d may be given any number of times; all occurrences accumulate in the order they appear. There is no separator character between entries, so a value may contain any character, including ;. As with any shell argument, quote the whole NAME=VALUE when it contains spaces or characters the shell would interpret (see "Quoting on the command line" below).

Relationship between the flag and the config field

The flag and the config field have the same effect, with one difference: $(name) references are only available in llcppg.cfg (see "References with $(name)").

When both sources are present, entries from llcppg.cfg are applied first and entries from -d after them. If the same NAME appears in both sources, the later entry wins, so a command-line -d can override a value from the config file. Defining the same NAME more than once within a single source (two entries with the same name in one Define list, or two -d flags with the same name) is an error, since it is almost certainly a typo.

Value typing

The generated constant is always an untyped Go constant, and its kind is decided by the value:

Value Result
100, 0x10, 0b101, -1 untyped integer constant
3.14, 1e9 untyped floating-point constant
anything else, e.g. v3.14.8 untyped string constant
'100' (wrapped in single quotes) untyped string constant, content 100

Numeric detection follows Go's own integer and floating-point literal syntax. The single-quote form is the escape hatch for when a value looks numeric but a string is wanted: '100' yields const NAME = "100". The wrapping single quotes are never part of the string. A value is treated as quoted when it starts with ' and ends with '; everything between the outermost pair is the content, and a ' inside the content is kept as is.

Examples (llcppg.cfg):

"Define": [
    "MaxRetries=3",
    "Scale=0.5",
    "Channel=stable",
    "BuildID='20261017'"
]

generates

const MaxRetries = 3
const Scale = 0.5
const Channel = "stable"
const BuildID = "20261017"

The same constants from the command line:

llcppg -d MaxRetries=3 -d Scale=0.5 -d Channel=stable -d "BuildID='20261017'"
Quoting on the command line

Single quotes are also the shell's own quoting character, so on the command line an entry that contains a single-quoted string value must be wrapped in double quotes, as in -d "BuildID='20261017'" above.

Values passed with -d are literal: llcppg does not interpret $ in them, so $(name) is not a reference there. Whatever reaches llcppg after the shell has processed the argument is used as is, and a $(name) text that survives the shell becomes part of the string value.

None of this quoting applies in llcppg.cfg: JSON strings carry ' and $ as they are, so entries there are written exactly as in the examples in this document.

References with $(name)

References are supported only in the Define field of llcppg.cfg. In such a value, $(name) refers to a constant defined in the target package. The reference is substituted when the package is generated.

The motivating example, for a Python binding whose package already contains const VERSION = "3.14.8":

"Define": [
    "LLGoLibVersion=v$(VERSION)"
]

generates

const LLGoLibVersion = "v3.14.8"

Resolution rules (for llcppg.cfg entries):

  1. Pure reference. If the whole value is exactly one reference, e.g. Ver=$(VERSION), the new constant takes the referenced constant's value and kind. Referencing an integer yields an integer; referencing a string yields a string.
  2. Interpolation. If a reference appears together with other text, e.g. v$(VERSION) or $(MAJOR).$(MINOR), the result is a string. Referenced numeric constants are rendered in their decimal form before being spliced in.
  3. Single quotes. A single-quoted value is always a string, and references inside it are still expanded. '$(MAJOR)' therefore converts an integer constant to its string form.
  4. Literal dollar. $$ produces a single literal $. A $ that is not followed by ( or $ is kept as is.

Entries given with -d are not subject to these rules; they are plain literals. If you need a derived constant, put it in llcppg.cfg.

What $(name) can refer to

name is resolved against the Go names in the generated package, i.e. after llcppg's own renaming has been applied. The candidates are:

  • constants produced from C macros (#define VERSION "3.14.8"),
  • enum constants,
  • other Define entries, whether they come from llcppg.cfg or from -d (after merging, so a -d that overrides a config entry is what a reference sees).

References between Define entries are allowed in any order; llcppg resolves them on demand. A reference cycle (A=$(B), B=$(A)) is an error.

A reference to a name that does not exist, or that exists but is not a constant (a type, a function, a variable), is an error that names the offending entry.

Placement in the pipeline

Within the cl package's three-phase conversion (preload, load, compile), Define is evaluated after the load phase, when every constant that can be referenced is known, and before the package is written out. The generated constants are emitted together in a single const block in the package's main generated file, in the order the entries were given (with llcppg.cfg entries first, then -d entries in command-line order), so output is deterministic.

Define constants have no C symbol behind them, so they are not recorded in llcppg.pub.

Name conflicts

If NAME collides with a symbol that the conversion already produced in the package (a function, type, variable or constant), llcppg reports an error instead of silently shadowing or dropping either side. If the intent is to replace a converted constant, ignore the original symbol via the existing config options and define the new one.

Error summary

llcppg fails with a message identifying the entry (and whether it came from -d or llcppg.cfg) when:

  • an entry has no =, or NAME is not a valid Go identifier;
  • the same NAME is defined twice within one source;
  • NAME conflicts with an existing symbol in the package;
  • a value has an unterminated single quote (it starts with ' but does not end with a closing ');
  • (llcppg.cfg only) a $(name) reference is unresolved, points to a non-constant, or is part of a cycle, or a value has an unterminated $(.

Usage

Command line, literal values only:

llcppg -d Channel=stable -d MaxRetries=3

Config file, for a Python binding, using references:

{
  "name": "python",
  "Define": [
    "LLGoLibVersion=v$(VERSION)",
    "ABIMajor=$(PY_MAJOR_VERSION)",
    "ABIMajorString='$(PY_MAJOR_VERSION)'"
  ]
}

Assuming the headers provide VERSION = "3.14.8" and PY_MAJOR_VERSION = 3, the generated package gains:

const LLGoLibVersion = "v3.14.8"
const ABIMajor = 3
const ABIMajorString = "3"

Alternatives considered

  • Post-processing generated files with a script. Works, but every project reinvents it and it is easy to forget after regeneration.
  • Hand-written companion .go file in the package. Cannot see macro-derived constants by their final Go names without duplicating the renaming logic, and cannot be expressed in llcppg.cfg alone.
  • Packing several entries into one -d argument separated by ;. Rejected: it forces an escaping rule for values containing ;, and one entry per flag is simpler to read, script and quote.
  • Backticks as the string marker. Rejected: backticks are command substitution in the shell, which makes them awkward to pass on the command line. Single quotes are used instead, in both the command line and llcppg.cfg, so the two forms stay identical.
  • $(name) references in -d values. Rejected: $ is the shell's expansion character, so it would force users to write \$(name) or single-quote the whole entry, and it would clash with single-quoted string values. Derived constants are a configuration concern and belong in llcppg.cfg; -d stays a simple way to pass literals.
  • Typed constants (e.g. NAME:type=VALUE). Rejected for now: untyped constants cover the use cases (they convert implicitly where needed), and the rule set stays small. It can be added later without breaking existing entries.

Open questions

  1. Should a quoted Go string such as "abc" (double quotes) be accepted as an explicit string form in addition to single quotes? The single-quote form is enough for the current needs, so this proposal leaves it out.
  2. Should $(name) be able to reference C-level names (before renaming) as a fallback, or only final Go names? This proposal uses final Go names only, since that is what the user sees in the generated package.
Ngôn ngữ chính
Go
Star
40
Fork
13
Merge trung bình
1 giờ 34 phút
Pull request đã merge (30 ngày)
251

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của goplus/llcppg

Tất cả issue của goplus/llcppg

Issue tương tự

Thêm issue về Go

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.