blob: 17ea3d086489f053d487eab2f96568d2df003ac5 [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package buildbinary
import (
"fmt"
"os/exec"
"path/filepath"
"strings"
"time"
"golang.org/x/pkgsite-metrics/internal/derrors"
)
type BinaryInfo struct {
BinaryPath string
ImportPath string
BuildTime time.Duration
Error error
}
// FindAndBuildBinaries finds and builds all possible binaries from a given module.
func FindAndBuildBinaries(modulePath string) (binaries []*BinaryInfo, err error) {
defer derrors.Wrap(&err, "FindAndBuildBinaries")
buildTargets, err := findBinaries(modulePath)
if err != nil {
return nil, err
}
for i, target := range buildTargets {
path, buildTime, err := runBuild(modulePath, target, i)
b := &BinaryInfo{
BinaryPath: path,
ImportPath: target,
BuildTime: buildTime,
}
if err != nil {
b.Error = err
}
binaries = append(binaries, b)
}
return binaries, nil
}
// runBuild takes a given module and import path and attempts to build a binary
func runBuild(modulePath, importPath string, i int) (binaryPath string, buildTime time.Duration, err error) {
binName := fmt.Sprintf("bin%d", i)
cmd := exec.Command("go", "build", "-C", modulePath, "-o", binName, importPath)
start := time.Now()
if err = cmd.Run(); err != nil {
return "", 0, err
}
buildTime = time.Since(start)
binaryPath = filepath.Join(modulePath, binName)
return binaryPath, buildTime, nil
}
// findBinaries finds all packages that compile to binaries in a given directory
// and returns a list of those package's import paths.
func findBinaries(dir string) (buildTargets []string, err error) {
// Running go list with the given arguments only prints the import paths of
// packages with package "main", that is packages that could potentially
// be built into binaries.
cmd := exec.Command("go", "list", "-f", `{{ if eq .Name "main" }} {{ .ImportPath }} {{end}}`, "./...")
cmd.Dir = dir
out, err := cmd.Output()
if err != nil {
return nil, err
}
return strings.Fields(string(out)), nil
}