diff options
author | Ayke van Laethem <[email protected]> | 2018-11-19 21:08:12 +0100 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2018-11-19 21:08:12 +0100 |
commit | 760bc5d0a42204e6a49ebca0cb686530a671e240 (patch) | |
tree | 3a9daef7c9ce5bcaca383ea561fa715ce9aba01b /target.go | |
parent | f02766265ced8b90444ce0382a6d1cc5196d7c30 (diff) | |
download | tinygo-760bc5d0a42204e6a49ebca0cb686530a671e240.tar.gz tinygo-760bc5d0a42204e6a49ebca0cb686530a671e240.zip |
targets: let specific targets inherit more general targets
This avoids a ton of duplication and makes it easier to change a generic
target (for example, the "cortex-m" target) for all boards that use it.
Also, by making it possible to inherit properties from a parent target
specification, it is easier to support out-of-tree boards that don't
have to be updated so often. A target specification for a
special-purpose board can simply inherit the specification of a
supported chip and override the properites it needs to override (like
the programming interface).
Diffstat (limited to 'target.go')
-rw-r--r-- | target.go | 129 |
1 files changed, 109 insertions, 20 deletions
@@ -2,6 +2,7 @@ package main import ( "encoding/json" + "io" "os" "os/user" "path/filepath" @@ -17,6 +18,7 @@ import ( // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html // https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json type TargetSpec struct { + Inherits []string `json:"inherits"` Triple string `json:"llvm-target"` BuildTags []string `json:"build-tags"` Linker string `json:"linker"` @@ -30,40 +32,127 @@ type TargetSpec struct { GDBCmds []string `json:"gdb-initial-cmds"` } -// Load a target specification +// copyProperties copies all properties that are set in spec2 into itself. +func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) { + // TODO: simplify this using reflection? Inherits and BuildTags are special + // cases, but the rest can simply be copied if set. + spec.Inherits = append(spec.Inherits, spec2.Inherits...) + if spec2.Triple != "" { + spec.Triple = spec2.Triple + } + spec.BuildTags = append(spec.BuildTags, spec2.BuildTags...) + if spec2.Linker != "" { + spec.Linker = spec2.Linker + } + if spec2.RTLib != "" { + spec.RTLib = spec2.RTLib + } + if len(spec2.PreLinkArgs) != 0 { + spec.PreLinkArgs = spec2.PreLinkArgs + } + if spec2.Objcopy != "" { + spec.Objcopy = spec2.Objcopy + } + if len(spec2.Emulator) != 0 { + spec.Emulator = spec2.Emulator + } + if spec2.Flasher != "" { + spec.Flasher = spec2.Flasher + } + if len(spec2.OCDDaemon) != 0 { + spec.OCDDaemon = spec2.OCDDaemon + } + if spec2.GDB != "" { + spec.GDB = spec2.GDB + } + if len(spec2.GDBCmds) != 0 { + spec.GDBCmds = spec2.GDBCmds + } +} + +// load reads a target specification from the JSON in the given io.Reader. It +// may load more targets specified using the "inherits" property. +func (spec *TargetSpec) load(r io.Reader) error { + err := json.NewDecoder(r).Decode(spec) + if err != nil { + return err + } + + return nil +} + +// loadFromName loads the given target from the targets/ directory inside the +// compiler sources. +func (spec *TargetSpec) loadFromName(name string) error { + path := filepath.Join(sourceDir(), "targets", strings.ToLower(name)+".json") + fp, err := os.Open(path) + if err != nil { + return err + } + defer fp.Close() + return spec.load(fp) +} + +// resolveInherits loads inherited targets, recursively. +func (spec *TargetSpec) resolveInherits() error { + // First create a new spec with all the inherited properties. + newSpec := &TargetSpec{} + for _, name := range spec.Inherits { + subtarget := &TargetSpec{} + err := subtarget.loadFromName(name) + if err != nil { + return err + } + err = subtarget.resolveInherits() + if err != nil { + return err + } + newSpec.copyProperties(subtarget) + } + + // When all properties are loaded, make sure they are properly inherited. + newSpec.copyProperties(spec) + *spec = *newSpec + + return nil +} + +// Load a target specification. func LoadTarget(target string) (*TargetSpec, error) { if target == "" { target = llvm.DefaultTargetTriple() } - spec := &TargetSpec{ - Triple: target, - BuildTags: []string{runtime.GOOS, runtime.GOARCH}, - Linker: "cc", - PreLinkArgs: []string{"-no-pie"}, // WARNING: clang < 5.0 requires -nopie - Objcopy: "objcopy", - GDB: "gdb", - GDBCmds: []string{"run"}, - } - // See whether there is a target specification for this target (e.g. // Arduino). - path := filepath.Join(sourceDir(), "targets", strings.ToLower(target)+".json") - if fp, err := os.Open(path); err == nil { - defer fp.Close() - *spec = TargetSpec{} // reset all fields - err := json.NewDecoder(fp).Decode(spec) + spec := &TargetSpec{} + err := spec.loadFromName(target) + if err == nil { + // Successfully loaded this target from a built-in .json file. Make sure + // it includes all parents as specified in the "inherits" key. + err = spec.resolveInherits() if err != nil { return nil, err } + return spec, nil } else if !os.IsNotExist(err) { - // Expected a 'file not found' error, got something else. + // Expected a 'file not found' error, got something else. Report it as + // an error. return nil, err } else { - // No target spec available. Use the default one. + // No target spec available. Use the default one, useful on most systems + // with a regular OS. + *spec = TargetSpec{ + Triple: target, + BuildTags: []string{runtime.GOOS, runtime.GOARCH}, + Linker: "cc", + PreLinkArgs: []string{"-no-pie"}, // WARNING: clang < 5.0 requires -nopie + Objcopy: "objcopy", + GDB: "gdb", + GDBCmds: []string{"run"}, + } + return spec, nil } - - return spec, nil } // Return the source directory of this package, or "." when it cannot be |