| // Copyright 2021 The Dawn & Tint Authors | 
 | // | 
 | // Redistribution and use in source and binary forms, with or without | 
 | // modification, are permitted provided that the following conditions are met: | 
 | // | 
 | // 1. Redistributions of source code must retain the above copyright notice, this | 
 | //    list of conditions and the following disclaimer. | 
 | // | 
 | // 2. Redistributions in binary form must reproduce the above copyright notice, | 
 | //    this list of conditions and the following disclaimer in the documentation | 
 | //    and/or other materials provided with the distribution. | 
 | // | 
 | // 3. Neither the name of the copyright holder nor the names of its | 
 | //    contributors may be used to endorse or promote products derived from | 
 | //    this software without specific prior written permission. | 
 | // | 
 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | // run-parallel is a tool to run an executable with the provided templated | 
 | // arguments across all the hardware threads. | 
 | package main | 
 |  | 
 | import ( | 
 | 	"flag" | 
 | 	"fmt" | 
 | 	"os" | 
 | 	"os/exec" | 
 | 	"runtime" | 
 | 	"strings" | 
 | 	"sync" | 
 | ) | 
 |  | 
 | func main() { | 
 | 	if err := run(); err != nil { | 
 | 		fmt.Println(err) | 
 | 		os.Exit(1) | 
 | 	} | 
 | } | 
 |  | 
 | func showUsage() { | 
 | 	fmt.Println(` | 
 | run-parallel is a tool to run an executable with the provided templated | 
 | arguments across all the hardware threads. | 
 |  | 
 | Usage: | 
 |   run-parallel <executable> [arguments...] -- [per-instance-value...] | 
 |  | 
 |   executable         - the path to the executable to run. | 
 |   arguments          - a list of arguments to pass to the executable. | 
 |                        Any occurrance of $ will be substituted with the | 
 |                        per-instance-value for the given invocation. | 
 |   per-instance-value - a list of values. The executable will be invoked for each | 
 |                        value in this list.`) | 
 | 	os.Exit(1) | 
 | } | 
 |  | 
 | func run() error { | 
 | 	onlyPrintFailures := flag.Bool("only-print-failures", false, "Omit output for processes that did not fail") | 
 | 	flag.Parse() | 
 |  | 
 | 	args := flag.Args() | 
 | 	if len(args) < 2 { | 
 | 		showUsage() | 
 | 	} | 
 | 	exe := args[0] | 
 | 	args = args[1:] | 
 |  | 
 | 	var perInstanceValues []string | 
 | 	for i, arg := range args { | 
 | 		if arg == "--" { | 
 | 			perInstanceValues = args[i+1:] | 
 | 			args = args[:i] | 
 | 			break | 
 | 		} | 
 | 	} | 
 | 	if perInstanceValues == nil { | 
 | 		showUsage() | 
 | 	} | 
 |  | 
 | 	taskIndices := make(chan int, 64) | 
 | 	type result struct { | 
 | 		cmd    string | 
 | 		msg    string | 
 | 		failed bool | 
 | 	} | 
 | 	results := make([]result, len(perInstanceValues)) | 
 |  | 
 | 	numCPU := runtime.NumCPU() | 
 | 	wg := sync.WaitGroup{} | 
 | 	wg.Add(numCPU) | 
 | 	for i := 0; i < numCPU; i++ { | 
 | 		go func() { | 
 | 			defer wg.Done() | 
 | 			for idx := range taskIndices { | 
 | 				taskArgs := make([]string, len(args)) | 
 | 				for i, arg := range args { | 
 | 					taskArgs[i] = strings.ReplaceAll(arg, "$", perInstanceValues[idx]) | 
 | 				} | 
 | 				success, out := invoke(exe, taskArgs) | 
 | 				if !success || !*onlyPrintFailures { | 
 | 					results[idx] = result{fmt.Sprint(append([]string{exe}, taskArgs...)), out, !success} | 
 | 				} | 
 | 			} | 
 | 		}() | 
 | 	} | 
 |  | 
 | 	for i := range perInstanceValues { | 
 | 		taskIndices <- i | 
 | 	} | 
 | 	close(taskIndices) | 
 |  | 
 | 	wg.Wait() | 
 |  | 
 | 	failed := false | 
 | 	for _, result := range results { | 
 | 		if result.msg != "" { | 
 | 			fmt.Printf("'%v' returned %v\n", result.cmd, result.msg) | 
 | 		} | 
 | 		failed = failed || result.failed | 
 | 	} | 
 | 	if failed { | 
 | 		os.Exit(1) | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func invoke(exe string, args []string) (ok bool, output string) { | 
 | 	cmd := exec.Command(exe, args...) | 
 | 	out, err := cmd.CombinedOutput() | 
 | 	str := string(out) | 
 | 	if err != nil { | 
 | 		if str != "" { | 
 | 			return false, str | 
 | 		} | 
 | 		return false, err.Error() | 
 | 	} | 
 | 	return true, str | 
 | } |