-
Notifications
You must be signed in to change notification settings - Fork 566
Proposal: pack Directive for gox.mod
This proposal introduces a new pack directive to gox.mod, enabling classfile framework packages to declare that an asset directory should be automatically packed (via xgo pack) before an XGo project is run, built, or tested. The directive specifies both the target directory and the exact index file name to use, eliminating any ambiguity about configuration format. The pack directive is project-scoped — it follows a project declaration and applies exclusively to that project, just like class and import. At most one pack directive is permitted per project block.
xgo pack merges a tree of scattered index.* configuration files into a single index_pack.* file, eliminating many small filesystem or network round-trips when loading asset metadata at runtime. This is especially important for web-deployed XGo applications (e.g., games built on the spx framework) where latency is sensitive to the number of individual resource fetches.
Currently, calling xgo pack is a manual, out-of-band step. There is no mechanism for a classfile framework to declare that packing is required, or to specify which directory should be packed. As a result:
- Developers frequently forget to run
xgo packbeforexgo run, leading to stale or missingindex_pack.*files. - CI pipelines must be configured separately and redundantly to invoke
xgo pack, duplicating knowledge that the framework itself already possesses. - New users following a framework's getting-started guide face an unexpected extra step with no clear diagnostic when they skip it.
Additionally, the existing xgo pack command must probe for index.json, index.yml, and index.yaml in each directory it visits, because it does not know in advance which format a given project uses. A framework author always knows the exact format — encoding this in gox.mod avoids the probing entirely.
The pack directive is placed inside a project block — alongside class and import — and takes two arguments: the directory to pack and the exact index file name. The toolchain uses this information to invoke xgo pack automatically before compilation, and passes the explicit file name so that xgo pack never needs to guess the format.
pack <directory> <indexfile>
Scope: Project-scoped. Must appear after a project declaration and before the next project declaration (or end of file). It is a sibling of class and import within the same project block.
Constraint: At most one pack directive is permitted per project. A second pack within the same project block is a parse error.
Arguments:
| Argument | Required | Description |
|---|---|---|
directory |
Required | A path relative to the project root (the directory passed to xgo run/xgo build) that xgo pack should be invoked on. |
indexfile |
Required | The exact file name of the index files in the tree (e.g. index.json, index.yaml). This name is used uniformly at every level of the directory tree. |
| Directive | Scope | Purpose |
|---|---|---|
xgo |
File | Declares the required XGo version |
project |
File | Declares a classfile project entry point |
class |
Project | Declares a work class within the current project |
import |
Project | Declares auto-imported packages for the current project |
pack |
Project | Declares the asset directory and index file name to pack before running the project |
xgo 1.6.0
project main.spx Game github.com/goplus/spx/v2 math
class -embed *.spx SpriteImpl
pack assets index.json
This tells the XGo toolchain: when running any project that uses the spx classfile framework, invoke xgo pack on the assets directory, treating index.json as the index file at every level of the tree.
When a gox.mod declares multiple project blocks, each may independently declare its own pack (or none at all):
xgo 1.6.0
project main.spx Game github.com/goplus/spx/v2 math
class -embed *.spx SpriteImpl
pack assets index.json
project .yap YapApp github.com/goplus/yap
import "github.com/goplus/yap/test"
# no pack directive — this project needs no asset packing
The toolchain packs only the directory declared for the matched project's classfile type.
The pack directive is honored by the following toolchain commands:
| Command | Behavior |
|---|---|
xgo run |
Packs before running (subject to skip logic described in §4.3). |
xgo build |
Packs before building (subject to skip logic described in §4.3). |
xgo install |
Packs before installing (subject to skip logic described in §4.3). |
xgo test |
Packs before testing (subject to skip logic described in §4.3). |
The pack step is not triggered by xgo fmt, xgo vet, xgo mod, or other non-compilation commands.
For any invocation that triggers packing, the toolchain follows this order:
- Resolve the project's
go.modand identify all//xgo:classdependencies. - For each such dependency, locate and parse its
gox.mod. - Identify which
projectblock applies to the current project's source files (based on file extension matching). - If that
projectblock contains apackdirective, apply the skip logic (§4.3). - If packing proceeds, resolve the target directory relative to the project root and invoke
xgo pack <directory>, passing<indexfile>as the index file name. - Proceed with compilation.
To keep xgo run fast in the normal development loop, the toolchain applies the following rule before invoking xgo pack:
If a packed output file (
index_pack.<ext>) already exists at the root of the target directory, the pack step is skipped.
The extension <ext> is derived from <indexfile> — for example, index.json implies index_pack.json.
This is an existence check only: the toolchain does not inspect modification times or compare file contents. The rationale is that asset trees are typically stable during a development session, and the overhead of a full freshness check on every xgo run is unnecessary for most workflows.
Forcing a pack run: To override the skip and unconditionally re-pack, pass the --pack flag:
xgo run --pack
xgo build --packThe --pack flag bypasses the existence check and always invokes xgo pack, even when index_pack.* already exists. This is useful after adding, removing, or renaming asset files.
Summary of skip behavior:
| Condition | Default (xgo run) |
With --pack (xgo run --pack) |
|---|---|---|
index_pack.* does not exist |
Runs xgo pack
|
Runs xgo pack
|
index_pack.* already exists |
Skips xgo pack
|
Runs xgo pack
|
The directory argument is always resolved relative to the project root — that is, the directory explicitly passed to (or inferred by) xgo run/xgo build/etc. as the project to compile. This is the directory that contains the project's source files (e.g. main.spx), which may or may not be the same directory as go.mod.
Example:
xgo run /home/user/mygameWith the directive pack assets index.json, the toolchain resolves the pack target as:
xgo pack /home/user/mygame/assetsIf the user runs from a subdirectory:
xgo run /home/user/mygame/levels/world1Then the pack target becomes:
xgo pack /home/user/mygame/levels/world1/assetsThis design ensures that each project instance packs its own co-located assets, independent of where go.mod lives in the module hierarchy.
| Condition | Behavior |
|---|---|
| Target directory does not exist | Non-fatal warning; packing is skipped. |
Target directory contains no <indexfile> at its root |
Follows existing xgo pack behavior (warning; no output produced). |
xgo pack fails on the target directory |
Fatal error; compilation does not proceed; error message includes the directory path and the underlying pack error. |
PackDir is added as a project-level directive alongside ClassDir and ImportDir:
GoxMod = XgoDir? ProjectBlock*
ProjectBlock = ProjectDir ProjectItem*
ProjectItem = ClassDir | ImportDir | PackDir
PackDir = "pack" Directory IndexFile
Directory = string // relative path, forward-slash separated, no ".." components
IndexFile = string // plain file name, no path separators (e.g. "index.json")
Constraints enforced at parse time:
- A
packdirective appearing before anyprojectdeclaration is a parse error. - A second
packdirective within the sameprojectblock is a parse error; the error message identifies the line numbers of both occurrences. -
..path components inDirectoryare not permitted. - Path separators (
/or\) inIndexFileare not permitted;IndexFilemust be a plain file name.
The updated gox.mod for spx would read:
xgo 1.6.0
project main.spx Game github.com/goplus/spx/v2 math
class -embed *.spx SpriteImpl
pack assets index.json
A typical spx project layout:
mygame/
├── go.mod # requires github.com/goplus/spx/v2 //xgo:class
├── main.spx # project file → compiled as Game
├── Cat.spx # work file → compiled as SpriteImpl
├── Dog.spx # work file → compiled as SpriteImpl
└── assets/
├── index.json # Stage/Game config ← pack root
├── index_pack.json # generated by xgo pack (once present, skipped on next run)
├── sprites/
│ ├── Cat/
│ │ └── index.json
│ └── Dog/
│ └── index.json
└── sounds/
└── bgm/
└── index.json
First xgo run — index_pack.json does not yet exist:
- Reads
go.mod, findsspx/v2annotated//xgo:class. - Loads
spx/v2'sgox.mod, matches the project block for*.spxfiles, findspack assets index.json. - Resolves
assetsrelative to the project root (mygame/);index_pack.jsonis absent → proceeds. - Runs
xgo pack mygame/assetswith index fileindex.json, producingassets/index_pack.json. - Compiles and runs the project.
Subsequent xgo run — index_pack.json already exists:
- Step 3:
index_pack.jsonis present → skipsxgo pack. - Proceeds directly to compilation.
After adding a new sprite asset:
xgo run --pack # forces re-pack to include the new sprite configThe pack directive integrates naturally with the existing -t (test) mode of xgo pack. CI pipelines that want to verify rather than regenerate packed files can still call xgo pack -t ./assets independently. The pack directive in gox.mod does not interfere with this workflow; it only affects the behavior of xgo run and related build commands.
An earlier design treated pack as a file-level directive (a sibling of project rather than a child of it). This was rejected because the asset directory and index format are properties of a specific classfile project type, not of the framework module as a whole. A file-level directive would also be ambiguous in gox.mod files with multiple project declarations.
An earlier revision of this proposal used pack <directory> without an explicit index file name, leaving xgo pack to probe for index.json, index.yml, or index.yaml. This was rejected because:
- A framework author always knows the exact format their framework uses; probing is unnecessary.
- Probing adds I/O overhead, particularly in deeply nested asset trees or on network-mounted filesystems.
- An explicit
indexfileargument makes the contract between the framework and the toolchain precise and self-documenting.
An earlier revision performed a modification-time comparison before deciding whether to skip xgo pack. This was replaced by the simpler existence-only check because:
- Traversing the entire asset tree to collect modification times imposes I/O cost on every
xgo run, even when assets are unchanged. - Most developers have a clear mental model of when assets change and can use
--packexplicitly when needed. - The existence check covers the most common case (first run after a fresh checkout) with zero extra overhead for all subsequent runs.
The toolchain could automatically scan for assets/ (or any directory containing index files) and pack them without any explicit directive. This was rejected because it is non-deterministic, imposes implicit cost on all projects, and couples the toolchain to naming conventions that frameworks should own.
The pack directive is purely additive. Existing gox.mod files without the directive are unaffected. The --pack flag is also new and has no effect on projects whose gox.mod contains no pack directive.
| Area | Change |
|---|---|
gox.mod grammar |
New project-scoped pack <directory> <indexfile> directive; at most one per project block |
gox.mod parser |
Parse and validate pack directives; error on duplicate within same project; reject .. in directory and path separators in indexfile |
xgo run / xgo build / xgo install / xgo test
|
Invoke xgo pack when index_pack.* is absent; skip when it already exists |
--pack flag |
New flag for xgo run, xgo build, xgo install, xgo test; forces xgo pack regardless of whether index_pack.* exists |
| Error handling | Fatal error on pack failure; warning on missing directory |
| Documentation | Update gox.mod reference; update xgo run command reference |