blob: e87e2f9569b567bed01401a6c6d2f3ddd0edab37 [file] [log] [blame]
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This program is used to create a tarball containing a precompiled Android test. This tarball
// can be passed to the adb_test rule to run the test on an Android device via adb.
//
// Design notes: Currently, building Go with Bazel on Mac can be slow. The problem is compounded
// by the fact that we roll the Skia Infra repo into Skia via a go.mod update, which busts Bazel's
// repository cache and causes Bazel to re-download a larg number of Go modules. To mitigate
// slowness on Mac, this program does not use any external dependencies. This by itself does not
// necessarily make the build faster on Macs, but it unblocks the following potential optimization.
// We could build this binary using a separate, minimalistic go.mod file that does not include the
// Skia Infra repository. If the rules_go[1] rules don't allow multiple go.mod files, we could work
// work around that limitation by shelling out to the Bazel-downloaded "go" binary from a genrule
// (something like "go build -o foo foo.go").
//
// [1] https://github.com/bazelbuild/rules_go
package main
import (
"archive/tar"
"bytes"
"compress/gzip"
"flag"
"fmt"
"io"
"os"
"strings"
)
func main() {
execpathsFlag := flag.String("execpaths", "", "Space-separated list of the execpaths of files to be included in the tarball.")
rootpathsFlag := flag.String("rootpaths", "", "Space-separated list of the rootpaths of files to be included in the tarball.")
outputFileFlag := flag.String("output-file", "", "Filename of the tarball to create.")
flag.Parse()
if *execpathsFlag == "" {
die("Flag --execpaths is required.\n")
}
if *rootpathsFlag == "" {
die("Flag --rootpaths is required.\n")
}
if *outputFileFlag == "" {
die("Flag --output-file is required.\n")
}
execpaths := flagToStrings(*execpathsFlag)
rootpaths := flagToStrings(*rootpathsFlag)
if len(execpaths) != len(rootpaths) {
die("Flags --execpaths and --rootpaths were passed lists of different lenghts: %d and %d.\n", len(execpaths), len(rootpaths))
}
outputFile, err := os.Create(*outputFileFlag)
if err != nil {
die("Could not create file %q: %s", *outputFileFlag, err)
}
defer outputFile.Close()
gzipWriter := gzip.NewWriter(outputFile)
defer gzipWriter.Close()
tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()
for i := range execpaths {
// execpaths point to physical files generated by Bazel (e.g.
// bazel-out/k8-linux_x64-dbg/bin/tests/some_test), whereas rootpaths are the paths that a
// binary running via "bazel run" or "bazel test" expects (e.g tests/some_test). Thus, we must
// map the former to the latter.
//
// Reference:
// https://bazel.build/reference/be/make-variables#predefined_label_variables
if err := addFileToTarball(tarWriter, execpaths[i], rootpaths[i]); err != nil {
die("Adding file %q to tarball: %s", execpaths[i], err)
}
}
}
func flagToStrings(flag string) []string {
var values []string
for _, value := range strings.Split(flag, " ") {
values = append(values, strings.TrimSpace(value))
}
return values
}
func addFileToTarball(w *tar.Writer, readFromPath, saveAsPath string) error {
contents, err := os.ReadFile(readFromPath)
if err != nil {
return err
}
stat, err := os.Stat(readFromPath)
if err != nil {
return err
}
header := &tar.Header{
Name: saveAsPath,
Size: stat.Size(),
Mode: int64(stat.Mode()),
}
if err := w.WriteHeader(header); err != nil {
return err
}
_, err = io.Copy(w, bytes.NewBuffer(contents))
return err
}
func die(msg string, a ...interface{}) {
fmt.Fprintf(os.Stderr, msg, a...)
os.Exit(1)
}