-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathBenchmarkCLI+Library+Render.swift
164 lines (142 loc) · 5.49 KB
/
BenchmarkCLI+Library+Render.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Collections open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//
import Foundation
import ArgumentParser
import SystemPackage
extension _BenchmarkCLI.Library {
internal struct Render: _BenchmarkCommand {
static var configuration: CommandConfiguration {
CommandConfiguration(
commandName: "render",
abstract: "Render a predefined set of benchmarks.")
}
@Option(
help: "Path to a library configuration file in JSON format. (default: built-in library)",
completion: .file(extensions: ["json"]),
transform: { str in FilePath(str) })
var library: FilePath?
@Argument(
help: "A path to a benchmark results document.",
completion: .file(),
transform: { str in FilePath(str) })
var input: FilePath
@Option(
help: "Output path.",
completion: .file(),
transform: { str in FilePath(str) })
var output: FilePath?
@Option(help: "The image file format to generate. (default: based on the output path)")
var format: ImageFormat?
@Flag(help: "Write a separate image file for each chart. (default: depends on output format)")
var multifile: Bool = false
@Flag(help: "Write a single document containing all charts. (default: depends on output format)")
var singlefile: Bool = false
@OptionGroup
var options: _BenchmarkCLI.Render.Options
func run(benchmark: Benchmark) throws {
let results = try BenchmarkResults.load(from: input)
let library = try benchmark._loadLibrary(self.library)
let renderer = Graphics.bestAvailableRenderer
let theme = try options.themeSpec.resolve(with: renderer)
let (output, format, multifile) = try ImageFormat.resolve(
stem: "results",
output: self.output,
format: self.format,
multifile: (self.multifile ? true
: self.singlefile ? false
: nil))
func draw(_ chart: Benchmark.ChartLibrary.Chart) throws -> Graphics {
var taskSelection = Benchmark.Options.TaskSelection.empty
taskSelection.tasks = chart.tasks
let tasks = try taskSelection.resolve(
allKnownTasks: results.alltaskIDs(),
ignoreLabels: false)
let chart = Chart(taskIDs: tasks,
in: results,
options: try options.chartOptions())
let graphics = chart.draw(
bounds: options._bounds,
theme: theme,
renderer: renderer)
return graphics
}
if multifile {
guard output._isDirectory else {
throw Benchmark.Error("Multifile output must be a directory: \(output)")
}
let output = URL(output, isDirectory: true)
print("Generating images:")
var count = 0
try library.apply { event in
switch event {
case .startGroup, .endGroup, .startVariants, .endVariants:
break
case let .chart(directory: dir, number: number, chart: chart):
let graphics = try draw(chart)
var url = output
if !dir.isEmpty {
url.appendPathComponent(dir, isDirectory: true)
}
try FileManager.default.createDirectory(
at: url,
withIntermediateDirectories: true)
let filename = "\(number) \(chart.title).\(format)"
url.appendPathComponent(filename._sanitizedPathComponent())
print(" \(url.relativePath)")
let data = try renderer.render(
graphics,
format: format.rawValue,
bitmapScale: options.scale)
try data.write(to: url)
count += 1
}
}
print("Done. \(count) images saved.")
let markdown = try library.markdown(format: format)
let mdfile = output.appendingPathComponent("Results.md")
try markdown.write(to: mdfile, atomically: true, encoding: .utf8)
print("Overview written to \(mdfile.relativePath).")
} else {
guard format.supportsSinglefileRendering else {
throw Benchmark.Error("Format '\(format)' does not support multiple charts in a single file")
}
var count = 0
var depth = 0
var doc = try renderer.documentRenderer(
title: "Benchmark results",
format: format,
style: .collapsible)
try library.apply { event in
switch event {
case .startGroup(let group):
depth += 1
try doc.beginSection(title: group.title, collapsed: depth > 2)
case .endGroup(_):
try doc.endSection()
depth -= 1
case .startVariants, .endVariants:
break
case let .chart(directory: _, number: number, chart: chart):
let graphics = try draw(chart)
try doc.item(
title: "\(number) \(chart.title)",
graphics: graphics,
collapsed: true)
count += 1
}
}
let url = URL(output, isDirectory: false)
try doc.render().write(to: url)
print("\(count) images written to \(url.relativePath).")
}
}
}
}