- docs
- Reference
- Reference
- Metadata
- Metadata: Environment Variables
Metadata: Environment Variables
The env array of metadata.json declares the environment variables OCX exposes when it runs commands with the package.
Environment Variables
Section titled “Environment Variables”The env array feeds ocx package exec and ocx env. Each entry is an object with a key, a type (path, constant or list) and a value template that OCX resolves when it composes the environment.
Interpolation Tokens
Section titled “Interpolation Tokens”${…} is a closed namespace: every occurrence in a value, or in an entry-point args element, must parse as one of four recognised bodies.
- A
${…}that matches none of them is refused, not passed through. - A value that resolves to more than 64 KiB is refused too.
| Body | Resolves to |
|---|---|
${installPath} | This package’s own content/ directory. |
${self.installPath} | Exact alias of ${installPath}; see Aliases. |
${self.env.KEY} | The resolved value of this package’s own KEY var, declared earlier in the same env array. Env values only, not legal in entry-point args; see self.env. |
${deps.NAME.installPath} | A declared dependency’s content/ directory, where NAME is the dependency’s repository basename or its explicit name field. |
{ "key": "PATH", "type": "path", "value": "${self.installPath}/bin:${deps.cmake.installPath}/bin"}- A token may appear more than once, and different bodies combine freely in one
value. - Three bodies take an optional
:native/:posixsuffix. See Render Modifiers.
Render Modifiers
Section titled “Render Modifiers”The three install-path bodies accept a :native or :posix suffix, for example ${installPath:posix}, that controls how path separators render. It is independent of type, which governs how the value as a whole composes.
:native(default when omitted): Windows paths render with\, POSIX paths with/.:posix: always/. It suits values read by tools that expect forward slashes regardless of host. Examples are an MSYS2/Git Bash binary or a value piped into a shell script.${self.env.KEY}: takes no modifier, and one written there is refused at exit 65, because the suffix rewrites every\in a resolved value OCX cannot inspect.- Render where the value is built and the reference inherits it, as in the example.
- Interface-visible
PATHvalue: a modifier takes the token out of binaries-scan scope. On Linux andanytheocx package createlibc lint refuses it with exit 65, naming the variable. On darwin and Windows the same exclusion is silent.
[ { "key": "SDK_ROOT", "type": "constant", "value": "${installPath:posix}/sdk" }, { "key": "SDK_TOOL", "type": "constant", "value": "${self.env.SDK_ROOT}/bin/tool" }]Escaping
Section titled “Escaping”Write $${ to emit a literal ${: $${workspaceFolder} publishes as the literal text ${workspaceFolder}. This is the only escape, and a bare $ not immediately followed by ${ is ordinary text.
- A value that ships another tool’s
${…}syntax, such as a devcontainer snippet, needs$${workspaceFolder}so ocx leaves it for that tool to resolve
Aliases
Section titled “Aliases”${self.installPath} is an exact alias of ${installPath}: same referent, same resolution, interchangeable.
- Preferred in new values, because it reads unambiguously next to
${self.env.KEY}and${deps.NAME.installPath}. ${installPath}and${self.installPath}are interchangeable.
self.env
Section titled “self.env”${self.env.KEY} resolves to the resolved value of this package’s own KEY env var. It is not its template text and not a value folded together with a consumer’s or a dependency’s contribution.
- Declaration order:
KEYmust be declared strictly earlier in the same package’senvarray - A forward reference, to a var declared later or to itself, is refused at publish time.
- Declaring the same key twice and then referencing it is refused as ambiguous.
- Legal only inside
envvalues, never inside entry-pointargs. - Declaration order is part of the grammar once
${self.env.KEY}is used. A generator must therefore emitenvfrom an ordered structure (a slice, aLinkedHashMap, a sorted map), not an unordered map type.
Unrecognised Tokens
Section titled “Unrecognised Tokens”OCX claims the entire ${…} namespace in a value: there is no fifth body and no pass-through for a token this ocx does not recognise. A refusal names what is wrong.
- A near-miss (
${slef.installPath}) suggests the root it is closest to. - An unknown root (
${workspaceFolder}) explains the$${escape. - A recognised root with an illegal body (
${self.env.A B}) lists the supported bodies for that root. - A recognised namespace with an unknown leaf (
${deps.cmake.version}) names the field and lists the leaves that exist for it (installPath). - Refusing (exit 65, naming the token):
ocx env,ocx package exec,ocx exec, any environment composition,ocx package create,ocx package push. - Refusing at run time, after install: a generated entry-point launcher whose baked
argselement holds the token. - Succeeding on such a document:
ocx package pull,ocx package install,ocx package inspect,ocx package description pull,ocx package whichandocx package deps, none of which resolveenvvalues.ocx package description pullnever readsenvat all, only the package’s__ocx.desctag. ocx package inspectechoes the declared template text verbatim, never a resolved value. Add--resolvefor a multi-platform image index.
Path Variables
Section titled “Path Variables”Path variables are prepended to any existing value of the environment variable, separated by the platform path delimiter. When required is true and the resolved path does not exist, the operation fails with an error.
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Environment variable name. |
type | string | Yes | Must be "path". |
required | boolean | No | If true, the resolved path must exist on disk. Defaults to false. |
value | string | Yes | Value template; see Interpolation Tokens. |
visibility | string | No | Entry visibility; see Entry Visibility. Default: "private". |
{ "key": "PATH", "type": "path", "required": true, "value": "${self.installPath}/bin"}Constant Variables
Section titled “Constant Variables”Constant variables replace any existing value of the environment variable.
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Environment variable name. |
type | string | Yes | Must be "constant". |
value | string | Yes | Value template; see Interpolation Tokens. |
visibility | string | No | Entry visibility; see Entry Visibility. Default: "private". |
{ "key": "JAVA_HOME", "type": "constant", "value": "${self.installPath}"}List Variables
Section titled “List Variables”List variables are appended to any existing value of the environment variable, joined by separator. Any earlier occurrence of the same contribution is removed first, so re-declaring a value moves it to the back instead of duplicating it.
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Environment variable name. |
type | string | Yes | Must be "list". |
separator | string | Yes | The string this contribution joins to the variable’s existing value (for example " " for JDK_JAVA_OPTIONS, "," for GODEBUG). Non-empty, and must not contain =, a newline, or a carriage return. See Separator Is Required. |
value | string | Yes | Value template; see Interpolation Tokens. Must not start or end with separator. |
visibility | string | No | Entry visibility; see Entry Visibility. Default: "private". |
{ "key": "GODEBUG", "type": "list", "separator": ",", "value": "gctrace=1"}- A contribution is opaque: OCX never splits it into elements. A value carrying its own separator (
"gctrace=1,madvdontneed=1"joined with",") is therefore removed and re-appended as a unit, never partially matched.
Separator Is Required
Section titled “Separator Is Required”Unlike path and constant, separator has no default in package metadata: a publisher must spell it out explicitly. A wrong separator fails silently, as Go’s GODEBUG ignores a setting it cannot parse, so the wire format refuses to guess one.
- The
[env]table inocx.tomland the--envflag may omit it. An omitted separator inherits whatever another contributor to the same key declared, or defaults to a single space if none did. - Rejected with exit 65, naming the variable: a separator that is empty, contains
=, a newline or a carriage return, or edges thevalue. - Enforced wherever
metadata.jsonis validated:ocx package create/push, and every later read.
See Env Composition for the per-key agreement rule across a composition.
Entry Visibility
Section titled “Entry Visibility”Each env entry carries a visibility field that controls which surface the entry contributes to when composing the runtime environment. It is distinct from the dependency-edge visibility, which controls how a dependency’s env propagates to its dependents.
| Value | Interface surface (--self off) | Private surface (--self on) | Use case |
|---|---|---|---|
private (default) | No | Yes | Internal paths the package’s own launchers need; not part of the public contract. |
public | Yes | Yes | Variables consumers should see: PATH, JAVA_HOME, package-specific prefix paths. |
interface | Yes | No | Values forwarded to consumers but not used by the package’s own runtime: PKG_CONFIG_PATH, library include hints. |
"sealed"is rejected at parse time onenventries, because an entry invisible on both surfaces is dead configuration.
See Env Composition for how entry values interact with dependency-edge visibility during the composition walk.