-
Notifications
You must be signed in to change notification settings - Fork 2
/
build.rs
193 lines (172 loc) · 7.46 KB
/
build.rs
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
use anyhow::Context;
use std::{
fs::OpenOptions,
io::{BufRead, BufReader, Write},
path::{Path, PathBuf},
};
const SOURCE_DIR: &str = "lib/opentelemetry-proto";
const PREPROCESS_DIR: &str = "preprocess";
const PROTO_OUT_DIR: &str = "proto";
const OTEL_PROTOS: &[&str] = &[
"opentelemetry/proto/common/v1/common.proto",
"opentelemetry/proto/resource/v1/resource.proto",
"opentelemetry/proto/trace/v1/trace.proto",
"opentelemetry/proto/trace/v1/trace_config.proto",
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
"opentelemetry/proto/metrics/v1/metrics.proto",
"opentelemetry/proto/collector/metrics/v1/metrics_service.proto",
"opentelemetry/proto/logs/v1/logs.proto",
"opentelemetry/proto/collector/logs/v1/logs_service.proto",
];
const OTEL_INCLUDES: &[&str] = &["."];
const SERIALIZABLE_TYPES: &[&str] = &[
"opentelemetry.proto.common.v1.KeyValue",
"opentelemetry.proto.common.v1.ArrayValue",
"opentelemetry.proto.common.v1.KeyValueList",
"opentelemetry.proto.common.v1.StringKeyValue",
"opentelemetry.proto.common.v1.AnyValue",
"opentelemetry.proto.common.v1.AnyValue.value",
"opentelemetry.proto.common.v1.InstrumentationLibrary",
"opentelemetry.proto.common.v1.InstrumentationScope",
"opentelemetry.proto.resource.v1.Resource",
"opentelemetry.proto.trace.v1.ResourceSpans",
"opentelemetry.proto.trace.v1.InstrumentationLibrarySpans",
"opentelemetry.proto.trace.v1.ScopeSpans",
"opentelemetry.proto.trace.v1.Span",
"opentelemetry.proto.trace.v1.Span.Event",
"opentelemetry.proto.trace.v1.Span.Link",
"opentelemetry.proto.trace.v1.Status",
"opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest",
"opentelemetry.proto.metrics.v1.ResourceMetrics",
"opentelemetry.proto.metrics.v1.InstrumentationLibraryMetrics",
"opentelemetry.proto.metrics.v1.ScopeMetrics",
"opentelemetry.proto.metrics.v1.Metric",
"opentelemetry.proto.metrics.v1.Metric.data",
"opentelemetry.proto.metrics.v1.IntGauge",
"opentelemetry.proto.metrics.v1.Gauge",
"opentelemetry.proto.metrics.v1.IntSum",
"opentelemetry.proto.metrics.v1.Sum",
"opentelemetry.proto.metrics.v1.IntHistogram",
"opentelemetry.proto.metrics.v1.Histogram",
"opentelemetry.proto.metrics.v1.ExponentialHistogram",
"opentelemetry.proto.metrics.v1.Summary",
"opentelemetry.proto.metrics.v1.NumberDataPoint",
"opentelemetry.proto.metrics.v1.NumberDataPoint.value",
"opentelemetry.proto.metrics.v1.HistogramDataPoint",
"opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint",
"opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets",
"opentelemetry.proto.metrics.v1.SummaryDataPoint",
"opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile",
"opentelemetry.proto.metrics.v1.Exemplar",
"opentelemetry.proto.metrics.v1.Exemplar.value",
"opentelemetry.proto.metrics.v1.IntDataPoint",
"opentelemetry.proto.metrics.v1.IntExemplar",
"opentelemetry.proto.metrics.v1.IntHistogramDataPoint",
"opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest",
"opentelemetry.proto.logs.v1.ResourceLogs",
"opentelemetry.proto.logs.v1.InstrumentationLibraryLogs",
"opentelemetry.proto.logs.v1.ScopeLogs",
"opentelemetry.proto.logs.v1.LogRecord",
"opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest",
];
const DEPRECATED_FIELDS: &[&str] = &[
"opentelemetry.proto.metrics.v1.Metric.data.int_gauge",
"opentelemetry.proto.metrics.v1.Metric.data.int_sum",
"opentelemetry.proto.metrics.v1.Metric.data.int_histogram",
];
fn main() -> anyhow::Result<()> {
let out_dir = std::env::var("OUT_DIR").context("Missing output directory")?;
// Preprocess all protobuf files
let preprocess_dir: PathBuf = [&out_dir, PREPROCESS_DIR].into_iter().collect();
preprocess(Path::new(SOURCE_DIR), &preprocess_dir, OTEL_PROTOS)
.context("error preprocessing protobuf files")?;
// Create the output directory
let out_dir: PathBuf = [&out_dir, PROTO_OUT_DIR].into_iter().collect();
std::fs::create_dir_all(&out_dir).context("Error creating output directory")?;
// Configure the builder
let builder = tonic_build::configure()
.build_client(false)
.out_dir(out_dir);
// Apply attributes to types
let builder = SERIALIZABLE_TYPES.iter().fold(builder, |builder, &path| {
builder.type_attribute(path, "#[derive(serde::Serialize)]")
});
let builder = DEPRECATED_FIELDS.iter().fold(builder, |builder, &path| {
builder.field_attribute(path, "#[deprecated]")
});
// Compile the protobuf files
let protos: Vec<_> = OTEL_PROTOS
.iter()
.map(|path| preprocess_dir.join(path))
.collect();
let includes: Vec<_> = OTEL_INCLUDES
.iter()
.map(|path| preprocess_dir.join(path))
.collect();
builder
.compile(&protos, &includes)
.context("Error compiling protobuf files")?;
Ok(())
}
/// Preprocesses the protobuf files. This modifies the comments in each protobuf
/// file to be surrounded by text blocks. Many of the files use indented
/// comments which translate to code blocks in Markdown, causing `cargo test` to
/// assume those are documentation tests.
fn preprocess(
input_dir: &Path,
output_dir: &Path,
protos: impl IntoIterator<Item = impl AsRef<Path>>,
) -> anyhow::Result<()> {
// Create output directory
std::fs::create_dir_all(output_dir).context("error creating preprocessor output directory")?;
// Convert all the comments into this format:
// ```text
// <comment text>
// ```
for path in protos {
// Open source file
let input_path = input_dir.join(path.as_ref());
let input_file = OpenOptions::new()
.read(true)
.open(&input_path)
.with_context(|| format!("error opening input file '{}'", input_path.display()))?;
let input_file = BufReader::new(input_file);
// Open output file
let output_path = output_dir.join(path.as_ref());
let output_path_dir = output_path.parent().context("invalid output path")?;
std::fs::create_dir_all(output_path_dir).with_context(|| {
format!(
"error creating output directory '{}'",
output_path_dir.display()
)
})?;
let mut output_file = OpenOptions::new()
.create(true)
.write(true)
.open(&output_path)
.with_context(|| format!("error opening output file '{}'", output_path.display()))?;
// Process lines
let mut was_comment = false;
for line in input_file.lines() {
let line =
line.with_context(|| format!("error reading line in '{}'", input_path.display()))?;
let is_comment = line.trim_start().starts_with("//");
match (is_comment, was_comment) {
// Open text block
(true, false) => writeln!(output_file, "// ```text")?,
// Close text block
(false, true) => writeln!(output_file, "// ```")?,
_ => {}
};
// Write line
writeln!(output_file, "{}", line)?;
// Keep track of whether the last read line was a comment
was_comment = is_comment;
}
// Close text block if last line of file was a comment
if was_comment {
write!(output_file, "// ```")?;
}
}
Ok(())
}