- No useless abstraction - don't abstract if not necessary
- Abstracted modules must have wide usage - only extract when broadly used
- All modules must be clean - each module should only handle its own responsibility
- All environment reads and writes under
x/, including tests and test helpers, must useinternal/execbroker(Getenv,LookupEnv,Setenv,Unsetenv,Clearenv,Environ, orExpandEnv). Do not call theosorsyscallenvironment APIs directly fromx/.
- Follow the existing Cobra command style.
- Add command flags next to the command's existing flags in
init()usingcmd.Flags(). - For example,
makeflags belong besidemakeVerboseandmakeOutputincmd/llar/internal/make.go. - Do not hand-roll
os.Argsnormalization or custom command parsing when Cobra/pflag already supports the command shape. - Single-dash flags are shorthands in pflag. Add a shorthand with
BoolVarP,StringVarP, etc. when short syntax is needed; use--flagfor long flags. - Keep command flags command-local unless the behavior is genuinely global.
- Write Cobra
Short/Longtext and README command summaries in terms of stable, user-facing product responsibilities. Do not define a command by implementation details such as internal services, callback names, cache steps, or internal directories. - Keep the core command boundaries explicit. For example,
installobtains and installs builds from LLAR Cloud,makebuilds from source using LLAR formulas, andtestverifies installed artifacts from a consumer's perspective.
- Put formula fixtures under the existing
testdata/formulastree instead of generating formula files inline in tests. - Prefer the existing test helpers such as
setupLocalFormulas,withMockRemoteStore,isolatedWorkspaceDir, andprepopulateCache. - Tests for
llar makeshould exercise the real Cobra command path where possible, then use fixtures and prepopulated cache to avoid network and source builds. - When tests touch package-level command flag variables, reset or restore those variables so one test cannot affect another.
- Before working on
.goxfiles,gox.mod, class framework registration, generated class types or entrypoints, base-class contracts,ClassKind/LookupClass, orGopt/Gops/Gopx/Gopoconventions, read and follow.agents/skills/xgo-classfile/SKILL.md. - Use the skill for changes that affect how XGo classfiles are parsed, registered, generated, or exposed from Go packages. Ordinary Go-only changes that do not cross the classfile boundary do not require it.
LLAR is a multi-language module manager built with XGo (gop) and xgo. It uses classfile mechanism for defining build formulas.
Classfile is a DSL (Domain Specific Language) mechanism in xgo that allows defining custom file extensions with specific behavior. Each classfile extension maps to a Go struct that acts as a "class".
-
Registration: Classfiles are registered via
xgobuild.RegisterProject()ininternal/ixgo/classfile.go -
File Extension Mapping:
_llar.gox->ModuleFclass (formula/classfile.go)_cmp.gox->CmpAppclass (formula/classfile.go)
-
Code Generation: When a
.goxfile is processed:- The filename without the registered
_llar.goxsuffix becomes the struct name - Example:
hello_llar.goxgenerates structhelloembeddingModuleF - A
MainEntry()method is generated containing the DSL code - A
Main()method callsGopt_ModuleF_Main(this)
- The filename without the registered
Source file hello_llar.gox:
id "DaveGamble/cJSON"
fromVer "v1.0.0"
onRequire (proj, deps) => {
echo "hello"
}
onBuild (ctx, proj, out) => {
echo "hello"
}
Generated Go code:
package main
import (
"fmt"
"github.com/goplus/llar/formula"
)
type hello struct {
formula.ModuleF
}
func (this *hello) MainEntry() {
this.Id("DaveGamble/cJSON")
this.FromVer("v1.0.0")
this.OnRequire(func(proj *formula.Project, deps *formula.ModuleDeps) {
fmt.Println("hello")
})
this.OnBuild(func(ctx *formula.Context, proj *formula.Project, out *formula.BuildResult) {
fmt.Println("hello")
})
}
func (this *hello) Main() {
formula.Gopt_ModuleF_Main(this)
}
func main() {
new(hello).Main()
}-
Struct Name Derivation: The struct name comes from
strings.TrimSuffix(filename, "_llar.gox"), preserving underscores in names such ascpu_features -
Class Entry Point:
Gopt_<ClassName>_Mainis the classfile entry point that:- Calls
MainEntry()to execute DSL code - Initializes the embedded
gsh.App
- Calls
-
Error Handling: If a file doesn't match any registered classfile pattern (wrong suffix),
BuildFilereturns "undefined" errors for DSL functions
# Run tests with required ldflags (Go 1.24+)
go test -ldflags="-checklinkname=0" ./...
# Run specific package tests with coverage
go test -ldflags="-checklinkname=0" -cover ./internal/formula/...formula/- Classfile definitions (ModuleF, CmpApp, Context, Project, etc.)internal/formula/- Formula loading and interpretation logicinternal/ixgo/- ixgo classfile registrationmod/- Module and version handling