diff --git a/Makefile b/Makefile index 23986b1c2..0fb5e5409 100644 --- a/Makefile +++ b/Makefile @@ -202,8 +202,11 @@ tinygo-test: $(TINYGO) test text/scanner $(TINYGO) test unicode/utf8 -.PHONY: smoketest +.PHONY: smoketest smoketest-commands smoketest: + @go run ./src/cmd/run-smoketest make smoketest-commands + +smoketest-commands: $(TINYGO) version # test all examples (except pwm) $(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1 diff --git a/go.mod b/go.mod index 4820d7747..ea23b8f2d 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,9 @@ require ( github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 github.com/mattn/go-colorable v0.1.8 + github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f // indirect go.bug.st/serial v1.1.2 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c diff --git a/go.sum b/go.sum index 1451afebf..9c5bb1cd8 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f h1:bHOcIl3AODQMzleLeSGs3xbIvAzhcgLTUwd8IUKqLMI= +github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f/go.mod h1:55Pg0jnassdnFxlhS8HIU5pdYhEBYftGaknIP4bBUq8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k= @@ -41,6 +43,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= diff --git a/src/cmd/run-smoketest/main.go b/src/cmd/run-smoketest/main.go new file mode 100644 index 000000000..76dbef123 --- /dev/null +++ b/src/cmd/run-smoketest/main.go @@ -0,0 +1,210 @@ +package main + +import ( + "bufio" + "bytes" + "crypto/md5" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/sago35/ochan" + "golang.org/x/sync/errgroup" +) + +func main() { + threads := flag.Int("threads", runtime.NumCPU(), "threads of make smoketest") + flag.Parse() + + if len(os.Args) < 2 { + fmt.Printf("usage: %s make smoketest\n", filepath.Base(os.Args[0])) + os.Exit(1) + } + + commands, err := getMakeCommands(flag.Args()) + if err != nil { + log.Fatalf(err.Error()) + } + + err = run(commands, *threads) + if err != nil { + log.Fatalf(err.Error()) + } +} + +func getMakeCommands(args []string) ([]string, error) { + var buf bytes.Buffer + cmd := exec.Command(args[0], args[1:]...) + cmd.Args = append(cmd.Args, "-n") + cmd.Stdout = &buf + cmd.Stderr = &buf + err := cmd.Run() + if err != nil { + return nil, err + } + + commands := []string{} + scanner := bufio.NewScanner(&buf) + + for scanner.Scan() { + line := scanner.Text() + + fields := strings.Fields(line) + if filepath.Base(fields[0]) == "tinygo" && (fields[1] == "build" || fields[1] == "version") { + commands = append(commands, line) + } else if strings.HasPrefix(line, "#") { + commands = append(commands, line) + } + } + + return commands, nil +} + +func run(commands []string, threads int) error { + ch := make(chan string, 1000) + o := ochan.NewOchan(ch, 100) + + go func() { + limit := make(chan struct{}, threads) + var eg errgroup.Group + + for _, command := range commands { + command := command + select { + case limit <- struct{}{}: + och := o.GetCh() + eg.Go(func() error { + var err error + + if runtime.GOOS == `windows` { + command = convertToWindowsPath(command) + } + + fields := strings.Fields(command) + if filepath.Base(fields[0]) == "tinygo" && fields[1] == "build" { + err = buildAndmd5sum(command, och) + } else if strings.HasPrefix(command, "#") { + och <- fmt.Sprintf("%s\n", command) + } else { + err = runCommand(command, och) + } + close(och) + <-limit + return err + }) + } + } + + if err := eg.Wait(); err != nil { + log.Fatal(err) + } + + o.Wait() + close(ch) + }() + + for s := range ch { + fmt.Print(s) + } + + return nil +} + +func runCommand(command string, ch chan string) error { + var buf bytes.Buffer + + fmt.Fprintf(&buf, "%s\n", command) + + args := strings.Fields(command) + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = &buf + cmd.Stderr = &buf + + err := cmd.Run() + if err != nil { + ch <- buf.String() + return err + } + + ch <- buf.String() + return nil +} + +func buildAndmd5sum(command string, ch chan string) error { + var buf bytes.Buffer + + fmt.Fprintf(&buf, "%s\n", command) + + args := strings.Fields(command) + + output := "" + tmpOutput := "" + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + return err + } + for i := range args { + if args[i] == "-o" { + output = args[i+1] + ext := filepath.Ext(output) + tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext)) + args[i+1] = tmpOutput + } else if strings.HasPrefix(args[i], "-o=") { + output = args[i][3:] + ext := filepath.Ext(output) + tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext)) + args[i] = fmt.Sprintf("-o=%s", tmpOutput) + } + } + + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = &buf + cmd.Stderr = &buf + + err = cmd.Run() + if err != nil { + ch <- buf.String() + return err + } + + md5str, err := calcMD5(tmpOutput) + if err != nil { + ch <- buf.String() + return err + } + fmt.Fprintf(&buf, "%s %s\n", md5str, output) + + ch <- buf.String() + return nil +} + +func calcMD5(f string) (string, error) { + r, err := os.Open(f) + if err != nil { + return "", err + } + defer r.Close() + + h := md5.New() + if _, err := io.Copy(h, r); err != nil { + return "", err + } + + return fmt.Sprintf("%x", h.Sum(nil)), nil +} + +func convertToWindowsPath(command string) string { + if !strings.HasPrefix(command, `/`) { + return command + } + + command = fmt.Sprintf("%c:%s", command[1], command[2:]) + return command +}