c40158ab98
The toolchain now uses extract_ar from https://skia-review.googlesource.com/c/buildbot/+/524764 which is a static executable to extract the .deb files. This was necessary because the llvm-ar that had previously been used requires glibc 2.31+ to run, but our Debian10 machines on Swarming have an older version (2.28). A longer-term fix is to have Bazel support .ar files, which I plan to attempt to contribute this week. The RBE task will be added as an experimental CQ job, to see how it handles the load of running often. With the remote execution cache, I hope it performs well, once the toolchains are cached on both the Swarming machines and in the RBE workers. Note: We had to add several files to the CAS spec (see compile_cas.go) which are required for Bazel to work. Change-Id: Ie70c70d5f33768c957760f9eeb7835025109b487 Bug: skia:12541 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/524759 Reviewed-by: Joe Gregorio <jcgregorio@google.com>
240 lines
6.0 KiB
Go
240 lines
6.0 KiB
Go
// Copyright 2020 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.
|
|
|
|
package gen_tasks_logic
|
|
|
|
import (
|
|
"log"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"go.skia.org/infra/go/cas/rbe"
|
|
"go.skia.org/infra/task_scheduler/go/specs"
|
|
)
|
|
|
|
const (
|
|
// If a parent path contains more than this many immediate child paths (ie.
|
|
// files and dirs which are directly inside it as opposed to indirect
|
|
// descendants), we will include the parent in the isolate file instead of
|
|
// the children. This results in a simpler CasSpec which should need to be
|
|
// changed less often.
|
|
combinePathsThreshold = 3
|
|
)
|
|
|
|
var (
|
|
// Any files in Git which match these patterns will be included, either
|
|
// directly or indirectly via a parent dir.
|
|
pathRegexes = []*regexp.Regexp{
|
|
regexp.MustCompile(`.*\.c$`),
|
|
regexp.MustCompile(`.*\.cc$`),
|
|
regexp.MustCompile(`.*\.cpp$`),
|
|
regexp.MustCompile(`.*\.gn$`),
|
|
regexp.MustCompile(`.*\.gni$`),
|
|
regexp.MustCompile(`.*\.h$`),
|
|
regexp.MustCompile(`.*\.mm$`),
|
|
regexp.MustCompile(`.*\.storyboard$`),
|
|
}
|
|
|
|
// These paths are always added to the inclusion list. Note that they may
|
|
// not appear in the CasSpec if they are included indirectly via a parent
|
|
// dir.
|
|
explicitPaths = []string{
|
|
".bazelrc",
|
|
".bazelversion",
|
|
".clang-format",
|
|
".clang-tidy",
|
|
".vpython",
|
|
"BUILD.bazel",
|
|
"WORKSPACE.bazel",
|
|
"bazel",
|
|
"bin/activate-emsdk",
|
|
"bin/fetch-clang-format",
|
|
"bin/fetch-gn",
|
|
"buildtools",
|
|
"example",
|
|
"go_repositories.bzl",
|
|
"infra/bots/assets/android_ndk_darwin/VERSION",
|
|
"infra/bots/assets/android_ndk_linux/VERSION",
|
|
"infra/bots/assets/android_ndk_windows/VERSION",
|
|
"infra/bots/assets/cast_toolchain/VERSION",
|
|
"infra/bots/assets/clang_linux/VERSION",
|
|
"infra/bots/assets/clang_win/VERSION",
|
|
"infra/bots/run_recipe.py",
|
|
"infra/bots/task_drivers",
|
|
"infra/canvaskit",
|
|
"infra/pathkit",
|
|
"requirements.txt",
|
|
"resources",
|
|
"third_party/externals",
|
|
"toolchain",
|
|
}
|
|
)
|
|
|
|
// getAllCheckedInPaths returns every path checked in to the repo.
|
|
func getAllCheckedInPaths(cfg *Config) []string {
|
|
cmd := exec.Command("git", "ls-files")
|
|
// Use cfg.PathToSkia to get to the Skia checkout, in case this is used by
|
|
// another repo.
|
|
cmd.Dir = filepath.Join(CheckoutRoot(), cfg.PathToSkia)
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
split := strings.Split(string(output), "\n")
|
|
rv := make([]string, 0, len(split))
|
|
for _, line := range split {
|
|
if line != "" {
|
|
rv = append(rv, line)
|
|
}
|
|
}
|
|
return rv
|
|
}
|
|
|
|
// getRelevantPaths returns all paths needed by compile tasks.
|
|
func getRelevantPaths(cfg *Config) []string {
|
|
rv := []string{}
|
|
for _, path := range getAllCheckedInPaths(cfg) {
|
|
for _, regex := range pathRegexes {
|
|
if regex.MatchString(path) {
|
|
rv = append(rv, path)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return append(rv, explicitPaths...)
|
|
}
|
|
|
|
// node is a single node in a directory tree of task inputs.
|
|
type node struct {
|
|
children map[string]*node
|
|
name string
|
|
isLeaf bool
|
|
}
|
|
|
|
// newNode returns a node instance.
|
|
func newNode(name string) *node {
|
|
return &node{
|
|
children: map[string]*node{},
|
|
name: name,
|
|
isLeaf: false,
|
|
}
|
|
}
|
|
|
|
// isRoot returns true iff this is the root node.
|
|
func (n *node) isRoot() bool {
|
|
return n.name == ""
|
|
}
|
|
|
|
// add the given entry (given as a slice of path components) to the node.
|
|
func (n *node) add(entry []string) {
|
|
// Remove the first element if we're not the root node.
|
|
if !n.isRoot() {
|
|
if entry[0] != n.name {
|
|
log.Fatalf("Failed to compute compile CAS inputs; attempting to add entry %v to node %q", entry, n.name)
|
|
}
|
|
entry = entry[1:]
|
|
|
|
// If the entry is now empty, this node is a leaf.
|
|
if len(entry) == 0 {
|
|
n.isLeaf = true
|
|
return
|
|
}
|
|
}
|
|
|
|
// Add a child node.
|
|
if !n.isLeaf {
|
|
name := entry[0]
|
|
child, ok := n.children[name]
|
|
if !ok {
|
|
child = newNode(name)
|
|
n.children[name] = child
|
|
}
|
|
child.add(entry)
|
|
|
|
// If we have more than combinePathsThreshold immediate children,
|
|
// combine them into this node.
|
|
immediateChilden := 0
|
|
for _, child := range n.children {
|
|
if child.isLeaf {
|
|
immediateChilden++
|
|
}
|
|
if !n.isRoot() && immediateChilden >= combinePathsThreshold {
|
|
n.isLeaf = true
|
|
n.children = map[string]*node{}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// entries returns the entries represented by this node and its children.
|
|
// Will not return children in the following cases:
|
|
// - This Node is a leaf, ie. it represents an entry which was explicitly
|
|
// inserted into the Tree, as opposed to only part of a path to other
|
|
// entries.
|
|
// - This Node has immediate children exceeding combinePathsThreshold and
|
|
// thus has been upgraded to a leaf node.
|
|
func (n *node) entries() [][]string {
|
|
if n.isLeaf {
|
|
return [][]string{{n.name}}
|
|
}
|
|
rv := [][]string{}
|
|
for _, child := range n.children {
|
|
for _, entry := range child.entries() {
|
|
if !n.isRoot() {
|
|
entry = append([]string{n.name}, entry...)
|
|
}
|
|
rv = append(rv, entry)
|
|
}
|
|
}
|
|
return rv
|
|
}
|
|
|
|
// tree represents a directory tree of task inputs.
|
|
type tree struct {
|
|
root *node
|
|
}
|
|
|
|
// newTree returns a tree instance.
|
|
func newTree() *tree {
|
|
return &tree{
|
|
root: newNode(""),
|
|
}
|
|
}
|
|
|
|
// add the given path to the tree. Entries may be combined as defined by
|
|
// combinePathsThreshold.
|
|
func (t *tree) add(p string) {
|
|
split := strings.Split(p, "/")
|
|
t.root.add(split)
|
|
}
|
|
|
|
// entries returns all entries in the tree. Entries may be combined as defined
|
|
// by combinePathsThreshold.
|
|
func (t *tree) entries() []string {
|
|
entries := t.root.entries()
|
|
rv := make([]string, 0, len(entries))
|
|
for _, entry := range entries {
|
|
rv = append(rv, strings.Join(append([]string{"skia"}, entry...), "/"))
|
|
}
|
|
sort.Strings(rv)
|
|
return rv
|
|
}
|
|
|
|
// generateCompileCAS creates the CasSpec used for tasks which build Skia.
|
|
func generateCompileCAS(b *specs.TasksCfgBuilder, cfg *Config) {
|
|
t := newTree()
|
|
for _, path := range getRelevantPaths(cfg) {
|
|
t.add(path)
|
|
}
|
|
spec := &specs.CasSpec{
|
|
Root: "..",
|
|
Paths: t.entries(),
|
|
Excludes: []string{rbe.ExcludeGitDir},
|
|
}
|
|
b.MustAddCasSpec(CAS_COMPILE, spec)
|
|
}
|