aboutsummaryrefslogtreecommitdiffhomepage
path: root/target.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2018-11-19 21:08:12 +0100
committerAyke van Laethem <[email protected]>2018-11-19 21:08:12 +0100
commit760bc5d0a42204e6a49ebca0cb686530a671e240 (patch)
tree3a9daef7c9ce5bcaca383ea561fa715ce9aba01b /target.go
parentf02766265ced8b90444ce0382a6d1cc5196d7c30 (diff)
downloadtinygo-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.go129
1 files changed, 109 insertions, 20 deletions
diff --git a/target.go b/target.go
index b67eddffe..8d4255953 100644
--- a/target.go
+++ b/target.go
@@ -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