blob: ebf2b10af0dd1db95fc6bc1390f26b0d7854dda2 [file] [log] [blame]
borenetdb182c72016-09-30 12:53:12 -07001// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package main
6
7/*
8 Generate the tasks.json file.
9*/
10
11import (
borenetdb182c72016-09-30 12:53:12 -070012 "encoding/json"
Eric Boren27225492017-02-01 15:56:55 -050013 "flag"
borenetdb182c72016-09-30 12:53:12 -070014 "fmt"
Eric Boren27225492017-02-01 15:56:55 -050015 "io/ioutil"
borenetdb182c72016-09-30 12:53:12 -070016 "os"
17 "path"
Eric Boren27225492017-02-01 15:56:55 -050018 "regexp"
borenetdb182c72016-09-30 12:53:12 -070019 "sort"
20 "strings"
Eric Boren4b254b92016-11-08 12:55:32 -050021 "time"
borenetdb182c72016-09-30 12:53:12 -070022
23 "github.com/skia-dev/glog"
borenetdb182c72016-09-30 12:53:12 -070024 "go.skia.org/infra/go/util"
25 "go.skia.org/infra/task_scheduler/go/specs"
26)
27
28const (
Kevin Lubick797ef162016-12-15 10:45:08 -050029 DEFAULT_OS = DEFAULT_OS_LINUX
30 DEFAULT_OS_LINUX = "Ubuntu-14.04"
borenetdb182c72016-09-30 12:53:12 -070031
borenetdb182c72016-09-30 12:53:12 -070032 // Name prefix for upload jobs.
33 PREFIX_UPLOAD = "Upload"
34)
35
36var (
37 // "Constants"
38
Eric Boren27225492017-02-01 15:56:55 -050039 // Top-level list of all jobs to run at each commit; loaded from
40 // jobs.json.
41 JOBS []string
42
43 // Mapping of human-friendly Android device names to a pair of {device_type, device_os}.
44 ANDROID_MAPPING map[string][]string
45
46 // General configuration information.
47 CONFIG struct {
Eric Boren965861b2017-02-06 15:38:41 -050048 GsBucketGm string `json:"gs_bucket_gm"`
49 GsBucketNano string `json:"gs_bucket_nano"`
50 NoUpload []string `json:"no_upload"`
51 Pool string `json:"pool"`
borenetdb182c72016-09-30 12:53:12 -070052 }
53
Eric Boren27225492017-02-01 15:56:55 -050054 // Mapping of human-friendly GPU names to PCI IDs.
55 GPU_MAPPING map[string]string
borenetdb182c72016-09-30 12:53:12 -070056
57 // Defines the structure of job names.
58 jobNameSchema *JobNameSchema
Eric Boren27225492017-02-01 15:56:55 -050059
60 // Flags.
Eric Boren1f8be682017-02-07 09:16:30 -050061 androidMapFile = flag.String("android_map", "", "JSON file containing a mapping of human-friendly Android device names to a pair of {device_type, device_os}.")
62 builderNameSchemaFile = flag.String("builder_name_schema", "", "Path to the builder_name_schema.json file. If not specified, uses infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json from this repo.")
63 assetsDir = flag.String("assets_dir", "", "Directory containing assets.")
64 cfgFile = flag.String("cfg_file", "", "JSON file containing general configuration information.")
65 gpuMapFile = flag.String("gpu_map", "", "JSON file containing a mapping of human-friendly GPU names to PCI IDs.")
66 jobsFile = flag.String("jobs", "", "JSON file containing jobs to run.")
borenetdb182c72016-09-30 12:53:12 -070067)
68
Eric Boren27225492017-02-01 15:56:55 -050069// linuxGceDimensions are the Swarming dimensions for Linux GCE
70// instances.
71func linuxGceDimensions() []string {
72 return []string{
73 "cpu:x86-64-avx2",
74 "gpu:none",
75 fmt.Sprintf("os:%s", DEFAULT_OS_LINUX),
76 fmt.Sprintf("pool:%s", CONFIG.Pool),
77 }
78}
79
borenetdb182c72016-09-30 12:53:12 -070080// deriveCompileTaskName returns the name of a compile task based on the given
81// job name.
82func deriveCompileTaskName(jobName string, parts map[string]string) string {
83 if parts["role"] == "Housekeeper" {
84 return "Build-Ubuntu-GCC-x86_64-Release-Shared"
85 } else if parts["role"] == "Test" || parts["role"] == "Perf" {
86 task_os := parts["os"]
87 ec := parts["extra_config"]
Kevin Lubickb03b5ac2016-11-14 13:42:27 -050088 ec = strings.TrimSuffix(ec, "_Skpbench")
Ben Wagner46df2a12017-02-21 19:10:24 -050089 ec = strings.TrimSuffix(ec, "_AbandonGpuContext")
90 ec = strings.TrimSuffix(ec, "_PreAbandonGpuContext")
Ben Wagneradfeaf12017-02-21 19:27:07 -050091 if ec == "Valgrind" {
92 // skia:6267
93 ec = ""
94 }
borenetdb182c72016-09-30 12:53:12 -070095 if task_os == "Android" {
96 if ec == "Vulkan" {
97 ec = "Android_Vulkan"
borenetdb182c72016-09-30 12:53:12 -070098 }
borenet52383432016-10-17 10:17:53 -070099 task_os = "Ubuntu"
borenetdb182c72016-09-30 12:53:12 -0700100 } else if task_os == "iOS" {
101 ec = task_os
102 task_os = "Mac"
103 } else if strings.Contains(task_os, "Win") {
104 task_os = "Win"
Kevin Lubick893c49e2017-01-17 15:15:40 -0500105 } else if strings.Contains(task_os, "Ubuntu") {
106 task_os = "Ubuntu"
borenetdb182c72016-09-30 12:53:12 -0700107 }
Eric Boren50831302016-11-18 13:10:51 -0500108 jobNameMap := map[string]string{
borenetdb182c72016-09-30 12:53:12 -0700109 "role": "Build",
110 "os": task_os,
111 "compiler": parts["compiler"],
112 "target_arch": parts["arch"],
113 "configuration": parts["configuration"],
Eric Boren50831302016-11-18 13:10:51 -0500114 }
115 if ec != "" {
116 jobNameMap["extra_config"] = ec
117 }
118 name, err := jobNameSchema.MakeJobName(jobNameMap)
borenetdb182c72016-09-30 12:53:12 -0700119 if err != nil {
120 glog.Fatal(err)
121 }
122 return name
123 } else {
124 return jobName
125 }
126}
127
128// swarmDimensions generates swarming bot dimensions for the given task.
129func swarmDimensions(parts map[string]string) []string {
borenetdb182c72016-09-30 12:53:12 -0700130 d := map[string]string{
Eric Boren27225492017-02-01 15:56:55 -0500131 "pool": CONFIG.Pool,
borenetdb182c72016-09-30 12:53:12 -0700132 }
133 if os, ok := parts["os"]; ok {
Eric Boren54ff2fc2016-12-02 12:09:10 -0500134 d["os"] = map[string]string{
Kevin Lubick893c49e2017-01-17 15:15:40 -0500135 "Android": "Android",
136 "Mac": "Mac-10.11",
137 "Ubuntu": DEFAULT_OS_LINUX,
Kevin Lubick9063eca2017-02-01 11:10:32 -0500138 "Ubuntu16": "Ubuntu-16.10",
Kevin Lubick893c49e2017-01-17 15:15:40 -0500139 "Win": "Windows-2008ServerR2-SP1",
140 "Win10": "Windows-10-14393",
141 "Win2k8": "Windows-2008ServerR2-SP1",
142 "Win8": "Windows-8.1-SP0",
143 "iOS": "iOS-9.3.1",
Eric Boren54ff2fc2016-12-02 12:09:10 -0500144 }[os]
Ben Wagner73557372016-12-29 16:27:03 -0500145 // Chrome Golo has a different Windows image.
146 if parts["model"] == "Golo" && os == "Win10" {
147 d["os"] = "Windows-10-10586"
Ben Wagner17f811b2016-12-22 08:40:14 -0500148 }
borenetdb182c72016-09-30 12:53:12 -0700149 } else {
150 d["os"] = DEFAULT_OS
151 }
borenetdb182c72016-09-30 12:53:12 -0700152 if parts["role"] == "Test" || parts["role"] == "Perf" {
153 if strings.Contains(parts["os"], "Android") {
154 // For Android, the device type is a better dimension
155 // than CPU or GPU.
Eric Boren27225492017-02-01 15:56:55 -0500156 deviceInfo, ok := ANDROID_MAPPING[parts["model"]]
157 if !ok {
158 glog.Fatalf("Entry %q not found in Android mapping: %v", parts["model"], ANDROID_MAPPING)
159 }
Eric Boren4b254b92016-11-08 12:55:32 -0500160 d["device_type"] = deviceInfo[0]
161 d["device_os"] = deviceInfo[1]
borenetdb182c72016-09-30 12:53:12 -0700162 } else if strings.Contains(parts["os"], "iOS") {
163 d["device"] = map[string]string{
Eric Boren792079cf2016-11-09 14:03:20 -0500164 "iPadMini4": "iPad5,1",
borenetdb182c72016-09-30 12:53:12 -0700165 }[parts["model"]]
borenetdb182c72016-09-30 12:53:12 -0700166 } else if parts["cpu_or_gpu"] == "CPU" {
167 d["gpu"] = "none"
168 d["cpu"] = map[string]string{
169 "AVX": "x86-64",
170 "AVX2": "x86-64-avx2",
171 "SSE4": "x86-64",
172 }[parts["cpu_or_gpu_value"]]
173 if strings.Contains(parts["os"], "Win") && parts["cpu_or_gpu_value"] == "AVX2" {
174 // AVX2 is not correctly detected on Windows. Fall back on other
175 // dimensions to ensure that we correctly target machines which we know
176 // have AVX2 support.
177 d["cpu"] = "x86-64"
178 d["os"] = "Windows-2008ServerR2-SP1"
179 }
180 } else {
Eric Boren27225492017-02-01 15:56:55 -0500181 gpu, ok := GPU_MAPPING[parts["cpu_or_gpu_value"]]
182 if !ok {
183 glog.Fatalf("Entry %q not found in GPU mapping: %v", parts["cpu_or_gpu_value"], GPU_MAPPING)
184 }
185 d["gpu"] = gpu
Ben Wagner08435892017-02-18 23:28:26 -0500186
187 // Hack: Specify machine_type dimension for NUCs and ShuttleCs. We
188 // temporarily have two types of machines with a GTX960. The only way to
189 // distinguish these bots is by machine_type.
190 machine_type, ok := map[string]string{
191 "NUC6i7KYK": "n1-highcpu-8",
192 "ShuttleC": "n1-standard-8",
193 }[parts["model"]]
194 if ok {
195 d["machine_type"] = machine_type
196 }
borenetdb182c72016-09-30 12:53:12 -0700197 }
198 } else {
199 d["gpu"] = "none"
200 }
201 rv := make([]string, 0, len(d))
202 for k, v := range d {
203 rv = append(rv, fmt.Sprintf("%s:%s", k, v))
204 }
205 sort.Strings(rv)
206 return rv
207}
208
borenetdb182c72016-09-30 12:53:12 -0700209// compile generates a compile task. Returns the name of the last task in the
210// generated chain of tasks, which the Job should add as a dependency.
boreneted20a702016-10-20 11:04:31 -0700211func compile(b *specs.TasksCfgBuilder, name string, parts map[string]string) string {
borenetdb182c72016-09-30 12:53:12 -0700212 // Collect the necessary CIPD packages.
213 pkgs := []*specs.CipdPackage{}
214
215 // Android bots require a toolchain.
216 if strings.Contains(name, "Android") {
borenetdb182c72016-09-30 12:53:12 -0700217 if strings.Contains(name, "Mac") {
boreneted20a702016-10-20 11:04:31 -0700218 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("android_ndk_darwin"))
Mike Klein86c2c0f2016-11-02 13:13:16 -0400219 } else if strings.Contains(name, "Win") {
Mike Kleine9215f02016-11-02 15:44:26 -0400220 pkg := b.MustGetCipdPackageFromAsset("android_ndk_windows")
221 pkg.Path = "n"
222 pkgs = append(pkgs, pkg)
borenetdb182c72016-09-30 12:53:12 -0700223 } else {
boreneted20a702016-10-20 11:04:31 -0700224 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("android_ndk_linux"))
borenetdb182c72016-09-30 12:53:12 -0700225 }
Kevin Lubick9c7dcac2017-01-18 09:24:56 -0500226 } else if strings.Contains(name, "Ubuntu") {
227 if strings.Contains(name, "Clang") {
228 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("clang_linux"))
229 }
230 if strings.Contains(name, "Vulkan") {
231 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("linux_vulkan_sdk"))
232 }
Mike Klein27dcee12016-11-09 16:31:42 -0500233 } else if strings.Contains(name, "Win") {
boreneted20a702016-10-20 11:04:31 -0700234 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("win_toolchain"))
borenetdb182c72016-09-30 12:53:12 -0700235 if strings.Contains(name, "Vulkan") {
boreneted20a702016-10-20 11:04:31 -0700236 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("win_vulkan_sdk"))
borenetdb182c72016-09-30 12:53:12 -0700237 }
238 }
239
240 // Add the task.
boreneted20a702016-10-20 11:04:31 -0700241 b.MustAddTask(name, &specs.TaskSpec{
borenetdb182c72016-09-30 12:53:12 -0700242 CipdPackages: pkgs,
243 Dimensions: swarmDimensions(parts),
244 ExtraArgs: []string{
245 "--workdir", "../../..", "swarm_compile",
skia.buildbots2478c732016-11-04 14:37:26 -0400246 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
borenetdb182c72016-09-30 12:53:12 -0700247 fmt.Sprintf("buildername=%s", name),
248 "mastername=fake-master",
249 "buildnumber=2",
250 "slavename=fake-buildslave",
borenet98b2e7a2016-10-13 06:23:45 -0700251 "nobuildbot=True",
borenetdb182c72016-09-30 12:53:12 -0700252 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
253 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
borenet98b2e7a2016-10-13 06:23:45 -0700254 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
skia.buildbots2478c732016-11-04 14:37:26 -0400255 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
256 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
borenetdb182c72016-09-30 12:53:12 -0700257 },
258 Isolate: "compile_skia.isolate",
259 Priority: 0.8,
boreneted20a702016-10-20 11:04:31 -0700260 })
Eric Boren8615fe52016-12-12 14:30:12 -0500261 // All compile tasks are runnable as their own Job. Assert that the Job
262 // is listed in JOBS.
263 if !util.In(name, JOBS) {
264 glog.Fatalf("Job %q is missing from the JOBS list!", name)
265 }
borenetdb182c72016-09-30 12:53:12 -0700266 return name
267}
268
269// recreateSKPs generates a RecreateSKPs task. Returns the name of the last
270// task in the generated chain of tasks, which the Job should add as a
271// dependency.
boreneted20a702016-10-20 11:04:31 -0700272func recreateSKPs(b *specs.TasksCfgBuilder, name string) string {
Eric Boren4b254b92016-11-08 12:55:32 -0500273 b.MustAddTask(name, &specs.TaskSpec{
Eric Borenf5a90e82016-11-15 15:18:20 -0500274 CipdPackages: []*specs.CipdPackage{},
Eric Boren27225492017-02-01 15:56:55 -0500275 Dimensions: linuxGceDimensions(),
Eric Borenf5a90e82016-11-15 15:18:20 -0500276 ExecutionTimeout: 4 * time.Hour,
Eric Boren4b254b92016-11-08 12:55:32 -0500277 ExtraArgs: []string{
278 "--workdir", "../../..", "swarm_RecreateSKPs",
279 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
280 fmt.Sprintf("buildername=%s", name),
281 "mastername=fake-master",
282 "buildnumber=2",
283 "slavename=fake-buildslave",
284 "nobuildbot=True",
285 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
286 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
287 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
288 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
289 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
290 },
Eric Borenf5a90e82016-11-15 15:18:20 -0500291 IoTimeout: 40 * time.Minute,
292 Isolate: "compile_skia.isolate",
293 Priority: 0.8,
Eric Boren4b254b92016-11-08 12:55:32 -0500294 })
borenetdb182c72016-09-30 12:53:12 -0700295 return name
296}
297
298// ctSKPs generates a CT SKPs task. Returns the name of the last task in the
299// generated chain of tasks, which the Job should add as a dependency.
boreneted20a702016-10-20 11:04:31 -0700300func ctSKPs(b *specs.TasksCfgBuilder, name string) string {
Eric Boren4b254b92016-11-08 12:55:32 -0500301 b.MustAddTask(name, &specs.TaskSpec{
302 CipdPackages: []*specs.CipdPackage{},
303 Dimensions: []string{"pool:SkiaCT"},
304 ExecutionTimeout: 24 * time.Hour,
305 ExtraArgs: []string{
306 "--workdir", "../../..", "swarm_ct_skps",
307 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
308 fmt.Sprintf("buildername=%s", name),
309 "mastername=fake-master",
310 "buildnumber=2",
311 "slavename=fake-buildslave",
312 "nobuildbot=True",
313 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
314 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
315 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
316 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
317 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
318 },
319 IoTimeout: time.Hour,
320 Isolate: "ct_skps_skia.isolate",
321 Priority: 0.8,
322 })
borenetdb182c72016-09-30 12:53:12 -0700323 return name
324}
325
326// housekeeper generates a Housekeeper task. Returns the name of the last task
327// in the generated chain of tasks, which the Job should add as a dependency.
boreneted20a702016-10-20 11:04:31 -0700328func housekeeper(b *specs.TasksCfgBuilder, name, compileTaskName string) string {
Eric Boren4b254b92016-11-08 12:55:32 -0500329 b.MustAddTask(name, &specs.TaskSpec{
Eric Boren22f5ef72016-12-02 11:01:33 -0500330 CipdPackages: []*specs.CipdPackage{b.MustGetCipdPackageFromAsset("go")},
Eric Boren4b254b92016-11-08 12:55:32 -0500331 Dependencies: []string{compileTaskName},
Eric Boren27225492017-02-01 15:56:55 -0500332 Dimensions: linuxGceDimensions(),
Eric Boren4b254b92016-11-08 12:55:32 -0500333 ExtraArgs: []string{
334 "--workdir", "../../..", "swarm_housekeeper",
335 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
336 fmt.Sprintf("buildername=%s", name),
337 "mastername=fake-master",
338 "buildnumber=2",
339 "slavename=fake-buildslave",
340 "nobuildbot=True",
341 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
342 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
343 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
344 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
345 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
346 },
347 Isolate: "housekeeper_skia.isolate",
348 Priority: 0.8,
349 })
borenetdb182c72016-09-30 12:53:12 -0700350 return name
351}
352
borenet2dbbfa52016-10-14 06:32:09 -0700353// infra generates an infra_tests task. Returns the name of the last task in the
354// generated chain of tasks, which the Job should add as a dependency.
boreneted20a702016-10-20 11:04:31 -0700355func infra(b *specs.TasksCfgBuilder, name string) string {
356 b.MustAddTask(name, &specs.TaskSpec{
borenet2dbbfa52016-10-14 06:32:09 -0700357 CipdPackages: []*specs.CipdPackage{},
Eric Boren27225492017-02-01 15:56:55 -0500358 Dimensions: linuxGceDimensions(),
borenet2dbbfa52016-10-14 06:32:09 -0700359 ExtraArgs: []string{
360 "--workdir", "../../..", "swarm_infra",
skia.buildbots2478c732016-11-04 14:37:26 -0400361 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
borenet2dbbfa52016-10-14 06:32:09 -0700362 fmt.Sprintf("buildername=%s", name),
363 "mastername=fake-master",
364 "buildnumber=2",
365 "slavename=fake-buildslave",
366 "nobuildbot=True",
367 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
368 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
369 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
skia.buildbots2478c732016-11-04 14:37:26 -0400370 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
371 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
borenet2dbbfa52016-10-14 06:32:09 -0700372 },
373 Isolate: "infra_skia.isolate",
374 Priority: 0.8,
boreneted20a702016-10-20 11:04:31 -0700375 })
borenet2dbbfa52016-10-14 06:32:09 -0700376 return name
377}
378
borenetdb182c72016-09-30 12:53:12 -0700379// doUpload indicates whether the given Job should upload its results.
380func doUpload(name string) bool {
Eric Boren27225492017-02-01 15:56:55 -0500381 for _, s := range CONFIG.NoUpload {
382 m, err := regexp.MatchString(s, name)
383 if err != nil {
384 glog.Fatal(err)
385 }
386 if m {
borenetdb182c72016-09-30 12:53:12 -0700387 return false
388 }
389 }
390 return true
391}
392
393// test generates a Test task. Returns the name of the last task in the
394// generated chain of tasks, which the Job should add as a dependency.
boreneted20a702016-10-20 11:04:31 -0700395func test(b *specs.TasksCfgBuilder, name string, parts map[string]string, compileTaskName string, pkgs []*specs.CipdPackage) string {
Eric Boren4b254b92016-11-08 12:55:32 -0500396 s := &specs.TaskSpec{
Eric Boren1f2f64b2016-11-09 18:35:15 -0500397 CipdPackages: pkgs,
398 Dependencies: []string{compileTaskName},
399 Dimensions: swarmDimensions(parts),
400 ExecutionTimeout: 4 * time.Hour,
401 Expiration: 20 * time.Hour,
borenetdb182c72016-09-30 12:53:12 -0700402 ExtraArgs: []string{
403 "--workdir", "../../..", "swarm_test",
skia.buildbots2478c732016-11-04 14:37:26 -0400404 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
borenetdb182c72016-09-30 12:53:12 -0700405 fmt.Sprintf("buildername=%s", name),
406 "mastername=fake-master",
407 "buildnumber=2",
408 "slavename=fake-buildslave",
borenet98b2e7a2016-10-13 06:23:45 -0700409 "nobuildbot=True",
borenetdb182c72016-09-30 12:53:12 -0700410 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
411 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
borenet98b2e7a2016-10-13 06:23:45 -0700412 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
skia.buildbots2478c732016-11-04 14:37:26 -0400413 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
414 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
borenetdb182c72016-09-30 12:53:12 -0700415 },
Eric Boren5d9f3bf2017-02-22 08:36:03 -0500416 IoTimeout: 40 * time.Minute,
417 Isolate: "test_skia.isolate",
418 MaxAttempts: 1,
419 Priority: 0.8,
Eric Boren4b254b92016-11-08 12:55:32 -0500420 }
421 if strings.Contains(parts["extra_config"], "Valgrind") {
422 s.ExecutionTimeout = 9 * time.Hour
423 s.Expiration = 48 * time.Hour
424 s.IoTimeout = time.Hour
425 } else if strings.Contains(parts["extra_config"], "MSAN") {
426 s.ExecutionTimeout = 9 * time.Hour
427 }
428 b.MustAddTask(name, s)
429
borenetdb182c72016-09-30 12:53:12 -0700430 // Upload results if necessary.
431 if doUpload(name) {
432 uploadName := fmt.Sprintf("%s%s%s", PREFIX_UPLOAD, jobNameSchema.Sep, name)
boreneted20a702016-10-20 11:04:31 -0700433 b.MustAddTask(uploadName, &specs.TaskSpec{
borenetdb182c72016-09-30 12:53:12 -0700434 Dependencies: []string{name},
Eric Boren27225492017-02-01 15:56:55 -0500435 Dimensions: linuxGceDimensions(),
borenetdb182c72016-09-30 12:53:12 -0700436 ExtraArgs: []string{
437 "--workdir", "../../..", "upload_dm_results",
skia.buildbots2478c732016-11-04 14:37:26 -0400438 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
borenetdb182c72016-09-30 12:53:12 -0700439 fmt.Sprintf("buildername=%s", name),
440 "mastername=fake-master",
441 "buildnumber=2",
442 "slavename=fake-buildslave",
borenet98b2e7a2016-10-13 06:23:45 -0700443 "nobuildbot=True",
borenetdb182c72016-09-30 12:53:12 -0700444 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
445 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
borenet98b2e7a2016-10-13 06:23:45 -0700446 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
skia.buildbots2478c732016-11-04 14:37:26 -0400447 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
448 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
Eric Boren965861b2017-02-06 15:38:41 -0500449 fmt.Sprintf("gs_bucket=%s", CONFIG.GsBucketGm),
borenetdb182c72016-09-30 12:53:12 -0700450 },
451 Isolate: "upload_dm_results.isolate",
452 Priority: 0.8,
boreneted20a702016-10-20 11:04:31 -0700453 })
borenetdb182c72016-09-30 12:53:12 -0700454 return uploadName
455 }
456 return name
457}
458
459// perf generates a Perf task. Returns the name of the last task in the
460// generated chain of tasks, which the Job should add as a dependency.
boreneted20a702016-10-20 11:04:31 -0700461func perf(b *specs.TasksCfgBuilder, name string, parts map[string]string, compileTaskName string, pkgs []*specs.CipdPackage) string {
Kevin Lubickb03b5ac2016-11-14 13:42:27 -0500462 recipe := "swarm_perf"
463 isolate := "perf_skia.isolate"
464 if strings.Contains(parts["extra_config"], "Skpbench") {
465 recipe = "swarm_skpbench"
466 isolate = "skpbench_skia.isolate"
467 }
Eric Boren4b254b92016-11-08 12:55:32 -0500468 s := &specs.TaskSpec{
Eric Boren1f2f64b2016-11-09 18:35:15 -0500469 CipdPackages: pkgs,
470 Dependencies: []string{compileTaskName},
471 Dimensions: swarmDimensions(parts),
472 ExecutionTimeout: 4 * time.Hour,
473 Expiration: 20 * time.Hour,
borenetdb182c72016-09-30 12:53:12 -0700474 ExtraArgs: []string{
Kevin Lubickb03b5ac2016-11-14 13:42:27 -0500475 "--workdir", "../../..", recipe,
skia.buildbots2478c732016-11-04 14:37:26 -0400476 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
borenetdb182c72016-09-30 12:53:12 -0700477 fmt.Sprintf("buildername=%s", name),
478 "mastername=fake-master",
479 "buildnumber=2",
480 "slavename=fake-buildslave",
borenet98b2e7a2016-10-13 06:23:45 -0700481 "nobuildbot=True",
borenetdb182c72016-09-30 12:53:12 -0700482 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
483 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
borenet98b2e7a2016-10-13 06:23:45 -0700484 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
skia.buildbots2478c732016-11-04 14:37:26 -0400485 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
486 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
borenetdb182c72016-09-30 12:53:12 -0700487 },
Eric Boren5d9f3bf2017-02-22 08:36:03 -0500488 IoTimeout: 40 * time.Minute,
489 Isolate: isolate,
490 MaxAttempts: 1,
491 Priority: 0.8,
Eric Boren4b254b92016-11-08 12:55:32 -0500492 }
493 if strings.Contains(parts["extra_config"], "Valgrind") {
494 s.ExecutionTimeout = 9 * time.Hour
495 s.Expiration = 48 * time.Hour
496 s.IoTimeout = time.Hour
497 } else if strings.Contains(parts["extra_config"], "MSAN") {
498 s.ExecutionTimeout = 9 * time.Hour
499 }
500 b.MustAddTask(name, s)
501
borenetdb182c72016-09-30 12:53:12 -0700502 // Upload results if necessary.
503 if strings.Contains(name, "Release") && doUpload(name) {
504 uploadName := fmt.Sprintf("%s%s%s", PREFIX_UPLOAD, jobNameSchema.Sep, name)
boreneted20a702016-10-20 11:04:31 -0700505 b.MustAddTask(uploadName, &specs.TaskSpec{
borenetdb182c72016-09-30 12:53:12 -0700506 Dependencies: []string{name},
Eric Boren27225492017-02-01 15:56:55 -0500507 Dimensions: linuxGceDimensions(),
borenetdb182c72016-09-30 12:53:12 -0700508 ExtraArgs: []string{
509 "--workdir", "../../..", "upload_nano_results",
skia.buildbots2478c732016-11-04 14:37:26 -0400510 fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
borenetdb182c72016-09-30 12:53:12 -0700511 fmt.Sprintf("buildername=%s", name),
512 "mastername=fake-master",
513 "buildnumber=2",
514 "slavename=fake-buildslave",
borenet98b2e7a2016-10-13 06:23:45 -0700515 "nobuildbot=True",
borenetdb182c72016-09-30 12:53:12 -0700516 fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
517 fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
borenet98b2e7a2016-10-13 06:23:45 -0700518 fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
skia.buildbots2478c732016-11-04 14:37:26 -0400519 fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
520 fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
Eric Boren965861b2017-02-06 15:38:41 -0500521 fmt.Sprintf("gs_bucket=%s", CONFIG.GsBucketNano),
borenetdb182c72016-09-30 12:53:12 -0700522 },
523 Isolate: "upload_nano_results.isolate",
524 Priority: 0.8,
boreneted20a702016-10-20 11:04:31 -0700525 })
borenetdb182c72016-09-30 12:53:12 -0700526 return uploadName
527 }
528 return name
529}
530
531// process generates tasks and jobs for the given job name.
boreneted20a702016-10-20 11:04:31 -0700532func process(b *specs.TasksCfgBuilder, name string) {
borenetdb182c72016-09-30 12:53:12 -0700533 deps := []string{}
534
535 parts, err := jobNameSchema.ParseJobName(name)
536 if err != nil {
537 glog.Fatal(err)
538 }
539
540 // RecreateSKPs.
541 if strings.Contains(name, "RecreateSKPs") {
boreneted20a702016-10-20 11:04:31 -0700542 deps = append(deps, recreateSKPs(b, name))
borenetdb182c72016-09-30 12:53:12 -0700543 }
544
545 // CT bots.
546 if strings.Contains(name, "-CT_") {
boreneted20a702016-10-20 11:04:31 -0700547 deps = append(deps, ctSKPs(b, name))
borenetdb182c72016-09-30 12:53:12 -0700548 }
549
borenet2dbbfa52016-10-14 06:32:09 -0700550 // Infra tests.
551 if name == "Housekeeper-PerCommit-InfraTests" {
boreneted20a702016-10-20 11:04:31 -0700552 deps = append(deps, infra(b, name))
borenet2dbbfa52016-10-14 06:32:09 -0700553 }
554
borenetdb182c72016-09-30 12:53:12 -0700555 // Compile bots.
556 if parts["role"] == "Build" {
boreneted20a702016-10-20 11:04:31 -0700557 deps = append(deps, compile(b, name, parts))
borenetdb182c72016-09-30 12:53:12 -0700558 }
559
Eric Borenf5a90e82016-11-15 15:18:20 -0500560 // Most remaining bots need a compile task.
borenetdb182c72016-09-30 12:53:12 -0700561 compileTaskName := deriveCompileTaskName(name, parts)
borenet52383432016-10-17 10:17:53 -0700562 compileTaskParts, err := jobNameSchema.ParseJobName(compileTaskName)
563 if err != nil {
564 glog.Fatal(err)
565 }
Eric Boren628e78b2016-11-17 11:33:27 -0500566 // These bots do not need a compile task.
Eric Borenf5a90e82016-11-15 15:18:20 -0500567 if parts["role"] != "Build" &&
568 name != "Housekeeper-PerCommit-InfraTests" &&
Eric Boren71b762f2016-11-30 14:05:16 -0500569 !strings.Contains(name, "RecreateSKPs") &&
570 !strings.Contains(name, "-CT_") {
boreneted20a702016-10-20 11:04:31 -0700571 compile(b, compileTaskName, compileTaskParts)
borenet52383432016-10-17 10:17:53 -0700572 }
borenetdb182c72016-09-30 12:53:12 -0700573
574 // Housekeeper.
Eric Boren22f5ef72016-12-02 11:01:33 -0500575 if name == "Housekeeper-PerCommit" {
boreneted20a702016-10-20 11:04:31 -0700576 deps = append(deps, housekeeper(b, name, compileTaskName))
borenetdb182c72016-09-30 12:53:12 -0700577 }
578
579 // Common assets needed by the remaining bots.
580 pkgs := []*specs.CipdPackage{
boreneted20a702016-10-20 11:04:31 -0700581 b.MustGetCipdPackageFromAsset("skimage"),
582 b.MustGetCipdPackageFromAsset("skp"),
583 b.MustGetCipdPackageFromAsset("svg"),
borenetdb182c72016-09-30 12:53:12 -0700584 }
Eric Boren4b254b92016-11-08 12:55:32 -0500585 if strings.Contains(name, "Ubuntu") && strings.Contains(name, "SAN") {
586 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("clang_linux"))
587 }
Kevin Lubick35115eb2017-02-17 10:25:34 -0500588 if strings.Contains(name, "Ubuntu16") {
589 if strings.Contains(name, "Vulkan") {
590 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("linux_vulkan_sdk"))
591 }
Kevin Lubick0a51b482017-02-06 12:45:29 -0500592 if strings.Contains(name, "Release") {
593 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("linux_vulkan_intel_driver_release"))
594 } else {
595 pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("linux_vulkan_intel_driver_debug"))
596 }
597 }
Kevin Lubickb03b5ac2016-11-14 13:42:27 -0500598 // Skpbench only needs skps
599 if strings.Contains(name, "Skpbench") {
600 pkgs = []*specs.CipdPackage{
601 b.MustGetCipdPackageFromAsset("skp"),
602 }
603 }
borenetdb182c72016-09-30 12:53:12 -0700604
605 // Test bots.
Eric Boren71b762f2016-11-30 14:05:16 -0500606 if parts["role"] == "Test" && !strings.Contains(name, "-CT_") {
boreneted20a702016-10-20 11:04:31 -0700607 deps = append(deps, test(b, name, parts, compileTaskName, pkgs))
borenetdb182c72016-09-30 12:53:12 -0700608 }
609
610 // Perf bots.
Eric Boren71b762f2016-11-30 14:05:16 -0500611 if parts["role"] == "Perf" && !strings.Contains(name, "-CT_") {
boreneted20a702016-10-20 11:04:31 -0700612 deps = append(deps, perf(b, name, parts, compileTaskName, pkgs))
borenetdb182c72016-09-30 12:53:12 -0700613 }
614
615 // Add the Job spec.
Eric Borenf5a90e82016-11-15 15:18:20 -0500616 j := &specs.JobSpec{
borenetdb182c72016-09-30 12:53:12 -0700617 Priority: 0.8,
618 TaskSpecs: deps,
Eric Borenf5a90e82016-11-15 15:18:20 -0500619 }
620 if name == "Housekeeper-Nightly-RecreateSKPs_Canary" {
621 j.Trigger = "nightly"
622 }
623 if name == "Housekeeper-Weekly-RecreateSKPs" {
624 j.Trigger = "weekly"
625 }
Eric Boren71b762f2016-11-30 14:05:16 -0500626 if name == "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs" {
627 j.Trigger = "weekly"
628 }
Eric Boren8615fe52016-12-12 14:30:12 -0500629 b.MustAddJob(name, j)
borenetdb182c72016-09-30 12:53:12 -0700630}
631
Eric Boren27225492017-02-01 15:56:55 -0500632func loadJson(flag *string, defaultFlag string, val interface{}) {
633 if *flag == "" {
634 *flag = defaultFlag
635 }
636 b, err := ioutil.ReadFile(*flag)
637 if err != nil {
638 glog.Fatal(err)
639 }
640 if err := json.Unmarshal(b, val); err != nil {
641 glog.Fatal(err)
642 }
643}
644
borenetdb182c72016-09-30 12:53:12 -0700645// Regenerate the tasks.json file.
646func main() {
boreneted20a702016-10-20 11:04:31 -0700647 b := specs.MustNewTasksCfgBuilder()
Eric Boren27225492017-02-01 15:56:55 -0500648 b.SetAssetsDir(*assetsDir)
649 infraBots := path.Join(b.CheckoutRoot(), "infra", "bots")
650
651 // Load the jobs from a JSON file.
652 loadJson(jobsFile, path.Join(infraBots, "jobs.json"), &JOBS)
653
654 // Load the GPU mapping from a JSON file.
655 loadJson(gpuMapFile, path.Join(infraBots, "gpu_map.json"), &GPU_MAPPING)
656
657 // Load the Android device mapping from a JSON file.
658 loadJson(androidMapFile, path.Join(infraBots, "android_map.json"), &ANDROID_MAPPING)
659
660 // Load general config information from a JSON file.
661 loadJson(cfgFile, path.Join(infraBots, "cfg.json"), &CONFIG)
662
borenetdb182c72016-09-30 12:53:12 -0700663 // Create the JobNameSchema.
Eric Boren1f8be682017-02-07 09:16:30 -0500664 if *builderNameSchemaFile == "" {
665 *builderNameSchemaFile = path.Join(b.CheckoutRoot(), "infra", "bots", "recipe_modules", "builder_name_schema", "builder_name_schema.json")
666 }
667 schema, err := NewJobNameSchema(*builderNameSchemaFile)
borenetdb182c72016-09-30 12:53:12 -0700668 if err != nil {
669 glog.Fatal(err)
670 }
671 jobNameSchema = schema
672
borenetdb182c72016-09-30 12:53:12 -0700673 // Create Tasks and Jobs.
boreneted20a702016-10-20 11:04:31 -0700674 for _, name := range JOBS {
675 process(b, name)
borenetdb182c72016-09-30 12:53:12 -0700676 }
677
boreneted20a702016-10-20 11:04:31 -0700678 b.MustFinish()
borenetdb182c72016-09-30 12:53:12 -0700679}
680
681// TODO(borenet): The below really belongs in its own file, probably next to the
682// builder_name_schema.json file.
683
684// JobNameSchema is a struct used for (de)constructing Job names in a
685// predictable format.
686type JobNameSchema struct {
687 Schema map[string][]string `json:"builder_name_schema"`
688 Sep string `json:"builder_name_sep"`
689}
690
691// NewJobNameSchema returns a JobNameSchema instance based on the given JSON
692// file.
693func NewJobNameSchema(jsonFile string) (*JobNameSchema, error) {
694 var rv JobNameSchema
695 f, err := os.Open(jsonFile)
696 if err != nil {
697 return nil, err
698 }
699 defer util.Close(f)
700 if err := json.NewDecoder(f).Decode(&rv); err != nil {
701 return nil, err
702 }
703 return &rv, nil
704}
705
706// ParseJobName splits the given Job name into its component parts, according
707// to the schema.
708func (s *JobNameSchema) ParseJobName(n string) (map[string]string, error) {
709 split := strings.Split(n, s.Sep)
710 if len(split) < 2 {
711 return nil, fmt.Errorf("Invalid job name: %q", n)
712 }
713 role := split[0]
714 split = split[1:]
715 keys, ok := s.Schema[role]
716 if !ok {
717 return nil, fmt.Errorf("Invalid job name; %q is not a valid role.", role)
718 }
719 extraConfig := ""
720 if len(split) == len(keys)+1 {
721 extraConfig = split[len(split)-1]
722 split = split[:len(split)-1]
723 }
724 if len(split) != len(keys) {
725 return nil, fmt.Errorf("Invalid job name; %q has incorrect number of parts.", n)
726 }
727 rv := make(map[string]string, len(keys)+2)
728 rv["role"] = role
729 if extraConfig != "" {
730 rv["extra_config"] = extraConfig
731 }
732 for i, k := range keys {
733 rv[k] = split[i]
734 }
735 return rv, nil
736}
737
738// MakeJobName assembles the given parts of a Job name, according to the schema.
739func (s *JobNameSchema) MakeJobName(parts map[string]string) (string, error) {
740 role, ok := parts["role"]
741 if !ok {
742 return "", fmt.Errorf("Invalid job parts; jobs must have a role.")
743 }
744 keys, ok := s.Schema[role]
745 if !ok {
746 return "", fmt.Errorf("Invalid job parts; unknown role %q", role)
747 }
748 rvParts := make([]string, 0, len(parts))
749 rvParts = append(rvParts, role)
750 for _, k := range keys {
751 v, ok := parts[k]
752 if !ok {
753 return "", fmt.Errorf("Invalid job parts; missing %q", k)
754 }
755 rvParts = append(rvParts, v)
756 }
757 if _, ok := parts["extra_config"]; ok {
758 rvParts = append(rvParts, parts["extra_config"])
759 }
760 return strings.Join(rvParts, s.Sep), nil
761}