authorbors <bors@rust-lang.org> 2024-12-05 23:42:57 UTC
committerbors <bors@rust-lang.org> 2024-12-05 23:42:57 UTC
log706141b8d9090228343340378b1d4a2b095fa1fb
treee87ce86ba71331485e2d1140a2684345050f220f
parentc94848c046d29f9a80c09aae758e27e418a289f2
parent5dc05a8d01d0608c38061b839f7da77e599f6479

Auto merge of #133940 - GuillaumeGomez:rollup-nm1cz5j, r=GuillaumeGomez

Rollup of 8 pull requests Successful merges: - #132155 (Always display first line of impl blocks even when collapsed) - #133256 (CI: use free runners for i686-gnu jobs) - #133607 (implement checks for tail calls) - #133821 (Replace black with ruff in `tidy`) - #133827 (CI: rfl: move job forward to Linux v6.13-rc1) - #133910 (Normalize target-cpus.rs stdout test for LLVM changes) - #133921 (Adapt codegen tests for NUW inference) - #133936 (Avoid fetching the anon const hir node that is already available) r? `@ghost` `@rustbot` modify labels: rollup

82 files changed, 2774 insertions(+), 1158 deletions(-)

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs+10-16
......@@ -29,10 +29,9 @@ use rustc_errors::codes::*;
2929use rustc_errors::{
3030 Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err,
3131};
32use rustc_hir as hir;
3332use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
3433use rustc_hir::def_id::{DefId, LocalDefId};
35use rustc_hir::{GenericArg, GenericArgs, HirId};
34use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
3635use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
3736use rustc_infer::traits::ObligationCause;
3837use rustc_middle::middle::stability::AllowUnstable;
......@@ -2089,7 +2088,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
20892088 qpath.span(),
20902089 format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
20912090 ),
2092 hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon.def_id),
2091 hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
20932092 hir::ConstArgKind::Infer(span) => self.ct_infer(None, span),
20942093 }
20952094 }
......@@ -2180,27 +2179,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
21802179 /// Literals and const generic parameters are eagerly converted to a constant, everything else
21812180 /// becomes `Unevaluated`.
21822181 #[instrument(skip(self), level = "debug")]
2183 fn lower_anon_const(&self, def: LocalDefId) -> Const<'tcx> {
2182 fn lower_anon_const(&self, anon: &AnonConst) -> Const<'tcx> {
21842183 let tcx = self.tcx();
21852184
2186 let body_id = match tcx.hir_node_by_def_id(def) {
2187 hir::Node::AnonConst(ac) => ac.body,
2188 node => span_bug!(
2189 tcx.def_span(def.to_def_id()),
2190 "from_anon_const can only process anonymous constants, not {node:?}"
2191 ),
2192 };
2193
2194 let expr = &tcx.hir().body(body_id).value;
2185 let expr = &tcx.hir().body(anon.body).value;
21952186 debug!(?expr);
21962187
2197 let ty = tcx.type_of(def).no_bound_vars().expect("const parameter types cannot be generic");
2188 let ty = tcx
2189 .type_of(anon.def_id)
2190 .no_bound_vars()
2191 .expect("const parameter types cannot be generic");
21982192
21992193 match self.try_lower_anon_const_lit(ty, expr) {
22002194 Some(v) => v,
22012195 None => ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst {
2202 def: def.to_def_id(),
2203 args: ty::GenericArgs::identity_for_item(tcx, def.to_def_id()),
2196 def: anon.def_id.to_def_id(),
2197 args: ty::GenericArgs::identity_for_item(tcx, anon.def_id.to_def_id()),
22042198 }),
22052199 }
22062200 }
compiler/rustc_middle/src/query/mod.rs+6
......@@ -916,6 +916,12 @@ rustc_queries! {
916916 cache_on_disk_if { true }
917917 }
918918
919 /// Checks well-formedness of tail calls (`become f()`).
920 query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
921 desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) }
922 cache_on_disk_if { true }
923 }
924
919925 /// Returns the types assumed to be well formed while "inside" of the given item.
920926 ///
921927 /// Note that we've liberated the late bound regions of function signatures, so
compiler/rustc_mir_build/src/build/mod.rs+4
......@@ -50,6 +50,10 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx
5050 return construct_error(tcx, def, e);
5151 }
5252
53 if let Err(err) = tcx.check_tail_calls(def) {
54 return construct_error(tcx, def, err);
55 }
56
5357 let body = match tcx.thir_body(def) {
5458 Err(error_reported) => construct_error(tcx, def, error_reported),
5559 Ok((thir, expr)) => {
compiler/rustc_mir_build/src/check_tail_calls.rs created+386
......@@ -0,0 +1,386 @@
1use rustc_abi::ExternAbi;
2use rustc_errors::Applicability;
3use rustc_hir::LangItem;
4use rustc_hir::def::DefKind;
5use rustc_middle::span_bug;
6use rustc_middle::thir::visit::{self, Visitor};
7use rustc_middle::thir::{BodyTy, Expr, ExprId, ExprKind, Thir};
8use rustc_middle::ty::{self, Ty, TyCtxt};
9use rustc_span::def_id::{DefId, LocalDefId};
10use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
11
12pub(crate) fn check_tail_calls(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), ErrorGuaranteed> {
13 let (thir, expr) = tcx.thir_body(def)?;
14 let thir = &thir.borrow();
15
16 // If `thir` is empty, a type error occurred, skip this body.
17 if thir.exprs.is_empty() {
18 return Ok(());
19 }
20
21 let is_closure = matches!(tcx.def_kind(def), DefKind::Closure);
22 let caller_ty = tcx.type_of(def).skip_binder();
23
24 let mut visitor = TailCallCkVisitor {
25 tcx,
26 thir,
27 found_errors: Ok(()),
28 // FIXME(#132279): we're clearly in a body here.
29 typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
30 is_closure,
31 caller_ty,
32 };
33
34 visitor.visit_expr(&thir[expr]);
35
36 visitor.found_errors
37}
38
39struct TailCallCkVisitor<'a, 'tcx> {
40 tcx: TyCtxt<'tcx>,
41 thir: &'a Thir<'tcx>,
42 typing_env: ty::TypingEnv<'tcx>,
43 /// Whatever the currently checked body is one of a closure
44 is_closure: bool,
45 /// The result of the checks, `Err(_)` if there was a problem with some
46 /// tail call, `Ok(())` if all of them were fine.
47 found_errors: Result<(), ErrorGuaranteed>,
48 /// Type of the caller function.
49 caller_ty: Ty<'tcx>,
50}
51
52impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
53 fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) {
54 if self.is_closure {
55 self.report_in_closure(expr);
56 return;
57 }
58
59 let BodyTy::Fn(caller_sig) = self.thir.body_type else {
60 span_bug!(
61 call.span,
62 "`become` outside of functions should have been disallowed by hit_typeck"
63 )
64 };
65
66 let ExprKind::Scope { value, .. } = call.kind else {
67 span_bug!(call.span, "expected scope, found: {call:?}")
68 };
69 let value = &self.thir[value];
70
71 if matches!(
72 value.kind,
73 ExprKind::Binary { .. }
74 | ExprKind::Unary { .. }
75 | ExprKind::AssignOp { .. }
76 | ExprKind::Index { .. }
77 ) {
78 self.report_builtin_op(call, expr);
79 return;
80 }
81
82 let ExprKind::Call { ty, fun, ref args, from_hir_call, fn_span } = value.kind else {
83 self.report_non_call(value, expr);
84 return;
85 };
86
87 if !from_hir_call {
88 self.report_op(ty, args, fn_span, expr);
89 }
90
91 // Closures in thir look something akin to
92 // `for<'a> extern "rust-call" fn(&'a [closure@...], ()) -> <[closure@...] as FnOnce<()>>::Output {<[closure@...] as Fn<()>>::call}`
93 // So we have to check for them in this weird way...
94 if let &ty::FnDef(did, args) = ty.kind() {
95 let parent = self.tcx.parent(did);
96 if self.tcx.fn_trait_kind_from_def_id(parent).is_some()
97 && args.first().and_then(|arg| arg.as_type()).is_some_and(Ty::is_closure)
98 {
99 self.report_calling_closure(&self.thir[fun], args[1].as_type().unwrap(), expr);
100
101 // Tail calling is likely to cause unrelated errors (ABI, argument mismatches),
102 // skip them, producing an error about calling a closure is enough.
103 return;
104 };
105 }
106
107 // Erase regions since tail calls don't care about lifetimes
108 let callee_sig =
109 self.tcx.normalize_erasing_late_bound_regions(self.typing_env, ty.fn_sig(self.tcx));
110
111 if caller_sig.abi != callee_sig.abi {
112 self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi);
113 }
114
115 if caller_sig.inputs_and_output != callee_sig.inputs_and_output {
116 if caller_sig.inputs() != callee_sig.inputs() {
117 self.report_arguments_mismatch(expr.span, caller_sig, callee_sig);
118 }
119
120 // FIXME(explicit_tail_calls): this currenly fails for cases where opaques are used.
121 // e.g.
122 // ```
123 // fn a() -> impl Sized { become b() } // ICE
124 // fn b() -> u8 { 0 }
125 // ```
126 // we should think what is the expected behavior here.
127 // (we should probably just accept this by revealing opaques?)
128 if caller_sig.output() != callee_sig.output() {
129 span_bug!(expr.span, "hir typeck should have checked the return type already");
130 }
131 }
132
133 {
134 let caller_needs_location = self.needs_location(self.caller_ty);
135 let callee_needs_location = self.needs_location(ty);
136
137 if caller_needs_location != callee_needs_location {
138 self.report_track_caller_mismatch(expr.span, caller_needs_location);
139 }
140 }
141
142 if caller_sig.c_variadic {
143 self.report_c_variadic_caller(expr.span);
144 }
145
146 if callee_sig.c_variadic {
147 self.report_c_variadic_callee(expr.span);
148 }
149 }
150
151 /// Returns true if function of type `ty` needs location argument
152 /// (i.e. if a function is marked as `#[track_caller]`)
153 fn needs_location(&self, ty: Ty<'tcx>) -> bool {
154 if let &ty::FnDef(did, substs) = ty.kind() {
155 let instance =
156 ty::Instance::expect_resolve(self.tcx, self.typing_env, did, substs, DUMMY_SP);
157
158 instance.def.requires_caller_location(self.tcx)
159 } else {
160 false
161 }
162 }
163
164 fn report_in_closure(&mut self, expr: &Expr<'_>) {
165 let err = self.tcx.dcx().span_err(expr.span, "`become` is not allowed in closures");
166 self.found_errors = Err(err);
167 }
168
169 fn report_builtin_op(&mut self, value: &Expr<'_>, expr: &Expr<'_>) {
170 let err = self
171 .tcx
172 .dcx()
173 .struct_span_err(value.span, "`become` does not support operators")
174 .with_note("using `become` on a builtin operator is not useful")
175 .with_span_suggestion(
176 value.span.until(expr.span),
177 "try using `return` instead",
178 "return ",
179 Applicability::MachineApplicable,
180 )
181 .emit();
182 self.found_errors = Err(err);
183 }
184
185 fn report_op(&mut self, fun_ty: Ty<'_>, args: &[ExprId], fn_span: Span, expr: &Expr<'_>) {
186 let mut err =
187 self.tcx.dcx().struct_span_err(fn_span, "`become` does not support operators");
188
189 if let &ty::FnDef(did, _substs) = fun_ty.kind()
190 && let parent = self.tcx.parent(did)
191 && matches!(self.tcx.def_kind(parent), DefKind::Trait)
192 && let Some(method) = op_trait_as_method_name(self.tcx, parent)
193 {
194 match args {
195 &[arg] => {
196 let arg = &self.thir[arg];
197
198 err.multipart_suggestion(
199 "try using the method directly",
200 vec![
201 (fn_span.shrink_to_lo().until(arg.span), "(".to_owned()),
202 (arg.span.shrink_to_hi(), format!(").{method}()")),
203 ],
204 Applicability::MaybeIncorrect,
205 );
206 }
207 &[lhs, rhs] => {
208 let lhs = &self.thir[lhs];
209 let rhs = &self.thir[rhs];
210
211 err.multipart_suggestion(
212 "try using the method directly",
213 vec![
214 (lhs.span.shrink_to_lo(), format!("(")),
215 (lhs.span.between(rhs.span), format!(").{method}(")),
216 (rhs.span.between(expr.span.shrink_to_hi()), ")".to_owned()),
217 ],
218 Applicability::MaybeIncorrect,
219 );
220 }
221 _ => span_bug!(expr.span, "operator with more than 2 args? {args:?}"),
222 }
223 }
224
225 self.found_errors = Err(err.emit());
226 }
227
228 fn report_non_call(&mut self, value: &Expr<'_>, expr: &Expr<'_>) {
229 let err = self
230 .tcx
231 .dcx()
232 .struct_span_err(value.span, "`become` requires a function call")
233 .with_span_note(value.span, "not a function call")
234 .with_span_suggestion(
235 value.span.until(expr.span),
236 "try using `return` instead",
237 "return ",
238 Applicability::MaybeIncorrect,
239 )
240 .emit();
241 self.found_errors = Err(err);
242 }
243
244 fn report_calling_closure(&mut self, fun: &Expr<'_>, tupled_args: Ty<'_>, expr: &Expr<'_>) {
245 let underscored_args = match tupled_args.kind() {
246 ty::Tuple(tys) if tys.is_empty() => "".to_owned(),
247 ty::Tuple(tys) => std::iter::repeat("_, ").take(tys.len() - 1).chain(["_"]).collect(),
248 _ => "_".to_owned(),
249 };
250
251 let err = self
252 .tcx
253 .dcx()
254 .struct_span_err(expr.span, "tail calling closures directly is not allowed")
255 .with_multipart_suggestion(
256 "try casting the closure to a function pointer type",
257 vec![
258 (fun.span.shrink_to_lo(), "(".to_owned()),
259 (fun.span.shrink_to_hi(), format!(" as fn({underscored_args}) -> _)")),
260 ],
261 Applicability::MaybeIncorrect,
262 )
263 .emit();
264 self.found_errors = Err(err);
265 }
266
267 fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: ExternAbi) {
268 let err = self
269 .tcx
270 .dcx()
271 .struct_span_err(sp, "mismatched function ABIs")
272 .with_note("`become` requires caller and callee to have the same ABI")
273 .with_note(format!("caller ABI is `{caller_abi}`, while callee ABI is `{callee_abi}`"))
274 .emit();
275 self.found_errors = Err(err);
276 }
277
278 fn report_arguments_mismatch(
279 &mut self,
280 sp: Span,
281 caller_sig: ty::FnSig<'_>,
282 callee_sig: ty::FnSig<'_>,
283 ) {
284 let err = self
285 .tcx
286 .dcx()
287 .struct_span_err(sp, "mismatched signatures")
288 .with_note("`become` requires caller and callee to have matching signatures")
289 .with_note(format!("caller signature: `{caller_sig}`"))
290 .with_note(format!("callee signature: `{callee_sig}`"))
291 .emit();
292 self.found_errors = Err(err);
293 }
294
295 fn report_track_caller_mismatch(&mut self, sp: Span, caller_needs_location: bool) {
296 let err = match caller_needs_location {
297 true => self
298 .tcx
299 .dcx()
300 .struct_span_err(
301 sp,
302 "a function marked with `#[track_caller]` cannot tail-call one that is not",
303 )
304 .emit(),
305 false => self
306 .tcx
307 .dcx()
308 .struct_span_err(
309 sp,
310 "a function mot marked with `#[track_caller]` cannot tail-call one that is",
311 )
312 .emit(),
313 };
314
315 self.found_errors = Err(err);
316 }
317
318 fn report_c_variadic_caller(&mut self, sp: Span) {
319 let err = self
320 .tcx
321 .dcx()
322 // FIXME(explicit_tail_calls): highlight the `...`
323 .struct_span_err(sp, "tail-calls are not allowed in c-variadic functions")
324 .emit();
325
326 self.found_errors = Err(err);
327 }
328
329 fn report_c_variadic_callee(&mut self, sp: Span) {
330 let err = self
331 .tcx
332 .dcx()
333 // FIXME(explicit_tail_calls): highlight the function or something...
334 .struct_span_err(sp, "c-variadic functions can't be tail-called")
335 .emit();
336
337 self.found_errors = Err(err);
338 }
339}
340
341impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> {
342 fn thir(&self) -> &'a Thir<'tcx> {
343 &self.thir
344 }
345
346 fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
347 if let ExprKind::Become { value } = expr.kind {
348 let call = &self.thir[value];
349 self.check_tail_call(call, expr);
350 }
351
352 visit::walk_expr(self, expr);
353 }
354}
355
356fn op_trait_as_method_name(tcx: TyCtxt<'_>, trait_did: DefId) -> Option<&'static str> {
357 let m = match tcx.as_lang_item(trait_did)? {
358 LangItem::Add => "add",
359 LangItem::Sub => "sub",
360 LangItem::Mul => "mul",
361 LangItem::Div => "div",
362 LangItem::Rem => "rem",
363 LangItem::Neg => "neg",
364 LangItem::Not => "not",
365 LangItem::BitXor => "bitxor",
366 LangItem::BitAnd => "bitand",
367 LangItem::BitOr => "bitor",
368 LangItem::Shl => "shl",
369 LangItem::Shr => "shr",
370 LangItem::AddAssign => "add_assign",
371 LangItem::SubAssign => "sub_assign",
372 LangItem::MulAssign => "mul_assign",
373 LangItem::DivAssign => "div_assign",
374 LangItem::RemAssign => "rem_assign",
375 LangItem::BitXorAssign => "bitxor_assign",
376 LangItem::BitAndAssign => "bitand_assign",
377 LangItem::BitOrAssign => "bitor_assign",
378 LangItem::ShlAssign => "shl_assign",
379 LangItem::ShrAssign => "shr_assign",
380 LangItem::Index => "index",
381 LangItem::IndexMut => "index_mut",
382 _ => return None,
383 };
384
385 Some(m)
386}
compiler/rustc_mir_build/src/lib.rs+2
......@@ -12,6 +12,7 @@
1212// tidy-alphabetical-end
1313
1414mod build;
15mod check_tail_calls;
1516mod check_unsafety;
1617mod errors;
1718pub mod lints;
......@@ -28,6 +29,7 @@ pub fn provide(providers: &mut Providers) {
2829 providers.closure_saved_names_of_captured_variables =
2930 build::closure_saved_names_of_captured_variables;
3031 providers.check_unsafety = check_unsafety::check_unsafety;
32 providers.check_tail_calls = check_tail_calls::check_tail_calls;
3133 providers.thir_body = thir::cx::thir_body;
3234 providers.hooks.thir_tree = thir::print::thir_tree;
3335 providers.hooks.thir_flat = thir::print::thir_flat;
library/core/src/unicode/printable.py+34-19
......@@ -9,7 +9,8 @@ import csv
99import os
1010import subprocess
1111
12NUM_CODEPOINTS=0x110000
12NUM_CODEPOINTS = 0x110000
13
1314
1415def to_ranges(iter):
1516 current = None
......@@ -23,11 +24,15 @@ def to_ranges(iter):
2324 if current is not None:
2425 yield tuple(current)
2526
27
2628def get_escaped(codepoints):
2729 for c in codepoints:
28 if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '):
30 if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(
31 " "
32 ):
2933 yield c.value
3034
35
3136def get_file(f):
3237 try:
3338 return open(os.path.basename(f))
......@@ -35,7 +40,9 @@ def get_file(f):
3540 subprocess.run(["curl", "-O", f], check=True)
3641 return open(os.path.basename(f))
3742
38Codepoint = namedtuple('Codepoint', 'value class_')
43
44Codepoint = namedtuple("Codepoint", "value class_")
45
3946
4047def get_codepoints(f):
4148 r = csv.reader(f, delimiter=";")
......@@ -66,13 +73,14 @@ def get_codepoints(f):
6673 for c in range(prev_codepoint + 1, NUM_CODEPOINTS):
6774 yield Codepoint(c, None)
6875
76
6977def compress_singletons(singletons):
70 uppers = [] # (upper, # items in lowers)
78 uppers = [] # (upper, # items in lowers)
7179 lowers = []
7280
7381 for i in singletons:
7482 upper = i >> 8
75 lower = i & 0xff
83 lower = i & 0xFF
7684 if len(uppers) == 0 or uppers[-1][0] != upper:
7785 uppers.append((upper, 1))
7886 else:
......@@ -82,10 +90,11 @@ def compress_singletons(singletons):
8290
8391 return uppers, lowers
8492
93
8594def compress_normal(normal):
8695 # lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f
8796 # lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff
88 compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)]
97 compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)]
8998
9099 prev_start = 0
91100 for start, count in normal:
......@@ -95,21 +104,22 @@ def compress_normal(normal):
95104
96105 assert truelen < 0x8000 and falselen < 0x8000
97106 entry = []
98 if truelen > 0x7f:
107 if truelen > 0x7F:
99108 entry.append(0x80 | (truelen >> 8))
100 entry.append(truelen & 0xff)
109 entry.append(truelen & 0xFF)
101110 else:
102 entry.append(truelen & 0x7f)
103 if falselen > 0x7f:
111 entry.append(truelen & 0x7F)
112 if falselen > 0x7F:
104113 entry.append(0x80 | (falselen >> 8))
105 entry.append(falselen & 0xff)
114 entry.append(falselen & 0xFF)
106115 else:
107 entry.append(falselen & 0x7f)
116 entry.append(falselen & 0x7F)
108117
109118 compressed.append(entry)
110119
111120 return compressed
112121
122
113123def print_singletons(uppers, lowers, uppersname, lowersname):
114124 print("#[rustfmt::skip]")
115125 print("const {}: &[(u8, u8)] = &[".format(uppersname))
......@@ -119,9 +129,12 @@ def print_singletons(uppers, lowers, uppersname, lowersname):
119129 print("#[rustfmt::skip]")
120130 print("const {}: &[u8] = &[".format(lowersname))
121131 for i in range(0, len(lowers), 8):
122 print(" {}".format(" ".join("{:#04x},".format(x) for x in lowers[i:i+8])))
132 print(
133 " {}".format(" ".join("{:#04x},".format(x) for x in lowers[i : i + 8]))
134 )
123135 print("];")
124136
137
125138def print_normal(normal, normalname):
126139 print("#[rustfmt::skip]")
127140 print("const {}: &[u8] = &[".format(normalname))
......@@ -129,12 +142,13 @@ def print_normal(normal, normalname):
129142 print(" {}".format(" ".join("{:#04x},".format(i) for i in v)))
130143 print("];")
131144
145
132146def main():
133147 file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt")
134148
135149 codepoints = get_codepoints(file)
136150
137 CUTOFF=0x10000
151 CUTOFF = 0x10000
138152 singletons0 = []
139153 singletons1 = []
140154 normal0 = []
......@@ -234,10 +248,11 @@ pub(crate) fn is_printable(x: char) -> bool {
234248}\
235249""")
236250 print()
237 print_singletons(singletons0u, singletons0l, 'SINGLETONS0U', 'SINGLETONS0L')
238 print_singletons(singletons1u, singletons1l, 'SINGLETONS1U', 'SINGLETONS1L')
239 print_normal(normal0, 'NORMAL0')
240 print_normal(normal1, 'NORMAL1')
251 print_singletons(singletons0u, singletons0l, "SINGLETONS0U", "SINGLETONS0L")
252 print_singletons(singletons1u, singletons1l, "SINGLETONS1U", "SINGLETONS1L")
253 print_normal(normal0, "NORMAL0")
254 print_normal(normal1, "NORMAL1")
255
241256
242if __name__ == '__main__':
257if __name__ == "__main__":
243258 main()
src/bootstrap/bootstrap.py+393-270
......@@ -19,14 +19,17 @@ try:
1919except ImportError:
2020 lzma = None
2121
22
2223def platform_is_win32():
23 return sys.platform == 'win32'
24 return sys.platform == "win32"
25
2426
2527if platform_is_win32():
2628 EXE_SUFFIX = ".exe"
2729else:
2830 EXE_SUFFIX = ""
2931
32
3033def get_cpus():
3134 if hasattr(os, "sched_getaffinity"):
3235 return len(os.sched_getaffinity(0))
......@@ -51,11 +54,14 @@ def get(base, url, path, checksums, verbose=False):
5154
5255 try:
5356 if url not in checksums:
54 raise RuntimeError(("src/stage0 doesn't contain a checksum for {}. "
55 "Pre-built artifacts might not be available for this "
56 "target at this time, see https://doc.rust-lang.org/nightly"
57 "/rustc/platform-support.html for more information.")
58 .format(url))
57 raise RuntimeError(
58 (
59 "src/stage0 doesn't contain a checksum for {}. "
60 "Pre-built artifacts might not be available for this "
61 "target at this time, see https://doc.rust-lang.org/nightly"
62 "/rustc/platform-support.html for more information."
63 ).format(url)
64 )
5965 sha256 = checksums[url]
6066 if os.path.exists(path):
6167 if verify(path, sha256, False):
......@@ -64,8 +70,11 @@ def get(base, url, path, checksums, verbose=False):
6470 return
6571 else:
6672 if verbose:
67 eprint("ignoring already-download file",
68 path, "due to failed verification")
73 eprint(
74 "ignoring already-download file",
75 path,
76 "due to failed verification",
77 )
6978 os.unlink(path)
7079 download(temp_path, "{}/{}".format(base, url), True, verbose)
7180 if not verify(temp_path, sha256, verbose):
......@@ -79,12 +88,14 @@ def get(base, url, path, checksums, verbose=False):
7988 eprint("removing", temp_path)
8089 os.unlink(temp_path)
8190
91
8292def curl_version():
8393 m = re.match(bytes("^curl ([0-9]+)\\.([0-9]+)", "utf8"), require(["curl", "-V"]))
8494 if m is None:
8595 return (0, 0)
8696 return (int(m[1]), int(m[2]))
8797
98
8899def download(path, url, probably_big, verbose):
89100 for _ in range(4):
90101 try:
......@@ -114,32 +125,53 @@ def _download(path, url, probably_big, verbose, exception):
114125 require(["curl", "--version"], exception=platform_is_win32())
115126 extra_flags = []
116127 if curl_version() > (7, 70):
117 extra_flags = [ "--retry-all-errors" ]
128 extra_flags = ["--retry-all-errors"]
118129 # options should be kept in sync with
119130 # src/bootstrap/src/core/download.rs
120131 # for consistency.
121132 # they are also more compreprensivly explained in that file.
122 run(["curl", option] + extra_flags + [
123 # Follow redirect.
124 "--location",
125 # timeout if speed is < 10 bytes/sec for > 30 seconds
126 "--speed-time", "30", "--speed-limit", "10",
127 # timeout if cannot connect within 30 seconds
128 "--connect-timeout", "30",
129 "--output", path,
130 "--continue-at", "-",
131 "--retry", "3", "--show-error", "--remote-time", "--fail", url],
133 run(
134 ["curl", option]
135 + extra_flags
136 + [
137 # Follow redirect.
138 "--location",
139 # timeout if speed is < 10 bytes/sec for > 30 seconds
140 "--speed-time",
141 "30",
142 "--speed-limit",
143 "10",
144 # timeout if cannot connect within 30 seconds
145 "--connect-timeout",
146 "30",
147 "--output",
148 path,
149 "--continue-at",
150 "-",
151 "--retry",
152 "3",
153 "--show-error",
154 "--remote-time",
155 "--fail",
156 url,
157 ],
132158 verbose=verbose,
133 exception=True, # Will raise RuntimeError on failure
159 exception=True, # Will raise RuntimeError on failure
134160 )
135161 except (subprocess.CalledProcessError, OSError, RuntimeError):
136162 # see http://serverfault.com/questions/301128/how-to-download
163 script = "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;"
137164 if platform_is_win32():
138 run_powershell([
139 "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
140 "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(url, path)],
165 run_powershell(
166 [
167 script,
168 "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(
169 url, path
170 ),
171 ],
141172 verbose=verbose,
142 exception=exception)
173 exception=exception,
174 )
143175 # Check if the RuntimeError raised by run(curl) should be silenced
144176 elif verbose or exception:
145177 raise
......@@ -153,9 +185,11 @@ def verify(path, expected, verbose):
153185 found = hashlib.sha256(source.read()).hexdigest()
154186 verified = found == expected
155187 if not verified:
156 eprint("invalid checksum:\n"
157 " found: {}\n"
158 " expected: {}".format(found, expected))
188 eprint(
189 "invalid checksum:\n" " found: {}\n" " expected: {}".format(
190 found, expected
191 )
192 )
159193 return verified
160194
161195
......@@ -170,7 +204,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None):
170204 name = member.replace(fname + "/", "", 1)
171205 if match is not None and not name.startswith(match):
172206 continue
173 name = name[len(match) + 1:]
207 name = name[len(match) + 1 :]
174208
175209 dst_path = os.path.join(dst, name)
176210 if verbose:
......@@ -186,18 +220,18 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None):
186220def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs):
187221 """Run a child program in a new process"""
188222 if verbose:
189 eprint("running: " + ' '.join(args))
223 eprint("running: " + " ".join(args))
190224 sys.stdout.flush()
191225 # Ensure that the .exe is used on Windows just in case a Linux ELF has been
192226 # compiled in the same directory.
193 if os.name == 'nt' and not args[0].endswith('.exe'):
194 args[0] += '.exe'
227 if os.name == "nt" and not args[0].endswith(".exe"):
228 args[0] += ".exe"
195229 # Use Popen here instead of call() as it apparently allows powershell on
196230 # Windows to not lock up waiting for input presumably.
197231 ret = subprocess.Popen(args, **kwargs)
198232 code = ret.wait()
199233 if code != 0:
200 err = "failed to run: " + ' '.join(args)
234 err = "failed to run: " + " ".join(args)
201235 if verbose or exception:
202236 raise RuntimeError(err)
203237 # For most failures, we definitely do want to print this error, or the user will have no
......@@ -209,30 +243,30 @@ def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs):
209243 else:
210244 sys.exit(err)
211245
246
212247def run_powershell(script, *args, **kwargs):
213248 """Run a powershell script"""
214249 run(["PowerShell.exe", "/nologo", "-Command"] + script, *args, **kwargs)
215250
216251
217252def require(cmd, exit=True, exception=False):
218 '''Run a command, returning its output.
253 """Run a command, returning its output.
219254 On error,
220255 If `exception` is `True`, raise the error
221256 Otherwise If `exit` is `True`, exit the process
222 Else return None.'''
257 Else return None."""
223258 try:
224259 return subprocess.check_output(cmd).strip()
225260 except (subprocess.CalledProcessError, OSError) as exc:
226261 if exception:
227262 raise
228263 elif exit:
229 eprint("ERROR: unable to run `{}`: {}".format(' '.join(cmd), exc))
264 eprint("ERROR: unable to run `{}`: {}".format(" ".join(cmd), exc))
230265 eprint("Please make sure it's installed and in the path.")
231266 sys.exit(1)
232267 return None
233268
234269
235
236270def format_build_time(duration):
237271 """Return a nicer format for build time
238272
......@@ -252,13 +286,16 @@ def default_build_triple(verbose):
252286
253287 if platform_is_win32():
254288 try:
255 version = subprocess.check_output(["rustc", "--version", "--verbose"],
256 stderr=subprocess.DEVNULL)
289 version = subprocess.check_output(
290 ["rustc", "--version", "--verbose"], stderr=subprocess.DEVNULL
291 )
257292 version = version.decode(default_encoding)
258 host = next(x for x in version.split('\n') if x.startswith("host: "))
293 host = next(x for x in version.split("\n") if x.startswith("host: "))
259294 triple = host.split("host: ")[1]
260295 if verbose:
261 eprint("detected default triple {} from pre-installed rustc".format(triple))
296 eprint(
297 "detected default triple {} from pre-installed rustc".format(triple)
298 )
262299 return triple
263300 except Exception as e:
264301 if verbose:
......@@ -270,148 +307,149 @@ def default_build_triple(verbose):
270307
271308 # If we do not have `uname`, assume Windows.
272309 if uname is None:
273 return 'x86_64-pc-windows-msvc'
310 return "x86_64-pc-windows-msvc"
274311
275312 kernel, cputype, processor = uname.decode(default_encoding).split(maxsplit=2)
276313
277314 # The goal here is to come up with the same triple as LLVM would,
278315 # at least for the subset of platforms we're willing to target.
279316 kerneltype_mapper = {
280 'Darwin': 'apple-darwin',
281 'DragonFly': 'unknown-dragonfly',
282 'FreeBSD': 'unknown-freebsd',
283 'Haiku': 'unknown-haiku',
284 'NetBSD': 'unknown-netbsd',
285 'OpenBSD': 'unknown-openbsd',
286 'GNU': 'unknown-hurd',
317 "Darwin": "apple-darwin",
318 "DragonFly": "unknown-dragonfly",
319 "FreeBSD": "unknown-freebsd",
320 "Haiku": "unknown-haiku",
321 "NetBSD": "unknown-netbsd",
322 "OpenBSD": "unknown-openbsd",
323 "GNU": "unknown-hurd",
287324 }
288325
289326 # Consider the direct transformation first and then the special cases
290327 if kernel in kerneltype_mapper:
291328 kernel = kerneltype_mapper[kernel]
292 elif kernel == 'Linux':
329 elif kernel == "Linux":
293330 # Apple doesn't support `-o` so this can't be used in the combined
294331 # uname invocation above
295332 ostype = require(["uname", "-o"], exit=required).decode(default_encoding)
296 if ostype == 'Android':
297 kernel = 'linux-android'
333 if ostype == "Android":
334 kernel = "linux-android"
298335 else:
299 kernel = 'unknown-linux-gnu'
300 elif kernel == 'SunOS':
301 kernel = 'pc-solaris'
336 kernel = "unknown-linux-gnu"
337 elif kernel == "SunOS":
338 kernel = "pc-solaris"
302339 # On Solaris, uname -m will return a machine classification instead
303340 # of a cpu type, so uname -p is recommended instead. However, the
304341 # output from that option is too generic for our purposes (it will
305342 # always emit 'i386' on x86/amd64 systems). As such, isainfo -k
306343 # must be used instead.
307 cputype = require(['isainfo', '-k']).decode(default_encoding)
344 cputype = require(["isainfo", "-k"]).decode(default_encoding)
308345 # sparc cpus have sun as a target vendor
309 if 'sparc' in cputype:
310 kernel = 'sun-solaris'
311 elif kernel.startswith('MINGW'):
346 if "sparc" in cputype:
347 kernel = "sun-solaris"
348 elif kernel.startswith("MINGW"):
312349 # msys' `uname` does not print gcc configuration, but prints msys
313350 # configuration. so we cannot believe `uname -m`:
314351 # msys1 is always i686 and msys2 is always x86_64.
315352 # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
316353 # MINGW64 on x86_64.
317 kernel = 'pc-windows-gnu'
318 cputype = 'i686'
319 if os.environ.get('MSYSTEM') == 'MINGW64':
320 cputype = 'x86_64'
321 elif kernel.startswith('MSYS'):
322 kernel = 'pc-windows-gnu'
323 elif kernel.startswith('CYGWIN_NT'):
324 cputype = 'i686'
325 if kernel.endswith('WOW64'):
326 cputype = 'x86_64'
327 kernel = 'pc-windows-gnu'
354 kernel = "pc-windows-gnu"
355 cputype = "i686"
356 if os.environ.get("MSYSTEM") == "MINGW64":
357 cputype = "x86_64"
358 elif kernel.startswith("MSYS"):
359 kernel = "pc-windows-gnu"
360 elif kernel.startswith("CYGWIN_NT"):
361 cputype = "i686"
362 if kernel.endswith("WOW64"):
363 cputype = "x86_64"
364 kernel = "pc-windows-gnu"
328365 elif platform_is_win32():
329366 # Some Windows platforms might have a `uname` command that returns a
330367 # non-standard string (e.g. gnuwin32 tools returns `windows32`). In
331368 # these cases, fall back to using sys.platform.
332 return 'x86_64-pc-windows-msvc'
333 elif kernel == 'AIX':
369 return "x86_64-pc-windows-msvc"
370 elif kernel == "AIX":
334371 # `uname -m` returns the machine ID rather than machine hardware on AIX,
335372 # so we are unable to use cputype to form triple. AIX 7.2 and
336373 # above supports 32-bit and 64-bit mode simultaneously and `uname -p`
337374 # returns `powerpc`, however we only supports `powerpc64-ibm-aix` in
338375 # rust on AIX. For above reasons, kerneltype_mapper and cputype_mapper
339376 # are not used to infer AIX's triple.
340 return 'powerpc64-ibm-aix'
377 return "powerpc64-ibm-aix"
341378 else:
342379 err = "unknown OS type: {}".format(kernel)
343380 sys.exit(err)
344381
345 if cputype in ['powerpc', 'riscv'] and kernel == 'unknown-freebsd':
346 cputype = subprocess.check_output(
347 ['uname', '-p']).strip().decode(default_encoding)
382 if cputype in ["powerpc", "riscv"] and kernel == "unknown-freebsd":
383 cputype = (
384 subprocess.check_output(["uname", "-p"]).strip().decode(default_encoding)
385 )
348386 cputype_mapper = {
349 'BePC': 'i686',
350 'aarch64': 'aarch64',
351 'aarch64eb': 'aarch64',
352 'amd64': 'x86_64',
353 'arm64': 'aarch64',
354 'i386': 'i686',
355 'i486': 'i686',
356 'i686': 'i686',
357 'i686-AT386': 'i686',
358 'i786': 'i686',
359 'loongarch64': 'loongarch64',
360 'm68k': 'm68k',
361 'csky': 'csky',
362 'powerpc': 'powerpc',
363 'powerpc64': 'powerpc64',
364 'powerpc64le': 'powerpc64le',
365 'ppc': 'powerpc',
366 'ppc64': 'powerpc64',
367 'ppc64le': 'powerpc64le',
368 'riscv64': 'riscv64gc',
369 's390x': 's390x',
370 'x64': 'x86_64',
371 'x86': 'i686',
372 'x86-64': 'x86_64',
373 'x86_64': 'x86_64'
387 "BePC": "i686",
388 "aarch64": "aarch64",
389 "aarch64eb": "aarch64",
390 "amd64": "x86_64",
391 "arm64": "aarch64",
392 "i386": "i686",
393 "i486": "i686",
394 "i686": "i686",
395 "i686-AT386": "i686",
396 "i786": "i686",
397 "loongarch64": "loongarch64",
398 "m68k": "m68k",
399 "csky": "csky",
400 "powerpc": "powerpc",
401 "powerpc64": "powerpc64",
402 "powerpc64le": "powerpc64le",
403 "ppc": "powerpc",
404 "ppc64": "powerpc64",
405 "ppc64le": "powerpc64le",
406 "riscv64": "riscv64gc",
407 "s390x": "s390x",
408 "x64": "x86_64",
409 "x86": "i686",
410 "x86-64": "x86_64",
411 "x86_64": "x86_64",
374412 }
375413
376414 # Consider the direct transformation first and then the special cases
377415 if cputype in cputype_mapper:
378416 cputype = cputype_mapper[cputype]
379 elif cputype in {'xscale', 'arm'}:
380 cputype = 'arm'
381 if kernel == 'linux-android':
382 kernel = 'linux-androideabi'
383 elif kernel == 'unknown-freebsd':
417 elif cputype in {"xscale", "arm"}:
418 cputype = "arm"
419 if kernel == "linux-android":
420 kernel = "linux-androideabi"
421 elif kernel == "unknown-freebsd":
384422 cputype = processor
385 kernel = 'unknown-freebsd'
386 elif cputype == 'armv6l':
387 cputype = 'arm'
388 if kernel == 'linux-android':
389 kernel = 'linux-androideabi'
423 kernel = "unknown-freebsd"
424 elif cputype == "armv6l":
425 cputype = "arm"
426 if kernel == "linux-android":
427 kernel = "linux-androideabi"
390428 else:
391 kernel += 'eabihf'
392 elif cputype in {'armv7l', 'armv8l'}:
393 cputype = 'armv7'
394 if kernel == 'linux-android':
395 kernel = 'linux-androideabi'
429 kernel += "eabihf"
430 elif cputype in {"armv7l", "armv8l"}:
431 cputype = "armv7"
432 if kernel == "linux-android":
433 kernel = "linux-androideabi"
396434 else:
397 kernel += 'eabihf'
398 elif cputype == 'mips':
399 if sys.byteorder == 'big':
400 cputype = 'mips'
401 elif sys.byteorder == 'little':
402 cputype = 'mipsel'
435 kernel += "eabihf"
436 elif cputype == "mips":
437 if sys.byteorder == "big":
438 cputype = "mips"
439 elif sys.byteorder == "little":
440 cputype = "mipsel"
403441 else:
404442 raise ValueError("unknown byteorder: {}".format(sys.byteorder))
405 elif cputype == 'mips64':
406 if sys.byteorder == 'big':
407 cputype = 'mips64'
408 elif sys.byteorder == 'little':
409 cputype = 'mips64el'
443 elif cputype == "mips64":
444 if sys.byteorder == "big":
445 cputype = "mips64"
446 elif sys.byteorder == "little":
447 cputype = "mips64el"
410448 else:
411 raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
449 raise ValueError("unknown byteorder: {}".format(sys.byteorder))
412450 # only the n64 ABI is supported, indicate it
413 kernel += 'abi64'
414 elif cputype == 'sparc' or cputype == 'sparcv9' or cputype == 'sparc64':
451 kernel += "abi64"
452 elif cputype == "sparc" or cputype == "sparcv9" or cputype == "sparc64":
415453 pass
416454 else:
417455 err = "unknown cpu type: {}".format(cputype)
......@@ -422,8 +460,8 @@ def default_build_triple(verbose):
422460
423461@contextlib.contextmanager
424462def output(filepath):
425 tmp = filepath + '.tmp'
426 with open(tmp, 'w') as f:
463 tmp = filepath + ".tmp"
464 with open(tmp, "w") as f:
427465 yield f
428466 try:
429467 if os.path.exists(filepath):
......@@ -467,6 +505,7 @@ class DownloadInfo:
467505 self.pattern = pattern
468506 self.verbose = verbose
469507
508
470509def download_component(download_info):
471510 if not os.path.exists(download_info.tarball_path):
472511 get(
......@@ -477,6 +516,7 @@ def download_component(download_info):
477516 verbose=download_info.verbose,
478517 )
479518
519
480520def unpack_component(download_info):
481521 unpack(
482522 download_info.tarball_path,
......@@ -486,26 +526,30 @@ def unpack_component(download_info):
486526 verbose=download_info.verbose,
487527 )
488528
529
489530class FakeArgs:
490531 """Used for unit tests to avoid updating all call sites"""
532
491533 def __init__(self):
492 self.build = ''
493 self.build_dir = ''
534 self.build = ""
535 self.build_dir = ""
494536 self.clean = False
495537 self.verbose = False
496538 self.json_output = False
497 self.color = 'auto'
498 self.warnings = 'default'
539 self.color = "auto"
540 self.warnings = "default"
541
499542
500543class RustBuild(object):
501544 """Provide all the methods required to build Rust"""
545
502546 def __init__(self, config_toml="", args=None):
503547 if args is None:
504548 args = FakeArgs()
505549 self.git_version = None
506550 self.nix_deps_dir = None
507551 self._should_fix_bins_and_dylibs = None
508 self.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
552 self.rust_root = os.path.abspath(os.path.join(__file__, "../../.."))
509553
510554 self.config_toml = config_toml
511555
......@@ -515,26 +559,28 @@ class RustBuild(object):
515559 self.color = args.color
516560 self.warnings = args.warnings
517561
518 config_verbose_count = self.get_toml('verbose', 'build')
562 config_verbose_count = self.get_toml("verbose", "build")
519563 if config_verbose_count is not None:
520564 self.verbose = max(self.verbose, int(config_verbose_count))
521565
522 self.use_vendored_sources = self.get_toml('vendor', 'build') == 'true'
523 self.use_locked_deps = self.get_toml('locked-deps', 'build') == 'true'
566 self.use_vendored_sources = self.get_toml("vendor", "build") == "true"
567 self.use_locked_deps = self.get_toml("locked-deps", "build") == "true"
524568
525 build_dir = args.build_dir or self.get_toml('build-dir', 'build') or 'build'
569 build_dir = args.build_dir or self.get_toml("build-dir", "build") or "build"
526570 self.build_dir = os.path.abspath(build_dir)
527571
528 self.stage0_data = parse_stage0_file(os.path.join(self.rust_root, "src", "stage0"))
572 self.stage0_data = parse_stage0_file(
573 os.path.join(self.rust_root, "src", "stage0")
574 )
529575 self.stage0_compiler = Stage0Toolchain(
530 self.stage0_data["compiler_date"],
531 self.stage0_data["compiler_version"]
576 self.stage0_data["compiler_date"], self.stage0_data["compiler_version"]
577 )
578 self.download_url = (
579 os.getenv("RUSTUP_DIST_SERVER") or self.stage0_data["dist_server"]
532580 )
533 self.download_url = os.getenv("RUSTUP_DIST_SERVER") or self.stage0_data["dist_server"]
534581
535582 self.build = args.build or self.build_triple()
536583
537
538584 def download_toolchain(self):
539585 """Fetch the build system for Rust, written in Rust
540586
......@@ -550,58 +596,73 @@ class RustBuild(object):
550596
551597 key = self.stage0_compiler.date
552598 is_outdated = self.program_out_of_date(self.rustc_stamp(), key)
553 need_rustc = self.rustc().startswith(bin_root) and (not os.path.exists(self.rustc()) \
554 or is_outdated)
555 need_cargo = self.cargo().startswith(bin_root) and (not os.path.exists(self.cargo()) \
556 or is_outdated)
599 need_rustc = self.rustc().startswith(bin_root) and (
600 not os.path.exists(self.rustc()) or is_outdated
601 )
602 need_cargo = self.cargo().startswith(bin_root) and (
603 not os.path.exists(self.cargo()) or is_outdated
604 )
557605
558606 if need_rustc or need_cargo:
559607 if os.path.exists(bin_root):
560608 # HACK: On Windows, we can't delete rust-analyzer-proc-macro-server while it's
561609 # running. Kill it.
562610 if platform_is_win32():
563 print("Killing rust-analyzer-proc-macro-srv before deleting stage0 toolchain")
564 regex = '{}\\\\(host|{})\\\\stage0\\\\libexec'.format(
565 os.path.basename(self.build_dir),
566 self.build
611 print(
612 "Killing rust-analyzer-proc-macro-srv before deleting stage0 toolchain"
613 )
614 regex = "{}\\\\(host|{})\\\\stage0\\\\libexec".format(
615 os.path.basename(self.build_dir), self.build
567616 )
568617 script = (
569618 # NOTE: can't use `taskkill` or `Get-Process -Name` because they error if
570619 # the server isn't running.
571 'Get-Process | ' +
572 'Where-Object {$_.Name -eq "rust-analyzer-proc-macro-srv"} |' +
573 'Where-Object {{$_.Path -match "{}"}} |'.format(regex) +
574 'Stop-Process'
620 "Get-Process | "
621 + 'Where-Object {$_.Name -eq "rust-analyzer-proc-macro-srv"} |'
622 + 'Where-Object {{$_.Path -match "{}"}} |'.format(regex)
623 + "Stop-Process"
575624 )
576625 run_powershell([script])
577626 shutil.rmtree(bin_root)
578627
579 cache_dst = (self.get_toml('bootstrap-cache-path', 'build') or
580 os.path.join(self.build_dir, "cache"))
628 cache_dst = self.get_toml("bootstrap-cache-path", "build") or os.path.join(
629 self.build_dir, "cache"
630 )
581631
582632 rustc_cache = os.path.join(cache_dst, key)
583633 if not os.path.exists(rustc_cache):
584634 os.makedirs(rustc_cache)
585635
586 tarball_suffix = '.tar.gz' if lzma is None else '.tar.xz'
636 tarball_suffix = ".tar.gz" if lzma is None else ".tar.xz"
587637
588 toolchain_suffix = "{}-{}{}".format(rustc_channel, self.build, tarball_suffix)
638 toolchain_suffix = "{}-{}{}".format(
639 rustc_channel, self.build, tarball_suffix
640 )
589641
590642 tarballs_to_download = []
591643
592644 if need_rustc:
593645 tarballs_to_download.append(
594 ("rust-std-{}".format(toolchain_suffix), "rust-std-{}".format(self.build))
646 (
647 "rust-std-{}".format(toolchain_suffix),
648 "rust-std-{}".format(self.build),
649 )
650 )
651 tarballs_to_download.append(
652 ("rustc-{}".format(toolchain_suffix), "rustc")
595653 )
596 tarballs_to_download.append(("rustc-{}".format(toolchain_suffix), "rustc"))
597654
598655 if need_cargo:
599 tarballs_to_download.append(("cargo-{}".format(toolchain_suffix), "cargo"))
656 tarballs_to_download.append(
657 ("cargo-{}".format(toolchain_suffix), "cargo")
658 )
600659
601660 tarballs_download_info = [
602661 DownloadInfo(
603662 base_download_url=self.download_url,
604 download_path="dist/{}/{}".format(self.stage0_compiler.date, filename),
663 download_path="dist/{}/{}".format(
664 self.stage0_compiler.date, filename
665 ),
605666 bin_root=self.bin_root(),
606667 tarball_path=os.path.join(rustc_cache, filename),
607668 tarball_suffix=tarball_suffix,
......@@ -620,7 +681,11 @@ class RustBuild(object):
620681 # In Python 2.7, Pool cannot be used as a context manager.
621682 pool_size = min(len(tarballs_download_info), get_cpus())
622683 if self.verbose:
623 print('Choosing a pool size of', pool_size, 'for the unpacking of the tarballs')
684 print(
685 "Choosing a pool size of",
686 pool_size,
687 "for the unpacking of the tarballs",
688 )
624689 p = Pool(pool_size)
625690 try:
626691 # FIXME: A cheap workaround for https://github.com/rust-lang/rust/issues/125578,
......@@ -639,7 +704,9 @@ class RustBuild(object):
639704
640705 self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root))
641706 self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root))
642 self.fix_bin_or_dylib("{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root))
707 self.fix_bin_or_dylib(
708 "{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root)
709 )
643710 lib_dir = "{}/lib".format(bin_root)
644711 rustlib_bin_dir = "{}/rustlib/{}/bin".format(lib_dir, self.build)
645712 self.fix_bin_or_dylib("{}/rust-lld".format(rustlib_bin_dir))
......@@ -667,12 +734,15 @@ class RustBuild(object):
667734 def get_answer():
668735 default_encoding = sys.getdefaultencoding()
669736 try:
670 ostype = subprocess.check_output(
671 ['uname', '-s']).strip().decode(default_encoding)
737 ostype = (
738 subprocess.check_output(["uname", "-s"])
739 .strip()
740 .decode(default_encoding)
741 )
672742 except subprocess.CalledProcessError:
673743 return False
674744 except OSError as reason:
675 if getattr(reason, 'winerror', None) is not None:
745 if getattr(reason, "winerror", None) is not None:
676746 return False
677747 raise reason
678748
......@@ -690,17 +760,23 @@ class RustBuild(object):
690760 # The latter one does not exist on NixOS when using tmpfs as root.
691761 try:
692762 with open("/etc/os-release", "r") as f:
693 is_nixos = any(ln.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"')
694 for ln in f)
763 is_nixos = any(
764 ln.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"')
765 for ln in f
766 )
695767 except FileNotFoundError:
696768 is_nixos = False
697769
698770 # If not on NixOS, then warn if user seems to be atop Nix shell
699771 if not is_nixos:
700 in_nix_shell = os.getenv('IN_NIX_SHELL')
772 in_nix_shell = os.getenv("IN_NIX_SHELL")
701773 if in_nix_shell:
702 eprint("The IN_NIX_SHELL environment variable is `{}`;".format(in_nix_shell),
703 "you may need to set `patch-binaries-for-nix=true` in config.toml")
774 eprint(
775 "The IN_NIX_SHELL environment variable is `{}`;".format(
776 in_nix_shell
777 ),
778 "you may need to set `patch-binaries-for-nix=true` in config.toml",
779 )
704780
705781 return is_nixos
706782
......@@ -736,7 +812,7 @@ class RustBuild(object):
736812 # zlib: Needed as a system dependency of `libLLVM-*.so`.
737813 # patchelf: Needed for patching ELF binaries (see doc comment above).
738814 nix_deps_dir = "{}/{}".format(self.build_dir, ".nix-deps")
739 nix_expr = '''
815 nix_expr = """
740816 with (import <nixpkgs> {});
741817 symlinkJoin {
742818 name = "rust-stage0-dependencies";
......@@ -746,24 +822,30 @@ class RustBuild(object):
746822 stdenv.cc.bintools
747823 ];
748824 }
749 '''
825 """
750826 try:
751 subprocess.check_output([
752 "nix-build", "-E", nix_expr, "-o", nix_deps_dir,
753 ])
827 subprocess.check_output(
828 [
829 "nix-build",
830 "-E",
831 nix_expr,
832 "-o",
833 nix_deps_dir,
834 ]
835 )
754836 except subprocess.CalledProcessError as reason:
755837 eprint("WARNING: failed to call nix-build:", reason)
756838 return
757839 self.nix_deps_dir = nix_deps_dir
758840
759841 patchelf = "{}/bin/patchelf".format(nix_deps_dir)
760 rpath_entries = [
761 os.path.join(os.path.realpath(nix_deps_dir), "lib")
762 ]
842 rpath_entries = [os.path.join(os.path.realpath(nix_deps_dir), "lib")]
763843 patchelf_args = ["--add-rpath", ":".join(rpath_entries)]
764844 if ".so" not in fname:
765845 # Finally, set the correct .interp for binaries
766 with open("{}/nix-support/dynamic-linker".format(nix_deps_dir)) as dynamic_linker:
846 with open(
847 "{}/nix-support/dynamic-linker".format(nix_deps_dir)
848 ) as dynamic_linker:
767849 patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()]
768850
769851 try:
......@@ -781,13 +863,13 @@ class RustBuild(object):
781863 >>> expected = os.path.join("build", "host", "stage0", ".rustc-stamp")
782864 >>> assert rb.rustc_stamp() == expected, rb.rustc_stamp()
783865 """
784 return os.path.join(self.bin_root(), '.rustc-stamp')
866 return os.path.join(self.bin_root(), ".rustc-stamp")
785867
786868 def program_out_of_date(self, stamp_path, key):
787869 """Check if the given program stamp is out of date"""
788870 if not os.path.exists(stamp_path) or self.clean:
789871 return True
790 with open(stamp_path, 'r') as stamp:
872 with open(stamp_path, "r") as stamp:
791873 return key != stamp.read()
792874
793875 def bin_root(self):
......@@ -834,11 +916,11 @@ class RustBuild(object):
834916 def get_toml_static(config_toml, key, section=None):
835917 cur_section = None
836918 for line in config_toml.splitlines():
837 section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
919 section_match = re.match(r"^\s*\[(.*)\]\s*$", line)
838920 if section_match is not None:
839921 cur_section = section_match.group(1)
840922
841 match = re.match(r'^{}\s*=(.*)$'.format(key), line)
923 match = re.match(r"^{}\s*=(.*)$".format(key), line)
842924 if match is not None:
843925 value = match.group(1)
844926 if section is None or section == cur_section:
......@@ -847,11 +929,11 @@ class RustBuild(object):
847929
848930 def cargo(self):
849931 """Return config path for cargo"""
850 return self.program_config('cargo')
932 return self.program_config("cargo")
851933
852934 def rustc(self):
853935 """Return config path for rustc"""
854 return self.program_config('rustc')
936 return self.program_config("rustc")
855937
856938 def program_config(self, program):
857939 """Return config path for the given program at the given stage
......@@ -886,12 +968,12 @@ class RustBuild(object):
886968 """
887969 start = line.find('"')
888970 if start != -1:
889 end = start + 1 + line[start + 1:].find('"')
890 return line[start + 1:end]
891 start = line.find('\'')
971 end = start + 1 + line[start + 1 :].find('"')
972 return line[start + 1 : end]
973 start = line.find("'")
892974 if start != -1:
893 end = start + 1 + line[start + 1:].find('\'')
894 return line[start + 1:end]
975 end = start + 1 + line[start + 1 :].find("'")
976 return line[start + 1 : end]
895977 return None
896978
897979 def bootstrap_out(self):
......@@ -941,24 +1023,37 @@ class RustBuild(object):
9411023 del env["CARGO_BUILD_TARGET"]
9421024 env["CARGO_TARGET_DIR"] = build_dir
9431025 env["RUSTC"] = self.rustc()
944 env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
945 (os.pathsep + env["LD_LIBRARY_PATH"]) \
946 if "LD_LIBRARY_PATH" in env else ""
947 env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
948 (os.pathsep + env["DYLD_LIBRARY_PATH"]) \
949 if "DYLD_LIBRARY_PATH" in env else ""
950 env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
951 (os.pathsep + env["LIBRARY_PATH"]) \
952 if "LIBRARY_PATH" in env else ""
953 env["LIBPATH"] = os.path.join(self.bin_root(), "lib") + \
954 (os.pathsep + env["LIBPATH"]) \
955 if "LIBPATH" in env else ""
1026 env["LD_LIBRARY_PATH"] = (
1027 os.path.join(self.bin_root(), "lib") + (os.pathsep + env["LD_LIBRARY_PATH"])
1028 if "LD_LIBRARY_PATH" in env
1029 else ""
1030 )
1031 env["DYLD_LIBRARY_PATH"] = (
1032 os.path.join(self.bin_root(), "lib")
1033 + (os.pathsep + env["DYLD_LIBRARY_PATH"])
1034 if "DYLD_LIBRARY_PATH" in env
1035 else ""
1036 )
1037 env["LIBRARY_PATH"] = (
1038 os.path.join(self.bin_root(), "lib") + (os.pathsep + env["LIBRARY_PATH"])
1039 if "LIBRARY_PATH" in env
1040 else ""
1041 )
1042 env["LIBPATH"] = (
1043 os.path.join(self.bin_root(), "lib") + (os.pathsep + env["LIBPATH"])
1044 if "LIBPATH" in env
1045 else ""
1046 )
9561047
9571048 # Export Stage0 snapshot compiler related env variables
9581049 build_section = "target.{}".format(self.build)
9591050 host_triple_sanitized = self.build.replace("-", "_")
9601051 var_data = {
961 "CC": "cc", "CXX": "cxx", "LD": "linker", "AR": "ar", "RANLIB": "ranlib"
1052 "CC": "cc",
1053 "CXX": "cxx",
1054 "LD": "linker",
1055 "AR": "ar",
1056 "RANLIB": "ranlib",
9621057 }
9631058 for var_name, toml_key in var_data.items():
9641059 toml_val = self.get_toml(toml_key, build_section)
......@@ -1023,14 +1118,16 @@ class RustBuild(object):
10231118 if "RUSTFLAGS_BOOTSTRAP" in env:
10241119 env["RUSTFLAGS"] += " " + env["RUSTFLAGS_BOOTSTRAP"]
10251120
1026 env["PATH"] = os.path.join(self.bin_root(), "bin") + \
1027 os.pathsep + env["PATH"]
1121 env["PATH"] = os.path.join(self.bin_root(), "bin") + os.pathsep + env["PATH"]
10281122 if not os.path.isfile(self.cargo()):
1029 raise Exception("no cargo executable found at `{}`".format(
1030 self.cargo()))
1031 args = [self.cargo(), "build", "--manifest-path",
1032 os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"),
1033 "-Zroot-dir="+self.rust_root]
1123 raise Exception("no cargo executable found at `{}`".format(self.cargo()))
1124 args = [
1125 self.cargo(),
1126 "build",
1127 "--manifest-path",
1128 os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"),
1129 "-Zroot-dir=" + self.rust_root,
1130 ]
10341131 args.extend("--verbose" for _ in range(self.verbose))
10351132 if self.use_locked_deps:
10361133 args.append("--locked")
......@@ -1058,83 +1155,103 @@ class RustBuild(object):
10581155 Note that `default_build_triple` is moderately expensive,
10591156 so use `self.build` where possible.
10601157 """
1061 config = self.get_toml('build')
1158 config = self.get_toml("build")
10621159 return config or default_build_triple(self.verbose)
10631160
10641161 def check_vendored_status(self):
10651162 """Check that vendoring is configured properly"""
10661163 # keep this consistent with the equivalent check in bootstrap:
10671164 # https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/lib.rs#L399-L405
1068 if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
1165 if "SUDO_USER" in os.environ and not self.use_vendored_sources:
10691166 if os.getuid() == 0:
10701167 self.use_vendored_sources = True
1071 eprint('INFO: looks like you\'re trying to run this command as root')
1072 eprint(' and so in order to preserve your $HOME this will now')
1073 eprint(' use vendored sources by default.')
1168 eprint("INFO: looks like you're trying to run this command as root")
1169 eprint(" and so in order to preserve your $HOME this will now")
1170 eprint(" use vendored sources by default.")
10741171
1075 cargo_dir = os.path.join(self.rust_root, '.cargo')
1172 cargo_dir = os.path.join(self.rust_root, ".cargo")
1173 url = "https://ci-artifacts.rust-lang.org/rustc-builds/<commit>/rustc-nightly-src.tar.xz"
10761174 if self.use_vendored_sources:
1077 vendor_dir = os.path.join(self.rust_root, 'vendor')
1175 vendor_dir = os.path.join(self.rust_root, "vendor")
10781176 if not os.path.exists(vendor_dir):
1079 eprint('ERROR: vendoring required, but vendor directory does not exist.')
1080 eprint(' Run `x.py vendor` to initialize the vendor directory.')
1081 eprint(' Alternatively, use the pre-vendored `rustc-src` dist component.')
1082 eprint(' To get a stable/beta/nightly version, download it from: ')
1083 eprint(' '
1084 'https://forge.rust-lang.org/infra/other-installation-methods.html#source-code')
1085 eprint(' To get a specific commit version, download it using the below URL,')
1086 eprint(' replacing <commit> with a specific commit checksum: ')
1087 eprint(' '
1088 'https://ci-artifacts.rust-lang.org/rustc-builds/<commit>/rustc-nightly-src.tar.xz')
1089 eprint(' Once you have the source downloaded, place the vendor directory')
1090 eprint(' from the archive in the root of the rust project.')
1177 eprint(
1178 "ERROR: vendoring required, but vendor directory does not exist."
1179 )
1180 eprint(" Run `x.py vendor` to initialize the vendor directory.")
1181 eprint(
1182 " Alternatively, use the pre-vendored `rustc-src` dist component."
1183 )
1184 eprint(
1185 " To get a stable/beta/nightly version, download it from: "
1186 )
1187 eprint(
1188 " "
1189 "https://forge.rust-lang.org/infra/other-installation-methods.html#source-code"
1190 )
1191 eprint(
1192 " To get a specific commit version, download it using the below URL,"
1193 )
1194 eprint(" replacing <commit> with a specific commit checksum: ")
1195 eprint(" ", url)
1196 eprint(
1197 " Once you have the source downloaded, place the vendor directory"
1198 )
1199 eprint(" from the archive in the root of the rust project.")
10911200 raise Exception("{} not found".format(vendor_dir))
10921201
10931202 if not os.path.exists(cargo_dir):
1094 eprint('ERROR: vendoring required, but .cargo/config does not exist.')
1203 eprint("ERROR: vendoring required, but .cargo/config does not exist.")
10951204 raise Exception("{} not found".format(cargo_dir))
10961205
1206
10971207def parse_args(args):
10981208 """Parse the command line arguments that the python script needs."""
10991209 parser = argparse.ArgumentParser(add_help=False)
1100 parser.add_argument('-h', '--help', action='store_true')
1101 parser.add_argument('--config')
1102 parser.add_argument('--build-dir')
1103 parser.add_argument('--build')
1104 parser.add_argument('--color', choices=['always', 'never', 'auto'])
1105 parser.add_argument('--clean', action='store_true')
1106 parser.add_argument('--json-output', action='store_true')
1107 parser.add_argument('--warnings', choices=['deny', 'warn', 'default'], default='default')
1108 parser.add_argument('-v', '--verbose', action='count', default=0)
1210 parser.add_argument("-h", "--help", action="store_true")
1211 parser.add_argument("--config")
1212 parser.add_argument("--build-dir")
1213 parser.add_argument("--build")
1214 parser.add_argument("--color", choices=["always", "never", "auto"])
1215 parser.add_argument("--clean", action="store_true")
1216 parser.add_argument("--json-output", action="store_true")
1217 parser.add_argument(
1218 "--warnings", choices=["deny", "warn", "default"], default="default"
1219 )
1220 parser.add_argument("-v", "--verbose", action="count", default=0)
11091221
11101222 return parser.parse_known_args(args)[0]
11111223
1224
11121225def parse_stage0_file(path):
11131226 result = {}
1114 with open(path, 'r') as file:
1227 with open(path, "r") as file:
11151228 for line in file:
11161229 line = line.strip()
1117 if line and not line.startswith('#'):
1118 key, value = line.split('=', 1)
1230 if line and not line.startswith("#"):
1231 key, value = line.split("=", 1)
11191232 result[key.strip()] = value.strip()
11201233 return result
11211234
1235
11221236def bootstrap(args):
11231237 """Configure, fetch, build and run the initial bootstrap"""
1124 rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
1238 rust_root = os.path.abspath(os.path.join(__file__, "../../.."))
11251239
1126 if not os.path.exists(os.path.join(rust_root, '.git')) and \
1127 os.path.exists(os.path.join(rust_root, '.github')):
1128 eprint("warn: Looks like you are trying to bootstrap Rust from a source that is neither a "
1129 "git clone nor distributed tarball.\nThis build may fail due to missing submodules "
1130 "unless you put them in place manually.")
1240 if not os.path.exists(os.path.join(rust_root, ".git")) and os.path.exists(
1241 os.path.join(rust_root, ".github")
1242 ):
1243 eprint(
1244 "warn: Looks like you are trying to bootstrap Rust from a source that is neither a "
1245 "git clone nor distributed tarball.\nThis build may fail due to missing submodules "
1246 "unless you put them in place manually."
1247 )
11311248
11321249 # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`,
11331250 # then `config.toml` in the root directory.
1134 toml_path = args.config or os.getenv('RUST_BOOTSTRAP_CONFIG')
1251 toml_path = args.config or os.getenv("RUST_BOOTSTRAP_CONFIG")
11351252 using_default_path = toml_path is None
11361253 if using_default_path:
1137 toml_path = 'config.toml'
1254 toml_path = "config.toml"
11381255 if not os.path.exists(toml_path):
11391256 toml_path = os.path.join(rust_root, toml_path)
11401257
......@@ -1144,23 +1261,23 @@ def bootstrap(args):
11441261 with open(toml_path) as config:
11451262 config_toml = config.read()
11461263 else:
1147 config_toml = ''
1264 config_toml = ""
11481265
1149 profile = RustBuild.get_toml_static(config_toml, 'profile')
1266 profile = RustBuild.get_toml_static(config_toml, "profile")
11501267 if profile is not None:
11511268 # Allows creating alias for profile names, allowing
11521269 # profiles to be renamed while maintaining back compatibility
11531270 # Keep in sync with `profile_aliases` in config.rs
1154 profile_aliases = {
1155 "user": "dist"
1156 }
1157 include_file = 'config.{}.toml'.format(profile_aliases.get(profile) or profile)
1158 include_dir = os.path.join(rust_root, 'src', 'bootstrap', 'defaults')
1271 profile_aliases = {"user": "dist"}
1272 include_file = "config.{}.toml".format(profile_aliases.get(profile) or profile)
1273 include_dir = os.path.join(rust_root, "src", "bootstrap", "defaults")
11591274 include_path = os.path.join(include_dir, include_file)
11601275
11611276 if not os.path.exists(include_path):
1162 raise Exception("Unrecognized config profile '{}'. Check src/bootstrap/defaults"
1163 " for available options.".format(profile))
1277 raise Exception(
1278 "Unrecognized config profile '{}'. Check src/bootstrap/defaults"
1279 " for available options.".format(profile)
1280 )
11641281
11651282 # HACK: This works because `self.get_toml()` returns the first match it finds for a
11661283 # specific key, so appending our defaults at the end allows the user to override them
......@@ -1196,8 +1313,8 @@ def main():
11961313 start_time = time()
11971314
11981315 # x.py help <cmd> ...
1199 if len(sys.argv) > 1 and sys.argv[1] == 'help':
1200 sys.argv[1] = '-h'
1316 if len(sys.argv) > 1 and sys.argv[1] == "help":
1317 sys.argv[1] = "-h"
12011318
12021319 args = parse_args(sys.argv)
12031320 help_triggered = args.help or len(sys.argv) == 1
......@@ -1207,14 +1324,15 @@ def main():
12071324 if help_triggered:
12081325 eprint(
12091326 "INFO: Downloading and building bootstrap before processing --help command.\n"
1210 " See src/bootstrap/README.md for help with common commands.")
1327 " See src/bootstrap/README.md for help with common commands."
1328 )
12111329
12121330 exit_code = 0
12131331 success_word = "successfully"
12141332 try:
12151333 bootstrap(args)
12161334 except (SystemExit, KeyboardInterrupt) as error:
1217 if hasattr(error, 'code') and isinstance(error.code, int):
1335 if hasattr(error, "code") and isinstance(error.code, int):
12181336 exit_code = error.code
12191337 else:
12201338 exit_code = 1
......@@ -1222,9 +1340,14 @@ def main():
12221340 success_word = "unsuccessfully"
12231341
12241342 if not help_triggered:
1225 eprint("Build completed", success_word, "in", format_build_time(time() - start_time))
1343 eprint(
1344 "Build completed",
1345 success_word,
1346 "in",
1347 format_build_time(time() - start_time),
1348 )
12261349 sys.exit(exit_code)
12271350
12281351
1229if __name__ == '__main__':
1352if __name__ == "__main__":
12301353 main()
src/bootstrap/bootstrap_test.py+29-13
......@@ -16,8 +16,9 @@ from shutil import rmtree
1616bootstrap_dir = os.path.dirname(os.path.abspath(__file__))
1717# For the import below, have Python search in src/bootstrap first.
1818sys.path.insert(0, bootstrap_dir)
19import bootstrap # noqa: E402
20import configure # noqa: E402
19import bootstrap # noqa: E402
20import configure # noqa: E402
21
2122
2223def serialize_and_parse(configure_args, bootstrap_args=None):
2324 from io import StringIO
......@@ -32,15 +33,20 @@ def serialize_and_parse(configure_args, bootstrap_args=None):
3233
3334 try:
3435 import tomllib
36
3537 # Verify this is actually valid TOML.
3638 tomllib.loads(build.config_toml)
3739 except ImportError:
38 print("WARNING: skipping TOML validation, need at least python 3.11", file=sys.stderr)
40 print(
41 "WARNING: skipping TOML validation, need at least python 3.11",
42 file=sys.stderr,
43 )
3944 return build
4045
4146
4247class VerifyTestCase(unittest.TestCase):
4348 """Test Case for verify"""
49
4450 def setUp(self):
4551 self.container = tempfile.mkdtemp()
4652 self.src = os.path.join(self.container, "src.txt")
......@@ -68,14 +74,14 @@ class VerifyTestCase(unittest.TestCase):
6874
6975class ProgramOutOfDate(unittest.TestCase):
7076 """Test if a program is out of date"""
77
7178 def setUp(self):
7279 self.container = tempfile.mkdtemp()
7380 os.mkdir(os.path.join(self.container, "stage0"))
7481 self.build = bootstrap.RustBuild()
7582 self.build.date = "2017-06-15"
7683 self.build.build_dir = self.container
77 self.rustc_stamp_path = os.path.join(self.container, "stage0",
78 ".rustc-stamp")
84 self.rustc_stamp_path = os.path.join(self.container, "stage0", ".rustc-stamp")
7985 self.key = self.build.date + str(None)
8086
8187 def tearDown(self):
......@@ -97,11 +103,14 @@ class ProgramOutOfDate(unittest.TestCase):
97103 """Return False both dates match"""
98104 with open(self.rustc_stamp_path, "w") as rustc_stamp:
99105 rustc_stamp.write("2017-06-15None")
100 self.assertFalse(self.build.program_out_of_date(self.rustc_stamp_path, self.key))
106 self.assertFalse(
107 self.build.program_out_of_date(self.rustc_stamp_path, self.key)
108 )
101109
102110
103111class ParseArgsInConfigure(unittest.TestCase):
104112 """Test if `parse_args` function in `configure.py` works properly"""
113
105114 @patch("configure.err")
106115 def test_unknown_args(self, err):
107116 # It should be print an error message if the argument doesn't start with '--'
......@@ -148,28 +157,35 @@ class ParseArgsInConfigure(unittest.TestCase):
148157
149158class GenerateAndParseConfig(unittest.TestCase):
150159 """Test that we can serialize and deserialize a config.toml file"""
160
151161 def test_no_args(self):
152162 build = serialize_and_parse([])
153 self.assertEqual(build.get_toml("profile"), 'dist')
163 self.assertEqual(build.get_toml("profile"), "dist")
154164 self.assertIsNone(build.get_toml("llvm.download-ci-llvm"))
155165
156166 def test_set_section(self):
157167 build = serialize_and_parse(["--set", "llvm.download-ci-llvm"])
158 self.assertEqual(build.get_toml("download-ci-llvm", section="llvm"), 'true')
168 self.assertEqual(build.get_toml("download-ci-llvm", section="llvm"), "true")
159169
160170 def test_set_target(self):
161171 build = serialize_and_parse(["--set", "target.x86_64-unknown-linux-gnu.cc=gcc"])
162 self.assertEqual(build.get_toml("cc", section="target.x86_64-unknown-linux-gnu"), 'gcc')
172 self.assertEqual(
173 build.get_toml("cc", section="target.x86_64-unknown-linux-gnu"), "gcc"
174 )
163175
164176 def test_set_top_level(self):
165177 build = serialize_and_parse(["--set", "profile=compiler"])
166 self.assertEqual(build.get_toml("profile"), 'compiler')
178 self.assertEqual(build.get_toml("profile"), "compiler")
167179
168180 def test_set_codegen_backends(self):
169181 build = serialize_and_parse(["--set", "rust.codegen-backends=cranelift"])
170 self.assertNotEqual(build.config_toml.find("codegen-backends = ['cranelift']"), -1)
182 self.assertNotEqual(
183 build.config_toml.find("codegen-backends = ['cranelift']"), -1
184 )
171185 build = serialize_and_parse(["--set", "rust.codegen-backends=cranelift,llvm"])
172 self.assertNotEqual(build.config_toml.find("codegen-backends = ['cranelift', 'llvm']"), -1)
186 self.assertNotEqual(
187 build.config_toml.find("codegen-backends = ['cranelift', 'llvm']"), -1
188 )
173189 build = serialize_and_parse(["--enable-full-tools"])
174190 self.assertNotEqual(build.config_toml.find("codegen-backends = ['llvm']"), -1)
175191
......@@ -223,7 +239,7 @@ class BuildBootstrap(unittest.TestCase):
223239 self.assertTrue("--timings" in args)
224240
225241 def test_warnings(self):
226 for toml_warnings in ['false', 'true', None]:
242 for toml_warnings in ["false", "true", None]:
227243 configure_args = []
228244 if toml_warnings is not None:
229245 configure_args = ["--set", "rust.deny-warnings=" + toml_warnings]
src/bootstrap/configure.py+350-169
......@@ -6,11 +6,12 @@ from __future__ import absolute_import, division, print_function
66import shlex
77import sys
88import os
9
910rust_dir = os.path.dirname(os.path.abspath(__file__))
1011rust_dir = os.path.dirname(rust_dir)
1112rust_dir = os.path.dirname(rust_dir)
1213sys.path.append(os.path.join(rust_dir, "src", "bootstrap"))
13import bootstrap # noqa: E402
14import bootstrap # noqa: E402
1415
1516
1617class Option(object):
......@@ -32,26 +33,62 @@ def v(*args):
3233 options.append(Option(*args, value=True))
3334
3435
35o("debug", "rust.debug", "enables debugging environment; does not affect optimization of bootstrapped code")
36o(
37 "debug",
38 "rust.debug",
39 "enables debugging environment; does not affect optimization of bootstrapped code",
40)
3641o("docs", "build.docs", "build standard library documentation")
3742o("compiler-docs", "build.compiler-docs", "build compiler documentation")
3843o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
3944o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")
40o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
45o(
46 "ccache",
47 "llvm.ccache",
48 "invoke gcc/clang via ccache to reuse object files between builds",
49)
4150o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
4251o("local-rust", None, "use an installed rustc rather than downloading a snapshot")
4352v("local-rust-root", None, "set prefix for local rust binary")
44o("local-rebuild", "build.local-rebuild", "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version")
45o("llvm-static-stdcpp", "llvm.static-libstdcpp", "statically link to libstdc++ for LLVM")
46o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)")
53o(
54 "local-rebuild",
55 "build.local-rebuild",
56 "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version",
57)
58o(
59 "llvm-static-stdcpp",
60 "llvm.static-libstdcpp",
61 "statically link to libstdc++ for LLVM",
62)
63o(
64 "llvm-link-shared",
65 "llvm.link-shared",
66 "prefer shared linking to LLVM (llvm-config --link-shared)",
67)
4768o("rpath", "rust.rpath", "build rpaths into rustc itself")
4869o("codegen-tests", "rust.codegen-tests", "run the tests/codegen tests")
49o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
70o(
71 "ninja",
72 "llvm.ninja",
73 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)",
74)
5075o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
5176o("vendor", "build.vendor", "enable usage of vendored Rust crates")
52o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, dfsan, lsan, msan, tsan, hwasan)")
53o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
54o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo")
77o(
78 "sanitizers",
79 "build.sanitizers",
80 "build the sanitizer runtimes (asan, dfsan, lsan, msan, tsan, hwasan)",
81)
82o(
83 "dist-src",
84 "rust.dist-src",
85 "when building tarballs enables building a source tarball",
86)
87o(
88 "cargo-native-static",
89 "build.cargo-native-static",
90 "static native libraries in cargo",
91)
5592o("profiler", "build.profiler", "build the profiler runtime")
5693o("full-tools", None, "enable all tools")
5794o("lld", "rust.lld", "build lld")
......@@ -59,7 +96,11 @@ o("llvm-bitcode-linker", "rust.llvm-bitcode-linker", "build llvm bitcode linker"
5996o("clang", "llvm.clang", "build clang")
6097o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++")
6198o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard")
62o("patch-binaries-for-nix", "build.patch-binaries-for-nix", "whether patch binaries for usage with Nix toolchains")
99o(
100 "patch-binaries-for-nix",
101 "build.patch-binaries-for-nix",
102 "whether patch binaries for usage with Nix toolchains",
103)
63104o("new-symbol-mangling", "rust.new-symbol-mangling", "use symbol-mangling-version v0")
64105
65106v("llvm-cflags", "llvm.cflags", "build LLVM with these extra compiler flags")
......@@ -76,16 +117,48 @@ o("llvm-enzyme", "llvm.enzyme", "build LLVM with enzyme")
76117o("llvm-offload", "llvm.offload", "build LLVM with gpu offload support")
77118o("llvm-plugins", "llvm.plugins", "build LLVM with plugin interface")
78119o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")
79o("debug-assertions-std", "rust.debug-assertions-std", "build the standard library with debugging assertions")
120o(
121 "debug-assertions-std",
122 "rust.debug-assertions-std",
123 "build the standard library with debugging assertions",
124)
80125o("overflow-checks", "rust.overflow-checks", "build with overflow checks")
81o("overflow-checks-std", "rust.overflow-checks-std", "build the standard library with overflow checks")
82o("llvm-release-debuginfo", "llvm.release-debuginfo", "build LLVM with debugger metadata")
126o(
127 "overflow-checks-std",
128 "rust.overflow-checks-std",
129 "build the standard library with overflow checks",
130)
131o(
132 "llvm-release-debuginfo",
133 "llvm.release-debuginfo",
134 "build LLVM with debugger metadata",
135)
83136v("debuginfo-level", "rust.debuginfo-level", "debuginfo level for Rust code")
84v("debuginfo-level-rustc", "rust.debuginfo-level-rustc", "debuginfo level for the compiler")
85v("debuginfo-level-std", "rust.debuginfo-level-std", "debuginfo level for the standard library")
86v("debuginfo-level-tools", "rust.debuginfo-level-tools", "debuginfo level for the tools")
87v("debuginfo-level-tests", "rust.debuginfo-level-tests", "debuginfo level for the test suites run with compiletest")
88v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file")
137v(
138 "debuginfo-level-rustc",
139 "rust.debuginfo-level-rustc",
140 "debuginfo level for the compiler",
141)
142v(
143 "debuginfo-level-std",
144 "rust.debuginfo-level-std",
145 "debuginfo level for the standard library",
146)
147v(
148 "debuginfo-level-tools",
149 "rust.debuginfo-level-tools",
150 "debuginfo level for the tools",
151)
152v(
153 "debuginfo-level-tests",
154 "rust.debuginfo-level-tests",
155 "debuginfo level for the test suites run with compiletest",
156)
157v(
158 "save-toolstates",
159 "rust.save-toolstates",
160 "save build and test status of external tools into this file",
161)
89162
90163v("prefix", "install.prefix", "set installation prefix")
91164v("localstatedir", "install.localstatedir", "local state directory")
......@@ -102,50 +175,117 @@ v("llvm-config", None, "set path to llvm-config")
102175v("llvm-filecheck", None, "set path to LLVM's FileCheck utility")
103176v("python", "build.python", "set path to python")
104177v("android-ndk", "build.android-ndk", "set path to Android NDK")
105v("musl-root", "target.x86_64-unknown-linux-musl.musl-root",
106 "MUSL root installation directory (deprecated)")
107v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root",
108 "x86_64-unknown-linux-musl install directory")
109v("musl-root-i586", "target.i586-unknown-linux-musl.musl-root",
110 "i586-unknown-linux-musl install directory")
111v("musl-root-i686", "target.i686-unknown-linux-musl.musl-root",
112 "i686-unknown-linux-musl install directory")
113v("musl-root-arm", "target.arm-unknown-linux-musleabi.musl-root",
114 "arm-unknown-linux-musleabi install directory")
115v("musl-root-armhf", "target.arm-unknown-linux-musleabihf.musl-root",
116 "arm-unknown-linux-musleabihf install directory")
117v("musl-root-armv5te", "target.armv5te-unknown-linux-musleabi.musl-root",
118 "armv5te-unknown-linux-musleabi install directory")
119v("musl-root-armv7", "target.armv7-unknown-linux-musleabi.musl-root",
120 "armv7-unknown-linux-musleabi install directory")
121v("musl-root-armv7hf", "target.armv7-unknown-linux-musleabihf.musl-root",
122 "armv7-unknown-linux-musleabihf install directory")
123v("musl-root-aarch64", "target.aarch64-unknown-linux-musl.musl-root",
124 "aarch64-unknown-linux-musl install directory")
125v("musl-root-mips", "target.mips-unknown-linux-musl.musl-root",
126 "mips-unknown-linux-musl install directory")
127v("musl-root-mipsel", "target.mipsel-unknown-linux-musl.musl-root",
128 "mipsel-unknown-linux-musl install directory")
129v("musl-root-mips64", "target.mips64-unknown-linux-muslabi64.musl-root",
130 "mips64-unknown-linux-muslabi64 install directory")
131v("musl-root-mips64el", "target.mips64el-unknown-linux-muslabi64.musl-root",
132 "mips64el-unknown-linux-muslabi64 install directory")
133v("musl-root-riscv32gc", "target.riscv32gc-unknown-linux-musl.musl-root",
134 "riscv32gc-unknown-linux-musl install directory")
135v("musl-root-riscv64gc", "target.riscv64gc-unknown-linux-musl.musl-root",
136 "riscv64gc-unknown-linux-musl install directory")
137v("musl-root-loongarch64", "target.loongarch64-unknown-linux-musl.musl-root",
138 "loongarch64-unknown-linux-musl install directory")
139v("qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs",
140 "rootfs in qemu testing, you probably don't want to use this")
141v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs",
142 "rootfs in qemu testing, you probably don't want to use this")
143v("qemu-riscv64-rootfs", "target.riscv64gc-unknown-linux-gnu.qemu-rootfs",
144 "rootfs in qemu testing, you probably don't want to use this")
145v("experimental-targets", "llvm.experimental-targets",
146 "experimental LLVM targets to build")
178v(
179 "musl-root",
180 "target.x86_64-unknown-linux-musl.musl-root",
181 "MUSL root installation directory (deprecated)",
182)
183v(
184 "musl-root-x86_64",
185 "target.x86_64-unknown-linux-musl.musl-root",
186 "x86_64-unknown-linux-musl install directory",
187)
188v(
189 "musl-root-i586",
190 "target.i586-unknown-linux-musl.musl-root",
191 "i586-unknown-linux-musl install directory",
192)
193v(
194 "musl-root-i686",
195 "target.i686-unknown-linux-musl.musl-root",
196 "i686-unknown-linux-musl install directory",
197)
198v(
199 "musl-root-arm",
200 "target.arm-unknown-linux-musleabi.musl-root",
201 "arm-unknown-linux-musleabi install directory",
202)
203v(
204 "musl-root-armhf",
205 "target.arm-unknown-linux-musleabihf.musl-root",
206 "arm-unknown-linux-musleabihf install directory",
207)
208v(
209 "musl-root-armv5te",
210 "target.armv5te-unknown-linux-musleabi.musl-root",
211 "armv5te-unknown-linux-musleabi install directory",
212)
213v(
214 "musl-root-armv7",
215 "target.armv7-unknown-linux-musleabi.musl-root",
216 "armv7-unknown-linux-musleabi install directory",
217)
218v(
219 "musl-root-armv7hf",
220 "target.armv7-unknown-linux-musleabihf.musl-root",
221 "armv7-unknown-linux-musleabihf install directory",
222)
223v(
224 "musl-root-aarch64",
225 "target.aarch64-unknown-linux-musl.musl-root",
226 "aarch64-unknown-linux-musl install directory",
227)
228v(
229 "musl-root-mips",
230 "target.mips-unknown-linux-musl.musl-root",
231 "mips-unknown-linux-musl install directory",
232)
233v(
234 "musl-root-mipsel",
235 "target.mipsel-unknown-linux-musl.musl-root",
236 "mipsel-unknown-linux-musl install directory",
237)
238v(
239 "musl-root-mips64",
240 "target.mips64-unknown-linux-muslabi64.musl-root",
241 "mips64-unknown-linux-muslabi64 install directory",
242)
243v(
244 "musl-root-mips64el",
245 "target.mips64el-unknown-linux-muslabi64.musl-root",
246 "mips64el-unknown-linux-muslabi64 install directory",
247)
248v(
249 "musl-root-riscv32gc",
250 "target.riscv32gc-unknown-linux-musl.musl-root",
251 "riscv32gc-unknown-linux-musl install directory",
252)
253v(
254 "musl-root-riscv64gc",
255 "target.riscv64gc-unknown-linux-musl.musl-root",
256 "riscv64gc-unknown-linux-musl install directory",
257)
258v(
259 "musl-root-loongarch64",
260 "target.loongarch64-unknown-linux-musl.musl-root",
261 "loongarch64-unknown-linux-musl install directory",
262)
263v(
264 "qemu-armhf-rootfs",
265 "target.arm-unknown-linux-gnueabihf.qemu-rootfs",
266 "rootfs in qemu testing, you probably don't want to use this",
267)
268v(
269 "qemu-aarch64-rootfs",
270 "target.aarch64-unknown-linux-gnu.qemu-rootfs",
271 "rootfs in qemu testing, you probably don't want to use this",
272)
273v(
274 "qemu-riscv64-rootfs",
275 "target.riscv64gc-unknown-linux-gnu.qemu-rootfs",
276 "rootfs in qemu testing, you probably don't want to use this",
277)
278v(
279 "experimental-targets",
280 "llvm.experimental-targets",
281 "experimental LLVM targets to build",
282)
147283v("release-channel", "rust.channel", "the name of the release channel to build")
148v("release-description", "rust.description", "optional descriptive string for version output")
284v(
285 "release-description",
286 "rust.description",
287 "optional descriptive string for version output",
288)
149289v("dist-compression-formats", None, "List of compression formats to use")
150290
151291# Used on systems where "cc" is unavailable
......@@ -154,7 +294,11 @@ v("default-linker", "rust.default-linker", "the default linker")
154294# Many of these are saved below during the "writing configuration" step
155295# (others are conditionally saved).
156296o("manage-submodules", "build.submodules", "let the build manage the git submodules")
157o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two (not recommended except for testing reproducible builds)")
297o(
298 "full-bootstrap",
299 "build.full-bootstrap",
300 "build three compilers instead of two (not recommended except for testing reproducible builds)",
301)
158302o("extended", "build.extended", "build an extended rust tool set")
159303
160304v("bootstrap-cache-path", None, "use provided path for the bootstrap cache")
......@@ -165,8 +309,16 @@ v("host", None, "List of GNUs ./configure syntax LLVM host triples")
165309v("target", None, "List of GNUs ./configure syntax LLVM target triples")
166310
167311# Options specific to this configure script
168o("option-checking", None, "complain about unrecognized options in this configure script")
169o("verbose-configure", None, "don't truncate options when printing them in this configure script")
312o(
313 "option-checking",
314 None,
315 "complain about unrecognized options in this configure script",
316)
317o(
318 "verbose-configure",
319 None,
320 "don't truncate options when printing them in this configure script",
321)
170322v("set", None, "set arbitrary key/value pairs in TOML configuration")
171323
172324
......@@ -178,39 +330,42 @@ def err(msg):
178330 print("\nconfigure: ERROR: " + msg + "\n")
179331 sys.exit(1)
180332
333
181334def is_value_list(key):
182335 for option in options:
183 if option.name == key and option.desc.startswith('List of'):
336 if option.name == key and option.desc.startswith("List of"):
184337 return True
185338 return False
186339
187if '--help' in sys.argv or '-h' in sys.argv:
188 print('Usage: ./configure [options]')
189 print('')
190 print('Options')
340
341if "--help" in sys.argv or "-h" in sys.argv:
342 print("Usage: ./configure [options]")
343 print("")
344 print("Options")
191345 for option in options:
192 if 'android' in option.name:
346 if "android" in option.name:
193347 # no one needs to know about these obscure options
194348 continue
195349 if option.value:
196 print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc))
350 print("\t{:30} {}".format("--{}=VAL".format(option.name), option.desc))
197351 else:
198 print('\t--enable-{:25} OR --disable-{}'.format(option.name, option.name))
199 print('\t\t' + option.desc)
200 print('')
201 print('This configure script is a thin configuration shim over the true')
202 print('configuration system, `config.toml`. You can explore the comments')
203 print('in `config.example.toml` next to this configure script to see')
204 print('more information about what each option is. Additionally you can')
205 print('pass `--set` as an argument to set arbitrary key/value pairs')
206 print('in the TOML configuration if desired')
207 print('')
208 print('Also note that all options which take `--enable` can similarly')
209 print('be passed with `--disable-foo` to forcibly disable the option')
352 print("\t--enable-{:25} OR --disable-{}".format(option.name, option.name))
353 print("\t\t" + option.desc)
354 print("")
355 print("This configure script is a thin configuration shim over the true")
356 print("configuration system, `config.toml`. You can explore the comments")
357 print("in `config.example.toml` next to this configure script to see")
358 print("more information about what each option is. Additionally you can")
359 print("pass `--set` as an argument to set arbitrary key/value pairs")
360 print("in the TOML configuration if desired")
361 print("")
362 print("Also note that all options which take `--enable` can similarly")
363 print("be passed with `--disable-foo` to forcibly disable the option")
210364 sys.exit(0)
211365
212366VERBOSE = False
213367
368
214369# Parse all command line arguments into one of these three lists, handling
215370# boolean and value-based options separately
216371def parse_args(args):
......@@ -222,7 +377,7 @@ def parse_args(args):
222377 while i < len(args):
223378 arg = args[i]
224379 i += 1
225 if not arg.startswith('--'):
380 if not arg.startswith("--"):
226381 unknown_args.append(arg)
227382 continue
228383
......@@ -230,7 +385,7 @@ def parse_args(args):
230385 for option in options:
231386 value = None
232387 if option.value:
233 keyval = arg[2:].split('=', 1)
388 keyval = arg[2:].split("=", 1)
234389 key = keyval[0]
235390 if option.name != key:
236391 continue
......@@ -244,9 +399,9 @@ def parse_args(args):
244399 need_value_args.append(arg)
245400 continue
246401 else:
247 if arg[2:] == 'enable-' + option.name:
402 if arg[2:] == "enable-" + option.name:
248403 value = True
249 elif arg[2:] == 'disable-' + option.name:
404 elif arg[2:] == "disable-" + option.name:
250405 value = False
251406 else:
252407 continue
......@@ -263,8 +418,9 @@ def parse_args(args):
263418 # NOTE: here and a few other places, we use [-1] to apply the *last* value
264419 # passed. But if option-checking is enabled, then the known_args loop will
265420 # also assert that options are only passed once.
266 option_checking = ('option-checking' not in known_args
267 or known_args['option-checking'][-1][1])
421 option_checking = (
422 "option-checking" not in known_args or known_args["option-checking"][-1][1]
423 )
268424 if option_checking:
269425 if len(unknown_args) > 0:
270426 err("Option '" + unknown_args[0] + "' is not recognized")
......@@ -272,18 +428,18 @@ def parse_args(args):
272428 err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
273429
274430 global VERBOSE
275 VERBOSE = 'verbose-configure' in known_args
431 VERBOSE = "verbose-configure" in known_args
276432
277433 config = {}
278434
279 set('build.configure-args', args, config)
435 set("build.configure-args", args, config)
280436 apply_args(known_args, option_checking, config)
281437 return parse_example_config(known_args, config)
282438
283439
284440def build(known_args):
285 if 'build' in known_args:
286 return known_args['build'][-1][1]
441 if "build" in known_args:
442 return known_args["build"][-1][1]
287443 return bootstrap.default_build_triple(verbose=False)
288444
289445
......@@ -291,7 +447,7 @@ def set(key, value, config):
291447 if isinstance(value, list):
292448 # Remove empty values, which value.split(',') tends to generate and
293449 # replace single quotes for double quotes to ensure correct parsing.
294 value = [v.replace('\'', '"') for v in value if v]
450 value = [v.replace("'", '"') for v in value if v]
295451
296452 s = "{:20} := {}".format(key, value)
297453 if len(s) < 70 or VERBOSE:
......@@ -310,7 +466,7 @@ def set(key, value, config):
310466 for i, part in enumerate(parts):
311467 if i == len(parts) - 1:
312468 if is_value_list(part) and isinstance(value, str):
313 value = value.split(',')
469 value = value.split(",")
314470 arr[part] = value
315471 else:
316472 if part not in arr:
......@@ -321,9 +477,9 @@ def set(key, value, config):
321477def apply_args(known_args, option_checking, config):
322478 for key in known_args:
323479 # The `set` option is special and can be passed a bunch of times
324 if key == 'set':
480 if key == "set":
325481 for _option, value in known_args[key]:
326 keyval = value.split('=', 1)
482 keyval = value.split("=", 1)
327483 if len(keyval) == 1 or keyval[1] == "true":
328484 value = True
329485 elif keyval[1] == "false":
......@@ -348,50 +504,55 @@ def apply_args(known_args, option_checking, config):
348504 # that here.
349505 build_triple = build(known_args)
350506
351 if option.name == 'sccache':
352 set('llvm.ccache', 'sccache', config)
353 elif option.name == 'local-rust':
354 for path in os.environ['PATH'].split(os.pathsep):
355 if os.path.exists(path + '/rustc'):
356 set('build.rustc', path + '/rustc', config)
507 if option.name == "sccache":
508 set("llvm.ccache", "sccache", config)
509 elif option.name == "local-rust":
510 for path in os.environ["PATH"].split(os.pathsep):
511 if os.path.exists(path + "/rustc"):
512 set("build.rustc", path + "/rustc", config)
357513 break
358 for path in os.environ['PATH'].split(os.pathsep):
359 if os.path.exists(path + '/cargo'):
360 set('build.cargo', path + '/cargo', config)
514 for path in os.environ["PATH"].split(os.pathsep):
515 if os.path.exists(path + "/cargo"):
516 set("build.cargo", path + "/cargo", config)
361517 break
362 elif option.name == 'local-rust-root':
363 set('build.rustc', value + '/bin/rustc', config)
364 set('build.cargo', value + '/bin/cargo', config)
365 elif option.name == 'llvm-root':
366 set('target.{}.llvm-config'.format(build_triple), value + '/bin/llvm-config', config)
367 elif option.name == 'llvm-config':
368 set('target.{}.llvm-config'.format(build_triple), value, config)
369 elif option.name == 'llvm-filecheck':
370 set('target.{}.llvm-filecheck'.format(build_triple), value, config)
371 elif option.name == 'tools':
372 set('build.tools', value.split(','), config)
373 elif option.name == 'bootstrap-cache-path':
374 set('build.bootstrap-cache-path', value, config)
375 elif option.name == 'codegen-backends':
376 set('rust.codegen-backends', value.split(','), config)
377 elif option.name == 'host':
378 set('build.host', value.split(','), config)
379 elif option.name == 'target':
380 set('build.target', value.split(','), config)
381 elif option.name == 'full-tools':
382 set('rust.codegen-backends', ['llvm'], config)
383 set('rust.lld', True, config)
384 set('rust.llvm-tools', True, config)
385 set('rust.llvm-bitcode-linker', True, config)
386 set('build.extended', True, config)
387 elif option.name in ['option-checking', 'verbose-configure']:
518 elif option.name == "local-rust-root":
519 set("build.rustc", value + "/bin/rustc", config)
520 set("build.cargo", value + "/bin/cargo", config)
521 elif option.name == "llvm-root":
522 set(
523 "target.{}.llvm-config".format(build_triple),
524 value + "/bin/llvm-config",
525 config,
526 )
527 elif option.name == "llvm-config":
528 set("target.{}.llvm-config".format(build_triple), value, config)
529 elif option.name == "llvm-filecheck":
530 set("target.{}.llvm-filecheck".format(build_triple), value, config)
531 elif option.name == "tools":
532 set("build.tools", value.split(","), config)
533 elif option.name == "bootstrap-cache-path":
534 set("build.bootstrap-cache-path", value, config)
535 elif option.name == "codegen-backends":
536 set("rust.codegen-backends", value.split(","), config)
537 elif option.name == "host":
538 set("build.host", value.split(","), config)
539 elif option.name == "target":
540 set("build.target", value.split(","), config)
541 elif option.name == "full-tools":
542 set("rust.codegen-backends", ["llvm"], config)
543 set("rust.lld", True, config)
544 set("rust.llvm-tools", True, config)
545 set("rust.llvm-bitcode-linker", True, config)
546 set("build.extended", True, config)
547 elif option.name in ["option-checking", "verbose-configure"]:
388548 # this was handled above
389549 pass
390 elif option.name == 'dist-compression-formats':
391 set('dist.compression-formats', value.split(','), config)
550 elif option.name == "dist-compression-formats":
551 set("dist.compression-formats", value.split(","), config)
392552 else:
393553 raise RuntimeError("unhandled option {}".format(option.name))
394554
555
395556# "Parse" the `config.example.toml` file into the various sections, and we'll
396557# use this as a template of a `config.toml` to write out which preserves
397558# all the various comments and whatnot.
......@@ -406,20 +567,22 @@ def parse_example_config(known_args, config):
406567 targets = {}
407568 top_level_keys = []
408569
409 with open(rust_dir + '/config.example.toml') as example_config:
570 with open(rust_dir + "/config.example.toml") as example_config:
410571 example_lines = example_config.read().split("\n")
411572 for line in example_lines:
412573 if cur_section is None:
413 if line.count('=') == 1:
414 top_level_key = line.split('=')[0]
415 top_level_key = top_level_key.strip(' #')
574 if line.count("=") == 1:
575 top_level_key = line.split("=")[0]
576 top_level_key = top_level_key.strip(" #")
416577 top_level_keys.append(top_level_key)
417 if line.startswith('['):
578 if line.startswith("["):
418579 cur_section = line[1:-1]
419 if cur_section.startswith('target'):
420 cur_section = 'target'
421 elif '.' in cur_section:
422 raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
580 if cur_section.startswith("target"):
581 cur_section = "target"
582 elif "." in cur_section:
583 raise RuntimeError(
584 "don't know how to deal with section: {}".format(cur_section)
585 )
423586 sections[cur_section] = [line]
424587 section_order.append(cur_section)
425588 else:
......@@ -428,22 +591,25 @@ def parse_example_config(known_args, config):
428591 # Fill out the `targets` array by giving all configured targets a copy of the
429592 # `target` section we just loaded from the example config
430593 configured_targets = [build(known_args)]
431 if 'build' in config:
432 if 'host' in config['build']:
433 configured_targets += config['build']['host']
434 if 'target' in config['build']:
435 configured_targets += config['build']['target']
436 if 'target' in config:
437 for target in config['target']:
594 if "build" in config:
595 if "host" in config["build"]:
596 configured_targets += config["build"]["host"]
597 if "target" in config["build"]:
598 configured_targets += config["build"]["target"]
599 if "target" in config:
600 for target in config["target"]:
438601 configured_targets.append(target)
439602 for target in configured_targets:
440 targets[target] = sections['target'][:]
603 targets[target] = sections["target"][:]
441604 # For `.` to be valid TOML, it needs to be quoted. But `bootstrap.py` doesn't use a proper TOML parser and fails to parse the target.
442605 # Avoid using quotes unless it's necessary.
443 targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", "'{}'".format(target) if "." in target else target)
606 targets[target][0] = targets[target][0].replace(
607 "x86_64-unknown-linux-gnu",
608 "'{}'".format(target) if "." in target else target,
609 )
444610
445 if 'profile' not in config:
446 set('profile', 'dist', config)
611 if "profile" not in config:
612 set("profile", "dist", config)
447613 configure_file(sections, top_level_keys, targets, config)
448614 return section_order, sections, targets
449615
......@@ -467,7 +633,7 @@ def to_toml(value):
467633 else:
468634 return "false"
469635 elif isinstance(value, list):
470 return '[' + ', '.join(map(to_toml, value)) + ']'
636 return "[" + ", ".join(map(to_toml, value)) + "]"
471637 elif isinstance(value, str):
472638 # Don't put quotes around numeric values
473639 if is_number(value):
......@@ -475,9 +641,18 @@ def to_toml(value):
475641 else:
476642 return "'" + value + "'"
477643 elif isinstance(value, dict):
478 return "{" + ", ".join(map(lambda a: "{} = {}".format(to_toml(a[0]), to_toml(a[1])), value.items())) + "}"
644 return (
645 "{"
646 + ", ".join(
647 map(
648 lambda a: "{} = {}".format(to_toml(a[0]), to_toml(a[1])),
649 value.items(),
650 )
651 )
652 + "}"
653 )
479654 else:
480 raise RuntimeError('no toml')
655 raise RuntimeError("no toml")
481656
482657
483658def configure_section(lines, config):
......@@ -485,7 +660,7 @@ def configure_section(lines, config):
485660 value = config[key]
486661 found = False
487662 for i, line in enumerate(lines):
488 if not line.startswith('#' + key + ' = '):
663 if not line.startswith("#" + key + " = "):
489664 continue
490665 found = True
491666 lines[i] = "{} = {}".format(key, to_toml(value))
......@@ -501,7 +676,9 @@ def configure_section(lines, config):
501676
502677def configure_top_level_key(lines, top_level_key, value):
503678 for i, line in enumerate(lines):
504 if line.startswith('#' + top_level_key + ' = ') or line.startswith(top_level_key + ' = '):
679 if line.startswith("#" + top_level_key + " = ") or line.startswith(
680 top_level_key + " = "
681 ):
505682 lines[i] = "{} = {}".format(top_level_key, to_toml(value))
506683 return
507684
......@@ -512,11 +689,13 @@ def configure_top_level_key(lines, top_level_key, value):
512689def configure_file(sections, top_level_keys, targets, config):
513690 for section_key, section_config in config.items():
514691 if section_key not in sections and section_key not in top_level_keys:
515 raise RuntimeError("config key {} not in sections or top_level_keys".format(section_key))
692 raise RuntimeError(
693 "config key {} not in sections or top_level_keys".format(section_key)
694 )
516695 if section_key in top_level_keys:
517696 configure_top_level_key(sections[None], section_key, section_config)
518697
519 elif section_key == 'target':
698 elif section_key == "target":
520699 for target in section_config:
521700 configure_section(targets[target], section_config[target])
522701 else:
......@@ -536,18 +715,19 @@ def write_uncommented(target, f):
536715 block = []
537716 is_comment = True
538717 continue
539 is_comment = is_comment and line.startswith('#')
718 is_comment = is_comment and line.startswith("#")
540719 return f
541720
542721
543722def write_config_toml(writer, section_order, targets, sections):
544723 for section in section_order:
545 if section == 'target':
724 if section == "target":
546725 for target in targets:
547726 writer = write_uncommented(targets[target], writer)
548727 else:
549728 writer = write_uncommented(sections[section], writer)
550729
730
551731def quit_if_file_exists(file):
552732 if os.path.isfile(file):
553733 msg = "Existing '{}' detected. Exiting".format(file)
......@@ -559,9 +739,10 @@ def quit_if_file_exists(file):
559739
560740 err(msg)
561741
742
562743if __name__ == "__main__":
563744 # If 'config.toml' already exists, exit the script at this point
564 quit_if_file_exists('config.toml')
745 quit_if_file_exists("config.toml")
565746
566747 if "GITHUB_ACTIONS" in os.environ:
567748 print("::group::Configure the build")
......@@ -575,13 +756,13 @@ if __name__ == "__main__":
575756 # order that we read it in.
576757 p("")
577758 p("writing `config.toml` in current directory")
578 with bootstrap.output('config.toml') as f:
759 with bootstrap.output("config.toml") as f:
579760 write_config_toml(f, section_order, targets, sections)
580761
581 with bootstrap.output('Makefile') as f:
582 contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
762 with bootstrap.output("Makefile") as f:
763 contents = os.path.join(rust_dir, "src", "bootstrap", "mk", "Makefile.in")
583764 contents = open(contents).read()
584 contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
765 contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + "/")
585766 contents = contents.replace("$(CFG_PYTHON)", sys.executable)
586767 f.write(contents)
587768
src/ci/cpu-usage-over-time.py+25-11
......@@ -40,12 +40,13 @@ import time
4040# Python 3.3 changed the value of `sys.platform` on Linux from "linux2" to just
4141# "linux". We check here with `.startswith` to keep compatibility with older
4242# Python versions (especially Python 2.7).
43if sys.platform.startswith('linux'):
43if sys.platform.startswith("linux"):
44
4445 class State:
4546 def __init__(self):
46 with open('/proc/stat', 'r') as file:
47 with open("/proc/stat", "r") as file:
4748 data = file.readline().split()
48 if data[0] != 'cpu':
49 if data[0] != "cpu":
4950 raise Exception('did not start with "cpu"')
5051 self.user = int(data[1])
5152 self.nice = int(data[2])
......@@ -69,10 +70,21 @@ if sys.platform.startswith('linux'):
6970 steal = self.steal - prev.steal
7071 guest = self.guest - prev.guest
7172 guest_nice = self.guest_nice - prev.guest_nice
72 total = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice
73 total = (
74 user
75 + nice
76 + system
77 + idle
78 + iowait
79 + irq
80 + softirq
81 + steal
82 + guest
83 + guest_nice
84 )
7385 return float(idle) / float(total) * 100
7486
75elif sys.platform == 'win32':
87elif sys.platform == "win32":
7688 from ctypes.wintypes import DWORD
7789 from ctypes import Structure, windll, WinError, GetLastError, byref
7890
......@@ -104,9 +116,10 @@ elif sys.platform == 'win32':
104116 kernel = self.kernel - prev.kernel
105117 return float(idle) / float(user + kernel) * 100
106118
107elif sys.platform == 'darwin':
119elif sys.platform == "darwin":
108120 from ctypes import *
109 libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
121
122 libc = cdll.LoadLibrary("/usr/lib/libc.dylib")
110123
111124 class host_cpu_load_info_data_t(Structure):
112125 _fields_ = [("cpu_ticks", c_uint * 4)]
......@@ -116,7 +129,7 @@ elif sys.platform == 'darwin':
116129 c_uint,
117130 c_int,
118131 POINTER(host_cpu_load_info_data_t),
119 POINTER(c_int)
132 POINTER(c_int),
120133 ]
121134 host_statistics.restype = c_int
122135
......@@ -124,13 +137,14 @@ elif sys.platform == 'darwin':
124137 CPU_STATE_SYSTEM = 1
125138 CPU_STATE_IDLE = 2
126139 CPU_STATE_NICE = 3
140
127141 class State:
128142 def __init__(self):
129143 stats = host_cpu_load_info_data_t()
130 count = c_int(4) # HOST_CPU_LOAD_INFO_COUNT
144 count = c_int(4) # HOST_CPU_LOAD_INFO_COUNT
131145 err = libc.host_statistics(
132146 libc.mach_host_self(),
133 c_int(3), # HOST_CPU_LOAD_INFO
147 c_int(3), # HOST_CPU_LOAD_INFO
134148 byref(stats),
135149 byref(count),
136150 )
......@@ -148,7 +162,7 @@ elif sys.platform == 'darwin':
148162 return float(idle) / float(user + system + idle + nice) * 100.0
149163
150164else:
151 print('unknown platform', sys.platform)
165 print("unknown platform", sys.platform)
152166 sys.exit(1)
153167
154168cur_state = State()
src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile+2-2
......@@ -27,5 +27,5 @@ RUN echo "[rust]" > /config/nopt-std-config.toml
2727RUN echo "optimize = false" >> /config/nopt-std-config.toml
2828
2929ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
30ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \
31 && python3 ../x.py --stage 2 test
30ARG SCRIPT_ARG
31ENV SCRIPT=${SCRIPT_ARG}
src/ci/docker/host-x86_64/i686-gnu/Dockerfile+2-7
......@@ -24,10 +24,5 @@ COPY scripts/sccache.sh /scripts/
2424RUN sh /scripts/sccache.sh
2525
2626ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu
27# Skip some tests that are unlikely to be platform specific, to speed up
28# this slow job.
29ENV SCRIPT python3 ../x.py --stage 2 test \
30 --skip src/bootstrap \
31 --skip tests/rustdoc-js \
32 --skip src/tools/error_index_generator \
33 --skip src/tools/linkchecker
27ARG SCRIPT_ARG
28ENV SCRIPT=${SCRIPT_ARG}
src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py+76-73
......@@ -8,78 +8,79 @@ import tempfile
88
99from pathlib import Path
1010
11TARGET_AARCH64 = 'aarch64-unknown-uefi'
12TARGET_I686 = 'i686-unknown-uefi'
13TARGET_X86_64 = 'x86_64-unknown-uefi'
11TARGET_AARCH64 = "aarch64-unknown-uefi"
12TARGET_I686 = "i686-unknown-uefi"
13TARGET_X86_64 = "x86_64-unknown-uefi"
14
1415
1516def run(*cmd, capture=False, check=True, env=None, timeout=None):
1617 """Print and run a command, optionally capturing the output."""
1718 cmd = [str(p) for p in cmd]
18 print(' '.join(cmd))
19 return subprocess.run(cmd,
20 capture_output=capture,
21 check=check,
22 env=env,
23 text=True,
24 timeout=timeout)
19 print(" ".join(cmd))
20 return subprocess.run(
21 cmd, capture_output=capture, check=check, env=env, text=True, timeout=timeout
22 )
23
2524
2625def build_and_run(tmp_dir, target):
2726 if target == TARGET_AARCH64:
28 boot_file_name = 'bootaa64.efi'
29 ovmf_dir = Path('/usr/share/AAVMF')
30 ovmf_code = 'AAVMF_CODE.fd'
31 ovmf_vars = 'AAVMF_VARS.fd'
32 qemu = 'qemu-system-aarch64'
33 machine = 'virt'
34 cpu = 'cortex-a72'
27 boot_file_name = "bootaa64.efi"
28 ovmf_dir = Path("/usr/share/AAVMF")
29 ovmf_code = "AAVMF_CODE.fd"
30 ovmf_vars = "AAVMF_VARS.fd"
31 qemu = "qemu-system-aarch64"
32 machine = "virt"
33 cpu = "cortex-a72"
3534 elif target == TARGET_I686:
36 boot_file_name = 'bootia32.efi'
37 ovmf_dir = Path('/usr/share/OVMF')
38 ovmf_code = 'OVMF32_CODE_4M.secboot.fd'
39 ovmf_vars = 'OVMF32_VARS_4M.fd'
35 boot_file_name = "bootia32.efi"
36 ovmf_dir = Path("/usr/share/OVMF")
37 ovmf_code = "OVMF32_CODE_4M.secboot.fd"
38 ovmf_vars = "OVMF32_VARS_4M.fd"
4039 # The i686 target intentionally uses 64-bit qemu; the important
4140 # difference is that the OVMF code provides a 32-bit environment.
42 qemu = 'qemu-system-x86_64'
43 machine = 'q35'
44 cpu = 'qemu64'
41 qemu = "qemu-system-x86_64"
42 machine = "q35"
43 cpu = "qemu64"
4544 elif target == TARGET_X86_64:
46 boot_file_name = 'bootx64.efi'
47 ovmf_dir = Path('/usr/share/OVMF')
48 ovmf_code = 'OVMF_CODE.fd'
49 ovmf_vars = 'OVMF_VARS.fd'
50 qemu = 'qemu-system-x86_64'
51 machine = 'q35'
52 cpu = 'qemu64'
45 boot_file_name = "bootx64.efi"
46 ovmf_dir = Path("/usr/share/OVMF")
47 ovmf_code = "OVMF_CODE.fd"
48 ovmf_vars = "OVMF_VARS.fd"
49 qemu = "qemu-system-x86_64"
50 machine = "q35"
51 cpu = "qemu64"
5352 else:
54 raise KeyError('invalid target')
53 raise KeyError("invalid target")
5554
56 host_artifacts = Path('/checkout/obj/build/x86_64-unknown-linux-gnu')
57 stage0 = host_artifacts / 'stage0/bin'
58 stage2 = host_artifacts / 'stage2/bin'
55 host_artifacts = Path("/checkout/obj/build/x86_64-unknown-linux-gnu")
56 stage0 = host_artifacts / "stage0/bin"
57 stage2 = host_artifacts / "stage2/bin"
5958
6059 env = dict(os.environ)
61 env['PATH'] = '{}:{}:{}'.format(stage2, stage0, env['PATH'])
60 env["PATH"] = "{}:{}:{}".format(stage2, stage0, env["PATH"])
6261
6362 # Copy the test create into `tmp_dir`.
64 test_crate = Path(tmp_dir) / 'uefi_qemu_test'
65 shutil.copytree('/uefi_qemu_test', test_crate)
63 test_crate = Path(tmp_dir) / "uefi_qemu_test"
64 shutil.copytree("/uefi_qemu_test", test_crate)
6665
6766 # Build the UEFI executable.
68 run('cargo',
69 'build',
70 '--manifest-path',
71 test_crate / 'Cargo.toml',
72 '--target',
67 run(
68 "cargo",
69 "build",
70 "--manifest-path",
71 test_crate / "Cargo.toml",
72 "--target",
7373 target,
74 env=env)
74 env=env,
75 )
7576
7677 # Create a mock EFI System Partition in a subdirectory.
77 esp = test_crate / 'esp'
78 boot = esp / 'efi/boot'
78 esp = test_crate / "esp"
79 boot = esp / "efi/boot"
7980 os.makedirs(boot, exist_ok=True)
8081
8182 # Copy the executable into the ESP.
82 src_exe_path = test_crate / 'target' / target / 'debug/uefi_qemu_test.efi'
83 src_exe_path = test_crate / "target" / target / "debug/uefi_qemu_test.efi"
8384 shutil.copy(src_exe_path, boot / boot_file_name)
8485 print(src_exe_path, boot / boot_file_name)
8586
......@@ -89,37 +90,39 @@ def build_and_run(tmp_dir, target):
8990
9091 # Make a writable copy of the vars file. aarch64 doesn't boot
9192 # correctly with read-only vars.
92 ovmf_rw_vars = Path(tmp_dir) / 'vars.fd'
93 ovmf_rw_vars = Path(tmp_dir) / "vars.fd"
9394 shutil.copy(ovmf_vars, ovmf_rw_vars)
9495
9596 # Run the executable in QEMU and capture the output.
96 output = run(qemu,
97 '-machine',
98 machine,
99 '-cpu',
100 cpu,
101 '-display',
102 'none',
103 '-serial',
104 'stdio',
105 '-drive',
106 f'if=pflash,format=raw,readonly=on,file={ovmf_code}',
107 '-drive',
108 f'if=pflash,format=raw,readonly=off,file={ovmf_rw_vars}',
109 '-drive',
110 f'format=raw,file=fat:rw:{esp}',
111 capture=True,
112 check=True,
113 # Set a timeout to kill the VM in case something goes wrong.
114 timeout=60).stdout
115
116 if 'Hello World!' in output:
117 print('VM produced expected output')
97 output = run(
98 qemu,
99 "-machine",
100 machine,
101 "-cpu",
102 cpu,
103 "-display",
104 "none",
105 "-serial",
106 "stdio",
107 "-drive",
108 f"if=pflash,format=raw,readonly=on,file={ovmf_code}",
109 "-drive",
110 f"if=pflash,format=raw,readonly=off,file={ovmf_rw_vars}",
111 "-drive",
112 f"format=raw,file=fat:rw:{esp}",
113 capture=True,
114 check=True,
115 # Set a timeout to kill the VM in case something goes wrong.
116 timeout=60,
117 ).stdout
118
119 if "Hello World!" in output:
120 print("VM produced expected output")
118121 else:
119 print('unexpected VM output:')
120 print('---start---')
122 print("unexpected VM output:")
123 print("---start---")
121124 print(output)
122 print('---end---')
125 print("---end---")
123126 sys.exit(1)
124127
125128
src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version+1-1
......@@ -1 +1 @@
10.18.1
\ No newline at end of file
10.18.2
\ No newline at end of file
src/ci/docker/run.sh+22-13
......@@ -105,6 +105,23 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
105105 # It seems that it cannot be the same as $IMAGE_TAG, otherwise it overwrites the cache
106106 CACHE_IMAGE_TAG=${REGISTRY}/${REGISTRY_USERNAME}/rust-ci-cache:${cksum}
107107
108 # Docker build arguments.
109 build_args=(
110 "build"
111 "--rm"
112 "-t" "rust-ci"
113 "-f" "$dockerfile"
114 "$context"
115 )
116
117 # If the environment variable DOCKER_SCRIPT is defined,
118 # set the build argument SCRIPT_ARG to DOCKER_SCRIPT.
119 # In this way, we run the script defined in CI,
120 # instead of the one defined in the Dockerfile.
121 if [ -n "${DOCKER_SCRIPT+x}" ]; then
122 build_args+=("--build-arg" "SCRIPT_ARG=${DOCKER_SCRIPT}")
123 fi
124
108125 # On non-CI jobs, we try to download a pre-built image from the rust-lang-ci
109126 # ghcr.io registry. If it is not possible, we fall back to building the image
110127 # locally.
......@@ -115,7 +132,7 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
115132 docker tag "${IMAGE_TAG}" rust-ci
116133 else
117134 echo "Building local Docker image"
118 retry docker build --rm -t rust-ci -f "$dockerfile" "$context"
135 retry docker "${build_args[@]}"
119136 fi
120137 # On PR CI jobs, we don't have permissions to write to the registry cache,
121138 # but we can still read from it.
......@@ -127,13 +144,9 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
127144 # Build the image using registry caching backend
128145 retry docker \
129146 buildx \
130 build \
131 --rm \
132 -t rust-ci \
133 -f "$dockerfile" \
147 "${build_args[@]}" \
134148 --cache-from type=registry,ref=${CACHE_IMAGE_TAG} \
135 --output=type=docker \
136 "$context"
149 --output=type=docker
137150 # On auto/try builds, we can also write to the cache.
138151 else
139152 # Log into the Docker registry, so that we can read/write cache and the final image
......@@ -147,14 +160,10 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
147160 # Build the image using registry caching backend
148161 retry docker \
149162 buildx \
150 build \
151 --rm \
152 -t rust-ci \
153 -f "$dockerfile" \
163 "${build_args[@]}" \
154164 --cache-from type=registry,ref=${CACHE_IMAGE_TAG} \
155165 --cache-to type=registry,ref=${CACHE_IMAGE_TAG},compression=zstd \
156 --output=type=docker \
157 "$context"
166 --output=type=docker
158167
159168 # Print images for debugging purposes
160169 docker images
src/ci/docker/scripts/android-sdk-manager.py+47-13
......@@ -35,6 +35,7 @@ MIRROR_BUCKET = "rust-lang-ci-mirrors"
3535MIRROR_BUCKET_REGION = "us-west-1"
3636MIRROR_BASE_DIR = "rustc/android/"
3737
38
3839class Package:
3940 def __init__(self, path, url, sha1, deps=None):
4041 if deps is None:
......@@ -53,18 +54,25 @@ class Package:
5354 sha1 = hashlib.sha1(f.read()).hexdigest()
5455 if sha1 != self.sha1:
5556 raise RuntimeError(
56 "hash mismatch for package " + self.path + ": " +
57 sha1 + " vs " + self.sha1 + " (known good)"
57 "hash mismatch for package "
58 + self.path
59 + ": "
60 + sha1
61 + " vs "
62 + self.sha1
63 + " (known good)"
5864 )
5965 return file
6066
6167 def __repr__(self):
62 return "<Package "+self.path+" at "+self.url+" (sha1="+self.sha1+")"
68 return "<Package " + self.path + " at " + self.url + " (sha1=" + self.sha1 + ")"
69
6370
6471def fetch_url(url):
6572 page = urllib.request.urlopen(url)
6673 return page.read()
6774
75
6876def fetch_repository(base, repo_url):
6977 packages = {}
7078 root = ET.fromstring(fetch_url(base + repo_url))
......@@ -92,12 +100,14 @@ def fetch_repository(base, repo_url):
92100
93101 return packages
94102
103
95104def fetch_repositories():
96105 packages = {}
97106 for repo in REPOSITORIES:
98107 packages.update(fetch_repository(BASE_REPOSITORY, repo))
99108 return packages
100109
110
101111class Lockfile:
102112 def __init__(self, path):
103113 self.path = path
......@@ -123,6 +133,7 @@ class Lockfile:
123133 for package in packages:
124134 f.write(package.path + " " + package.url + " " + package.sha1 + "\n")
125135
136
126137def cli_add_to_lockfile(args):
127138 lockfile = Lockfile(args.lockfile)
128139 packages = fetch_repositories()
......@@ -130,28 +141,49 @@ def cli_add_to_lockfile(args):
130141 lockfile.add(packages, package)
131142 lockfile.save()
132143
144
133145def cli_update_mirror(args):
134146 lockfile = Lockfile(args.lockfile)
135147 for package in lockfile.packages.values():
136148 path = package.download(BASE_REPOSITORY)
137 subprocess.run([
138 "aws", "s3", "mv", path,
139 "s3://" + MIRROR_BUCKET + "/" + MIRROR_BASE_DIR + package.url,
140 "--profile=" + args.awscli_profile,
141 ], check=True)
149 subprocess.run(
150 [
151 "aws",
152 "s3",
153 "mv",
154 path,
155 "s3://" + MIRROR_BUCKET + "/" + MIRROR_BASE_DIR + package.url,
156 "--profile=" + args.awscli_profile,
157 ],
158 check=True,
159 )
160
142161
143162def cli_install(args):
144163 lockfile = Lockfile(args.lockfile)
145164 for package in lockfile.packages.values():
146165 # Download the file from the mirror into a temp file
147 url = "https://" + MIRROR_BUCKET + ".s3-" + MIRROR_BUCKET_REGION + \
148 ".amazonaws.com/" + MIRROR_BASE_DIR
166 url = (
167 "https://"
168 + MIRROR_BUCKET
169 + ".s3-"
170 + MIRROR_BUCKET_REGION
171 + ".amazonaws.com/"
172 + MIRROR_BASE_DIR
173 )
149174 downloaded = package.download(url)
150175 # Extract the file in a temporary directory
151176 extract_dir = tempfile.mkdtemp()
152 subprocess.run([
153 "unzip", "-q", downloaded, "-d", extract_dir,
154 ], check=True)
177 subprocess.run(
178 [
179 "unzip",
180 "-q",
181 downloaded,
182 "-d",
183 extract_dir,
184 ],
185 check=True,
186 )
155187 # Figure out the prefix used in the zip
156188 subdirs = [d for d in os.listdir(extract_dir) if not d.startswith(".")]
157189 if len(subdirs) != 1:
......@@ -162,6 +194,7 @@ def cli_install(args):
162194 os.rename(os.path.join(extract_dir, subdirs[0]), dest)
163195 os.unlink(downloaded)
164196
197
165198def cli():
166199 parser = argparse.ArgumentParser()
167200 subparsers = parser.add_subparsers()
......@@ -187,5 +220,6 @@ def cli():
187220 exit(1)
188221 args.func(args)
189222
223
190224if __name__ == "__main__":
191225 cli()
src/ci/docker/scripts/fuchsia-test-runner.py+3-5
......@@ -588,7 +588,7 @@ class TestEnvironment:
588588 "--repo-path",
589589 self.repo_dir(),
590590 "--repository",
591 self.TEST_REPO_NAME
591 self.TEST_REPO_NAME,
592592 ],
593593 env=ffx_env,
594594 stdout_handler=self.subprocess_logger.debug,
......@@ -619,9 +619,7 @@ class TestEnvironment:
619619 # `facet` statement required for TCP testing via
620620 # protocol `fuchsia.posix.socket.Provider`. See
621621 # https://fuchsia.dev/fuchsia-src/development/testing/components/test_runner_framework?hl=en#legacy_non-hermetic_tests
622 CML_TEMPLATE: ClassVar[
623 str
624 ] = """
622 CML_TEMPLATE: ClassVar[str] = """
625623 {{
626624 program: {{
627625 runner: "elf_test_runner",
......@@ -994,7 +992,7 @@ class TestEnvironment:
994992 "repository",
995993 "server",
996994 "stop",
997 self.TEST_REPO_NAME
995 self.TEST_REPO_NAME,
998996 ],
999997 env=self.ffx_cmd_env(),
1000998 stdout_handler=self.subprocess_logger.debug,
src/ci/docker/scripts/rfl-build.sh+2-2
......@@ -2,7 +2,7 @@
22
33set -euo pipefail
44
5LINUX_VERSION=28e848386b92645f93b9f2fdba5882c3ca7fb3e2
5LINUX_VERSION=v6.13-rc1
66
77# Build rustc, rustdoc, cargo, clippy-driver and rustfmt
88../x.py build --stage 2 library rustdoc clippy rustfmt
......@@ -64,7 +64,7 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \
6464
6565BUILD_TARGETS="
6666 samples/rust/rust_minimal.o
67 samples/rust/rust_print.o
67 samples/rust/rust_print_main.o
6868 drivers/net/phy/ax88796b_rust.o
6969 rust/doctests_kernel_generated.o
7070"
src/ci/github-actions/calculate-job-matrix.py+9-4
......@@ -7,6 +7,7 @@ be executed on CI.
77It reads job definitions from `src/ci/github-actions/jobs.yml`
88and filters them based on the event that happened on CI.
99"""
10
1011import dataclasses
1112import json
1213import logging
......@@ -94,7 +95,7 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]:
9495 try_build = ctx.ref in (
9596 "refs/heads/try",
9697 "refs/heads/try-perf",
97 "refs/heads/automation/bors/try"
98 "refs/heads/automation/bors/try",
9899 )
99100
100101 # Unrolled branch from a rollup for testing perf
......@@ -135,11 +136,15 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[
135136 continue
136137 jobs.append(job[0])
137138 if unknown_jobs:
138 raise Exception(f"Custom job(s) `{unknown_jobs}` not found in auto jobs")
139 raise Exception(
140 f"Custom job(s) `{unknown_jobs}` not found in auto jobs"
141 )
139142
140143 return add_base_env(name_jobs(jobs, "try"), job_data["envs"]["try"])
141144 elif isinstance(run_type, AutoRunType):
142 return add_base_env(name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"])
145 return add_base_env(
146 name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"]
147 )
143148
144149 return []
145150
......@@ -161,7 +166,7 @@ def get_github_ctx() -> GitHubCtx:
161166 event_name=event_name,
162167 ref=os.environ["GITHUB_REF"],
163168 repository=os.environ["GITHUB_REPOSITORY"],
164 commit_message=commit_message
169 commit_message=commit_message,
165170 )
166171
167172
src/ci/github-actions/jobs.yml+51-4
......@@ -58,6 +58,22 @@ envs:
5858 NO_DEBUG_ASSERTIONS: 1
5959 NO_OVERFLOW_CHECKS: 1
6060
61 # Different set of tests to run tests in parallel in multiple jobs.
62 stage_2_test_set1: &stage_2_test_set1
63 DOCKER_SCRIPT: >-
64 python3 ../x.py --stage 2 test
65 --skip compiler
66 --skip src
67
68 stage_2_test_set2: &stage_2_test_set2
69 DOCKER_SCRIPT: >-
70 python3 ../x.py --stage 2 test
71 --skip tests
72 --skip coverage-map
73 --skip coverage-run
74 --skip library
75 --skip tidyselftest
76
6177 production:
6278 &production
6379 DEPLOY_BUCKET: rust-lang-ci2
......@@ -212,11 +228,42 @@ auto:
212228 - image: dist-x86_64-netbsd
213229 <<: *job-linux-4c
214230
215 - image: i686-gnu
216 <<: *job-linux-8c
231 # The i686-gnu job is split into multiple jobs to run tests in parallel.
232 # i686-gnu-1 skips tests that run in i686-gnu-2.
233 - image: i686-gnu-1
234 env:
235 IMAGE: i686-gnu
236 <<: *stage_2_test_set1
237 <<: *job-linux-4c
217238
218 - image: i686-gnu-nopt
219 <<: *job-linux-8c
239 # Skip tests that run in i686-gnu-1
240 - image: i686-gnu-2
241 env:
242 IMAGE: i686-gnu
243 <<: *stage_2_test_set2
244 <<: *job-linux-4c
245
246 # The i686-gnu-nopt job is split into multiple jobs to run tests in parallel.
247 # i686-gnu-nopt-1 skips tests that run in i686-gnu-nopt-2
248 - image: i686-gnu-nopt-1
249 env:
250 IMAGE: i686-gnu-nopt
251 <<: *stage_2_test_set1
252 <<: *job-linux-4c
253
254 # Skip tests that run in i686-gnu-nopt-1
255 - image: i686-gnu-nopt-2
256 env:
257 IMAGE: i686-gnu-nopt
258 DOCKER_SCRIPT: >-
259 python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std &&
260 python3 ../x.py --stage 2 test
261 --skip tests
262 --skip coverage-map
263 --skip coverage-run
264 --skip library
265 --skip tidyselftest
266 <<: *job-linux-4c
220267
221268 - image: mingw-check
222269 <<: *job-linux-4c
src/ci/scripts/upload-build-metrics.py+9-12
......@@ -19,6 +19,7 @@ $ python3 upload-build-metrics.py <path-to-CPU-usage-CSV>
1919
2020`path-to-CPU-usage-CSV` is a path to a CSV generated by the `src/ci/cpu-usage-over-time.py` script.
2121"""
22
2223import argparse
2324import csv
2425import os
......@@ -31,7 +32,7 @@ from typing import List
3132def load_cpu_usage(path: Path) -> List[float]:
3233 usage = []
3334 with open(path) as f:
34 reader = csv.reader(f, delimiter=',')
35 reader = csv.reader(f, delimiter=",")
3536 for row in reader:
3637 # The log might contain incomplete rows or some Python exception
3738 if len(row) == 2:
......@@ -50,25 +51,21 @@ def upload_datadog_measure(name: str, value: float):
5051 print(f"Metric {name}: {value:.4f}")
5152
5253 datadog_cmd = "datadog-ci"
53 if os.getenv("GITHUB_ACTIONS") is not None and sys.platform.lower().startswith("win"):
54 if os.getenv("GITHUB_ACTIONS") is not None and sys.platform.lower().startswith(
55 "win"
56 ):
5457 # Due to weird interaction of MSYS2 and Python, we need to use an absolute path,
5558 # and also specify the ".cmd" at the end. See https://github.com/rust-lang/rust/pull/125771.
5659 datadog_cmd = "C:\\npm\\prefix\\datadog-ci.cmd"
5760
58 subprocess.run([
59 datadog_cmd,
60 "measure",
61 "--level", "job",
62 "--measures", f"{name}:{value}"
63 ],
64 check=False
61 subprocess.run(
62 [datadog_cmd, "measure", "--level", "job", "--measures", f"{name}:{value}"],
63 check=False,
6564 )
6665
6766
6867if __name__ == "__main__":
69 parser = argparse.ArgumentParser(
70 prog="DataDog metric uploader"
71 )
68 parser = argparse.ArgumentParser(prog="DataDog metric uploader")
7269 parser.add_argument("cpu-usage-history-csv")
7370 args = parser.parse_args()
7471
src/etc/dec2flt_table.py+24-22
......@@ -13,6 +13,7 @@ i.e., within 0.5 ULP of the true value.
1313Adapted from Daniel Lemire's fast_float ``table_generation.py``,
1414available here: <https://github.com/fastfloat/fast_float/blob/main/script/table_generation.py>.
1515"""
16
1617from __future__ import print_function
1718from math import ceil, floor, log
1819from collections import deque
......@@ -34,6 +35,7 @@ STATIC_WARNING = """
3435// the final binary.
3536"""
3637
38
3739def main():
3840 min_exp = minimum_exponent(10)
3941 max_exp = maximum_exponent(10)
......@@ -41,10 +43,10 @@ def main():
4143
4244 print(HEADER.strip())
4345 print()
44 print('pub const SMALLEST_POWER_OF_FIVE: i32 = {};'.format(min_exp))
45 print('pub const LARGEST_POWER_OF_FIVE: i32 = {};'.format(max_exp))
46 print('pub const N_POWERS_OF_FIVE: usize = ', end='')
47 print('(LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;')
46 print("pub const SMALLEST_POWER_OF_FIVE: i32 = {};".format(min_exp))
47 print("pub const LARGEST_POWER_OF_FIVE: i32 = {};".format(max_exp))
48 print("pub const N_POWERS_OF_FIVE: usize = ", end="")
49 print("(LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;")
4850 print()
4951 print_proper_powers(min_exp, max_exp, bias)
5052
......@@ -54,7 +56,7 @@ def minimum_exponent(base):
5456
5557
5658def maximum_exponent(base):
57 return floor(log(1.7976931348623157e+308, base))
59 return floor(log(1.7976931348623157e308, base))
5860
5961
6062def print_proper_powers(min_exp, max_exp, bias):
......@@ -64,46 +66,46 @@ def print_proper_powers(min_exp, max_exp, bias):
6466 # 2^(2b)/(5^−q) with b=64 + int(math.ceil(log2(5^−q)))
6567 powers = []
6668 for q in range(min_exp, 0):
67 power5 = 5 ** -q
69 power5 = 5**-q
6870 z = 0
6971 while (1 << z) < power5:
7072 z += 1
7173 if q >= -27:
7274 b = z + 127
73 c = 2 ** b // power5 + 1
75 c = 2**b // power5 + 1
7476 powers.append((c, q))
7577 else:
7678 b = 2 * z + 2 * 64
77 c = 2 ** b // power5 + 1
79 c = 2**b // power5 + 1
7880 # truncate
79 while c >= (1<<128):
81 while c >= (1 << 128):
8082 c //= 2
8183 powers.append((c, q))
8284
8385 # Add positive exponents
8486 for q in range(0, max_exp + 1):
85 power5 = 5 ** q
87 power5 = 5**q
8688 # move the most significant bit in position
87 while power5 < (1<<127):
89 while power5 < (1 << 127):
8890 power5 *= 2
8991 # *truncate*
90 while power5 >= (1<<128):
92 while power5 >= (1 << 128):
9193 power5 //= 2
9294 powers.append((power5, q))
9395
9496 # Print the powers.
9597 print(STATIC_WARNING.strip())
96 print('#[rustfmt::skip]')
97 typ = '[(u64, u64); N_POWERS_OF_FIVE]'
98 print('pub static POWER_OF_FIVE_128: {} = ['.format(typ))
98 print("#[rustfmt::skip]")
99 typ = "[(u64, u64); N_POWERS_OF_FIVE]"
100 print("pub static POWER_OF_FIVE_128: {} = [".format(typ))
99101 for c, exp in powers:
100 hi = '0x{:x}'.format(c // (1 << 64))
101 lo = '0x{:x}'.format(c % (1 << 64))
102 value = ' ({}, {}), '.format(hi, lo)
103 comment = '// {}^{}'.format(5, exp)
104 print(value.ljust(46, ' ') + comment)
105 print('];')
102 hi = "0x{:x}".format(c // (1 << 64))
103 lo = "0x{:x}".format(c % (1 << 64))
104 value = " ({}, {}), ".format(hi, lo)
105 comment = "// {}^{}".format(5, exp)
106 print(value.ljust(46, " ") + comment)
107 print("];")
106108
107109
108if __name__ == '__main__':
110if __name__ == "__main__":
109111 main()
src/etc/gdb_load_rust_pretty_printers.py+1
......@@ -1,6 +1,7 @@
11# Add this folder to the python sys path; GDB Python-interpreter will now find modules in this path
22import sys
33from os import path
4
45self_dir = path.dirname(path.realpath(__file__))
56sys.path.append(self_dir)
67
src/etc/gdb_lookup.py+5-2
......@@ -6,8 +6,11 @@ from gdb_providers import *
66from rust_types import *
77
88
9_gdb_version_matched = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION)
10gdb_version = [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else []
9_gdb_version_matched = re.search("([0-9]+)\\.([0-9]+)", gdb.VERSION)
10gdb_version = (
11 [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else []
12)
13
1114
1215def register_printers(objfile):
1316 objfile.pretty_printers.append(printer)
src/etc/gdb_providers.py+26-9
......@@ -21,7 +21,7 @@ def unwrap_unique_or_non_null(unique_or_nonnull):
2121# GDB 14 has a tag class that indicates that extension methods are ok
2222# to call. Use of this tag only requires that printers hide local
2323# attributes and methods by prefixing them with "_".
24if hasattr(gdb, 'ValuePrinter'):
24if hasattr(gdb, "ValuePrinter"):
2525 printer_base = gdb.ValuePrinter
2626else:
2727 printer_base = object
......@@ -98,7 +98,7 @@ class StdStrProvider(printer_base):
9898
9999
100100def _enumerate_array_elements(element_ptrs):
101 for (i, element_ptr) in enumerate(element_ptrs):
101 for i, element_ptr in enumerate(element_ptrs):
102102 key = "[{}]".format(i)
103103 element = element_ptr.dereference()
104104
......@@ -173,7 +173,8 @@ class StdVecDequeProvider(printer_base):
173173
174174 def children(self):
175175 return _enumerate_array_elements(
176 (self._data_ptr + ((self._head + index) % self._cap)) for index in xrange(self._size)
176 (self._data_ptr + ((self._head + index) % self._cap))
177 for index in xrange(self._size)
177178 )
178179
179180 @staticmethod
......@@ -270,7 +271,9 @@ def children_of_btree_map(map):
270271 # Yields each key/value pair in the node and in any child nodes.
271272 def children_of_node(node_ptr, height):
272273 def cast_to_internal(node):
273 internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1)
274 internal_type_name = node.type.target().name.replace(
275 "LeafNode", "InternalNode", 1
276 )
274277 internal_type = gdb.lookup_type(internal_type_name)
275278 return node.cast(internal_type.pointer())
276279
......@@ -293,8 +296,16 @@ def children_of_btree_map(map):
293296 # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
294297 key_type_size = keys.type.sizeof
295298 val_type_size = vals.type.sizeof
296 key = keys[i]["value"]["value"] if key_type_size > 0 else gdb.parse_and_eval("()")
297 val = vals[i]["value"]["value"] if val_type_size > 0 else gdb.parse_and_eval("()")
299 key = (
300 keys[i]["value"]["value"]
301 if key_type_size > 0
302 else gdb.parse_and_eval("()")
303 )
304 val = (
305 vals[i]["value"]["value"]
306 if val_type_size > 0
307 else gdb.parse_and_eval("()")
308 )
298309 yield key, val
299310
300311 if map["length"] > 0:
......@@ -352,7 +363,7 @@ class StdOldHashMapProvider(printer_base):
352363 self._hashes = self._table["hashes"]
353364 self._hash_uint_type = self._hashes.type
354365 self._hash_uint_size = self._hashes.type.sizeof
355 self._modulo = 2 ** self._hash_uint_size
366 self._modulo = 2**self._hash_uint_size
356367 self._data_ptr = self._hashes[ZERO_FIELD]["pointer"]
357368
358369 self._capacity_mask = int(self._table["capacity_mask"])
......@@ -382,8 +393,14 @@ class StdOldHashMapProvider(printer_base):
382393
383394 hashes = self._hash_uint_size * self._capacity
384395 align = self._pair_type_size
385 len_rounded_up = (((((hashes + align) % self._modulo - 1) % self._modulo) & ~(
386 (align - 1) % self._modulo)) % self._modulo - hashes) % self._modulo
396 len_rounded_up = (
397 (
398 (((hashes + align) % self._modulo - 1) % self._modulo)
399 & ~((align - 1) % self._modulo)
400 )
401 % self._modulo
402 - hashes
403 ) % self._modulo
387404
388405 pairs_offset = hashes + len_rounded_up
389406 pairs_start = gdb.Value(start + pairs_offset).cast(self._pair_type.pointer())
src/etc/generate-deriving-span-tests.py+35-27
......@@ -12,7 +12,8 @@ import os
1212import stat
1313
1414TEST_DIR = os.path.abspath(
15 os.path.join(os.path.dirname(__file__), '../test/ui/derives/'))
15 os.path.join(os.path.dirname(__file__), "../test/ui/derives/")
16)
1617
1718TEMPLATE = """\
1819// This file was auto-generated using 'src/etc/generate-deriving-span-tests.py'
......@@ -56,28 +57,33 @@ ENUM_TUPLE, ENUM_STRUCT, STRUCT_FIELDS, STRUCT_TUPLE = range(4)
5657
5758
5859def create_test_case(type, trait, super_traits, error_count):
59 string = [ENUM_STRING, ENUM_STRUCT_VARIANT_STRING, STRUCT_STRING, STRUCT_TUPLE_STRING][type]
60 all_traits = ','.join([trait] + super_traits)
61 super_traits = ','.join(super_traits)
62 error_deriving = '#[derive(%s)]' % super_traits if super_traits else ''
63
64 errors = '\n'.join('//~%s ERROR' % ('^' * n) for n in range(error_count))
60 string = [
61 ENUM_STRING,
62 ENUM_STRUCT_VARIANT_STRING,
63 STRUCT_STRING,
64 STRUCT_TUPLE_STRING,
65 ][type]
66 all_traits = ",".join([trait] + super_traits)
67 super_traits = ",".join(super_traits)
68 error_deriving = "#[derive(%s)]" % super_traits if super_traits else ""
69
70 errors = "\n".join("//~%s ERROR" % ("^" * n) for n in range(error_count))
6571 code = string.format(traits=all_traits, errors=errors)
6672 return TEMPLATE.format(error_deriving=error_deriving, code=code)
6773
6874
6975def write_file(name, string):
70 test_file = os.path.join(TEST_DIR, 'derives-span-%s.rs' % name)
76 test_file = os.path.join(TEST_DIR, "derives-span-%s.rs" % name)
7177
7278 # set write permission if file exists, so it can be changed
7379 if os.path.exists(test_file):
7480 os.chmod(test_file, stat.S_IWUSR)
7581
76 with open(test_file, 'w') as f:
82 with open(test_file, "w") as f:
7783 f.write(string)
7884
7985 # mark file read-only
80 os.chmod(test_file, stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH)
86 os.chmod(test_file, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
8187
8288
8389ENUM = 1
......@@ -85,29 +91,31 @@ STRUCT = 2
8591ALL = STRUCT | ENUM
8692
8793traits = {
88 'Default': (STRUCT, [], 1),
89 'FromPrimitive': (0, [], 0), # only works for C-like enums
90
91 'Decodable': (0, [], 0), # FIXME: quoting gives horrible spans
92 'Encodable': (0, [], 0), # FIXME: quoting gives horrible spans
94 "Default": (STRUCT, [], 1),
95 "FromPrimitive": (0, [], 0), # only works for C-like enums
96 "Decodable": (0, [], 0), # FIXME: quoting gives horrible spans
97 "Encodable": (0, [], 0), # FIXME: quoting gives horrible spans
9398}
9499
95for (trait, supers, errs) in [('Clone', [], 1),
96 ('PartialEq', [], 2),
97 ('PartialOrd', ['PartialEq'], 1),
98 ('Eq', ['PartialEq'], 1),
99 ('Ord', ['Eq', 'PartialOrd', 'PartialEq'], 1),
100 ('Debug', [], 1),
101 ('Hash', [], 1)]:
100for trait, supers, errs in [
101 ("Clone", [], 1),
102 ("PartialEq", [], 2),
103 ("PartialOrd", ["PartialEq"], 1),
104 ("Eq", ["PartialEq"], 1),
105 ("Ord", ["Eq", "PartialOrd", "PartialEq"], 1),
106 ("Debug", [], 1),
107 ("Hash", [], 1),
108]:
102109 traits[trait] = (ALL, supers, errs)
103110
104for (trait, (types, super_traits, error_count)) in traits.items():
111for trait, (types, super_traits, error_count) in traits.items():
112
105113 def mk(ty, t=trait, st=super_traits, ec=error_count):
106114 return create_test_case(ty, t, st, ec)
107115
108116 if types & ENUM:
109 write_file(trait + '-enum', mk(ENUM_TUPLE))
110 write_file(trait + '-enum-struct-variant', mk(ENUM_STRUCT))
117 write_file(trait + "-enum", mk(ENUM_TUPLE))
118 write_file(trait + "-enum-struct-variant", mk(ENUM_STRUCT))
111119 if types & STRUCT:
112 write_file(trait + '-struct', mk(STRUCT_FIELDS))
113 write_file(trait + '-tuple-struct', mk(STRUCT_TUPLE))
120 write_file(trait + "-struct", mk(STRUCT_FIELDS))
121 write_file(trait + "-tuple-struct", mk(STRUCT_TUPLE))
src/etc/generate-keyword-tests.py+3-5
......@@ -22,18 +22,16 @@ fn main() {
2222}
2323"""
2424
25test_dir = os.path.abspath(
26 os.path.join(os.path.dirname(__file__), '../test/ui/parser')
27)
25test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../test/ui/parser"))
2826
2927for kw in sys.argv[1:]:
30 test_file = os.path.join(test_dir, 'keyword-%s-as-identifier.rs' % kw)
28 test_file = os.path.join(test_dir, "keyword-%s-as-identifier.rs" % kw)
3129
3230 # set write permission if file exists, so it can be changed
3331 if os.path.exists(test_file):
3432 os.chmod(test_file, stat.S_IWUSR)
3533
36 with open(test_file, 'wt') as f:
34 with open(test_file, "wt") as f:
3735 f.write(template % (kw, kw, kw))
3836
3937 # mark file read-only
src/etc/htmldocck.py+151-109
......@@ -127,6 +127,7 @@ import os.path
127127import re
128128import shlex
129129from collections import namedtuple
130
130131try:
131132 from html.parser import HTMLParser
132133except ImportError:
......@@ -142,12 +143,28 @@ except ImportError:
142143 from htmlentitydefs import name2codepoint
143144
144145# "void elements" (no closing tag) from the HTML Standard section 12.1.2
145VOID_ELEMENTS = {'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
146 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'}
146VOID_ELEMENTS = {
147 "area",
148 "base",
149 "br",
150 "col",
151 "embed",
152 "hr",
153 "img",
154 "input",
155 "keygen",
156 "link",
157 "menuitem",
158 "meta",
159 "param",
160 "source",
161 "track",
162 "wbr",
163}
147164
148165# Python 2 -> 3 compatibility
149166try:
150 unichr # noqa: B018 FIXME: py2
167 unichr # noqa: B018 FIXME: py2
151168except NameError:
152169 unichr = chr
153170
......@@ -158,18 +175,20 @@ channel = os.environ["DOC_RUST_LANG_ORG_CHANNEL"]
158175rust_test_path = None
159176bless = None
160177
178
161179class CustomHTMLParser(HTMLParser):
162180 """simplified HTML parser.
163181
164182 this is possible because we are dealing with very regular HTML from
165183 rustdoc; we only have to deal with i) void elements and ii) empty
166184 attributes."""
185
167186 def __init__(self, target=None):
168187 HTMLParser.__init__(self)
169188 self.__builder = target or ET.TreeBuilder()
170189
171190 def handle_starttag(self, tag, attrs):
172 attrs = {k: v or '' for k, v in attrs}
191 attrs = {k: v or "" for k, v in attrs}
173192 self.__builder.start(tag, attrs)
174193 if tag in VOID_ELEMENTS:
175194 self.__builder.end(tag)
......@@ -178,7 +197,7 @@ class CustomHTMLParser(HTMLParser):
178197 self.__builder.end(tag)
179198
180199 def handle_startendtag(self, tag, attrs):
181 attrs = {k: v or '' for k, v in attrs}
200 attrs = {k: v or "" for k, v in attrs}
182201 self.__builder.start(tag, attrs)
183202 self.__builder.end(tag)
184203
......@@ -189,7 +208,7 @@ class CustomHTMLParser(HTMLParser):
189208 self.__builder.data(unichr(name2codepoint[name]))
190209
191210 def handle_charref(self, name):
192 code = int(name[1:], 16) if name.startswith(('x', 'X')) else int(name, 10)
211 code = int(name[1:], 16) if name.startswith(("x", "X")) else int(name, 10)
193212 self.__builder.data(unichr(code))
194213
195214 def close(self):
......@@ -197,7 +216,7 @@ class CustomHTMLParser(HTMLParser):
197216 return self.__builder.close()
198217
199218
200Command = namedtuple('Command', 'negated cmd args lineno context')
219Command = namedtuple("Command", "negated cmd args lineno context")
201220
202221
203222class FailedCheck(Exception):
......@@ -216,17 +235,17 @@ def concat_multi_lines(f):
216235 concatenated."""
217236 lastline = None # set to the last line when the last line has a backslash
218237 firstlineno = None
219 catenated = ''
238 catenated = ""
220239 for lineno, line in enumerate(f):
221 line = line.rstrip('\r\n')
240 line = line.rstrip("\r\n")
222241
223242 # strip the common prefix from the current line if needed
224243 if lastline is not None:
225244 common_prefix = os.path.commonprefix([line, lastline])
226 line = line[len(common_prefix):].lstrip()
245 line = line[len(common_prefix) :].lstrip()
227246
228247 firstlineno = firstlineno or lineno
229 if line.endswith('\\'):
248 if line.endswith("\\"):
230249 if lastline is None:
231250 lastline = line[:-1]
232251 catenated += line[:-1]
......@@ -234,10 +253,10 @@ def concat_multi_lines(f):
234253 yield firstlineno, catenated + line
235254 lastline = None
236255 firstlineno = None
237 catenated = ''
256 catenated = ""
238257
239258 if lastline is not None:
240 print_err(lineno, line, 'Trailing backslash at the end of the file')
259 print_err(lineno, line, "Trailing backslash at the end of the file")
241260
242261
243262def get_known_directive_names():
......@@ -253,12 +272,12 @@ def get_known_directive_names():
253272 "tools/compiletest/src/directive-list.rs",
254273 ),
255274 "r",
256 encoding="utf8"
275 encoding="utf8",
257276 ) as fd:
258277 content = fd.read()
259278 return [
260 line.strip().replace('",', '').replace('"', '')
261 for line in content.split('\n')
279 line.strip().replace('",', "").replace('"', "")
280 for line in content.split("\n")
262281 if filter_line(line)
263282 ]
264283
......@@ -269,35 +288,42 @@ def get_known_directive_names():
269288# See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
270289KNOWN_DIRECTIVE_NAMES = get_known_directive_names()
271290
272LINE_PATTERN = re.compile(r'''
291LINE_PATTERN = re.compile(
292 r"""
273293 //@\s+
274294 (?P<negated>!?)(?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
275295 (?P<args>.*)$
276''', re.X | re.UNICODE)
296""",
297 re.X | re.UNICODE,
298)
277299
278300
279301def get_commands(template):
280 with io.open(template, encoding='utf-8') as f:
302 with io.open(template, encoding="utf-8") as f:
281303 for lineno, line in concat_multi_lines(f):
282304 m = LINE_PATTERN.search(line)
283305 if not m:
284306 continue
285307
286 cmd = m.group('cmd')
287 negated = (m.group('negated') == '!')
308 cmd = m.group("cmd")
309 negated = m.group("negated") == "!"
288310 if not negated and cmd in KNOWN_DIRECTIVE_NAMES:
289311 continue
290 args = m.group('args')
312 args = m.group("args")
291313 if args and not args[:1].isspace():
292 print_err(lineno, line, 'Invalid template syntax')
314 print_err(lineno, line, "Invalid template syntax")
293315 continue
294316 try:
295317 args = shlex.split(args)
296318 except UnicodeEncodeError:
297 args = [arg.decode('utf-8') for arg in shlex.split(args.encode('utf-8'))]
319 args = [
320 arg.decode("utf-8") for arg in shlex.split(args.encode("utf-8"))
321 ]
298322 except Exception as exc:
299323 raise Exception("line {}: {}".format(lineno + 1, exc)) from None
300 yield Command(negated=negated, cmd=cmd, args=args, lineno=lineno+1, context=line)
324 yield Command(
325 negated=negated, cmd=cmd, args=args, lineno=lineno + 1, context=line
326 )
301327
302328
303329def _flatten(node, acc):
......@@ -312,22 +338,24 @@ def _flatten(node, acc):
312338def flatten(node):
313339 acc = []
314340 _flatten(node, acc)
315 return ''.join(acc)
341 return "".join(acc)
316342
317343
318344def make_xml(text):
319 xml = ET.XML('<xml>%s</xml>' % text)
345 xml = ET.XML("<xml>%s</xml>" % text)
320346 return xml
321347
322348
323349def normalize_xpath(path):
324350 path = path.replace("{{channel}}", channel)
325 if path.startswith('//'):
326 return '.' + path # avoid warnings
327 elif path.startswith('.//'):
351 if path.startswith("//"):
352 return "." + path # avoid warnings
353 elif path.startswith(".//"):
328354 return path
329355 else:
330 raise InvalidCheck('Non-absolute XPath is not supported due to implementation issues')
356 raise InvalidCheck(
357 "Non-absolute XPath is not supported due to implementation issues"
358 )
331359
332360
333361class CachedFiles(object):
......@@ -338,12 +366,12 @@ class CachedFiles(object):
338366 self.last_path = None
339367
340368 def resolve_path(self, path):
341 if path != '-':
369 if path != "-":
342370 path = os.path.normpath(path)
343371 self.last_path = path
344372 return path
345373 elif self.last_path is None:
346 raise InvalidCheck('Tried to use the previous path in the first command')
374 raise InvalidCheck("Tried to use the previous path in the first command")
347375 else:
348376 return self.last_path
349377
......@@ -356,10 +384,10 @@ class CachedFiles(object):
356384 return self.files[path]
357385
358386 abspath = self.get_absolute_path(path)
359 if not(os.path.exists(abspath) and os.path.isfile(abspath)):
360 raise FailedCheck('File does not exist {!r}'.format(path))
387 if not (os.path.exists(abspath) and os.path.isfile(abspath)):
388 raise FailedCheck("File does not exist {!r}".format(path))
361389
362 with io.open(abspath, encoding='utf-8') as f:
390 with io.open(abspath, encoding="utf-8") as f:
363391 data = f.read()
364392 self.files[path] = data
365393 return data
......@@ -370,15 +398,15 @@ class CachedFiles(object):
370398 return self.trees[path]
371399
372400 abspath = self.get_absolute_path(path)
373 if not(os.path.exists(abspath) and os.path.isfile(abspath)):
374 raise FailedCheck('File does not exist {!r}'.format(path))
401 if not (os.path.exists(abspath) and os.path.isfile(abspath)):
402 raise FailedCheck("File does not exist {!r}".format(path))
375403
376 with io.open(abspath, encoding='utf-8') as f:
404 with io.open(abspath, encoding="utf-8") as f:
377405 try:
378406 tree = ET.fromstringlist(f.readlines(), CustomHTMLParser())
379407 except Exception as e:
380 raise RuntimeError( # noqa: B904 FIXME: py2
381 'Cannot parse an HTML file {!r}: {}'.format(path, e)
408 raise RuntimeError( # noqa: B904 FIXME: py2
409 "Cannot parse an HTML file {!r}: {}".format(path, e)
382410 )
383411 self.trees[path] = tree
384412 return self.trees[path]
......@@ -386,8 +414,8 @@ class CachedFiles(object):
386414 def get_dir(self, path):
387415 path = self.resolve_path(path)
388416 abspath = self.get_absolute_path(path)
389 if not(os.path.exists(abspath) and os.path.isdir(abspath)):
390 raise FailedCheck('Directory does not exist {!r}'.format(path))
417 if not (os.path.exists(abspath) and os.path.isdir(abspath)):
418 raise FailedCheck("Directory does not exist {!r}".format(path))
391419
392420
393421def check_string(data, pat, regexp):
......@@ -397,8 +425,8 @@ def check_string(data, pat, regexp):
397425 elif regexp:
398426 return re.search(pat, data, flags=re.UNICODE) is not None
399427 else:
400 data = ' '.join(data.split())
401 pat = ' '.join(pat.split())
428 data = " ".join(data.split())
429 pat = " ".join(pat.split())
402430 return pat in data
403431
404432
......@@ -444,19 +472,19 @@ def get_tree_count(tree, path):
444472
445473
446474def check_snapshot(snapshot_name, actual_tree, normalize_to_text):
447 assert rust_test_path.endswith('.rs')
448 snapshot_path = '{}.{}.{}'.format(rust_test_path[:-3], snapshot_name, 'html')
475 assert rust_test_path.endswith(".rs")
476 snapshot_path = "{}.{}.{}".format(rust_test_path[:-3], snapshot_name, "html")
449477 try:
450 with open(snapshot_path, 'r') as snapshot_file:
478 with open(snapshot_path, "r") as snapshot_file:
451479 expected_str = snapshot_file.read().replace("{{channel}}", channel)
452480 except FileNotFoundError:
453481 if bless:
454482 expected_str = None
455483 else:
456 raise FailedCheck('No saved snapshot value') # noqa: B904 FIXME: py2
484 raise FailedCheck("No saved snapshot value") # noqa: B904 FIXME: py2
457485
458486 if not normalize_to_text:
459 actual_str = ET.tostring(actual_tree).decode('utf-8')
487 actual_str = ET.tostring(actual_tree).decode("utf-8")
460488 else:
461489 actual_str = flatten(actual_tree)
462490
......@@ -464,64 +492,66 @@ def check_snapshot(snapshot_name, actual_tree, normalize_to_text):
464492 # 1. Is --bless
465493 # 2. Are actual and expected tree different
466494 # 3. Are actual and expected text different
467 if not expected_str \
468 or (not normalize_to_text and \
469 not compare_tree(make_xml(actual_str), make_xml(expected_str), stderr)) \
470 or (normalize_to_text and actual_str != expected_str):
471
495 if (
496 not expected_str
497 or (
498 not normalize_to_text
499 and not compare_tree(make_xml(actual_str), make_xml(expected_str), stderr)
500 )
501 or (normalize_to_text and actual_str != expected_str)
502 ):
472503 if bless:
473 with open(snapshot_path, 'w') as snapshot_file:
504 with open(snapshot_path, "w") as snapshot_file:
474505 actual_str = actual_str.replace(channel, "{{channel}}")
475506 snapshot_file.write(actual_str)
476507 else:
477 print('--- expected ---\n')
508 print("--- expected ---\n")
478509 print(expected_str)
479 print('\n\n--- actual ---\n')
510 print("\n\n--- actual ---\n")
480511 print(actual_str)
481512 print()
482 raise FailedCheck('Actual snapshot value is different than expected')
513 raise FailedCheck("Actual snapshot value is different than expected")
483514
484515
485516# Adapted from https://github.com/formencode/formencode/blob/3a1ba9de2fdd494dd945510a4568a3afeddb0b2e/formencode/doctest_xml_compare.py#L72-L120
486517def compare_tree(x1, x2, reporter=None):
487518 if x1.tag != x2.tag:
488519 if reporter:
489 reporter('Tags do not match: %s and %s' % (x1.tag, x2.tag))
520 reporter("Tags do not match: %s and %s" % (x1.tag, x2.tag))
490521 return False
491522 for name, value in x1.attrib.items():
492523 if x2.attrib.get(name) != value:
493524 if reporter:
494 reporter('Attributes do not match: %s=%r, %s=%r'
495 % (name, value, name, x2.attrib.get(name)))
525 reporter(
526 "Attributes do not match: %s=%r, %s=%r"
527 % (name, value, name, x2.attrib.get(name))
528 )
496529 return False
497530 for name in x2.attrib:
498531 if name not in x1.attrib:
499532 if reporter:
500 reporter('x2 has an attribute x1 is missing: %s'
501 % name)
533 reporter("x2 has an attribute x1 is missing: %s" % name)
502534 return False
503535 if not text_compare(x1.text, x2.text):
504536 if reporter:
505 reporter('text: %r != %r' % (x1.text, x2.text))
537 reporter("text: %r != %r" % (x1.text, x2.text))
506538 return False
507539 if not text_compare(x1.tail, x2.tail):
508540 if reporter:
509 reporter('tail: %r != %r' % (x1.tail, x2.tail))
541 reporter("tail: %r != %r" % (x1.tail, x2.tail))
510542 return False
511543 cl1 = list(x1)
512544 cl2 = list(x2)
513545 if len(cl1) != len(cl2):
514546 if reporter:
515 reporter('children length differs, %i != %i'
516 % (len(cl1), len(cl2)))
547 reporter("children length differs, %i != %i" % (len(cl1), len(cl2)))
517548 return False
518549 i = 0
519550 for c1, c2 in zip(cl1, cl2):
520551 i += 1
521552 if not compare_tree(c1, c2, reporter=reporter):
522553 if reporter:
523 reporter('children %i do not match: %s'
524 % (i, c1.tag))
554 reporter("children %i do not match: %s" % (i, c1.tag))
525555 return False
526556 return True
527557
......@@ -529,14 +559,14 @@ def compare_tree(x1, x2, reporter=None):
529559def text_compare(t1, t2):
530560 if not t1 and not t2:
531561 return True
532 if t1 == '*' or t2 == '*':
562 if t1 == "*" or t2 == "*":
533563 return True
534 return (t1 or '').strip() == (t2 or '').strip()
564 return (t1 or "").strip() == (t2 or "").strip()
535565
536566
537567def stderr(*args):
538568 if sys.version_info.major < 3:
539 file = codecs.getwriter('utf-8')(sys.stderr)
569 file = codecs.getwriter("utf-8")(sys.stderr)
540570 else:
541571 file = sys.stderr
542572
......@@ -556,21 +586,25 @@ def print_err(lineno, context, err, message=None):
556586
557587def get_nb_matching_elements(cache, c, regexp, stop_at_first):
558588 tree = cache.get_tree(c.args[0])
559 pat, sep, attr = c.args[1].partition('/@')
589 pat, sep, attr = c.args[1].partition("/@")
560590 if sep: # attribute
561591 tree = cache.get_tree(c.args[0])
562592 return check_tree_attr(tree, pat, attr, c.args[2], False)
563593 else: # normalized text
564594 pat = c.args[1]
565 if pat.endswith('/text()'):
595 if pat.endswith("/text()"):
566596 pat = pat[:-7]
567 return check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp, stop_at_first)
597 return check_tree_text(
598 cache.get_tree(c.args[0]), pat, c.args[2], regexp, stop_at_first
599 )
568600
569601
570602def check_files_in_folder(c, cache, folder, files):
571603 files = files.strip()
572 if not files.startswith('[') or not files.endswith(']'):
573 raise InvalidCheck("Expected list as second argument of {} (ie '[]')".format(c.cmd))
604 if not files.startswith("[") or not files.endswith("]"):
605 raise InvalidCheck(
606 "Expected list as second argument of {} (ie '[]')".format(c.cmd)
607 )
574608
575609 folder = cache.get_absolute_path(folder)
576610
......@@ -592,12 +626,18 @@ def check_files_in_folder(c, cache, folder, files):
592626
593627 error = 0
594628 if len(files_set) != 0:
595 print_err(c.lineno, c.context, "Entries not found in folder `{}`: `{}`".format(
596 folder, files_set))
629 print_err(
630 c.lineno,
631 c.context,
632 "Entries not found in folder `{}`: `{}`".format(folder, files_set),
633 )
597634 error += 1
598635 if len(folder_set) != 0:
599 print_err(c.lineno, c.context, "Extra entries in folder `{}`: `{}`".format(
600 folder, folder_set))
636 print_err(
637 c.lineno,
638 c.context,
639 "Extra entries in folder `{}`: `{}`".format(folder, folder_set),
640 )
601641 error += 1
602642 return error == 0
603643
......@@ -608,11 +648,11 @@ ERR_COUNT = 0
608648def check_command(c, cache):
609649 try:
610650 cerr = ""
611 if c.cmd in ['has', 'hasraw', 'matches', 'matchesraw']: # string test
612 regexp = c.cmd.startswith('matches')
651 if c.cmd in ["has", "hasraw", "matches", "matchesraw"]: # string test
652 regexp = c.cmd.startswith("matches")
613653
614654 # has <path> = file existence
615 if len(c.args) == 1 and not regexp and 'raw' not in c.cmd:
655 if len(c.args) == 1 and not regexp and "raw" not in c.cmd:
616656 try:
617657 cache.get_file(c.args[0])
618658 ret = True
......@@ -620,24 +660,24 @@ def check_command(c, cache):
620660 cerr = str(err)
621661 ret = False
622662 # hasraw/matchesraw <path> <pat> = string test
623 elif len(c.args) == 2 and 'raw' in c.cmd:
663 elif len(c.args) == 2 and "raw" in c.cmd:
624664 cerr = "`PATTERN` did not match"
625665 ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
626666 # has/matches <path> <pat> <match> = XML tree test
627 elif len(c.args) == 3 and 'raw' not in c.cmd:
667 elif len(c.args) == 3 and "raw" not in c.cmd:
628668 cerr = "`XPATH PATTERN` did not match"
629669 ret = get_nb_matching_elements(cache, c, regexp, True) != 0
630670 else:
631 raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd))
671 raise InvalidCheck("Invalid number of {} arguments".format(c.cmd))
632672
633 elif c.cmd == 'files': # check files in given folder
634 if len(c.args) != 2: # files <folder path> <file list>
673 elif c.cmd == "files": # check files in given folder
674 if len(c.args) != 2: # files <folder path> <file list>
635675 raise InvalidCheck("Invalid number of {} arguments".format(c.cmd))
636676 elif c.negated:
637677 raise InvalidCheck("{} doesn't support negative check".format(c.cmd))
638678 ret = check_files_in_folder(c, cache, c.args[0], c.args[1])
639679
640 elif c.cmd == 'count': # count test
680 elif c.cmd == "count": # count test
641681 if len(c.args) == 3: # count <path> <pat> <count> = count test
642682 expected = int(c.args[2])
643683 found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
......@@ -649,15 +689,15 @@ def check_command(c, cache):
649689 cerr = "Expected {} occurrences but found {}".format(expected, found)
650690 ret = found == expected
651691 else:
652 raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd))
692 raise InvalidCheck("Invalid number of {} arguments".format(c.cmd))
653693
654 elif c.cmd == 'snapshot': # snapshot test
694 elif c.cmd == "snapshot": # snapshot test
655695 if len(c.args) == 3: # snapshot <snapshot-name> <html-path> <xpath>
656696 [snapshot_name, html_path, pattern] = c.args
657697 tree = cache.get_tree(html_path)
658698 xpath = normalize_xpath(pattern)
659699 normalize_to_text = False
660 if xpath.endswith('/text()'):
700 if xpath.endswith("/text()"):
661701 xpath = xpath[:-7]
662702 normalize_to_text = True
663703
......@@ -671,13 +711,15 @@ def check_command(c, cache):
671711 cerr = str(err)
672712 ret = False
673713 elif len(subtrees) == 0:
674 raise FailedCheck('XPATH did not match')
714 raise FailedCheck("XPATH did not match")
675715 else:
676 raise FailedCheck('Expected 1 match, but found {}'.format(len(subtrees)))
716 raise FailedCheck(
717 "Expected 1 match, but found {}".format(len(subtrees))
718 )
677719 else:
678 raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd))
720 raise InvalidCheck("Invalid number of {} arguments".format(c.cmd))
679721
680 elif c.cmd == 'has-dir': # has-dir test
722 elif c.cmd == "has-dir": # has-dir test
681723 if len(c.args) == 1: # has-dir <path> = has-dir test
682724 try:
683725 cache.get_dir(c.args[0])
......@@ -686,22 +728,22 @@ def check_command(c, cache):
686728 cerr = str(err)
687729 ret = False
688730 else:
689 raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd))
731 raise InvalidCheck("Invalid number of {} arguments".format(c.cmd))
690732
691 elif c.cmd == 'valid-html':
692 raise InvalidCheck('Unimplemented valid-html')
733 elif c.cmd == "valid-html":
734 raise InvalidCheck("Unimplemented valid-html")
693735
694 elif c.cmd == 'valid-links':
695 raise InvalidCheck('Unimplemented valid-links')
736 elif c.cmd == "valid-links":
737 raise InvalidCheck("Unimplemented valid-links")
696738
697739 else:
698 raise InvalidCheck('Unrecognized {}'.format(c.cmd))
740 raise InvalidCheck("Unrecognized {}".format(c.cmd))
699741
700742 if ret == c.negated:
701743 raise FailedCheck(cerr)
702744
703745 except FailedCheck as err:
704 message = '{}{} check failed'.format('!' if c.negated else '', c.cmd)
746 message = "{}{} check failed".format("!" if c.negated else "", c.cmd)
705747 print_err(c.lineno, c.context, str(err), message)
706748 except InvalidCheck as err:
707749 print_err(c.lineno, c.context, str(err))
......@@ -713,18 +755,18 @@ def check(target, commands):
713755 check_command(c, cache)
714756
715757
716if __name__ == '__main__':
758if __name__ == "__main__":
717759 if len(sys.argv) not in [3, 4]:
718 stderr('Usage: {} <doc dir> <template> [--bless]'.format(sys.argv[0]))
760 stderr("Usage: {} <doc dir> <template> [--bless]".format(sys.argv[0]))
719761 raise SystemExit(1)
720762
721763 rust_test_path = sys.argv[2]
722 if len(sys.argv) > 3 and sys.argv[3] == '--bless':
764 if len(sys.argv) > 3 and sys.argv[3] == "--bless":
723765 bless = True
724766 else:
725767 # We only support `--bless` at the end of the arguments.
726768 # This assert is to prevent silent failures.
727 assert '--bless' not in sys.argv
769 assert "--bless" not in sys.argv
728770 bless = False
729771 check(sys.argv[1], get_commands(rust_test_path))
730772 if ERR_COUNT:
src/etc/lldb_batchmode.py+48-21
......@@ -45,7 +45,7 @@ def normalize_whitespace(s):
4545
4646def breakpoint_callback(frame, bp_loc, dict):
4747 """This callback is registered with every breakpoint and makes sure that the
48 frame containing the breakpoint location is selected """
48 frame containing the breakpoint location is selected"""
4949
5050 # HACK(eddyb) print a newline to avoid continuing an unfinished line.
5151 print("")
......@@ -79,7 +79,7 @@ def execute_command(command_interpreter, command):
7979
8080 if res.Succeeded():
8181 if res.HasResult():
82 print(normalize_whitespace(res.GetOutput() or ''), end='\n')
82 print(normalize_whitespace(res.GetOutput() or ""), end="\n")
8383
8484 # If the command introduced any breakpoints, make sure to register
8585 # them with the breakpoint
......@@ -89,20 +89,32 @@ def execute_command(command_interpreter, command):
8989 breakpoint_id = new_breakpoints.pop()
9090
9191 if breakpoint_id in registered_breakpoints:
92 print_debug("breakpoint with id %s is already registered. Ignoring." %
93 str(breakpoint_id))
92 print_debug(
93 "breakpoint with id %s is already registered. Ignoring."
94 % str(breakpoint_id)
95 )
9496 else:
95 print_debug("registering breakpoint callback, id = " + str(breakpoint_id))
96 callback_command = ("breakpoint command add -F breakpoint_callback " +
97 str(breakpoint_id))
97 print_debug(
98 "registering breakpoint callback, id = " + str(breakpoint_id)
99 )
100 callback_command = (
101 "breakpoint command add -F breakpoint_callback "
102 + str(breakpoint_id)
103 )
98104 command_interpreter.HandleCommand(callback_command, res)
99105 if res.Succeeded():
100 print_debug("successfully registered breakpoint callback, id = " +
101 str(breakpoint_id))
106 print_debug(
107 "successfully registered breakpoint callback, id = "
108 + str(breakpoint_id)
109 )
102110 registered_breakpoints.add(breakpoint_id)
103111 else:
104 print("Error while trying to register breakpoint callback, id = " +
105 str(breakpoint_id) + ", message = " + str(res.GetError()))
112 print(
113 "Error while trying to register breakpoint callback, id = "
114 + str(breakpoint_id)
115 + ", message = "
116 + str(res.GetError())
117 )
106118 else:
107119 print(res.GetError())
108120
......@@ -117,14 +129,16 @@ def start_breakpoint_listener(target):
117129 try:
118130 while True:
119131 if listener.WaitForEvent(120, event):
120 if lldb.SBBreakpoint.EventIsBreakpointEvent(event) and \
121 lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event) == \
122 lldb.eBreakpointEventTypeAdded:
132 if (
133 lldb.SBBreakpoint.EventIsBreakpointEvent(event)
134 and lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event)
135 == lldb.eBreakpointEventTypeAdded
136 ):
123137 global new_breakpoints
124138 breakpoint = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
125139 print_debug("breakpoint added, id = " + str(breakpoint.id))
126140 new_breakpoints.append(breakpoint.id)
127 except BaseException: # explicitly catch ctrl+c/sysexit
141 except BaseException: # explicitly catch ctrl+c/sysexit
128142 print_debug("breakpoint listener shutting down")
129143
130144 # Start the listener and let it run as a daemon
......@@ -133,7 +147,9 @@ def start_breakpoint_listener(target):
133147 listener_thread.start()
134148
135149 # Register the listener with the target
136 target.GetBroadcaster().AddListener(listener, lldb.SBTarget.eBroadcastBitBreakpointChanged)
150 target.GetBroadcaster().AddListener(
151 listener, lldb.SBTarget.eBroadcastBitBreakpointChanged
152 )
137153
138154
139155def start_watchdog():
......@@ -159,6 +175,7 @@ def start_watchdog():
159175 watchdog_thread.daemon = True
160176 watchdog_thread.start()
161177
178
162179####################################################################################################
163180# ~main
164181####################################################################################################
......@@ -193,8 +210,14 @@ target_error = lldb.SBError()
193210target = debugger.CreateTarget(target_path, None, None, True, target_error)
194211
195212if not target:
196 print("Could not create debugging target '" + target_path + "': " +
197 str(target_error) + ". Aborting.", file=sys.stderr)
213 print(
214 "Could not create debugging target '"
215 + target_path
216 + "': "
217 + str(target_error)
218 + ". Aborting.",
219 file=sys.stderr,
220 )
198221 sys.exit(1)
199222
200223
......@@ -204,15 +227,19 @@ start_breakpoint_listener(target)
204227command_interpreter = debugger.GetCommandInterpreter()
205228
206229try:
207 script_file = open(script_path, 'r')
230 script_file = open(script_path, "r")
208231
209232 for line in script_file:
210233 command = line.strip()
211 if command == "run" or command == "r" or re.match("^process\s+launch.*", command):
234 if (
235 command == "run"
236 or command == "r"
237 or re.match("^process\s+launch.*", command)
238 ):
212239 # Before starting to run the program, let the thread sleep a bit, so all
213240 # breakpoint added events can be processed
214241 time.sleep(0.5)
215 if command != '':
242 if command != "":
216243 execute_command(command_interpreter, command)
217244
218245except IOError as e:
src/etc/lldb_providers.py+131-55
......@@ -1,7 +1,12 @@
11import sys
22
3from lldb import SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \
4 eBasicTypeUnsignedChar
3from lldb import (
4 SBData,
5 SBError,
6 eBasicTypeLong,
7 eBasicTypeUnsignedLong,
8 eBasicTypeUnsignedChar,
9)
510
611# from lldb.formatters import Logger
712
......@@ -50,13 +55,17 @@ class ValueBuilder:
5055 def from_int(self, name, value):
5156 # type: (str, int) -> SBValue
5257 type = self.valobj.GetType().GetBasicType(eBasicTypeLong)
53 data = SBData.CreateDataFromSInt64Array(self.endianness, self.pointer_size, [value])
58 data = SBData.CreateDataFromSInt64Array(
59 self.endianness, self.pointer_size, [value]
60 )
5461 return self.valobj.CreateValueFromData(name, data, type)
5562
5663 def from_uint(self, name, value):
5764 # type: (str, int) -> SBValue
5865 type = self.valobj.GetType().GetBasicType(eBasicTypeUnsignedLong)
59 data = SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [value])
66 data = SBData.CreateDataFromUInt64Array(
67 self.endianness, self.pointer_size, [value]
68 )
6069 return self.valobj.CreateValueFromData(name, data, type)
6170
6271
......@@ -127,13 +136,17 @@ class EmptySyntheticProvider:
127136
128137def SizeSummaryProvider(valobj, dict):
129138 # type: (SBValue, dict) -> str
130 return 'size=' + str(valobj.GetNumChildren())
139 return "size=" + str(valobj.GetNumChildren())
131140
132141
133142def vec_to_string(vec):
134143 length = vec.GetNumChildren()
135144 chars = [vec.GetChildAtIndex(i).GetValueAsUnsigned() for i in range(length)]
136 return bytes(chars).decode(errors='replace') if PY3 else "".join(chr(char) for char in chars)
145 return (
146 bytes(chars).decode(errors="replace")
147 if PY3
148 else "".join(chr(char) for char in chars)
149 )
137150
138151
139152def StdStringSummaryProvider(valobj, dict):
......@@ -172,7 +185,7 @@ def StdStrSummaryProvider(valobj, dict):
172185 error = SBError()
173186 process = data_ptr.GetProcess()
174187 data = process.ReadMemory(start, length, error)
175 data = data.decode(encoding='UTF-8') if PY3 else data
188 data = data.decode(encoding="UTF-8") if PY3 else data
176189 return '"%s"' % data
177190
178191
......@@ -199,9 +212,9 @@ def StdPathSummaryProvider(valobj, dict):
199212 data = process.ReadMemory(start, length, error)
200213 if PY3:
201214 try:
202 data = data.decode(encoding='UTF-8')
215 data = data.decode(encoding="UTF-8")
203216 except UnicodeDecodeError:
204 return '%r' % data
217 return "%r" % data
205218 return '"%s"' % data
206219
207220
......@@ -250,8 +263,10 @@ class StructSyntheticProvider:
250263 # type: () -> bool
251264 return True
252265
266
253267class ClangEncodedEnumProvider:
254268 """Pretty-printer for 'clang-encoded' enums support implemented in LLDB"""
269
255270 DISCRIMINANT_MEMBER_NAME = "$discr$"
256271 VALUE_MEMBER_NAME = "value"
257272
......@@ -260,7 +275,7 @@ class ClangEncodedEnumProvider:
260275 self.update()
261276
262277 def has_children(self):
263 return True
278 return True
264279
265280 def num_children(self):
266281 if self.is_default:
......@@ -276,25 +291,32 @@ class ClangEncodedEnumProvider:
276291
277292 def get_child_at_index(self, index):
278293 if index == 0:
279 return self.variant.GetChildMemberWithName(ClangEncodedEnumProvider.VALUE_MEMBER_NAME)
294 return self.variant.GetChildMemberWithName(
295 ClangEncodedEnumProvider.VALUE_MEMBER_NAME
296 )
280297 if index == 1:
281298 return self.variant.GetChildMemberWithName(
282 ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME)
283
299 ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME
300 )
284301
285302 def update(self):
286303 all_variants = self.valobj.GetChildAtIndex(0)
287304 index = self._getCurrentVariantIndex(all_variants)
288305 self.variant = all_variants.GetChildAtIndex(index)
289 self.is_default = self.variant.GetIndexOfChildWithName(
290 ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME) == -1
306 self.is_default = (
307 self.variant.GetIndexOfChildWithName(
308 ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME
309 )
310 == -1
311 )
291312
292313 def _getCurrentVariantIndex(self, all_variants):
293314 default_index = 0
294315 for i in range(all_variants.GetNumChildren()):
295316 variant = all_variants.GetChildAtIndex(i)
296317 discr = variant.GetChildMemberWithName(
297 ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME)
318 ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME
319 )
298320 if discr.IsValid():
299321 discr_unsigned_value = discr.GetValueAsUnsigned()
300322 if variant.GetName() == f"$variant${discr_unsigned_value}":
......@@ -303,6 +325,7 @@ class ClangEncodedEnumProvider:
303325 default_index = i
304326 return default_index
305327
328
306329class TupleSyntheticProvider:
307330 """Pretty-printer for tuples and tuple enum variants"""
308331
......@@ -336,7 +359,9 @@ class TupleSyntheticProvider:
336359 else:
337360 field = self.type.GetFieldAtIndex(index)
338361 element = self.valobj.GetChildMemberWithName(field.name)
339 return self.valobj.CreateValueFromData(str(index), element.GetData(), element.GetType())
362 return self.valobj.CreateValueFromData(
363 str(index), element.GetData(), element.GetType()
364 )
340365
341366 def update(self):
342367 # type: () -> None
......@@ -373,7 +398,7 @@ class StdVecSyntheticProvider:
373398
374399 def get_child_index(self, name):
375400 # type: (str) -> int
376 index = name.lstrip('[').rstrip(']')
401 index = name.lstrip("[").rstrip("]")
377402 if index.isdigit():
378403 return int(index)
379404 else:
......@@ -383,15 +408,21 @@ class StdVecSyntheticProvider:
383408 # type: (int) -> SBValue
384409 start = self.data_ptr.GetValueAsUnsigned()
385410 address = start + index * self.element_type_size
386 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
411 element = self.data_ptr.CreateValueFromAddress(
412 "[%s]" % index, address, self.element_type
413 )
387414 return element
388415
389416 def update(self):
390417 # type: () -> None
391418 self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned()
392 self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName("inner")
419 self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName(
420 "inner"
421 )
393422
394 self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr"))
423 self.data_ptr = unwrap_unique_or_non_null(
424 self.buf.GetChildMemberWithName("ptr")
425 )
395426
396427 self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
397428 self.element_type_size = self.element_type.GetByteSize()
......@@ -412,7 +443,7 @@ class StdSliceSyntheticProvider:
412443
413444 def get_child_index(self, name):
414445 # type: (str) -> int
415 index = name.lstrip('[').rstrip(']')
446 index = name.lstrip("[").rstrip("]")
416447 if index.isdigit():
417448 return int(index)
418449 else:
......@@ -422,7 +453,9 @@ class StdSliceSyntheticProvider:
422453 # type: (int) -> SBValue
423454 start = self.data_ptr.GetValueAsUnsigned()
424455 address = start + index * self.element_type_size
425 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
456 element = self.data_ptr.CreateValueFromAddress(
457 "[%s]" % index, address, self.element_type
458 )
426459 return element
427460
428461 def update(self):
......@@ -457,7 +490,7 @@ class StdVecDequeSyntheticProvider:
457490
458491 def get_child_index(self, name):
459492 # type: (str) -> int
460 index = name.lstrip('[').rstrip(']')
493 index = name.lstrip("[").rstrip("]")
461494 if index.isdigit() and int(index) < self.size:
462495 return int(index)
463496 else:
......@@ -467,20 +500,26 @@ class StdVecDequeSyntheticProvider:
467500 # type: (int) -> SBValue
468501 start = self.data_ptr.GetValueAsUnsigned()
469502 address = start + ((index + self.head) % self.cap) * self.element_type_size
470 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
503 element = self.data_ptr.CreateValueFromAddress(
504 "[%s]" % index, address, self.element_type
505 )
471506 return element
472507
473508 def update(self):
474509 # type: () -> None
475510 self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned()
476511 self.size = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned()
477 self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName("inner")
512 self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName(
513 "inner"
514 )
478515 cap = self.buf.GetChildMemberWithName("cap")
479516 if cap.GetType().num_fields == 1:
480517 cap = cap.GetChildAtIndex(0)
481518 self.cap = cap.GetValueAsUnsigned()
482519
483 self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr"))
520 self.data_ptr = unwrap_unique_or_non_null(
521 self.buf.GetChildMemberWithName("ptr")
522 )
484523
485524 self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
486525 self.element_type_size = self.element_type.GetByteSize()
......@@ -510,7 +549,7 @@ class StdOldHashMapSyntheticProvider:
510549
511550 def get_child_index(self, name):
512551 # type: (str) -> int
513 index = name.lstrip('[').rstrip(']')
552 index = name.lstrip("[").rstrip("]")
514553 if index.isdigit():
515554 return int(index)
516555 else:
......@@ -525,8 +564,14 @@ class StdOldHashMapSyntheticProvider:
525564 hashes = self.hash_uint_size * self.capacity
526565 align = self.pair_type_size
527566 # See `libcore/alloc.rs:padding_needed_for`
528 len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~(
529 (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo
567 len_rounded_up = (
568 (
569 (((hashes + align) % self.modulo - 1) % self.modulo)
570 & ~((align - 1) % self.modulo)
571 )
572 % self.modulo
573 - hashes
574 ) % self.modulo
530575 # len_rounded_up = ((hashes + align - 1) & ~(align - 1)) - hashes
531576
532577 pairs_offset = hashes + len_rounded_up
......@@ -535,12 +580,16 @@ class StdOldHashMapSyntheticProvider:
535580 table_index = self.valid_indices[index]
536581 idx = table_index & self.capacity_mask
537582 address = pairs_start + idx * self.pair_type_size
538 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
583 element = self.data_ptr.CreateValueFromAddress(
584 "[%s]" % index, address, self.pair_type
585 )
539586 if self.show_values:
540587 return element
541588 else:
542589 key = element.GetChildAtIndex(0)
543 return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType())
590 return self.valobj.CreateValueFromData(
591 "[%s]" % index, key.GetData(), key.GetType()
592 )
544593
545594 def update(self):
546595 # type: () -> None
......@@ -551,10 +600,12 @@ class StdOldHashMapSyntheticProvider:
551600 self.hashes = self.table.GetChildMemberWithName("hashes")
552601 self.hash_uint_type = self.hashes.GetType()
553602 self.hash_uint_size = self.hashes.GetType().GetByteSize()
554 self.modulo = 2 ** self.hash_uint_size
603 self.modulo = 2**self.hash_uint_size
555604 self.data_ptr = self.hashes.GetChildAtIndex(0).GetChildAtIndex(0)
556605
557 self.capacity_mask = self.table.GetChildMemberWithName("capacity_mask").GetValueAsUnsigned()
606 self.capacity_mask = self.table.GetChildMemberWithName(
607 "capacity_mask"
608 ).GetValueAsUnsigned()
558609 self.capacity = (self.capacity_mask + 1) % self.modulo
559610
560611 marker = self.table.GetChildMemberWithName("marker").GetType() # type: SBType
......@@ -564,8 +615,9 @@ class StdOldHashMapSyntheticProvider:
564615 self.valid_indices = []
565616 for idx in range(self.capacity):
566617 address = self.data_ptr.GetValueAsUnsigned() + idx * self.hash_uint_size
567 hash_uint = self.data_ptr.CreateValueFromAddress("[%s]" % idx, address,
568 self.hash_uint_type)
618 hash_uint = self.data_ptr.CreateValueFromAddress(
619 "[%s]" % idx, address, self.hash_uint_type
620 )
569621 hash_ptr = hash_uint.GetChildAtIndex(0).GetChildAtIndex(0)
570622 if hash_ptr.GetValueAsUnsigned() != 0:
571623 self.valid_indices.append(idx)
......@@ -592,7 +644,7 @@ class StdHashMapSyntheticProvider:
592644
593645 def get_child_index(self, name):
594646 # type: (str) -> int
595 index = name.lstrip('[').rstrip(']')
647 index = name.lstrip("[").rstrip("]")
596648 if index.isdigit():
597649 return int(index)
598650 else:
......@@ -605,19 +657,25 @@ class StdHashMapSyntheticProvider:
605657 if self.new_layout:
606658 idx = -(idx + 1)
607659 address = pairs_start + idx * self.pair_type_size
608 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
660 element = self.data_ptr.CreateValueFromAddress(
661 "[%s]" % index, address, self.pair_type
662 )
609663 if self.show_values:
610664 return element
611665 else:
612666 key = element.GetChildAtIndex(0)
613 return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType())
667 return self.valobj.CreateValueFromData(
668 "[%s]" % index, key.GetData(), key.GetType()
669 )
614670
615671 def update(self):
616672 # type: () -> None
617673 table = self.table()
618674 inner_table = table.GetChildMemberWithName("table")
619675
620 capacity = inner_table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1
676 capacity = (
677 inner_table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1
678 )
621679 ctrl = inner_table.GetChildMemberWithName("ctrl").GetChildAtIndex(0)
622680
623681 self.size = inner_table.GetChildMemberWithName("items").GetValueAsUnsigned()
......@@ -630,16 +688,21 @@ class StdHashMapSyntheticProvider:
630688 if self.new_layout:
631689 self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType())
632690 else:
633 self.data_ptr = inner_table.GetChildMemberWithName("data").GetChildAtIndex(0)
691 self.data_ptr = inner_table.GetChildMemberWithName("data").GetChildAtIndex(
692 0
693 )
634694
635695 u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar)
636 u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize()
696 u8_type_size = (
697 self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize()
698 )
637699
638700 self.valid_indices = []
639701 for idx in range(capacity):
640702 address = ctrl.GetValueAsUnsigned() + idx * u8_type_size
641 value = ctrl.CreateValueFromAddress("ctrl[%s]" % idx, address,
642 u8_type).GetValueAsUnsigned()
703 value = ctrl.CreateValueFromAddress(
704 "ctrl[%s]" % idx, address, u8_type
705 ).GetValueAsUnsigned()
643706 is_present = value & 128 == 0
644707 if is_present:
645708 self.valid_indices.append(idx)
......@@ -691,10 +754,16 @@ class StdRcSyntheticProvider:
691754
692755 self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value")
693756
694 self.strong = self.ptr.GetChildMemberWithName("strong").GetChildAtIndex(
695 0).GetChildMemberWithName("value")
696 self.weak = self.ptr.GetChildMemberWithName("weak").GetChildAtIndex(
697 0).GetChildMemberWithName("value")
757 self.strong = (
758 self.ptr.GetChildMemberWithName("strong")
759 .GetChildAtIndex(0)
760 .GetChildMemberWithName("value")
761 )
762 self.weak = (
763 self.ptr.GetChildMemberWithName("weak")
764 .GetChildAtIndex(0)
765 .GetChildMemberWithName("value")
766 )
698767
699768 self.value_builder = ValueBuilder(valobj)
700769
......@@ -772,7 +841,9 @@ class StdCellSyntheticProvider:
772841def StdRefSummaryProvider(valobj, dict):
773842 # type: (SBValue, dict) -> str
774843 borrow = valobj.GetChildMemberWithName("borrow").GetValueAsSigned()
775 return "borrow={}".format(borrow) if borrow >= 0 else "borrow_mut={}".format(-borrow)
844 return (
845 "borrow={}".format(borrow) if borrow >= 0 else "borrow_mut={}".format(-borrow)
846 )
776847
777848
778849class StdRefSyntheticProvider:
......@@ -785,11 +856,16 @@ class StdRefSyntheticProvider:
785856 borrow = valobj.GetChildMemberWithName("borrow")
786857 value = valobj.GetChildMemberWithName("value")
787858 if is_cell:
788 self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName("value")
859 self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName(
860 "value"
861 )
789862 self.value = value.GetChildMemberWithName("value")
790863 else:
791 self.borrow = borrow.GetChildMemberWithName("borrow").GetChildMemberWithName(
792 "value").GetChildMemberWithName("value")
864 self.borrow = (
865 borrow.GetChildMemberWithName("borrow")
866 .GetChildMemberWithName("value")
867 .GetChildMemberWithName("value")
868 )
793869 self.value = value.Dereference()
794870
795871 self.value_builder = ValueBuilder(valobj)
......@@ -832,7 +908,7 @@ def StdNonZeroNumberSummaryProvider(valobj, _dict):
832908
833909 # FIXME: Avoid printing as character literal,
834910 # see https://github.com/llvm/llvm-project/issues/65076.
835 if inner_inner.GetTypeName() in ['char', 'unsigned char']:
836 return str(inner_inner.GetValueAsSigned())
911 if inner_inner.GetTypeName() in ["char", "unsigned char"]:
912 return str(inner_inner.GetValueAsSigned())
837913 else:
838 return inner_inner.GetValue()
914 return inner_inner.GetValue()
src/etc/rust_types.py+2-1
......@@ -54,7 +54,7 @@ STD_REF_MUT_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefMut<.+>$")
5454STD_REF_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefCell<.+>$")
5555STD_NONZERO_NUMBER_REGEX = re.compile(r"^(core::([a-z_]+::)+)NonZero<.+>$")
5656STD_PATHBUF_REGEX = re.compile(r"^(std::([a-z_]+::)+)PathBuf$")
57STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$")
57STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$")
5858
5959TUPLE_ITEM_REGEX = re.compile(r"__\d+$")
6060
......@@ -84,6 +84,7 @@ STD_TYPE_TO_REGEX = {
8484 RustType.STD_PATH: STD_PATH_REGEX,
8585}
8686
87
8788def is_tuple_fields(fields):
8889 # type: (list) -> bool
8990 return all(TUPLE_ITEM_REGEX.match(str(field.name)) for field in fields)
src/librustdoc/html/markdown.rs+76-20
......@@ -32,6 +32,8 @@ use std::iter::Peekable;
3232use std::ops::{ControlFlow, Range};
3333use std::path::PathBuf;
3434use std::str::{self, CharIndices};
35use std::sync::atomic::AtomicUsize;
36use std::sync::{Arc, Weak};
3537
3638use pulldown_cmark::{
3739 BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
......@@ -1301,8 +1303,20 @@ impl LangString {
13011303 }
13021304}
13031305
1304impl Markdown<'_> {
1306impl<'a> Markdown<'a> {
13051307 pub fn into_string(self) -> String {
1308 // This is actually common enough to special-case
1309 if self.content.is_empty() {
1310 return String::new();
1311 }
1312
1313 let mut s = String::with_capacity(self.content.len() * 3 / 2);
1314 html::push_html(&mut s, self.into_iter());
1315
1316 s
1317 }
1318
1319 fn into_iter(self) -> CodeBlocks<'a, 'a, impl Iterator<Item = Event<'a>>> {
13061320 let Markdown {
13071321 content: md,
13081322 links,
......@@ -1313,32 +1327,72 @@ impl Markdown<'_> {
13131327 heading_offset,
13141328 } = self;
13151329
1316 // This is actually common enough to special-case
1317 if md.is_empty() {
1318 return String::new();
1319 }
1320 let mut replacer = |broken_link: BrokenLink<'_>| {
1330 let replacer = move |broken_link: BrokenLink<'_>| {
13211331 links
13221332 .iter()
13231333 .find(|link| *link.original_text == *broken_link.reference)
13241334 .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
13251335 };
13261336
1327 let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer));
1337 let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(replacer));
13281338 let p = p.into_offset_iter();
13291339
1330 let mut s = String::with_capacity(md.len() * 3 / 2);
1331
13321340 ids.handle_footnotes(|ids, existing_footnotes| {
13331341 let p = HeadingLinks::new(p, None, ids, heading_offset);
13341342 let p = footnotes::Footnotes::new(p, existing_footnotes);
13351343 let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
13361344 let p = TableWrapper::new(p);
1337 let p = CodeBlocks::new(p, codes, edition, playground);
1338 html::push_html(&mut s, p);
1339 });
1345 CodeBlocks::new(p, codes, edition, playground)
1346 })
1347 }
13401348
1341 s
1349 /// Convert markdown to (summary, remaining) HTML.
1350 ///
1351 /// - The summary is the first top-level Markdown element (usually a paragraph, but potentially
1352 /// any block).
1353 /// - The remaining docs contain everything after the summary.
1354 pub(crate) fn split_summary_and_content(self) -> (Option<String>, Option<String>) {
1355 if self.content.is_empty() {
1356 return (None, None);
1357 }
1358 let mut p = self.into_iter();
1359
1360 let mut event_level = 0;
1361 let mut summary_events = Vec::new();
1362 let mut get_next_tag = false;
1363
1364 let mut end_of_summary = false;
1365 while let Some(event) = p.next() {
1366 match event {
1367 Event::Start(_) => event_level += 1,
1368 Event::End(kind) => {
1369 event_level -= 1;
1370 if event_level == 0 {
1371 // We're back at the "top" so it means we're done with the summary.
1372 end_of_summary = true;
1373 // We surround tables with `<div>` HTML tags so this is a special case.
1374 get_next_tag = kind == TagEnd::Table;
1375 }
1376 }
1377 _ => {}
1378 }
1379 summary_events.push(event);
1380 if end_of_summary {
1381 if get_next_tag && let Some(event) = p.next() {
1382 summary_events.push(event);
1383 }
1384 break;
1385 }
1386 }
1387 let mut summary = String::new();
1388 html::push_html(&mut summary, summary_events.into_iter());
1389 if summary.is_empty() {
1390 return (None, None);
1391 }
1392 let mut content = String::new();
1393 html::push_html(&mut content, p);
1394
1395 if content.is_empty() { (Some(summary), None) } else { (Some(summary), Some(content)) }
13421396 }
13431397}
13441398
......@@ -1882,7 +1936,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
18821936#[derive(Clone, Default, Debug)]
18831937pub struct IdMap {
18841938 map: FxHashMap<String, usize>,
1885 existing_footnotes: usize,
1939 existing_footnotes: Arc<AtomicUsize>,
18861940}
18871941
18881942fn is_default_id(id: &str) -> bool {
......@@ -1942,7 +1996,7 @@ fn is_default_id(id: &str) -> bool {
19421996
19431997impl IdMap {
19441998 pub fn new() -> Self {
1945 IdMap { map: FxHashMap::default(), existing_footnotes: 0 }
1999 IdMap { map: FxHashMap::default(), existing_footnotes: Arc::new(AtomicUsize::new(0)) }
19462000 }
19472001
19482002 pub(crate) fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
......@@ -1970,15 +2024,17 @@ impl IdMap {
19702024
19712025 /// Method to handle `existing_footnotes` increment automatically (to prevent forgetting
19722026 /// about it).
1973 pub(crate) fn handle_footnotes<F: FnOnce(&mut Self, &mut usize)>(&mut self, closure: F) {
1974 let mut existing_footnotes = self.existing_footnotes;
2027 pub(crate) fn handle_footnotes<'a, T, F: FnOnce(&'a mut Self, Weak<AtomicUsize>) -> T>(
2028 &'a mut self,
2029 closure: F,
2030 ) -> T {
2031 let existing_footnotes = Arc::downgrade(&self.existing_footnotes);
19752032
1976 closure(self, &mut existing_footnotes);
1977 self.existing_footnotes = existing_footnotes;
2033 closure(self, existing_footnotes)
19782034 }
19792035
19802036 pub(crate) fn clear(&mut self) {
19812037 self.map.clear();
1982 self.existing_footnotes = 0;
2038 self.existing_footnotes = Arc::new(AtomicUsize::new(0));
19832039 }
19842040}
src/librustdoc/html/markdown/footnotes.rs+16-9
......@@ -1,5 +1,8 @@
11//! Markdown footnote handling.
2
23use std::fmt::Write as _;
4use std::sync::atomic::{AtomicUsize, Ordering};
5use std::sync::{Arc, Weak};
36
47use pulldown_cmark::{CowStr, Event, Tag, TagEnd, html};
58use rustc_data_structures::fx::FxIndexMap;
......@@ -8,10 +11,11 @@ use super::SpannedEvent;
811
912/// Moves all footnote definitions to the end and add back links to the
1013/// references.
11pub(super) struct Footnotes<'a, 'b, I> {
14pub(super) struct Footnotes<'a, I> {
1215 inner: I,
1316 footnotes: FxIndexMap<String, FootnoteDef<'a>>,
14 existing_footnotes: &'b mut usize,
17 existing_footnotes: Arc<AtomicUsize>,
18 start_id: usize,
1519}
1620
1721/// The definition of a single footnote.
......@@ -21,13 +25,16 @@ struct FootnoteDef<'a> {
2125 id: usize,
2226}
2327
24impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
25 pub(super) fn new(iter: I, existing_footnotes: &'b mut usize) -> Self {
26 Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes }
28impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, I> {
29 pub(super) fn new(iter: I, existing_footnotes: Weak<AtomicUsize>) -> Self {
30 let existing_footnotes =
31 existing_footnotes.upgrade().expect("`existing_footnotes` was dropped");
32 let start_id = existing_footnotes.load(Ordering::Relaxed);
33 Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes, start_id }
2734 }
2835
2936 fn get_entry(&mut self, key: &str) -> (&mut Vec<Event<'a>>, usize) {
30 let new_id = self.footnotes.len() + 1 + *self.existing_footnotes;
37 let new_id = self.footnotes.len() + 1 + self.start_id;
3138 let key = key.to_owned();
3239 let FootnoteDef { content, id } =
3340 self.footnotes.entry(key).or_insert(FootnoteDef { content: Vec::new(), id: new_id });
......@@ -44,7 +51,7 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
4451 id,
4552 // Although the ID count is for the whole page, the footnote reference
4653 // are local to the item so we make this ID "local" when displayed.
47 id - *self.existing_footnotes
54 id - self.start_id
4855 );
4956 Event::Html(reference.into())
5057 }
......@@ -64,7 +71,7 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
6471 }
6572}
6673
67impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I> {
74impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
6875 type Item = SpannedEvent<'a>;
6976
7077 fn next(&mut self) -> Option<Self::Item> {
......@@ -87,7 +94,7 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I>
8794 // After all the markdown is emmited, emit an <hr> then all the footnotes
8895 // in a list.
8996 let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
90 *self.existing_footnotes += defs.len();
97 self.existing_footnotes.fetch_add(defs.len(), Ordering::Relaxed);
9198 let defs_html = render_footnotes_defs(defs);
9299 return Some((Event::Html(defs_html.into()), 0..0));
93100 } else {
src/librustdoc/html/render/mod.rs+28-17
......@@ -1904,7 +1904,6 @@ fn render_impl(
19041904 }
19051905 }
19061906
1907 let trait_is_none = trait_.is_none();
19081907 // If we've implemented a trait, then also emit documentation for all
19091908 // default items which weren't overridden in the implementation block.
19101909 // We don't emit documentation for default items if they appear in the
......@@ -1936,6 +1935,23 @@ fn render_impl(
19361935 if rendering_params.toggle_open_by_default { " open" } else { "" }
19371936 );
19381937 }
1938
1939 let (before_dox, after_dox) = i
1940 .impl_item
1941 .opt_doc_value()
1942 .map(|dox| {
1943 Markdown {
1944 content: &*dox,
1945 links: &i.impl_item.links(cx),
1946 ids: &mut cx.id_map.borrow_mut(),
1947 error_codes: cx.shared.codes,
1948 edition: cx.shared.edition(),
1949 playground: &cx.shared.playground,
1950 heading_offset: HeadingOffset::H4,
1951 }
1952 .split_summary_and_content()
1953 })
1954 .unwrap_or((None, None));
19391955 render_impl_summary(
19401956 w,
19411957 cx,
......@@ -1944,33 +1960,23 @@ fn render_impl(
19441960 rendering_params.show_def_docs,
19451961 use_absolute,
19461962 aliases,
1963 &before_dox,
19471964 );
19481965 if toggled {
19491966 w.write_str("</summary>");
19501967 }
19511968
1952 if let Some(ref dox) = i.impl_item.opt_doc_value() {
1953 if trait_is_none && impl_.items.is_empty() {
1969 if before_dox.is_some() {
1970 if trait_.is_none() && impl_.items.is_empty() {
19541971 w.write_str(
19551972 "<div class=\"item-info\">\
19561973 <div class=\"stab empty-impl\">This impl block contains no items.</div>\
19571974 </div>",
19581975 );
19591976 }
1960 write!(
1961 w,
1962 "<div class=\"docblock\">{}</div>",
1963 Markdown {
1964 content: dox,
1965 links: &i.impl_item.links(cx),
1966 ids: &mut cx.id_map.borrow_mut(),
1967 error_codes: cx.shared.codes,
1968 edition: cx.shared.edition(),
1969 playground: &cx.shared.playground,
1970 heading_offset: HeadingOffset::H4,
1971 }
1972 .into_string()
1973 );
1977 if let Some(after_dox) = after_dox {
1978 write!(w, "<div class=\"docblock\">{after_dox}</div>");
1979 }
19741980 }
19751981 if !default_impl_items.is_empty() || !impl_items.is_empty() {
19761982 w.write_str("<div class=\"impl-items\">");
......@@ -2031,6 +2037,7 @@ pub(crate) fn render_impl_summary(
20312037 // This argument is used to reference same type with different paths to avoid duplication
20322038 // in documentation pages for trait with automatic implementations like "Send" and "Sync".
20332039 aliases: &[String],
2040 doc: &Option<String>,
20342041) {
20352042 let inner_impl = i.inner_impl();
20362043 let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
......@@ -2082,6 +2089,10 @@ pub(crate) fn render_impl_summary(
20822089 );
20832090 }
20842091
2092 if let Some(doc) = doc {
2093 write!(w, "<div class=\"docblock\">{doc}</div>");
2094 }
2095
20852096 w.write_str("</section>");
20862097}
20872098
src/librustdoc/html/static/css/rustdoc.css+33
......@@ -2210,6 +2210,39 @@ details.toggle[open] > summary::after {
22102210 content: "Collapse";
22112211}
22122212
2213details.toggle:not([open]) > summary .docblock {
2214 max-height: calc(1.5em + 0.75em);
2215 overflow-y: hidden;
2216}
2217details.toggle:not([open]) > summary .docblock > :first-child {
2218 max-width: 100%;
2219 overflow: hidden;
2220 width: fit-content;
2221 white-space: nowrap;
2222 position: relative;
2223 padding-right: 1em;
2224}
2225details.toggle:not([open]) > summary .docblock > :first-child::after {
2226 content: "…";
2227 position: absolute;
2228 right: 0;
2229 top: 0;
2230 bottom: 0;
2231 z-index: 1;
2232 background-color: var(--main-background-color);
2233 font: 1rem/1.5 "Source Serif 4", NanumBarunGothic, serif;
2234 /* To make it look a bit better and not have it stuck to the preceding element. */
2235 padding-left: 0.2em;
2236}
2237details.toggle:not([open]) > summary .docblock > div:first-child::after {
2238 /* This is to make the "..." always appear at the bottom. */
2239 padding-top: calc(1.5em + 0.75em - 1.2rem);
2240}
2241
2242details.toggle > summary .docblock {
2243 margin-top: 0.75em;
2244}
2245
22132246/* This is needed in docblocks to have the "â–¶" element to be on the same line. */
22142247.docblock summary > * {
22152248 display: inline-block;
src/tools/publish_toolstate.py+125-101
......@@ -14,6 +14,7 @@ import json
1414import datetime
1515import collections
1616import textwrap
17
1718try:
1819 import urllib2
1920 from urllib2 import HTTPError
......@@ -21,7 +22,7 @@ except ImportError:
2122 import urllib.request as urllib2
2223 from urllib.error import HTTPError
2324try:
24 import typing # noqa: F401 FIXME: py2
25 import typing # noqa: F401 FIXME: py2
2526except ImportError:
2627 pass
2728
......@@ -29,40 +30,41 @@ except ImportError:
2930# These should be collaborators of the rust-lang/rust repository (with at least
3031# read privileges on it). CI will fail otherwise.
3132MAINTAINERS = {
32 'book': {'carols10cents'},
33 'nomicon': {'frewsxcv', 'Gankra', 'JohnTitor'},
34 'reference': {'Havvy', 'matthewjasper', 'ehuss'},
35 'rust-by-example': {'marioidival'},
36 'embedded-book': {'adamgreig', 'andre-richter', 'jamesmunns', 'therealprof'},
37 'edition-guide': {'ehuss'},
38 'rustc-dev-guide': {'spastorino', 'amanjeev', 'JohnTitor'},
33 "book": {"carols10cents"},
34 "nomicon": {"frewsxcv", "Gankra", "JohnTitor"},
35 "reference": {"Havvy", "matthewjasper", "ehuss"},
36 "rust-by-example": {"marioidival"},
37 "embedded-book": {"adamgreig", "andre-richter", "jamesmunns", "therealprof"},
38 "edition-guide": {"ehuss"},
39 "rustc-dev-guide": {"spastorino", "amanjeev", "JohnTitor"},
3940}
4041
4142LABELS = {
42 'book': ['C-bug'],
43 'nomicon': ['C-bug'],
44 'reference': ['C-bug'],
45 'rust-by-example': ['C-bug'],
46 'embedded-book': ['C-bug'],
47 'edition-guide': ['C-bug'],
48 'rustc-dev-guide': ['C-bug'],
43 "book": ["C-bug"],
44 "nomicon": ["C-bug"],
45 "reference": ["C-bug"],
46 "rust-by-example": ["C-bug"],
47 "embedded-book": ["C-bug"],
48 "edition-guide": ["C-bug"],
49 "rustc-dev-guide": ["C-bug"],
4950}
5051
5152REPOS = {
52 'book': 'https://github.com/rust-lang/book',
53 'nomicon': 'https://github.com/rust-lang/nomicon',
54 'reference': 'https://github.com/rust-lang/reference',
55 'rust-by-example': 'https://github.com/rust-lang/rust-by-example',
56 'embedded-book': 'https://github.com/rust-embedded/book',
57 'edition-guide': 'https://github.com/rust-lang/edition-guide',
58 'rustc-dev-guide': 'https://github.com/rust-lang/rustc-dev-guide',
53 "book": "https://github.com/rust-lang/book",
54 "nomicon": "https://github.com/rust-lang/nomicon",
55 "reference": "https://github.com/rust-lang/reference",
56 "rust-by-example": "https://github.com/rust-lang/rust-by-example",
57 "embedded-book": "https://github.com/rust-embedded/book",
58 "edition-guide": "https://github.com/rust-lang/edition-guide",
59 "rustc-dev-guide": "https://github.com/rust-lang/rustc-dev-guide",
5960}
6061
62
6163def load_json_from_response(resp):
6264 # type: (typing.Any) -> typing.Any
6365 content = resp.read()
6466 if isinstance(content, bytes):
65 content_str = content.decode('utf-8')
67 content_str = content.decode("utf-8")
6668 else:
6769 print("Refusing to decode " + str(type(content)) + " to str")
6870 return json.loads(content_str)
......@@ -70,11 +72,10 @@ def load_json_from_response(resp):
7072
7173def read_current_status(current_commit, path):
7274 # type: (str, str) -> typing.Mapping[str, typing.Any]
73 '''Reads build status of `current_commit` from content of `history/*.tsv`
74 '''
75 with open(path, 'r') as f:
75 """Reads build status of `current_commit` from content of `history/*.tsv`"""
76 with open(path, "r") as f:
7677 for line in f:
77 (commit, status) = line.split('\t', 1)
78 (commit, status) = line.split("\t", 1)
7879 if commit == current_commit:
7980 return json.loads(status)
8081 return {}
......@@ -82,12 +83,12 @@ def read_current_status(current_commit, path):
8283
8384def gh_url():
8485 # type: () -> str
85 return os.environ['TOOLSTATE_ISSUES_API_URL']
86 return os.environ["TOOLSTATE_ISSUES_API_URL"]
8687
8788
8889def maybe_remove_mention(message):
8990 # type: (str) -> str
90 if os.environ.get('TOOLSTATE_SKIP_MENTIONS') is not None:
91 if os.environ.get("TOOLSTATE_SKIP_MENTIONS") is not None:
9192 return message.replace("@", "")
9293 return message
9394
......@@ -102,36 +103,45 @@ def issue(
102103 github_token,
103104):
104105 # type: (str, str, typing.Iterable[str], str, str, typing.List[str], str) -> None
105 '''Open an issue about the toolstate failure.'''
106 if status == 'test-fail':
107 status_description = 'has failing tests'
106 """Open an issue about the toolstate failure."""
107 if status == "test-fail":
108 status_description = "has failing tests"
108109 else:
109 status_description = 'no longer builds'
110 request = json.dumps({
111 'body': maybe_remove_mention(textwrap.dedent('''\
110 status_description = "no longer builds"
111 request = json.dumps(
112 {
113 "body": maybe_remove_mention(
114 textwrap.dedent("""\
112115 Hello, this is your friendly neighborhood mergebot.
113116 After merging PR {}, I observed that the tool {} {}.
114117 A follow-up PR to the repository {} is needed to fix the fallout.
115118
116119 cc @{}, do you think you would have time to do the follow-up work?
117120 If so, that would be great!
118 ''').format(
119 relevant_pr_number, tool, status_description,
120 REPOS.get(tool), relevant_pr_user
121 )),
122 'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number),
123 'assignees': list(assignees),
124 'labels': labels,
125 })
126 print("Creating issue:\n{}".format(request))
127 response = urllib2.urlopen(urllib2.Request(
128 gh_url(),
129 request.encode(),
130 {
131 'Authorization': 'token ' + github_token,
132 'Content-Type': 'application/json',
121 """).format(
122 relevant_pr_number,
123 tool,
124 status_description,
125 REPOS.get(tool),
126 relevant_pr_user,
127 )
128 ),
129 "title": "`{}` no longer builds after {}".format(tool, relevant_pr_number),
130 "assignees": list(assignees),
131 "labels": labels,
133132 }
134 ))
133 )
134 print("Creating issue:\n{}".format(request))
135 response = urllib2.urlopen(
136 urllib2.Request(
137 gh_url(),
138 request.encode(),
139 {
140 "Authorization": "token " + github_token,
141 "Content-Type": "application/json",
142 },
143 )
144 )
135145 response.read()
136146
137147
......@@ -145,27 +155,26 @@ def update_latest(
145155 github_token,
146156):
147157 # type: (str, str, str, str, str, str, str) -> str
148 '''Updates `_data/latest.json` to match build result of the given commit.
149 '''
150 with open('_data/latest.json', 'r+') as f:
158 """Updates `_data/latest.json` to match build result of the given commit."""
159 with open("_data/latest.json", "r+") as f:
151160 latest = json.load(f, object_pairs_hook=collections.OrderedDict)
152161
153162 current_status = {
154 os_: read_current_status(current_commit, 'history/' + os_ + '.tsv')
155 for os_ in ['windows', 'linux']
163 os_: read_current_status(current_commit, "history/" + os_ + ".tsv")
164 for os_ in ["windows", "linux"]
156165 }
157166
158 slug = 'rust-lang/rust'
159 message = textwrap.dedent('''\
167 slug = "rust-lang/rust"
168 message = textwrap.dedent("""\
160169 📣 Toolstate changed by {}!
161170
162171 Tested on commit {}@{}.
163172 Direct link to PR: <{}>
164173
165 ''').format(relevant_pr_number, slug, current_commit, relevant_pr_url)
174 """).format(relevant_pr_number, slug, current_commit, relevant_pr_url)
166175 anything_changed = False
167176 for status in latest:
168 tool = status['tool']
177 tool = status["tool"]
169178 changed = False
170179 create_issue_for_status = None # set to the status that caused the issue
171180
......@@ -173,57 +182,70 @@ def update_latest(
173182 old = status[os_]
174183 new = s.get(tool, old)
175184 status[os_] = new
176 maintainers = ' '.join('@'+name for name in MAINTAINERS.get(tool, ()))
185 maintainers = " ".join("@" + name for name in MAINTAINERS.get(tool, ()))
177186 # comparing the strings, but they are ordered appropriately:
178187 # "test-pass" > "test-fail" > "build-fail"
179188 if new > old:
180189 # things got fixed or at least the status quo improved
181190 changed = True
182 message += '🎉 {} on {}: {} → {} (cc {}).\n' \
183 .format(tool, os_, old, new, maintainers)
191 message += "🎉 {} on {}: {} → {} (cc {}).\n".format(
192 tool, os_, old, new, maintainers
193 )
184194 elif new < old:
185195 # tests or builds are failing and were not failing before
186196 changed = True
187 title = '💔 {} on {}: {} → {}' \
188 .format(tool, os_, old, new)
189 message += '{} (cc {}).\n' \
190 .format(title, maintainers)
197 title = "💔 {} on {}: {} → {}".format(tool, os_, old, new)
198 message += "{} (cc {}).\n".format(title, maintainers)
191199 # See if we need to create an issue.
192200 # Create issue if things no longer build.
193201 # (No issue for mere test failures to avoid spurious issues.)
194 if new == 'build-fail':
202 if new == "build-fail":
195203 create_issue_for_status = new
196204
197205 if create_issue_for_status is not None:
198206 try:
199207 issue(
200 tool, create_issue_for_status, MAINTAINERS.get(tool, ()),
201 relevant_pr_number, relevant_pr_user, LABELS.get(tool, []),
208 tool,
209 create_issue_for_status,
210 MAINTAINERS.get(tool, ()),
211 relevant_pr_number,
212 relevant_pr_user,
213 LABELS.get(tool, []),
202214 github_token,
203215 )
204216 except HTTPError as e:
205217 # network errors will simply end up not creating an issue, but that's better
206218 # than failing the entire build job
207 print("HTTPError when creating issue for status regression: {0}\n{1!r}"
208 .format(e, e.read()))
219 print(
220 "HTTPError when creating issue for status regression: {0}\n{1!r}".format(
221 e, e.read()
222 )
223 )
209224 except IOError as e:
210 print("I/O error when creating issue for status regression: {0}".format(e))
225 print(
226 "I/O error when creating issue for status regression: {0}".format(
227 e
228 )
229 )
211230 except:
212 print("Unexpected error when creating issue for status regression: {0}"
213 .format(sys.exc_info()[0]))
231 print(
232 "Unexpected error when creating issue for status regression: {0}".format(
233 sys.exc_info()[0]
234 )
235 )
214236 raise
215237
216238 if changed:
217 status['commit'] = current_commit
218 status['datetime'] = current_datetime
239 status["commit"] = current_commit
240 status["datetime"] = current_datetime
219241 anything_changed = True
220242
221243 if not anything_changed:
222 return ''
244 return ""
223245
224246 f.seek(0)
225247 f.truncate(0)
226 json.dump(latest, f, indent=4, separators=(',', ': '))
248 json.dump(latest, f, indent=4, separators=(",", ": "))
227249 return message
228250
229251
......@@ -231,12 +253,12 @@ def update_latest(
231253# There are variables declared within that are implicitly global; it is unknown
232254# which ones precisely but at least this is true for `github_token`.
233255try:
234 if __name__ != '__main__':
256 if __name__ != "__main__":
235257 exit(0)
236258
237259 cur_commit = sys.argv[1]
238260 cur_datetime = datetime.datetime.now(datetime.timezone.utc).strftime(
239 '%Y-%m-%dT%H:%M:%SZ'
261 "%Y-%m-%dT%H:%M:%SZ"
240262 )
241263 cur_commit_msg = sys.argv[2]
242264 save_message_to_path = sys.argv[3]
......@@ -244,21 +266,21 @@ try:
244266
245267 # assume that PR authors are also owners of the repo where the branch lives
246268 relevant_pr_match = re.search(
247 r'Auto merge of #([0-9]+) - ([^:]+):[^,]+, r=(\S+)',
269 r"Auto merge of #([0-9]+) - ([^:]+):[^,]+, r=(\S+)",
248270 cur_commit_msg,
249271 )
250272 if relevant_pr_match:
251273 number = relevant_pr_match.group(1)
252274 relevant_pr_user = relevant_pr_match.group(2)
253 relevant_pr_number = 'rust-lang/rust#' + number
254 relevant_pr_url = 'https://github.com/rust-lang/rust/pull/' + number
275 relevant_pr_number = "rust-lang/rust#" + number
276 relevant_pr_url = "https://github.com/rust-lang/rust/pull/" + number
255277 pr_reviewer = relevant_pr_match.group(3)
256278 else:
257 number = '-1'
258 relevant_pr_user = 'ghost'
259 relevant_pr_number = '<unknown PR>'
260 relevant_pr_url = '<unknown>'
261 pr_reviewer = 'ghost'
279 number = "-1"
280 relevant_pr_user = "ghost"
281 relevant_pr_number = "<unknown PR>"
282 relevant_pr_url = "<unknown>"
283 pr_reviewer = "ghost"
262284
263285 message = update_latest(
264286 cur_commit,
......@@ -270,28 +292,30 @@ try:
270292 github_token,
271293 )
272294 if not message:
273 print('<Nothing changed>')
295 print("<Nothing changed>")
274296 sys.exit(0)
275297
276298 print(message)
277299
278300 if not github_token:
279 print('Dry run only, not committing anything')
301 print("Dry run only, not committing anything")
280302 sys.exit(0)
281303
282 with open(save_message_to_path, 'w') as f:
304 with open(save_message_to_path, "w") as f:
283305 f.write(message)
284306
285307 # Write the toolstate comment on the PR as well.
286 issue_url = gh_url() + '/{}/comments'.format(number)
287 response = urllib2.urlopen(urllib2.Request(
288 issue_url,
289 json.dumps({'body': maybe_remove_mention(message)}).encode(),
290 {
291 'Authorization': 'token ' + github_token,
292 'Content-Type': 'application/json',
293 }
294 ))
308 issue_url = gh_url() + "/{}/comments".format(number)
309 response = urllib2.urlopen(
310 urllib2.Request(
311 issue_url,
312 json.dumps({"body": maybe_remove_mention(message)}).encode(),
313 {
314 "Authorization": "token " + github_token,
315 "Content-Type": "application/json",
316 },
317 )
318 )
295319 response.read()
296320except HTTPError as e:
297321 print("HTTPError: %s\n%r" % (e, e.read()))
src/tools/tidy/config/black.toml deleted-18
......@@ -1,18 +0,0 @@
1[tool.black]
2# Ignore all submodules
3extend-exclude = """(\
4 src/doc/nomicon|\
5 src/tools/cargo/|\
6 src/doc/reference/|\
7 src/doc/book/|\
8 src/doc/rust-by-example/|\
9 library/stdarch/|\
10 src/doc/rustc-dev-guide/|\
11 src/doc/edition-guide/|\
12 src/llvm-project/|\
13 src/doc/embedded-book/|\
14 src/tools/rustc-perf/|\
15 src/tools/enzyme/|\
16 library/backtrace/|\
17 src/gcc/
18 )"""
src/tools/tidy/config/requirements.in-1
......@@ -6,6 +6,5 @@
66# Note: this generation step should be run with the oldest supported python
77# version (currently 3.9) to ensure backward compatibility
88
9black==24.4.2
109ruff==0.4.9
1110clang-format==18.1.7
src/tools/tidy/config/requirements.txt-52
......@@ -4,30 +4,6 @@
44#
55# pip-compile --generate-hashes --strip-extras src/tools/tidy/config/requirements.in
66#
7black==24.4.2 \
8 --hash=sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474 \
9 --hash=sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1 \
10 --hash=sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0 \
11 --hash=sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8 \
12 --hash=sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96 \
13 --hash=sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1 \
14 --hash=sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04 \
15 --hash=sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021 \
16 --hash=sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94 \
17 --hash=sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d \
18 --hash=sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c \
19 --hash=sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7 \
20 --hash=sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c \
21 --hash=sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc \
22 --hash=sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7 \
23 --hash=sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d \
24 --hash=sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c \
25 --hash=sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741 \
26 --hash=sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce \
27 --hash=sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb \
28 --hash=sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063 \
29 --hash=sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e
30 # via -r src/tools/tidy/config/requirements.in
317clang-format==18.1.7 \
328 --hash=sha256:035204410f65d03f98cb81c9c39d6d193f9987917cc88de9d0dbd01f2aa9c302 \
339 --hash=sha256:05c482a854287a5d21f7567186c0bd4b8dbd4a871751e655a45849185f30b931 \
......@@ -45,26 +21,6 @@ clang-format==18.1.7 \
4521 --hash=sha256:f4f77ac0f4f9a659213fedda0f2d216886c410132e6e7dd4b13f92b34e925554 \
4622 --hash=sha256:f935d34152a2e11e55120eb9182862f432bc9789ab819f680c9f6db4edebf9e3
4723 # via -r src/tools/tidy/config/requirements.in
48click==8.1.3 \
49 --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \
50 --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48
51 # via black
52mypy-extensions==1.0.0 \
53 --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
54 --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782
55 # via black
56packaging==23.1 \
57 --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \
58 --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f
59 # via black
60pathspec==0.11.1 \
61 --hash=sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687 \
62 --hash=sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293
63 # via black
64platformdirs==4.2.2 \
65 --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
66 --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
67 # via black
6824ruff==0.4.9 \
6925 --hash=sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893 \
7026 --hash=sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff \
......@@ -84,11 +40,3 @@ ruff==0.4.9 \
8440 --hash=sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde \
8541 --hash=sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3
8642 # via -r src/tools/tidy/config/requirements.in
87tomli==2.0.1 \
88 --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
89 --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
90 # via black
91typing-extensions==4.12.2 \
92 --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
93 --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
94 # via black
src/tools/tidy/config/ruff.toml+6
......@@ -19,6 +19,9 @@ extend-exclude = [
1919 "src/tools/enzyme/",
2020 "src/tools/rustc-perf/",
2121 "src/gcc/",
22 "compiler/rustc_codegen_gcc",
23 "src/tools/clippy",
24 "src/tools/miri",
2225 # Hack: CI runs from a subdirectory under the main checkout
2326 "../src/doc/nomicon/",
2427 "../src/tools/cargo/",
......@@ -34,6 +37,9 @@ extend-exclude = [
3437 "../src/tools/enzyme/",
3538 "../src/tools/rustc-perf/",
3639 "../src/gcc/",
40 "../compiler/rustc_codegen_gcc",
41 "../src/tools/clippy",
42 "../src/tools/miri",
3743]
3844
3945[lint]
src/tools/tidy/src/ext_tool_checks.rs+16-14
......@@ -32,9 +32,8 @@ const REL_PY_PATH: &[&str] = &["Scripts", "python3.exe"];
3232const REL_PY_PATH: &[&str] = &["bin", "python3"];
3333
3434const RUFF_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "ruff.toml"];
35const BLACK_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "black.toml"];
3635/// Location within build directory
37const RUFF_CACH_PATH: &[&str] = &["cache", "ruff_cache"];
36const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"];
3837const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"];
3938
4039pub fn check(
......@@ -96,7 +95,7 @@ fn check_impl(
9695 let mut cfg_path = root_path.to_owned();
9796 cfg_path.extend(RUFF_CONFIG_PATH);
9897 let mut cache_dir = outdir.to_owned();
99 cache_dir.extend(RUFF_CACH_PATH);
98 cache_dir.extend(RUFF_CACHE_PATH);
10099
101100 cfg_args_ruff.extend([
102101 "--config".as_ref(),
......@@ -124,33 +123,36 @@ fn check_impl(
124123 }
125124
126125 if python_fmt {
127 let mut cfg_args_black = cfg_args.clone();
128 let mut file_args_black = file_args.clone();
126 let mut cfg_args_ruff = cfg_args.clone();
127 let mut file_args_ruff = file_args.clone();
129128
130129 if bless {
131130 eprintln!("formatting python files");
132131 } else {
133132 eprintln!("checking python file formatting");
134 cfg_args_black.push("--check".as_ref());
133 cfg_args_ruff.push("--check".as_ref());
135134 }
136135
137136 let mut cfg_path = root_path.to_owned();
138 cfg_path.extend(BLACK_CONFIG_PATH);
137 cfg_path.extend(RUFF_CONFIG_PATH);
138 let mut cache_dir = outdir.to_owned();
139 cache_dir.extend(RUFF_CACHE_PATH);
139140
140 cfg_args_black.extend(["--config".as_ref(), cfg_path.as_os_str()]);
141 cfg_args_ruff.extend(["--config".as_ref(), cfg_path.as_os_str()]);
141142
142 if file_args_black.is_empty() {
143 file_args_black.push(root_path.as_os_str());
143 if file_args_ruff.is_empty() {
144 file_args_ruff.push(root_path.as_os_str());
144145 }
145146
146 let mut args = merge_args(&cfg_args_black, &file_args_black);
147 let res = py_runner(py_path.as_ref().unwrap(), true, None, "black", &args);
147 let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
148 args.insert(0, "format".as_ref());
149 let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
148150
149151 if res.is_err() && show_diff {
150152 eprintln!("\npython formatting does not match! Printing diff:");
151153
152154 args.insert(0, "--diff".as_ref());
153 let _ = py_runner(py_path.as_ref().unwrap(), true, None, "black", &args);
155 let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
154156 }
155157 // Rethrow error
156158 let _ = res?;
......@@ -445,7 +447,7 @@ fn shellcheck_runner(args: &[&OsStr]) -> Result<(), Error> {
445447 }
446448
447449 let status = Command::new("shellcheck").args(args).status()?;
448 if status.success() { Ok(()) } else { Err(Error::FailedCheck("black")) }
450 if status.success() { Ok(()) } else { Err(Error::FailedCheck("shellcheck")) }
449451}
450452
451453/// Check git for tracked files matching an extension
tests/codegen/emcripten-catch-unwind.rs+1-1
......@@ -57,7 +57,7 @@ pub unsafe fn test_catch_unwind(
5757 // CHECK: [[IS_RUST_EXN_I8:%.*]] = zext i1 [[IS_RUST_EXN]] to i8
5858
5959 // CHECK: store ptr [[EXCEPTION]], ptr [[ALLOCA]]
60 // CHECK: [[IS_RUST_SLOT:%.*]] = getelementptr inbounds i8, ptr [[ALLOCA]], [[PTR_SIZE]]
60 // CHECK: [[IS_RUST_SLOT:%.*]] = getelementptr inbounds{{( nuw)?}} i8, ptr [[ALLOCA]], [[PTR_SIZE]]
6161 // CHECK: store i8 [[IS_RUST_EXN_I8]], ptr [[IS_RUST_SLOT]]
6262
6363 // CHECK: call void %catch_fn(ptr %data, ptr nonnull [[ALLOCA]])
tests/codegen/issues/issue-118306.rs+1-1
......@@ -12,7 +12,7 @@ pub fn branchy(input: u64) -> u64 {
1212 // CHECK-LABEL: @branchy(
1313 // CHECK-NEXT: start:
1414 // CHECK-NEXT: [[_2:%.*]] = and i64 [[INPUT:%.*]], 3
15 // CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x i64], ptr @switch.table.branchy, i64 0, i64 [[_2]]
15 // CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds{{( nuw)?}} [4 x i64], ptr @switch.table.branchy, i64 0, i64 [[_2]]
1616 // CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]]
1717 // CHECK-NEXT: ret i64 [[SWITCH_LOAD]]
1818 match input % 4 {
tests/codegen/issues/issue-122805.rs+7-7
......@@ -17,19 +17,19 @@
1717// CHECK-LABEL: define{{.*}}void @convert(
1818// CHECK-NOT: shufflevector
1919// OPT2: store i16
20// OPT2-NEXT: getelementptr inbounds i8, {{.+}} 2
20// OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 2
2121// OPT2-NEXT: store i16
22// OPT2-NEXT: getelementptr inbounds i8, {{.+}} 4
22// OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 4
2323// OPT2-NEXT: store i16
24// OPT2-NEXT: getelementptr inbounds i8, {{.+}} 6
24// OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 6
2525// OPT2-NEXT: store i16
26// OPT2-NEXT: getelementptr inbounds i8, {{.+}} 8
26// OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 8
2727// OPT2-NEXT: store i16
28// OPT2-NEXT: getelementptr inbounds i8, {{.+}} 10
28// OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 10
2929// OPT2-NEXT: store i16
30// OPT2-NEXT: getelementptr inbounds i8, {{.+}} 12
30// OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 12
3131// OPT2-NEXT: store i16
32// OPT2-NEXT: getelementptr inbounds i8, {{.+}} 14
32// OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 14
3333// OPT2-NEXT: store i16
3434// OPT3LINX64: load <8 x i16>
3535// OPT3LINX64-NEXT: call <8 x i16> @llvm.bswap
tests/codegen/slice-iter-nonnull.rs+6-6
......@@ -14,7 +14,7 @@
1414// CHECK-LABEL: @slice_iter_next(
1515#[no_mangle]
1616pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> {
17 // CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
17 // CHECK: %[[ENDP:.+]] = getelementptr inbounds{{( nuw)?}} i8, ptr %it, {{i32 4|i64 8}}
1818 // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
1919 // CHECK-SAME: !nonnull
2020 // CHECK-SAME: !noundef
......@@ -31,7 +31,7 @@ pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32
3131// CHECK-LABEL: @slice_iter_next_back(
3232#[no_mangle]
3333pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> {
34 // CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
34 // CHECK: %[[ENDP:.+]] = getelementptr inbounds{{( nuw)?}} i8, ptr %it, {{i32 4|i64 8}}
3535 // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
3636 // CHECK-SAME: !nonnull
3737 // CHECK-SAME: !noundef
......@@ -55,7 +55,7 @@ pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'
5555#[no_mangle]
5656pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> {
5757 // CHECK-NOT: slice
58 // CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1
58 // CHECK: %[[END:.+]] = getelementptr inbounds{{( nuw)?}} i32{{.+}} %slice.0{{.+}} %slice.1
5959 // CHECK-NOT: slice
6060 // CHECK: insertvalue {{.+}} ptr %slice.0, 0
6161 // CHECK-NOT: slice
......@@ -70,7 +70,7 @@ pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> {
7070#[no_mangle]
7171pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> {
7272 // CHECK-NOT: slice
73 // CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1
73 // CHECK: %[[END:.+]] = getelementptr inbounds{{( nuw)?}} i32{{.+}} %slice.0{{.+}} %slice.1
7474 // CHECK-NOT: slice
7575 // CHECK: insertvalue {{.+}} ptr %slice.0, 0
7676 // CHECK-NOT: slice
......@@ -83,7 +83,7 @@ pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> {
8383// CHECK-LABEL: @slice_iter_is_empty
8484#[no_mangle]
8585pub fn slice_iter_is_empty(it: &std::slice::Iter<'_, u32>) -> bool {
86 // CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
86 // CHECK: %[[ENDP:.+]] = getelementptr inbounds{{( nuw)?}} i8, ptr %it, {{i32 4|i64 8}}
8787 // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
8888 // CHECK-SAME: !nonnull
8989 // CHECK-SAME: !noundef
......@@ -99,7 +99,7 @@ pub fn slice_iter_is_empty(it: &std::slice::Iter<'_, u32>) -> bool {
9999// CHECK-LABEL: @slice_iter_len
100100#[no_mangle]
101101pub fn slice_iter_len(it: &std::slice::Iter<'_, u32>) -> usize {
102 // CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
102 // CHECK: %[[ENDP:.+]] = getelementptr inbounds{{( nuw)?}} i8, ptr %it, {{i32 4|i64 8}}
103103 // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
104104 // CHECK-SAME: !nonnull
105105 // CHECK-SAME: !noundef
tests/debuginfo/auxiliary/dependency-with-embedded-visualizers.py+3
......@@ -1,5 +1,6 @@
11import gdb
22
3
34class PersonPrinter:
45 "Print a Person"
56
......@@ -11,6 +12,7 @@ class PersonPrinter:
1112 def to_string(self):
1213 return "{} is {} years old.".format(self.name, self.age)
1314
15
1416def lookup(val):
1517 lookup_tag = val.type.tag
1618 if lookup_tag is None:
......@@ -20,4 +22,5 @@ def lookup(val):
2022
2123 return None
2224
25
2326gdb.current_objfile().pretty_printers.append(lookup)
tests/debuginfo/embedded-visualizer-point.py+3
......@@ -1,5 +1,6 @@
11import gdb
22
3
34class PointPrinter:
45 "Print a Point"
56
......@@ -11,6 +12,7 @@ class PointPrinter:
1112 def to_string(self):
1213 return "({}, {})".format(self.x, self.y)
1314
15
1416def lookup(val):
1517 lookup_tag = val.type.tag
1618 if lookup_tag is None:
......@@ -20,4 +22,5 @@ def lookup(val):
2022
2123 return None
2224
25
2326gdb.current_objfile().pretty_printers.append(lookup)
tests/debuginfo/embedded-visualizer.py+3
......@@ -1,5 +1,6 @@
11import gdb
22
3
34class LinePrinter:
45 "Print a Line"
56
......@@ -11,6 +12,7 @@ class LinePrinter:
1112 def to_string(self):
1213 return "({}, {})".format(self.a, self.b)
1314
15
1416def lookup(val):
1517 lookup_tag = val.type.tag
1618 if lookup_tag is None:
......@@ -20,4 +22,5 @@ def lookup(val):
2022
2123 return None
2224
25
2326gdb.current_objfile().pretty_printers.append(lookup)
tests/rustdoc-gui/docblock-table-overflow.goml+3-7
......@@ -10,12 +10,8 @@ assert-property: (".top-doc .docblock table", {"scrollWidth": "1572"})
1010
1111// Checking it works on other doc blocks as well...
1212
13// Logically, the ".docblock" and the "<p>" should have the same scroll width.
14compare-elements-property: (
15 "#implementations-list > details .docblock",
16 "#implementations-list > details .docblock > p",
17 ["scrollWidth"],
18)
19assert-property: ("#implementations-list > details .docblock", {"scrollWidth": "835"})
13// Logically, the ".docblock" and the "<p>" should have the same scroll width (if we exclude the margin).
14assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 816})
15assert-property: ("#implementations-list > details .docblock > p", {"scrollWidth": 835})
2016// However, since there is overflow in the <table>, its scroll width is bigger.
2117assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"})
tests/rustdoc-gui/impl-block-doc.goml created+42
......@@ -0,0 +1,42 @@
1// Checks that the first sentence of an impl block doc is always visible even when the impl
2// block is collapsed.
3go-to: "file://" + |DOC_PATH| + "/test_docs/struct.ImplDoc.html"
4
5set-window-size: (900, 600)
6
7define-function: (
8 "compare-size-and-pos",
9 [nth_impl],
10 block {
11 // First we collapse the impl block.
12 store-value: (impl_path, "#implementations-list details:nth-of-type(" + |nth_impl| + ")")
13 set-property: (|impl_path|, {"open": false})
14 wait-for: |impl_path| + ":not([open])"
15
16 store-value: (impl_path, |impl_path| + " summary")
17 store-size: (|impl_path|, {"height": impl_height})
18 store-position: (|impl_path|, {"y": impl_y})
19
20 store-size: (|impl_path| + " .docblock", {"height": doc_height})
21 store-position: (|impl_path| + " .docblock", {"y": doc_y})
22
23 assert: |impl_y| + |impl_height| >= |doc_y|
24 }
25)
26
27call-function: ("compare-size-and-pos", {"nth_impl": 1})
28// Since the first impl block has a long line, we ensure that it doesn't display all of it.
29assert: (|impl_y| + |impl_height|) <= (|doc_y| + |doc_height|)
30
31call-function: ("compare-size-and-pos", {"nth_impl": 2})
32// The second impl block has a short line.
33assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|)
34
35// FIXME: Needs `if` condition to make this test check that `padding-top` on the "..." element
36// is as expected for tables.
37call-function: ("compare-size-and-pos", {"nth_impl": 3})
38assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|)
39call-function: ("compare-size-and-pos", {"nth_impl": 4})
40assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|)
41call-function: ("compare-size-and-pos", {"nth_impl": 5})
42assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|)
tests/rustdoc-gui/impl-doc.goml+1-1
......@@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/struct.TypeWithImplDoc.html"
33
44// The text is about 24px tall, so if there's a margin, then their position will be >24px apart
55compare-elements-position-near-false: (
6 "#implementations-list > .implementors-toggle > .docblock > p",
6 "#implementations-list > .implementors-toggle .docblock > p",
77 "#implementations-list > .implementors-toggle > .impl-items",
88 {"y": 24}
99)
tests/rustdoc-gui/item-info-overflow.goml+1-1
......@@ -16,7 +16,7 @@ assert-text: (
1616go-to: "file://" + |DOC_PATH| + "/lib2/struct.LongItemInfo2.html"
1717compare-elements-property: (
1818 "#impl-SimpleTrait-for-LongItemInfo2 .item-info",
19 "#impl-SimpleTrait-for-LongItemInfo2 + .docblock",
19 "#impl-SimpleTrait-for-LongItemInfo2 .docblock",
2020 ["scrollWidth"],
2121)
2222assert-property: (
tests/rustdoc-gui/source-code-page-code-scroll.goml+2-2
......@@ -2,7 +2,7 @@
22go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
33set-window-size: (800, 1000)
44// "scrollWidth" should be superior than "clientWidth".
5assert-property: ("body", {"scrollWidth": 1114, "clientWidth": 800})
5assert-property: ("body", {"scrollWidth": 1776, "clientWidth": 800})
66
77// Both properties should be equal (ie, no scroll on the code block).
8assert-property: (".example-wrap .rust", {"scrollWidth": 1000, "clientWidth": 1000})
8assert-property: (".example-wrap .rust", {"scrollWidth": 1662, "clientWidth": 1662})
tests/rustdoc-gui/src/test_docs/lib.rs+39
......@@ -652,3 +652,42 @@ pub mod long_list {
652652 //! * [`FromBytes`](#a) indicates that a type may safely be converted from an arbitrary byte
653653 //! sequence
654654}
655
656pub struct ImplDoc;
657
658/// bla sondfosdnf sdfasd fadsd fdsa f ads fad sf sad f sad fasdfsafsa df dsafasdasd fsa dfadsfasd
659/// fads fadfadd
660///
661/// bla
662impl ImplDoc {
663 pub fn bar() {}
664}
665
666/// bla
667///
668/// bla
669impl ImplDoc {
670 pub fn bar2() {}
671}
672
673// ignore-tidy-linelength
674/// | this::is::a::kinda::very::long::header::number::one | this::is::a::kinda::very::long::header::number::two | this::is::a::kinda::very::long::header::number::three |
675/// |-|-|-|
676/// | bla | bli | blob |
677impl ImplDoc {
678 pub fn bar3() {}
679}
680
681/// # h1
682///
683/// bla
684impl ImplDoc {
685 pub fn bar4() {}
686}
687
688/// * list
689/// * list
690/// * list
691impl ImplDoc {
692 pub fn bar5() {}
693}
tests/ui/codegen/target-cpus.rs+6
......@@ -1,3 +1,9 @@
11//@ needs-llvm-components: webassembly
22//@ compile-flags: --print=target-cpus --target=wasm32-unknown-unknown
33//@ check-pass
4
5// LLVM at HEAD has added support for the `lime1` CPU. Remove it from the
6// output so that the stdout with LLVM-at-HEAD matches the output of the LLVM
7// versions currently used by default.
8// FIXME(#133919): Once Rust upgrades to LLVM 20, remove this.
9//@ normalize-stdout-test: "(?m)^ *lime1\n" -> ""
tests/ui/explicit-tail-calls/become-macro.rs created+13
......@@ -0,0 +1,13 @@
1//@ check-pass
2#![expect(incomplete_features)]
3#![feature(explicit_tail_calls, decl_macro)]
4
5macro call($f:expr $(, $args:expr)* $(,)?) {
6 ($f)($($args),*)
7}
8
9fn main() {
10 become call!(f);
11}
12
13fn f() {}
tests/ui/explicit-tail-calls/become-operator.fixed created+42
......@@ -0,0 +1,42 @@
1//@ run-rustfix
2#![expect(incomplete_features)]
3#![feature(explicit_tail_calls)]
4#![allow(unused)]
5use std::num::Wrapping;
6use std::ops::{Not, Add, BitXorAssign};
7
8// built-ins and overloaded operators are handled differently
9
10fn f(a: u64, b: u64) -> u64 {
11 return a + b; //~ error: `become` does not support operators
12}
13
14fn g(a: String, b: &str) -> String {
15 become (a).add(b); //~ error: `become` does not support operators
16}
17
18fn h(x: u64) -> u64 {
19 return !x; //~ error: `become` does not support operators
20}
21
22fn i_do_not_know_any_more_letters(x: Wrapping<u32>) -> Wrapping<u32> {
23 become (x).not(); //~ error: `become` does not support operators
24}
25
26fn builtin_index(x: &[u8], i: usize) -> u8 {
27 return x[i] //~ error: `become` does not support operators
28}
29
30// FIXME(explicit_tail_calls): overloaded index is represented like `[&]*x.index(i)`,
31// and so need additional handling
32
33fn a(a: &mut u8, _: u8) {
34 return *a ^= 1; //~ error: `become` does not support operators
35}
36
37fn b(b: &mut Wrapping<u8>, _: u8) {
38 become (*b).bitxor_assign(1); //~ error: `become` does not support operators
39}
40
41
42fn main() {}
tests/ui/explicit-tail-calls/become-operator.rs created+42
......@@ -0,0 +1,42 @@
1//@ run-rustfix
2#![expect(incomplete_features)]
3#![feature(explicit_tail_calls)]
4#![allow(unused)]
5use std::num::Wrapping;
6use std::ops::{Not, Add, BitXorAssign};
7
8// built-ins and overloaded operators are handled differently
9
10fn f(a: u64, b: u64) -> u64 {
11 become a + b; //~ error: `become` does not support operators
12}
13
14fn g(a: String, b: &str) -> String {
15 become a + b; //~ error: `become` does not support operators
16}
17
18fn h(x: u64) -> u64 {
19 become !x; //~ error: `become` does not support operators
20}
21
22fn i_do_not_know_any_more_letters(x: Wrapping<u32>) -> Wrapping<u32> {
23 become !x; //~ error: `become` does not support operators
24}
25
26fn builtin_index(x: &[u8], i: usize) -> u8 {
27 become x[i] //~ error: `become` does not support operators
28}
29
30// FIXME(explicit_tail_calls): overloaded index is represented like `[&]*x.index(i)`,
31// and so need additional handling
32
33fn a(a: &mut u8, _: u8) {
34 become *a ^= 1; //~ error: `become` does not support operators
35}
36
37fn b(b: &mut Wrapping<u8>, _: u8) {
38 become *b ^= 1; //~ error: `become` does not support operators
39}
40
41
42fn main() {}
tests/ui/explicit-tail-calls/become-operator.stderr created+75
......@@ -0,0 +1,75 @@
1error: `become` does not support operators
2 --> $DIR/become-operator.rs:11:12
3 |
4LL | become a + b;
5 | -------^^^^^
6 | |
7 | help: try using `return` instead: `return`
8 |
9 = note: using `become` on a builtin operator is not useful
10
11error: `become` does not support operators
12 --> $DIR/become-operator.rs:15:12
13 |
14LL | become a + b;
15 | ^^^^^
16 |
17help: try using the method directly
18 |
19LL | become (a).add(b);
20 | + ~~~~~~ +
21
22error: `become` does not support operators
23 --> $DIR/become-operator.rs:19:12
24 |
25LL | become !x;
26 | -------^^
27 | |
28 | help: try using `return` instead: `return`
29 |
30 = note: using `become` on a builtin operator is not useful
31
32error: `become` does not support operators
33 --> $DIR/become-operator.rs:23:12
34 |
35LL | become !x;
36 | ^^
37 |
38help: try using the method directly
39 |
40LL | become (x).not();
41 | ~ +++++++
42
43error: `become` does not support operators
44 --> $DIR/become-operator.rs:27:12
45 |
46LL | become x[i]
47 | -------^^^^
48 | |
49 | help: try using `return` instead: `return`
50 |
51 = note: using `become` on a builtin operator is not useful
52
53error: `become` does not support operators
54 --> $DIR/become-operator.rs:34:12
55 |
56LL | become *a ^= 1;
57 | -------^^^^^^^
58 | |
59 | help: try using `return` instead: `return`
60 |
61 = note: using `become` on a builtin operator is not useful
62
63error: `become` does not support operators
64 --> $DIR/become-operator.rs:38:12
65 |
66LL | become *b ^= 1;
67 | ^^^^^^^
68 |
69help: try using the method directly
70 |
71LL | become (*b).bitxor_assign(1);
72 | + ~~~~~~~~~~~~~~~~ +
73
74error: aborting due to 7 previous errors
75
tests/ui/explicit-tail-calls/become-outside.rs+1-1
......@@ -1,5 +1,5 @@
11//@ revisions: constant array
2#![allow(incomplete_features)]
2#![expect(incomplete_features)]
33#![feature(explicit_tail_calls)]
44
55#[cfg(constant)]
tests/ui/explicit-tail-calls/become-uncallable.fixed created+18
......@@ -0,0 +1,18 @@
1//@ run-rustfix
2#![expect(incomplete_features)]
3#![feature(explicit_tail_calls)]
4#![allow(unused)]
5
6fn f() -> u64 {
7 return 1; //~ error: `become` requires a function call
8}
9
10fn g() {
11 return { h() }; //~ error: `become` requires a function call
12}
13
14fn h() {
15 return *&g(); //~ error: `become` requires a function call
16}
17
18fn main() {}
tests/ui/explicit-tail-calls/become-uncallable.rs created+18
......@@ -0,0 +1,18 @@
1//@ run-rustfix
2#![expect(incomplete_features)]
3#![feature(explicit_tail_calls)]
4#![allow(unused)]
5
6fn f() -> u64 {
7 become 1; //~ error: `become` requires a function call
8}
9
10fn g() {
11 become { h() }; //~ error: `become` requires a function call
12}
13
14fn h() {
15 become *&g(); //~ error: `become` requires a function call
16}
17
18fn main() {}
tests/ui/explicit-tail-calls/become-uncallable.stderr created+44
......@@ -0,0 +1,44 @@
1error: `become` requires a function call
2 --> $DIR/become-uncallable.rs:7:12
3 |
4LL | become 1;
5 | -------^
6 | |
7 | help: try using `return` instead: `return`
8 |
9note: not a function call
10 --> $DIR/become-uncallable.rs:7:12
11 |
12LL | become 1;
13 | ^
14
15error: `become` requires a function call
16 --> $DIR/become-uncallable.rs:11:12
17 |
18LL | become { h() };
19 | -------^^^^^^^
20 | |
21 | help: try using `return` instead: `return`
22 |
23note: not a function call
24 --> $DIR/become-uncallable.rs:11:12
25 |
26LL | become { h() };
27 | ^^^^^^^
28
29error: `become` requires a function call
30 --> $DIR/become-uncallable.rs:15:12
31 |
32LL | become *&g();
33 | -------^^^^^
34 | |
35 | help: try using `return` instead: `return`
36 |
37note: not a function call
38 --> $DIR/become-uncallable.rs:15:12
39 |
40LL | become *&g();
41 | ^^^^^
42
43error: aborting due to 3 previous errors
44
tests/ui/explicit-tail-calls/closure.fixed created+31
......@@ -0,0 +1,31 @@
1//@ run-rustfix
2#![expect(incomplete_features)]
3#![feature(explicit_tail_calls)]
4
5fn a() {
6 become ((|| ()) as fn() -> _)();
7 //~^ ERROR: tail calling closures directly is not allowed
8}
9
10fn aa((): ()) {
11 become ((|()| ()) as fn(_) -> _)(());
12 //~^ ERROR: tail calling closures directly is not allowed
13}
14
15fn aaa((): (), _: i32) {
16 become ((|(), _| ()) as fn(_, _) -> _)((), 1);
17 //~^ ERROR: tail calling closures directly is not allowed
18}
19
20fn v((): (), ((), ()): ((), ())) -> (((), ()), ()) {
21 let f = |(), ((), ())| (((), ()), ());
22 become (f as fn(_, _) -> _)((), ((), ()));
23 //~^ ERROR: tail calling closures directly is not allowed
24}
25
26fn main() {
27 a();
28 aa(());
29 aaa((), 1);
30 v((), ((), ()));
31}
tests/ui/explicit-tail-calls/closure.rs created+31
......@@ -0,0 +1,31 @@
1//@ run-rustfix
2#![expect(incomplete_features)]
3#![feature(explicit_tail_calls)]
4
5fn a() {
6 become (|| ())();
7 //~^ ERROR: tail calling closures directly is not allowed
8}
9
10fn aa((): ()) {
11 become (|()| ())(());
12 //~^ ERROR: tail calling closures directly is not allowed
13}
14
15fn aaa((): (), _: i32) {
16 become (|(), _| ())((), 1);
17 //~^ ERROR: tail calling closures directly is not allowed
18}
19
20fn v((): (), ((), ()): ((), ())) -> (((), ()), ()) {
21 let f = |(), ((), ())| (((), ()), ());
22 become f((), ((), ()));
23 //~^ ERROR: tail calling closures directly is not allowed
24}
25
26fn main() {
27 a();
28 aa(());
29 aaa((), 1);
30 v((), ((), ()));
31}
tests/ui/explicit-tail-calls/closure.stderr created+46
......@@ -0,0 +1,46 @@
1error: tail calling closures directly is not allowed
2 --> $DIR/closure.rs:6:5
3 |
4LL | become (|| ())();
5 | ^^^^^^^^^^^^^^^^
6 |
7help: try casting the closure to a function pointer type
8 |
9LL | become ((|| ()) as fn() -> _)();
10 | + +++++++++++++
11
12error: tail calling closures directly is not allowed
13 --> $DIR/closure.rs:11:5
14 |
15LL | become (|()| ())(());
16 | ^^^^^^^^^^^^^^^^^^^^
17 |
18help: try casting the closure to a function pointer type
19 |
20LL | become ((|()| ()) as fn(_) -> _)(());
21 | + ++++++++++++++
22
23error: tail calling closures directly is not allowed
24 --> $DIR/closure.rs:16:5
25 |
26LL | become (|(), _| ())((), 1);
27 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
28 |
29help: try casting the closure to a function pointer type
30 |
31LL | become ((|(), _| ()) as fn(_, _) -> _)((), 1);
32 | + +++++++++++++++++
33
34error: tail calling closures directly is not allowed
35 --> $DIR/closure.rs:22:5
36 |
37LL | become f((), ((), ()));
38 | ^^^^^^^^^^^^^^^^^^^^^^
39 |
40help: try casting the closure to a function pointer type
41 |
42LL | become (f as fn(_, _) -> _)((), ((), ()));
43 | + +++++++++++++++++
44
45error: aborting due to 4 previous errors
46
tests/ui/explicit-tail-calls/constck.rs+1-1
......@@ -1,4 +1,4 @@
1#![allow(incomplete_features)]
1#![expect(incomplete_features)]
22#![feature(explicit_tail_calls)]
33
44const fn f() {
tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.rs+1-1
......@@ -1,4 +1,4 @@
1#![allow(incomplete_features)]
1#![expect(incomplete_features)]
22#![feature(explicit_tail_calls)]
33
44pub const fn test(_: &Type) {
tests/ui/explicit-tail-calls/ctfe-arg-good-borrow.rs+1-1
......@@ -1,5 +1,5 @@
11//@ check-pass
2#![allow(incomplete_features)]
2#![expect(incomplete_features)]
33#![feature(explicit_tail_calls)]
44
55pub const fn test(x: &Type) {
tests/ui/explicit-tail-calls/ctfe-arg-move.rs+1-1
......@@ -1,5 +1,5 @@
11//@ check-pass
2#![allow(incomplete_features)]
2#![expect(incomplete_features)]
33#![feature(explicit_tail_calls)]
44
55pub const fn test(s: String) -> String {
tests/ui/explicit-tail-calls/ctfe-collatz-multi-rec.rs+1-1
......@@ -1,5 +1,5 @@
11//@ run-pass
2#![allow(incomplete_features)]
2#![expect(incomplete_features)]
33#![feature(explicit_tail_calls)]
44
55/// A very unnecessarily complicated "implementation" of the Collatz conjecture.
tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs+1-1
......@@ -1,6 +1,6 @@
11//@ revisions: become return
22//@ [become] run-pass
3#![allow(incomplete_features)]
3#![expect(incomplete_features)]
44#![feature(explicit_tail_calls)]
55
66// This is an identity function (`|x| x`), but implemented using recursion.
tests/ui/explicit-tail-calls/ctfe-tail-call-panic.rs+1-1
......@@ -1,4 +1,4 @@
1#![allow(incomplete_features)]
1#![expect(incomplete_features)]
22#![feature(explicit_tail_calls)]
33
44pub const fn f() {
tests/ui/explicit-tail-calls/drop-order.rs+1-1
......@@ -1,7 +1,7 @@
11// FIXME(explicit_tail_calls): enable this test once rustc_codegen_ssa supports tail calls
22//@ ignore-test: tail calls are not implemented in rustc_codegen_ssa yet, so this causes 🧊
33//@ run-pass
4#![allow(incomplete_features)]
4#![expect(incomplete_features)]
55#![feature(explicit_tail_calls)]
66use std::cell::RefCell;
77
tests/ui/explicit-tail-calls/in-closure.rs created+8
......@@ -0,0 +1,8 @@
1#![expect(incomplete_features)]
2#![feature(explicit_tail_calls)]
3
4fn main() {
5 || become f(); //~ error: `become` is not allowed in closures
6}
7
8fn f() {}
tests/ui/explicit-tail-calls/in-closure.stderr created+8
......@@ -0,0 +1,8 @@
1error: `become` is not allowed in closures
2 --> $DIR/in-closure.rs:5:8
3 |
4LL | || become f();
5 | ^^^^^^^^^^
6
7error: aborting due to 1 previous error
8
tests/ui/explicit-tail-calls/return-lifetime-sub.rs+1-1
......@@ -1,5 +1,5 @@
11//@ check-pass
2#![allow(incomplete_features)]
2#![expect(incomplete_features)]
33#![feature(explicit_tail_calls)]
44
55fn _f<'a>() -> &'a [u8] {
tests/ui/explicit-tail-calls/return-mismatches.rs+1-1
......@@ -1,4 +1,4 @@
1#![allow(incomplete_features)]
1#![expect(incomplete_features)]
22#![feature(explicit_tail_calls)]
33
44fn _f0<'a>() -> &'static [u8] {
tests/ui/explicit-tail-calls/signature-mismatch.rs created+33
......@@ -0,0 +1,33 @@
1#![expect(incomplete_features)]
2#![feature(explicit_tail_calls)]
3#![feature(c_variadic)]
4
5fn _f0((): ()) {
6 become _g0(); //~ error: mismatched signatures
7}
8
9fn _g0() {}
10
11
12fn _f1() {
13 become _g1(()); //~ error: mismatched signatures
14}
15
16fn _g1((): ()) {}
17
18
19extern "C" fn _f2() {
20 become _g2(); //~ error: mismatched function ABIs
21}
22
23fn _g2() {}
24
25
26fn _f3() {
27 become _g3(); //~ error: mismatched function ABIs
28}
29
30extern "C" fn _g3() {}
31
32
33fn main() {}
tests/ui/explicit-tail-calls/signature-mismatch.stderr created+40
......@@ -0,0 +1,40 @@
1error: mismatched signatures
2 --> $DIR/signature-mismatch.rs:6:5
3 |
4LL | become _g0();
5 | ^^^^^^^^^^^^
6 |
7 = note: `become` requires caller and callee to have matching signatures
8 = note: caller signature: `fn(())`
9 = note: callee signature: `fn()`
10
11error: mismatched signatures
12 --> $DIR/signature-mismatch.rs:13:5
13 |
14LL | become _g1(());
15 | ^^^^^^^^^^^^^^
16 |
17 = note: `become` requires caller and callee to have matching signatures
18 = note: caller signature: `fn()`
19 = note: callee signature: `fn(())`
20
21error: mismatched function ABIs
22 --> $DIR/signature-mismatch.rs:20:5
23 |
24LL | become _g2();
25 | ^^^^^^^^^^^^
26 |
27 = note: `become` requires caller and callee to have the same ABI
28 = note: caller ABI is `"C"`, while callee ABI is `"Rust"`
29
30error: mismatched function ABIs
31 --> $DIR/signature-mismatch.rs:27:5
32 |
33LL | become _g3();
34 | ^^^^^^^^^^^^
35 |
36 = note: `become` requires caller and callee to have the same ABI
37 = note: caller ABI is `"Rust"`, while callee ABI is `"C"`
38
39error: aborting due to 4 previous errors
40
tests/ui/explicit-tail-calls/unsafeck.rs+1-1
......@@ -1,4 +1,4 @@
1#![allow(incomplete_features)]
1#![expect(incomplete_features)]
22#![feature(explicit_tail_calls)]
33
44const fn f() {
x.py+6-3
......@@ -6,7 +6,7 @@
66
77# Parts of `bootstrap.py` use the `multiprocessing` module, so this entry point
88# must use the normal `if __name__ == '__main__':` convention to avoid problems.
9if __name__ == '__main__':
9if __name__ == "__main__":
1010 import os
1111 import sys
1212 import warnings
......@@ -32,14 +32,16 @@ if __name__ == '__main__':
3232 # soft deprecation of old python versions
3333 skip_check = os.environ.get("RUST_IGNORE_OLD_PYTHON") == "1"
3434 if not skip_check and (major < 3 or (major == 3 and minor < 6)):
35 msg = cleandoc("""
35 msg = cleandoc(
36 """
3637 Using python {}.{} but >= 3.6 is recommended. Your python version
3738 should continue to work for the near future, but this will
3839 eventually change. If python >= 3.6 is not available on your system,
3940 please file an issue to help us understand timelines.
4041
4142 This message can be suppressed by setting `RUST_IGNORE_OLD_PYTHON=1`
42 """.format(major, minor))
43 """.format(major, minor)
44 )
4345 warnings.warn(msg, stacklevel=1)
4446
4547 rust_dir = os.path.dirname(os.path.abspath(__file__))
......@@ -47,4 +49,5 @@ if __name__ == '__main__':
4749 sys.path.insert(0, os.path.join(rust_dir, "src", "bootstrap"))
4850
4951 import bootstrap
52
5053 bootstrap.main()