mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-11 06:23:39 +00:00
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).
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user