feat: implement target configuration system for issue #1176
Add comprehensive target configuration parsing and inheritance system: - Create internal/targets package with config structures - Support JSON configuration loading with inheritance resolution - Implement multi-level inheritance (e.g., rp2040 → cortex-m0plus → cortex-m) - Add 206 target configurations from TinyGo for embedded platforms - Support core fields: name, llvm-target, cpu, features, build-tags, goos, goarch, cflags, ldflags - Provide high-level resolver interface for target lookup - Include comprehensive unit tests with 100% target parsing coverage This foundation enables future -target parameter support for cross-compilation to diverse embedded platforms beyond current GOOS/GOARCH limitations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
176
internal/targets/loader.go
Normal file
176
internal/targets/loader.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package targets
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Loader handles loading and parsing target configurations
|
||||
type Loader struct {
|
||||
targetsDir string
|
||||
cache map[string]*RawConfig
|
||||
}
|
||||
|
||||
// NewLoader creates a new target configuration loader
|
||||
func NewLoader(targetsDir string) *Loader {
|
||||
return &Loader{
|
||||
targetsDir: targetsDir,
|
||||
cache: make(map[string]*RawConfig),
|
||||
}
|
||||
}
|
||||
|
||||
// LoadRaw loads a raw configuration without resolving inheritance
|
||||
func (l *Loader) LoadRaw(name string) (*RawConfig, error) {
|
||||
// Check cache first
|
||||
if config, exists := l.cache[name]; exists {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Construct file path
|
||||
configPath := filepath.Join(l.targetsDir, name+".json")
|
||||
|
||||
// Read file
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read target config %s: %w", name, err)
|
||||
}
|
||||
|
||||
// Parse JSON
|
||||
var config RawConfig
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse target config %s: %w", name, err)
|
||||
}
|
||||
|
||||
// Set the name
|
||||
config.Name = name
|
||||
|
||||
// Cache the result
|
||||
l.cache[name] = &config
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// Load loads a target configuration with inheritance resolved
|
||||
func (l *Loader) Load(name string) (*Config, error) {
|
||||
raw, err := l.LoadRaw(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l.resolveInheritance(raw)
|
||||
}
|
||||
|
||||
// LoadAll loads all target configurations in the targets directory
|
||||
func (l *Loader) LoadAll() (map[string]*Config, error) {
|
||||
entries, err := os.ReadDir(l.targetsDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read targets directory: %w", err)
|
||||
}
|
||||
|
||||
configs := make(map[string]*Config)
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
|
||||
name := strings.TrimSuffix(entry.Name(), ".json")
|
||||
config, err := l.Load(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load target %s: %w", name, err)
|
||||
}
|
||||
|
||||
configs[name] = config
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// resolveInheritance resolves inheritance chain for a configuration
|
||||
func (l *Loader) resolveInheritance(raw *RawConfig) (*Config, error) {
|
||||
if !raw.HasInheritance() {
|
||||
// No inheritance, return as-is
|
||||
return &raw.Config, nil
|
||||
}
|
||||
|
||||
// Start with base config
|
||||
result := &Config{Name: raw.Name}
|
||||
|
||||
// Apply inheritance in order
|
||||
for _, parentName := range raw.GetInherits() {
|
||||
parent, err := l.Load(parentName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load parent config %s: %w", parentName, err)
|
||||
}
|
||||
|
||||
// Merge parent into result
|
||||
l.mergeConfig(result, parent)
|
||||
}
|
||||
|
||||
// Finally, apply current config on top
|
||||
l.mergeConfig(result, &raw.Config)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// mergeConfig merges source config into destination config
|
||||
// Non-empty values in source override those in destination
|
||||
func (l *Loader) mergeConfig(dst, src *Config) {
|
||||
if src.LLVMTarget != "" {
|
||||
dst.LLVMTarget = src.LLVMTarget
|
||||
}
|
||||
if src.CPU != "" {
|
||||
dst.CPU = src.CPU
|
||||
}
|
||||
if src.Features != "" {
|
||||
dst.Features = src.Features
|
||||
}
|
||||
if src.GOOS != "" {
|
||||
dst.GOOS = src.GOOS
|
||||
}
|
||||
if src.GOARCH != "" {
|
||||
dst.GOARCH = src.GOARCH
|
||||
}
|
||||
if src.Linker != "" {
|
||||
dst.Linker = src.Linker
|
||||
}
|
||||
|
||||
// Merge slices (append, don't replace)
|
||||
if len(src.BuildTags) > 0 {
|
||||
dst.BuildTags = append(dst.BuildTags, src.BuildTags...)
|
||||
}
|
||||
if len(src.CFlags) > 0 {
|
||||
dst.CFlags = append(dst.CFlags, src.CFlags...)
|
||||
}
|
||||
if len(src.LDFlags) > 0 {
|
||||
dst.LDFlags = append(dst.LDFlags, src.LDFlags...)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTargetsDir returns the targets directory path
|
||||
func (l *Loader) GetTargetsDir() string {
|
||||
return l.targetsDir
|
||||
}
|
||||
|
||||
// ListTargets returns a list of all available target names
|
||||
func (l *Loader) ListTargets() ([]string, error) {
|
||||
entries, err := os.ReadDir(l.targetsDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read targets directory: %w", err)
|
||||
}
|
||||
|
||||
var targets []string
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
|
||||
name := strings.TrimSuffix(entry.Name(), ".json")
|
||||
targets = append(targets, name)
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
}
|
||||
Reference in New Issue
Block a user