Proposal: `Define` — inject constants into the generated package
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ả
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 aLLGoLibVersion = "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:
NAMEmust be a valid Go identifier.- The entry is split at the first
=;VALUEmay itself contain=. - Leading and trailing whitespace around
NAMEand around the wholeVALUEis 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):
- 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. - 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. - 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. - 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
Defineentries, whether they come fromllcppg.cfgor from-d(after merging, so a-dthat 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
=, orNAMEis not a valid Go identifier; - the same
NAMEis defined twice within one source; NAMEconflicts 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.cfgonly) 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
.gofile in the package. Cannot see macro-derived constants by their final Go names without duplicating the renaming logic, and cannot be expressed inllcppg.cfgalone. - Packing several entries into one
-dargument 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-dvalues. 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 inllcppg.cfg;-dstays 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
- 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. - 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
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của goplus/llcppg
-
Architecture-Design configuration
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 28/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 55/100
Maintainer thường phản hồi trong vòng 1 ngày
-
feature proposal
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 18/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của goplus/llcppg
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
grafana/tempo-operator#1691 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
fix: presentation.go with mcp-table.go: the message for each MCP server comes backCó thể đã có người làm @mge1512 đã nhận hôm nay. Đang mởbug
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 78/100
mark3labs/kit#242 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Project submission: Sno StationĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
slavakurilyak/awesome-ai-agents#827 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug P3
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
mpyw/declscope#262 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày