baremetal,wasm: support command line params and environment variables

This is mainly useful to be able to run `tinygo test`, for example:

    tinygo test -target=cortex-m-qemu -v math

This is not currently supported, but will be in the future.
Этот коммит содержится в:
Ayke van Laethem 2021-08-10 21:35:52 +02:00 коммит произвёл Ron Evans
родитель c25a7cc747
коммит f57e9622fd
6 изменённых файлов: 126 добавлений и 37 удалений

Просмотреть файл

@ -208,7 +208,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
// with the TinyGo version. This is the case on some targets.
func needsSyscallPackage(buildTags []string) bool {
for _, tag := range buildTags {
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" {
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "tinygo.wasm" {
return true
}
}

Просмотреть файл

@ -162,15 +162,15 @@ func runPlatTests(target string, tests []string, t *testing.T) {
runTest(name, target, t, nil, nil)
})
}
t.Run("env.go", func(t *testing.T) {
t.Parallel()
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
})
if target == "" || target == "wasi" {
t.Run("filesystem.go", func(t *testing.T) {
t.Parallel()
runTest("filesystem.go", target, t, nil, nil)
})
t.Run("env.go", func(t *testing.T) {
t.Parallel()
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
})
}
if target == "" || target == "wasi" || target == "wasm" {
t.Run("rand.go", func(t *testing.T) {
@ -233,6 +233,42 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
}
}()
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
// we need to pass command line arguments and environment variables through
// global variables (built into the binary directly) instead of the
// conventional way.
spec, err := compileopts.LoadTarget(target)
if err != nil {
t.Fatal("failed to load target spec:", err)
}
needsEnvInVars := spec.GOOS == "js"
for _, tag := range spec.BuildTags {
if tag == "baremetal" {
needsEnvInVars = true
}
}
if needsEnvInVars {
runtimeGlobals := make(map[string]string)
if len(cmdArgs) != 0 {
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
}
if len(environmentVars) != 0 {
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
}
if len(runtimeGlobals) != 0 {
// This sets the global variables like they would be set with
// `-ldflags="-X=runtime.osArgs=first\x00second`.
// The runtime package has two variables (osArgs and osEnv) that are
// both strings, from which the parameters and environment variables
// are read.
options.GlobalValues = map[string]map[string]string{
"runtime": runtimeGlobals,
}
}
}
// Build the test binary.
binary := filepath.Join(tmpdir, "test")
err = runBuild("./"+path, binary, &options)
@ -242,37 +278,31 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
return
}
// Run the test.
runComplete := make(chan struct{})
// Create the test command, taking care of emulators etc.
var cmd *exec.Cmd
ranTooLong := false
if target == "" {
if len(spec.Emulator) == 0 {
cmd = exec.Command(binary)
cmd.Env = append(cmd.Env, environmentVars...)
} else {
args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...)
}
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
// Allow reading from the current directory.
cmd.Args = append(cmd.Args, "--dir=.")
for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v)
}
cmd.Args = append(cmd.Args, cmdArgs...)
} else {
spec, err := compileopts.LoadTarget(target)
if err != nil {
t.Fatal("failed to load target spec:", err)
}
if len(spec.Emulator) == 0 {
cmd = exec.Command(binary)
} else {
args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...)
}
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
// Allow reading from the current directory.
cmd.Args = append(cmd.Args, "--dir=.")
for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v)
}
cmd.Args = append(cmd.Args, cmdArgs...)
} else {
if !needsEnvInVars {
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
cmd.Env = append(cmd.Env, environmentVars...)
}
}
// Run the test.
runComplete := make(chan struct{})
ranTooLong := false
stdout := &bytes.Buffer{}
cmd.Stdout = stdout
cmd.Stderr = os.Stderr

48
src/runtime/nonhosted.go Обычный файл
Просмотреть файл

@ -0,0 +1,48 @@
// +build baremetal js
package runtime
// This file is for non-hosted environments, that don't support command line
// parameters or environment variables. To still be able to run certain tests,
// command line parameters and environment variables can be passed to the binary
// by setting the variables `runtime.osArgs` and `runtime.osEnv`, both of which
// are strings separated by newlines.
//
// The primary use case is `tinygo test`, which takes some parameters (such as
// -test.v).
var env []string
//go:linkname syscall_runtime_envs syscall.runtime_envs
func syscall_runtime_envs() []string {
return env
}
var osArgs string
var osEnv string
func init() {
if osArgs != "" {
s := osArgs
start := 0
for i := 0; i < len(s); i++ {
if s[i] == 0 {
args = append(args, s[start:i])
start = i + 1
}
}
args = append(args, s[start:])
}
if osEnv != "" {
s := osEnv
start := 0
for i := 0; i < len(s); i++ {
if s[i] == 0 {
env = append(env, s[start:i])
start = i + 1
}
}
env = append(env, s[start:])
}
}

Просмотреть файл

@ -15,11 +15,6 @@ func _start() {
run()
}
//go:linkname syscall_runtime_envs syscall.runtime_envs
func syscall_runtime_envs() []string {
return nil
}
var handleEvent func()
//go:linkname setEventHandler syscall/js.setEventHandler

Просмотреть файл

@ -1,4 +1,4 @@
// +build baremetal
// +build baremetal js
package syscall
@ -47,8 +47,24 @@ const (
O_CLOEXEC = 0
)
func runtime_envs() []string
func Getenv(key string) (value string, found bool) {
return "", false // stub
env := runtime_envs()
for _, keyval := range env {
// Split at '=' character.
var k, v string
for i := 0; i < len(keyval); i++ {
if keyval[i] == '=' {
k = keyval[:i]
v = keyval[i+1:]
}
}
if k == key {
return v, true
}
}
return "", false
}
func Open(path string, mode int, perm uint32) (fd int, err error) {

Просмотреть файл

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build baremetal nintendoswitch
// +build baremetal nintendoswitch js
package syscall