| author | bors <bors@rust-lang.org> 2024-12-05 23:42:57 UTC |
| committer | bors <bors@rust-lang.org> 2024-12-05 23:42:57 UTC |
| log | 706141b8d9090228343340378b1d4a2b095fa1fb |
| tree | e87ce86ba71331485e2d1140a2684345050f220f |
| parent | c94848c046d29f9a80c09aae758e27e418a289f2 |
| parent | 5dc05a8d01d0608c38061b839f7da77e599f6479 |
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: rollup82 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::*; |
| 29 | 29 | use rustc_errors::{ |
| 30 | 30 | Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, |
| 31 | 31 | }; |
| 32 | use rustc_hir as hir; | |
| 33 | 32 | use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; |
| 34 | 33 | use rustc_hir::def_id::{DefId, LocalDefId}; |
| 35 | use rustc_hir::{GenericArg, GenericArgs, HirId}; | |
| 34 | use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; | |
| 36 | 35 | use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; |
| 37 | 36 | use rustc_infer::traits::ObligationCause; |
| 38 | 37 | use rustc_middle::middle::stability::AllowUnstable; |
| ... | ... | @@ -2089,7 +2088,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { |
| 2089 | 2088 | qpath.span(), |
| 2090 | 2089 | format!("Const::lower_const_arg: invalid qpath {qpath:?}"), |
| 2091 | 2090 | ), |
| 2092 | hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon.def_id), | |
| 2091 | hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon), | |
| 2093 | 2092 | hir::ConstArgKind::Infer(span) => self.ct_infer(None, span), |
| 2094 | 2093 | } |
| 2095 | 2094 | } |
| ... | ... | @@ -2180,27 +2179,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { |
| 2180 | 2179 | /// Literals and const generic parameters are eagerly converted to a constant, everything else |
| 2181 | 2180 | /// becomes `Unevaluated`. |
| 2182 | 2181 | #[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> { | |
| 2184 | 2183 | let tcx = self.tcx(); |
| 2185 | 2184 | |
| 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; | |
| 2195 | 2186 | debug!(?expr); |
| 2196 | 2187 | |
| 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"); | |
| 2198 | 2192 | |
| 2199 | 2193 | match self.try_lower_anon_const_lit(ty, expr) { |
| 2200 | 2194 | Some(v) => v, |
| 2201 | 2195 | 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()), | |
| 2204 | 2198 | }), |
| 2205 | 2199 | } |
| 2206 | 2200 | } |
compiler/rustc_middle/src/query/mod.rs+6| ... | ... | @@ -916,6 +916,12 @@ rustc_queries! { |
| 916 | 916 | cache_on_disk_if { true } |
| 917 | 917 | } |
| 918 | 918 | |
| 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 | ||
| 919 | 925 | /// Returns the types assumed to be well formed while "inside" of the given item. |
| 920 | 926 | /// |
| 921 | 927 | /// 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 |
| 50 | 50 | return construct_error(tcx, def, e); |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | if let Err(err) = tcx.check_tail_calls(def) { | |
| 54 | return construct_error(tcx, def, err); | |
| 55 | } | |
| 56 | ||
| 53 | 57 | let body = match tcx.thir_body(def) { |
| 54 | 58 | Err(error_reported) => construct_error(tcx, def, error_reported), |
| 55 | 59 | Ok((thir, expr)) => { |
compiler/rustc_mir_build/src/check_tail_calls.rs created+386| ... | ... | @@ -0,0 +1,386 @@ |
| 1 | use rustc_abi::ExternAbi; | |
| 2 | use rustc_errors::Applicability; | |
| 3 | use rustc_hir::LangItem; | |
| 4 | use rustc_hir::def::DefKind; | |
| 5 | use rustc_middle::span_bug; | |
| 6 | use rustc_middle::thir::visit::{self, Visitor}; | |
| 7 | use rustc_middle::thir::{BodyTy, Expr, ExprId, ExprKind, Thir}; | |
| 8 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
| 9 | use rustc_span::def_id::{DefId, LocalDefId}; | |
| 10 | use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; | |
| 11 | ||
| 12 | pub(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 | ||
| 39 | struct 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 | ||
| 52 | impl<'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 | ||
| 341 | impl<'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 | ||
| 356 | fn 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 @@ |
| 12 | 12 | // tidy-alphabetical-end |
| 13 | 13 | |
| 14 | 14 | mod build; |
| 15 | mod check_tail_calls; | |
| 15 | 16 | mod check_unsafety; |
| 16 | 17 | mod errors; |
| 17 | 18 | pub mod lints; |
| ... | ... | @@ -28,6 +29,7 @@ pub fn provide(providers: &mut Providers) { |
| 28 | 29 | providers.closure_saved_names_of_captured_variables = |
| 29 | 30 | build::closure_saved_names_of_captured_variables; |
| 30 | 31 | providers.check_unsafety = check_unsafety::check_unsafety; |
| 32 | providers.check_tail_calls = check_tail_calls::check_tail_calls; | |
| 31 | 33 | providers.thir_body = thir::cx::thir_body; |
| 32 | 34 | providers.hooks.thir_tree = thir::print::thir_tree; |
| 33 | 35 | providers.hooks.thir_flat = thir::print::thir_flat; |
library/core/src/unicode/printable.py+34-19| ... | ... | @@ -9,7 +9,8 @@ import csv |
| 9 | 9 | import os |
| 10 | 10 | import subprocess |
| 11 | 11 | |
| 12 | NUM_CODEPOINTS=0x110000 | |
| 12 | NUM_CODEPOINTS = 0x110000 | |
| 13 | ||
| 13 | 14 | |
| 14 | 15 | def to_ranges(iter): |
| 15 | 16 | current = None |
| ... | ... | @@ -23,11 +24,15 @@ def to_ranges(iter): |
| 23 | 24 | if current is not None: |
| 24 | 25 | yield tuple(current) |
| 25 | 26 | |
| 27 | ||
| 26 | 28 | def get_escaped(codepoints): |
| 27 | 29 | 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 | ): | |
| 29 | 33 | yield c.value |
| 30 | 34 | |
| 35 | ||
| 31 | 36 | def get_file(f): |
| 32 | 37 | try: |
| 33 | 38 | return open(os.path.basename(f)) |
| ... | ... | @@ -35,7 +40,9 @@ def get_file(f): |
| 35 | 40 | subprocess.run(["curl", "-O", f], check=True) |
| 36 | 41 | return open(os.path.basename(f)) |
| 37 | 42 | |
| 38 | Codepoint = namedtuple('Codepoint', 'value class_') | |
| 43 | ||
| 44 | Codepoint = namedtuple("Codepoint", "value class_") | |
| 45 | ||
| 39 | 46 | |
| 40 | 47 | def get_codepoints(f): |
| 41 | 48 | r = csv.reader(f, delimiter=";") |
| ... | ... | @@ -66,13 +73,14 @@ def get_codepoints(f): |
| 66 | 73 | for c in range(prev_codepoint + 1, NUM_CODEPOINTS): |
| 67 | 74 | yield Codepoint(c, None) |
| 68 | 75 | |
| 76 | ||
| 69 | 77 | def compress_singletons(singletons): |
| 70 | uppers = [] # (upper, # items in lowers) | |
| 78 | uppers = [] # (upper, # items in lowers) | |
| 71 | 79 | lowers = [] |
| 72 | 80 | |
| 73 | 81 | for i in singletons: |
| 74 | 82 | upper = i >> 8 |
| 75 | lower = i & 0xff | |
| 83 | lower = i & 0xFF | |
| 76 | 84 | if len(uppers) == 0 or uppers[-1][0] != upper: |
| 77 | 85 | uppers.append((upper, 1)) |
| 78 | 86 | else: |
| ... | ... | @@ -82,10 +90,11 @@ def compress_singletons(singletons): |
| 82 | 90 | |
| 83 | 91 | return uppers, lowers |
| 84 | 92 | |
| 93 | ||
| 85 | 94 | def compress_normal(normal): |
| 86 | 95 | # lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f |
| 87 | 96 | # 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)] | |
| 89 | 98 | |
| 90 | 99 | prev_start = 0 |
| 91 | 100 | for start, count in normal: |
| ... | ... | @@ -95,21 +104,22 @@ def compress_normal(normal): |
| 95 | 104 | |
| 96 | 105 | assert truelen < 0x8000 and falselen < 0x8000 |
| 97 | 106 | entry = [] |
| 98 | if truelen > 0x7f: | |
| 107 | if truelen > 0x7F: | |
| 99 | 108 | entry.append(0x80 | (truelen >> 8)) |
| 100 | entry.append(truelen & 0xff) | |
| 109 | entry.append(truelen & 0xFF) | |
| 101 | 110 | else: |
| 102 | entry.append(truelen & 0x7f) | |
| 103 | if falselen > 0x7f: | |
| 111 | entry.append(truelen & 0x7F) | |
| 112 | if falselen > 0x7F: | |
| 104 | 113 | entry.append(0x80 | (falselen >> 8)) |
| 105 | entry.append(falselen & 0xff) | |
| 114 | entry.append(falselen & 0xFF) | |
| 106 | 115 | else: |
| 107 | entry.append(falselen & 0x7f) | |
| 116 | entry.append(falselen & 0x7F) | |
| 108 | 117 | |
| 109 | 118 | compressed.append(entry) |
| 110 | 119 | |
| 111 | 120 | return compressed |
| 112 | 121 | |
| 122 | ||
| 113 | 123 | def print_singletons(uppers, lowers, uppersname, lowersname): |
| 114 | 124 | print("#[rustfmt::skip]") |
| 115 | 125 | print("const {}: &[(u8, u8)] = &[".format(uppersname)) |
| ... | ... | @@ -119,9 +129,12 @@ def print_singletons(uppers, lowers, uppersname, lowersname): |
| 119 | 129 | print("#[rustfmt::skip]") |
| 120 | 130 | print("const {}: &[u8] = &[".format(lowersname)) |
| 121 | 131 | 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 | ) | |
| 123 | 135 | print("];") |
| 124 | 136 | |
| 137 | ||
| 125 | 138 | def print_normal(normal, normalname): |
| 126 | 139 | print("#[rustfmt::skip]") |
| 127 | 140 | print("const {}: &[u8] = &[".format(normalname)) |
| ... | ... | @@ -129,12 +142,13 @@ def print_normal(normal, normalname): |
| 129 | 142 | print(" {}".format(" ".join("{:#04x},".format(i) for i in v))) |
| 130 | 143 | print("];") |
| 131 | 144 | |
| 145 | ||
| 132 | 146 | def main(): |
| 133 | 147 | file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt") |
| 134 | 148 | |
| 135 | 149 | codepoints = get_codepoints(file) |
| 136 | 150 | |
| 137 | CUTOFF=0x10000 | |
| 151 | CUTOFF = 0x10000 | |
| 138 | 152 | singletons0 = [] |
| 139 | 153 | singletons1 = [] |
| 140 | 154 | normal0 = [] |
| ... | ... | @@ -234,10 +248,11 @@ pub(crate) fn is_printable(x: char) -> bool { |
| 234 | 248 | }\ |
| 235 | 249 | """) |
| 236 | 250 | 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 | ||
| 241 | 256 | |
| 242 | if __name__ == '__main__': | |
| 257 | if __name__ == "__main__": | |
| 243 | 258 | main() |
src/bootstrap/bootstrap.py+393-270| ... | ... | @@ -19,14 +19,17 @@ try: |
| 19 | 19 | except ImportError: |
| 20 | 20 | lzma = None |
| 21 | 21 | |
| 22 | ||
| 22 | 23 | def platform_is_win32(): |
| 23 | return sys.platform == 'win32' | |
| 24 | return sys.platform == "win32" | |
| 25 | ||
| 24 | 26 | |
| 25 | 27 | if platform_is_win32(): |
| 26 | 28 | EXE_SUFFIX = ".exe" |
| 27 | 29 | else: |
| 28 | 30 | EXE_SUFFIX = "" |
| 29 | 31 | |
| 32 | ||
| 30 | 33 | def get_cpus(): |
| 31 | 34 | if hasattr(os, "sched_getaffinity"): |
| 32 | 35 | return len(os.sched_getaffinity(0)) |
| ... | ... | @@ -51,11 +54,14 @@ def get(base, url, path, checksums, verbose=False): |
| 51 | 54 | |
| 52 | 55 | try: |
| 53 | 56 | 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 | ) | |
| 59 | 65 | sha256 = checksums[url] |
| 60 | 66 | if os.path.exists(path): |
| 61 | 67 | if verify(path, sha256, False): |
| ... | ... | @@ -64,8 +70,11 @@ def get(base, url, path, checksums, verbose=False): |
| 64 | 70 | return |
| 65 | 71 | else: |
| 66 | 72 | 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 | ) | |
| 69 | 78 | os.unlink(path) |
| 70 | 79 | download(temp_path, "{}/{}".format(base, url), True, verbose) |
| 71 | 80 | if not verify(temp_path, sha256, verbose): |
| ... | ... | @@ -79,12 +88,14 @@ def get(base, url, path, checksums, verbose=False): |
| 79 | 88 | eprint("removing", temp_path) |
| 80 | 89 | os.unlink(temp_path) |
| 81 | 90 | |
| 91 | ||
| 82 | 92 | def curl_version(): |
| 83 | 93 | m = re.match(bytes("^curl ([0-9]+)\\.([0-9]+)", "utf8"), require(["curl", "-V"])) |
| 84 | 94 | if m is None: |
| 85 | 95 | return (0, 0) |
| 86 | 96 | return (int(m[1]), int(m[2])) |
| 87 | 97 | |
| 98 | ||
| 88 | 99 | def download(path, url, probably_big, verbose): |
| 89 | 100 | for _ in range(4): |
| 90 | 101 | try: |
| ... | ... | @@ -114,32 +125,53 @@ def _download(path, url, probably_big, verbose, exception): |
| 114 | 125 | require(["curl", "--version"], exception=platform_is_win32()) |
| 115 | 126 | extra_flags = [] |
| 116 | 127 | if curl_version() > (7, 70): |
| 117 | extra_flags = [ "--retry-all-errors" ] | |
| 128 | extra_flags = ["--retry-all-errors"] | |
| 118 | 129 | # options should be kept in sync with |
| 119 | 130 | # src/bootstrap/src/core/download.rs |
| 120 | 131 | # for consistency. |
| 121 | 132 | # 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 | ], | |
| 132 | 158 | verbose=verbose, |
| 133 | exception=True, # Will raise RuntimeError on failure | |
| 159 | exception=True, # Will raise RuntimeError on failure | |
| 134 | 160 | ) |
| 135 | 161 | except (subprocess.CalledProcessError, OSError, RuntimeError): |
| 136 | 162 | # see http://serverfault.com/questions/301128/how-to-download |
| 163 | script = "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;" | |
| 137 | 164 | 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 | ], | |
| 141 | 172 | verbose=verbose, |
| 142 | exception=exception) | |
| 173 | exception=exception, | |
| 174 | ) | |
| 143 | 175 | # Check if the RuntimeError raised by run(curl) should be silenced |
| 144 | 176 | elif verbose or exception: |
| 145 | 177 | raise |
| ... | ... | @@ -153,9 +185,11 @@ def verify(path, expected, verbose): |
| 153 | 185 | found = hashlib.sha256(source.read()).hexdigest() |
| 154 | 186 | verified = found == expected |
| 155 | 187 | 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 | ) | |
| 159 | 193 | return verified |
| 160 | 194 | |
| 161 | 195 | |
| ... | ... | @@ -170,7 +204,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None): |
| 170 | 204 | name = member.replace(fname + "/", "", 1) |
| 171 | 205 | if match is not None and not name.startswith(match): |
| 172 | 206 | continue |
| 173 | name = name[len(match) + 1:] | |
| 207 | name = name[len(match) + 1 :] | |
| 174 | 208 | |
| 175 | 209 | dst_path = os.path.join(dst, name) |
| 176 | 210 | if verbose: |
| ... | ... | @@ -186,18 +220,18 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None): |
| 186 | 220 | def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs): |
| 187 | 221 | """Run a child program in a new process""" |
| 188 | 222 | if verbose: |
| 189 | eprint("running: " + ' '.join(args)) | |
| 223 | eprint("running: " + " ".join(args)) | |
| 190 | 224 | sys.stdout.flush() |
| 191 | 225 | # Ensure that the .exe is used on Windows just in case a Linux ELF has been |
| 192 | 226 | # 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" | |
| 195 | 229 | # Use Popen here instead of call() as it apparently allows powershell on |
| 196 | 230 | # Windows to not lock up waiting for input presumably. |
| 197 | 231 | ret = subprocess.Popen(args, **kwargs) |
| 198 | 232 | code = ret.wait() |
| 199 | 233 | if code != 0: |
| 200 | err = "failed to run: " + ' '.join(args) | |
| 234 | err = "failed to run: " + " ".join(args) | |
| 201 | 235 | if verbose or exception: |
| 202 | 236 | raise RuntimeError(err) |
| 203 | 237 | # 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): |
| 209 | 243 | else: |
| 210 | 244 | sys.exit(err) |
| 211 | 245 | |
| 246 | ||
| 212 | 247 | def run_powershell(script, *args, **kwargs): |
| 213 | 248 | """Run a powershell script""" |
| 214 | 249 | run(["PowerShell.exe", "/nologo", "-Command"] + script, *args, **kwargs) |
| 215 | 250 | |
| 216 | 251 | |
| 217 | 252 | def require(cmd, exit=True, exception=False): |
| 218 | '''Run a command, returning its output. | |
| 253 | """Run a command, returning its output. | |
| 219 | 254 | On error, |
| 220 | 255 | If `exception` is `True`, raise the error |
| 221 | 256 | Otherwise If `exit` is `True`, exit the process |
| 222 | Else return None.''' | |
| 257 | Else return None.""" | |
| 223 | 258 | try: |
| 224 | 259 | return subprocess.check_output(cmd).strip() |
| 225 | 260 | except (subprocess.CalledProcessError, OSError) as exc: |
| 226 | 261 | if exception: |
| 227 | 262 | raise |
| 228 | 263 | elif exit: |
| 229 | eprint("ERROR: unable to run `{}`: {}".format(' '.join(cmd), exc)) | |
| 264 | eprint("ERROR: unable to run `{}`: {}".format(" ".join(cmd), exc)) | |
| 230 | 265 | eprint("Please make sure it's installed and in the path.") |
| 231 | 266 | sys.exit(1) |
| 232 | 267 | return None |
| 233 | 268 | |
| 234 | 269 | |
| 235 | ||
| 236 | 270 | def format_build_time(duration): |
| 237 | 271 | """Return a nicer format for build time |
| 238 | 272 | |
| ... | ... | @@ -252,13 +286,16 @@ def default_build_triple(verbose): |
| 252 | 286 | |
| 253 | 287 | if platform_is_win32(): |
| 254 | 288 | 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 | ) | |
| 257 | 292 | 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: ")) | |
| 259 | 294 | triple = host.split("host: ")[1] |
| 260 | 295 | 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 | ) | |
| 262 | 299 | return triple |
| 263 | 300 | except Exception as e: |
| 264 | 301 | if verbose: |
| ... | ... | @@ -270,148 +307,149 @@ def default_build_triple(verbose): |
| 270 | 307 | |
| 271 | 308 | # If we do not have `uname`, assume Windows. |
| 272 | 309 | if uname is None: |
| 273 | return 'x86_64-pc-windows-msvc' | |
| 310 | return "x86_64-pc-windows-msvc" | |
| 274 | 311 | |
| 275 | 312 | kernel, cputype, processor = uname.decode(default_encoding).split(maxsplit=2) |
| 276 | 313 | |
| 277 | 314 | # The goal here is to come up with the same triple as LLVM would, |
| 278 | 315 | # at least for the subset of platforms we're willing to target. |
| 279 | 316 | 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", | |
| 287 | 324 | } |
| 288 | 325 | |
| 289 | 326 | # Consider the direct transformation first and then the special cases |
| 290 | 327 | if kernel in kerneltype_mapper: |
| 291 | 328 | kernel = kerneltype_mapper[kernel] |
| 292 | elif kernel == 'Linux': | |
| 329 | elif kernel == "Linux": | |
| 293 | 330 | # Apple doesn't support `-o` so this can't be used in the combined |
| 294 | 331 | # uname invocation above |
| 295 | 332 | 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" | |
| 298 | 335 | 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" | |
| 302 | 339 | # On Solaris, uname -m will return a machine classification instead |
| 303 | 340 | # of a cpu type, so uname -p is recommended instead. However, the |
| 304 | 341 | # output from that option is too generic for our purposes (it will |
| 305 | 342 | # always emit 'i386' on x86/amd64 systems). As such, isainfo -k |
| 306 | 343 | # must be used instead. |
| 307 | cputype = require(['isainfo', '-k']).decode(default_encoding) | |
| 344 | cputype = require(["isainfo", "-k"]).decode(default_encoding) | |
| 308 | 345 | # 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"): | |
| 312 | 349 | # msys' `uname` does not print gcc configuration, but prints msys |
| 313 | 350 | # configuration. so we cannot believe `uname -m`: |
| 314 | 351 | # msys1 is always i686 and msys2 is always x86_64. |
| 315 | 352 | # instead, msys defines $MSYSTEM which is MINGW32 on i686 and |
| 316 | 353 | # 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" | |
| 328 | 365 | elif platform_is_win32(): |
| 329 | 366 | # Some Windows platforms might have a `uname` command that returns a |
| 330 | 367 | # non-standard string (e.g. gnuwin32 tools returns `windows32`). In |
| 331 | 368 | # 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": | |
| 334 | 371 | # `uname -m` returns the machine ID rather than machine hardware on AIX, |
| 335 | 372 | # so we are unable to use cputype to form triple. AIX 7.2 and |
| 336 | 373 | # above supports 32-bit and 64-bit mode simultaneously and `uname -p` |
| 337 | 374 | # returns `powerpc`, however we only supports `powerpc64-ibm-aix` in |
| 338 | 375 | # rust on AIX. For above reasons, kerneltype_mapper and cputype_mapper |
| 339 | 376 | # are not used to infer AIX's triple. |
| 340 | return 'powerpc64-ibm-aix' | |
| 377 | return "powerpc64-ibm-aix" | |
| 341 | 378 | else: |
| 342 | 379 | err = "unknown OS type: {}".format(kernel) |
| 343 | 380 | sys.exit(err) |
| 344 | 381 | |
| 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 | ) | |
| 348 | 386 | 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", | |
| 374 | 412 | } |
| 375 | 413 | |
| 376 | 414 | # Consider the direct transformation first and then the special cases |
| 377 | 415 | if cputype in cputype_mapper: |
| 378 | 416 | 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": | |
| 384 | 422 | 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" | |
| 390 | 428 | 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" | |
| 396 | 434 | 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" | |
| 403 | 441 | else: |
| 404 | 442 | 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" | |
| 410 | 448 | else: |
| 411 | raise ValueError('unknown byteorder: {}'.format(sys.byteorder)) | |
| 449 | raise ValueError("unknown byteorder: {}".format(sys.byteorder)) | |
| 412 | 450 | # 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": | |
| 415 | 453 | pass |
| 416 | 454 | else: |
| 417 | 455 | err = "unknown cpu type: {}".format(cputype) |
| ... | ... | @@ -422,8 +460,8 @@ def default_build_triple(verbose): |
| 422 | 460 | |
| 423 | 461 | @contextlib.contextmanager |
| 424 | 462 | def output(filepath): |
| 425 | tmp = filepath + '.tmp' | |
| 426 | with open(tmp, 'w') as f: | |
| 463 | tmp = filepath + ".tmp" | |
| 464 | with open(tmp, "w") as f: | |
| 427 | 465 | yield f |
| 428 | 466 | try: |
| 429 | 467 | if os.path.exists(filepath): |
| ... | ... | @@ -467,6 +505,7 @@ class DownloadInfo: |
| 467 | 505 | self.pattern = pattern |
| 468 | 506 | self.verbose = verbose |
| 469 | 507 | |
| 508 | ||
| 470 | 509 | def download_component(download_info): |
| 471 | 510 | if not os.path.exists(download_info.tarball_path): |
| 472 | 511 | get( |
| ... | ... | @@ -477,6 +516,7 @@ def download_component(download_info): |
| 477 | 516 | verbose=download_info.verbose, |
| 478 | 517 | ) |
| 479 | 518 | |
| 519 | ||
| 480 | 520 | def unpack_component(download_info): |
| 481 | 521 | unpack( |
| 482 | 522 | download_info.tarball_path, |
| ... | ... | @@ -486,26 +526,30 @@ def unpack_component(download_info): |
| 486 | 526 | verbose=download_info.verbose, |
| 487 | 527 | ) |
| 488 | 528 | |
| 529 | ||
| 489 | 530 | class FakeArgs: |
| 490 | 531 | """Used for unit tests to avoid updating all call sites""" |
| 532 | ||
| 491 | 533 | def __init__(self): |
| 492 | self.build = '' | |
| 493 | self.build_dir = '' | |
| 534 | self.build = "" | |
| 535 | self.build_dir = "" | |
| 494 | 536 | self.clean = False |
| 495 | 537 | self.verbose = False |
| 496 | 538 | self.json_output = False |
| 497 | self.color = 'auto' | |
| 498 | self.warnings = 'default' | |
| 539 | self.color = "auto" | |
| 540 | self.warnings = "default" | |
| 541 | ||
| 499 | 542 | |
| 500 | 543 | class RustBuild(object): |
| 501 | 544 | """Provide all the methods required to build Rust""" |
| 545 | ||
| 502 | 546 | def __init__(self, config_toml="", args=None): |
| 503 | 547 | if args is None: |
| 504 | 548 | args = FakeArgs() |
| 505 | 549 | self.git_version = None |
| 506 | 550 | self.nix_deps_dir = None |
| 507 | 551 | 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__, "../../..")) | |
| 509 | 553 | |
| 510 | 554 | self.config_toml = config_toml |
| 511 | 555 | |
| ... | ... | @@ -515,26 +559,28 @@ class RustBuild(object): |
| 515 | 559 | self.color = args.color |
| 516 | 560 | self.warnings = args.warnings |
| 517 | 561 | |
| 518 | config_verbose_count = self.get_toml('verbose', 'build') | |
| 562 | config_verbose_count = self.get_toml("verbose", "build") | |
| 519 | 563 | if config_verbose_count is not None: |
| 520 | 564 | self.verbose = max(self.verbose, int(config_verbose_count)) |
| 521 | 565 | |
| 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" | |
| 524 | 568 | |
| 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" | |
| 526 | 570 | self.build_dir = os.path.abspath(build_dir) |
| 527 | 571 | |
| 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 | ) | |
| 529 | 575 | 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"] | |
| 532 | 580 | ) |
| 533 | self.download_url = os.getenv("RUSTUP_DIST_SERVER") or self.stage0_data["dist_server"] | |
| 534 | 581 | |
| 535 | 582 | self.build = args.build or self.build_triple() |
| 536 | 583 | |
| 537 | ||
| 538 | 584 | def download_toolchain(self): |
| 539 | 585 | """Fetch the build system for Rust, written in Rust |
| 540 | 586 | |
| ... | ... | @@ -550,58 +596,73 @@ class RustBuild(object): |
| 550 | 596 | |
| 551 | 597 | key = self.stage0_compiler.date |
| 552 | 598 | 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 | ) | |
| 557 | 605 | |
| 558 | 606 | if need_rustc or need_cargo: |
| 559 | 607 | if os.path.exists(bin_root): |
| 560 | 608 | # HACK: On Windows, we can't delete rust-analyzer-proc-macro-server while it's |
| 561 | 609 | # running. Kill it. |
| 562 | 610 | 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 | |
| 567 | 616 | ) |
| 568 | 617 | script = ( |
| 569 | 618 | # NOTE: can't use `taskkill` or `Get-Process -Name` because they error if |
| 570 | 619 | # 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" | |
| 575 | 624 | ) |
| 576 | 625 | run_powershell([script]) |
| 577 | 626 | shutil.rmtree(bin_root) |
| 578 | 627 | |
| 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 | ) | |
| 581 | 631 | |
| 582 | 632 | rustc_cache = os.path.join(cache_dst, key) |
| 583 | 633 | if not os.path.exists(rustc_cache): |
| 584 | 634 | os.makedirs(rustc_cache) |
| 585 | 635 | |
| 586 | tarball_suffix = '.tar.gz' if lzma is None else '.tar.xz' | |
| 636 | tarball_suffix = ".tar.gz" if lzma is None else ".tar.xz" | |
| 587 | 637 | |
| 588 | toolchain_suffix = "{}-{}{}".format(rustc_channel, self.build, tarball_suffix) | |
| 638 | toolchain_suffix = "{}-{}{}".format( | |
| 639 | rustc_channel, self.build, tarball_suffix | |
| 640 | ) | |
| 589 | 641 | |
| 590 | 642 | tarballs_to_download = [] |
| 591 | 643 | |
| 592 | 644 | if need_rustc: |
| 593 | 645 | 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") | |
| 595 | 653 | ) |
| 596 | tarballs_to_download.append(("rustc-{}".format(toolchain_suffix), "rustc")) | |
| 597 | 654 | |
| 598 | 655 | 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 | ) | |
| 600 | 659 | |
| 601 | 660 | tarballs_download_info = [ |
| 602 | 661 | DownloadInfo( |
| 603 | 662 | 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 | ), | |
| 605 | 666 | bin_root=self.bin_root(), |
| 606 | 667 | tarball_path=os.path.join(rustc_cache, filename), |
| 607 | 668 | tarball_suffix=tarball_suffix, |
| ... | ... | @@ -620,7 +681,11 @@ class RustBuild(object): |
| 620 | 681 | # In Python 2.7, Pool cannot be used as a context manager. |
| 621 | 682 | pool_size = min(len(tarballs_download_info), get_cpus()) |
| 622 | 683 | 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 | ) | |
| 624 | 689 | p = Pool(pool_size) |
| 625 | 690 | try: |
| 626 | 691 | # FIXME: A cheap workaround for https://github.com/rust-lang/rust/issues/125578, |
| ... | ... | @@ -639,7 +704,9 @@ class RustBuild(object): |
| 639 | 704 | |
| 640 | 705 | self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root)) |
| 641 | 706 | 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 | ) | |
| 643 | 710 | lib_dir = "{}/lib".format(bin_root) |
| 644 | 711 | rustlib_bin_dir = "{}/rustlib/{}/bin".format(lib_dir, self.build) |
| 645 | 712 | self.fix_bin_or_dylib("{}/rust-lld".format(rustlib_bin_dir)) |
| ... | ... | @@ -667,12 +734,15 @@ class RustBuild(object): |
| 667 | 734 | def get_answer(): |
| 668 | 735 | default_encoding = sys.getdefaultencoding() |
| 669 | 736 | 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 | ) | |
| 672 | 742 | except subprocess.CalledProcessError: |
| 673 | 743 | return False |
| 674 | 744 | except OSError as reason: |
| 675 | if getattr(reason, 'winerror', None) is not None: | |
| 745 | if getattr(reason, "winerror", None) is not None: | |
| 676 | 746 | return False |
| 677 | 747 | raise reason |
| 678 | 748 | |
| ... | ... | @@ -690,17 +760,23 @@ class RustBuild(object): |
| 690 | 760 | # The latter one does not exist on NixOS when using tmpfs as root. |
| 691 | 761 | try: |
| 692 | 762 | 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 | ) | |
| 695 | 767 | except FileNotFoundError: |
| 696 | 768 | is_nixos = False |
| 697 | 769 | |
| 698 | 770 | # If not on NixOS, then warn if user seems to be atop Nix shell |
| 699 | 771 | if not is_nixos: |
| 700 | in_nix_shell = os.getenv('IN_NIX_SHELL') | |
| 772 | in_nix_shell = os.getenv("IN_NIX_SHELL") | |
| 701 | 773 | 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 | ) | |
| 704 | 780 | |
| 705 | 781 | return is_nixos |
| 706 | 782 | |
| ... | ... | @@ -736,7 +812,7 @@ class RustBuild(object): |
| 736 | 812 | # zlib: Needed as a system dependency of `libLLVM-*.so`. |
| 737 | 813 | # patchelf: Needed for patching ELF binaries (see doc comment above). |
| 738 | 814 | nix_deps_dir = "{}/{}".format(self.build_dir, ".nix-deps") |
| 739 | nix_expr = ''' | |
| 815 | nix_expr = """ | |
| 740 | 816 | with (import <nixpkgs> {}); |
| 741 | 817 | symlinkJoin { |
| 742 | 818 | name = "rust-stage0-dependencies"; |
| ... | ... | @@ -746,24 +822,30 @@ class RustBuild(object): |
| 746 | 822 | stdenv.cc.bintools |
| 747 | 823 | ]; |
| 748 | 824 | } |
| 749 | ''' | |
| 825 | """ | |
| 750 | 826 | 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 | ) | |
| 754 | 836 | except subprocess.CalledProcessError as reason: |
| 755 | 837 | eprint("WARNING: failed to call nix-build:", reason) |
| 756 | 838 | return |
| 757 | 839 | self.nix_deps_dir = nix_deps_dir |
| 758 | 840 | |
| 759 | 841 | 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")] | |
| 763 | 843 | patchelf_args = ["--add-rpath", ":".join(rpath_entries)] |
| 764 | 844 | if ".so" not in fname: |
| 765 | 845 | # 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: | |
| 767 | 849 | patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()] |
| 768 | 850 | |
| 769 | 851 | try: |
| ... | ... | @@ -781,13 +863,13 @@ class RustBuild(object): |
| 781 | 863 | >>> expected = os.path.join("build", "host", "stage0", ".rustc-stamp") |
| 782 | 864 | >>> assert rb.rustc_stamp() == expected, rb.rustc_stamp() |
| 783 | 865 | """ |
| 784 | return os.path.join(self.bin_root(), '.rustc-stamp') | |
| 866 | return os.path.join(self.bin_root(), ".rustc-stamp") | |
| 785 | 867 | |
| 786 | 868 | def program_out_of_date(self, stamp_path, key): |
| 787 | 869 | """Check if the given program stamp is out of date""" |
| 788 | 870 | if not os.path.exists(stamp_path) or self.clean: |
| 789 | 871 | return True |
| 790 | with open(stamp_path, 'r') as stamp: | |
| 872 | with open(stamp_path, "r") as stamp: | |
| 791 | 873 | return key != stamp.read() |
| 792 | 874 | |
| 793 | 875 | def bin_root(self): |
| ... | ... | @@ -834,11 +916,11 @@ class RustBuild(object): |
| 834 | 916 | def get_toml_static(config_toml, key, section=None): |
| 835 | 917 | cur_section = None |
| 836 | 918 | for line in config_toml.splitlines(): |
| 837 | section_match = re.match(r'^\s*\[(.*)\]\s*$', line) | |
| 919 | section_match = re.match(r"^\s*\[(.*)\]\s*$", line) | |
| 838 | 920 | if section_match is not None: |
| 839 | 921 | cur_section = section_match.group(1) |
| 840 | 922 | |
| 841 | match = re.match(r'^{}\s*=(.*)$'.format(key), line) | |
| 923 | match = re.match(r"^{}\s*=(.*)$".format(key), line) | |
| 842 | 924 | if match is not None: |
| 843 | 925 | value = match.group(1) |
| 844 | 926 | if section is None or section == cur_section: |
| ... | ... | @@ -847,11 +929,11 @@ class RustBuild(object): |
| 847 | 929 | |
| 848 | 930 | def cargo(self): |
| 849 | 931 | """Return config path for cargo""" |
| 850 | return self.program_config('cargo') | |
| 932 | return self.program_config("cargo") | |
| 851 | 933 | |
| 852 | 934 | def rustc(self): |
| 853 | 935 | """Return config path for rustc""" |
| 854 | return self.program_config('rustc') | |
| 936 | return self.program_config("rustc") | |
| 855 | 937 | |
| 856 | 938 | def program_config(self, program): |
| 857 | 939 | """Return config path for the given program at the given stage |
| ... | ... | @@ -886,12 +968,12 @@ class RustBuild(object): |
| 886 | 968 | """ |
| 887 | 969 | start = line.find('"') |
| 888 | 970 | 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("'") | |
| 892 | 974 | 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] | |
| 895 | 977 | return None |
| 896 | 978 | |
| 897 | 979 | def bootstrap_out(self): |
| ... | ... | @@ -941,24 +1023,37 @@ class RustBuild(object): |
| 941 | 1023 | del env["CARGO_BUILD_TARGET"] |
| 942 | 1024 | env["CARGO_TARGET_DIR"] = build_dir |
| 943 | 1025 | 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 | ) | |
| 956 | 1047 | |
| 957 | 1048 | # Export Stage0 snapshot compiler related env variables |
| 958 | 1049 | build_section = "target.{}".format(self.build) |
| 959 | 1050 | host_triple_sanitized = self.build.replace("-", "_") |
| 960 | 1051 | 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", | |
| 962 | 1057 | } |
| 963 | 1058 | for var_name, toml_key in var_data.items(): |
| 964 | 1059 | toml_val = self.get_toml(toml_key, build_section) |
| ... | ... | @@ -1023,14 +1118,16 @@ class RustBuild(object): |
| 1023 | 1118 | if "RUSTFLAGS_BOOTSTRAP" in env: |
| 1024 | 1119 | env["RUSTFLAGS"] += " " + env["RUSTFLAGS_BOOTSTRAP"] |
| 1025 | 1120 | |
| 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"] | |
| 1028 | 1122 | 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 | ] | |
| 1034 | 1131 | args.extend("--verbose" for _ in range(self.verbose)) |
| 1035 | 1132 | if self.use_locked_deps: |
| 1036 | 1133 | args.append("--locked") |
| ... | ... | @@ -1058,83 +1155,103 @@ class RustBuild(object): |
| 1058 | 1155 | Note that `default_build_triple` is moderately expensive, |
| 1059 | 1156 | so use `self.build` where possible. |
| 1060 | 1157 | """ |
| 1061 | config = self.get_toml('build') | |
| 1158 | config = self.get_toml("build") | |
| 1062 | 1159 | return config or default_build_triple(self.verbose) |
| 1063 | 1160 | |
| 1064 | 1161 | def check_vendored_status(self): |
| 1065 | 1162 | """Check that vendoring is configured properly""" |
| 1066 | 1163 | # keep this consistent with the equivalent check in bootstrap: |
| 1067 | 1164 | # 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: | |
| 1069 | 1166 | if os.getuid() == 0: |
| 1070 | 1167 | 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.") | |
| 1074 | 1171 | |
| 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" | |
| 1076 | 1174 | 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") | |
| 1078 | 1176 | 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.") | |
| 1091 | 1200 | raise Exception("{} not found".format(vendor_dir)) |
| 1092 | 1201 | |
| 1093 | 1202 | 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.") | |
| 1095 | 1204 | raise Exception("{} not found".format(cargo_dir)) |
| 1096 | 1205 | |
| 1206 | ||
| 1097 | 1207 | def parse_args(args): |
| 1098 | 1208 | """Parse the command line arguments that the python script needs.""" |
| 1099 | 1209 | 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) | |
| 1109 | 1221 | |
| 1110 | 1222 | return parser.parse_known_args(args)[0] |
| 1111 | 1223 | |
| 1224 | ||
| 1112 | 1225 | def parse_stage0_file(path): |
| 1113 | 1226 | result = {} |
| 1114 | with open(path, 'r') as file: | |
| 1227 | with open(path, "r") as file: | |
| 1115 | 1228 | for line in file: |
| 1116 | 1229 | 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) | |
| 1119 | 1232 | result[key.strip()] = value.strip() |
| 1120 | 1233 | return result |
| 1121 | 1234 | |
| 1235 | ||
| 1122 | 1236 | def bootstrap(args): |
| 1123 | 1237 | """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__, "../../..")) | |
| 1125 | 1239 | |
| 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 | ) | |
| 1131 | 1248 | |
| 1132 | 1249 | # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`, |
| 1133 | 1250 | # 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") | |
| 1135 | 1252 | using_default_path = toml_path is None |
| 1136 | 1253 | if using_default_path: |
| 1137 | toml_path = 'config.toml' | |
| 1254 | toml_path = "config.toml" | |
| 1138 | 1255 | if not os.path.exists(toml_path): |
| 1139 | 1256 | toml_path = os.path.join(rust_root, toml_path) |
| 1140 | 1257 | |
| ... | ... | @@ -1144,23 +1261,23 @@ def bootstrap(args): |
| 1144 | 1261 | with open(toml_path) as config: |
| 1145 | 1262 | config_toml = config.read() |
| 1146 | 1263 | else: |
| 1147 | config_toml = '' | |
| 1264 | config_toml = "" | |
| 1148 | 1265 | |
| 1149 | profile = RustBuild.get_toml_static(config_toml, 'profile') | |
| 1266 | profile = RustBuild.get_toml_static(config_toml, "profile") | |
| 1150 | 1267 | if profile is not None: |
| 1151 | 1268 | # Allows creating alias for profile names, allowing |
| 1152 | 1269 | # profiles to be renamed while maintaining back compatibility |
| 1153 | 1270 | # 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") | |
| 1159 | 1274 | include_path = os.path.join(include_dir, include_file) |
| 1160 | 1275 | |
| 1161 | 1276 | 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 | ) | |
| 1164 | 1281 | |
| 1165 | 1282 | # HACK: This works because `self.get_toml()` returns the first match it finds for a |
| 1166 | 1283 | # specific key, so appending our defaults at the end allows the user to override them |
| ... | ... | @@ -1196,8 +1313,8 @@ def main(): |
| 1196 | 1313 | start_time = time() |
| 1197 | 1314 | |
| 1198 | 1315 | # 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" | |
| 1201 | 1318 | |
| 1202 | 1319 | args = parse_args(sys.argv) |
| 1203 | 1320 | help_triggered = args.help or len(sys.argv) == 1 |
| ... | ... | @@ -1207,14 +1324,15 @@ def main(): |
| 1207 | 1324 | if help_triggered: |
| 1208 | 1325 | eprint( |
| 1209 | 1326 | "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 | ) | |
| 1211 | 1329 | |
| 1212 | 1330 | exit_code = 0 |
| 1213 | 1331 | success_word = "successfully" |
| 1214 | 1332 | try: |
| 1215 | 1333 | bootstrap(args) |
| 1216 | 1334 | 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): | |
| 1218 | 1336 | exit_code = error.code |
| 1219 | 1337 | else: |
| 1220 | 1338 | exit_code = 1 |
| ... | ... | @@ -1222,9 +1340,14 @@ def main(): |
| 1222 | 1340 | success_word = "unsuccessfully" |
| 1223 | 1341 | |
| 1224 | 1342 | 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 | ) | |
| 1226 | 1349 | sys.exit(exit_code) |
| 1227 | 1350 | |
| 1228 | 1351 | |
| 1229 | if __name__ == '__main__': | |
| 1352 | if __name__ == "__main__": | |
| 1230 | 1353 | main() |
src/bootstrap/bootstrap_test.py+29-13| ... | ... | @@ -16,8 +16,9 @@ from shutil import rmtree |
| 16 | 16 | bootstrap_dir = os.path.dirname(os.path.abspath(__file__)) |
| 17 | 17 | # For the import below, have Python search in src/bootstrap first. |
| 18 | 18 | sys.path.insert(0, bootstrap_dir) |
| 19 | import bootstrap # noqa: E402 | |
| 20 | import configure # noqa: E402 | |
| 19 | import bootstrap # noqa: E402 | |
| 20 | import configure # noqa: E402 | |
| 21 | ||
| 21 | 22 | |
| 22 | 23 | def serialize_and_parse(configure_args, bootstrap_args=None): |
| 23 | 24 | from io import StringIO |
| ... | ... | @@ -32,15 +33,20 @@ def serialize_and_parse(configure_args, bootstrap_args=None): |
| 32 | 33 | |
| 33 | 34 | try: |
| 34 | 35 | import tomllib |
| 36 | ||
| 35 | 37 | # Verify this is actually valid TOML. |
| 36 | 38 | tomllib.loads(build.config_toml) |
| 37 | 39 | 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 | ) | |
| 39 | 44 | return build |
| 40 | 45 | |
| 41 | 46 | |
| 42 | 47 | class VerifyTestCase(unittest.TestCase): |
| 43 | 48 | """Test Case for verify""" |
| 49 | ||
| 44 | 50 | def setUp(self): |
| 45 | 51 | self.container = tempfile.mkdtemp() |
| 46 | 52 | self.src = os.path.join(self.container, "src.txt") |
| ... | ... | @@ -68,14 +74,14 @@ class VerifyTestCase(unittest.TestCase): |
| 68 | 74 | |
| 69 | 75 | class ProgramOutOfDate(unittest.TestCase): |
| 70 | 76 | """Test if a program is out of date""" |
| 77 | ||
| 71 | 78 | def setUp(self): |
| 72 | 79 | self.container = tempfile.mkdtemp() |
| 73 | 80 | os.mkdir(os.path.join(self.container, "stage0")) |
| 74 | 81 | self.build = bootstrap.RustBuild() |
| 75 | 82 | self.build.date = "2017-06-15" |
| 76 | 83 | 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") | |
| 79 | 85 | self.key = self.build.date + str(None) |
| 80 | 86 | |
| 81 | 87 | def tearDown(self): |
| ... | ... | @@ -97,11 +103,14 @@ class ProgramOutOfDate(unittest.TestCase): |
| 97 | 103 | """Return False both dates match""" |
| 98 | 104 | with open(self.rustc_stamp_path, "w") as rustc_stamp: |
| 99 | 105 | 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 | ) | |
| 101 | 109 | |
| 102 | 110 | |
| 103 | 111 | class ParseArgsInConfigure(unittest.TestCase): |
| 104 | 112 | """Test if `parse_args` function in `configure.py` works properly""" |
| 113 | ||
| 105 | 114 | @patch("configure.err") |
| 106 | 115 | def test_unknown_args(self, err): |
| 107 | 116 | # It should be print an error message if the argument doesn't start with '--' |
| ... | ... | @@ -148,28 +157,35 @@ class ParseArgsInConfigure(unittest.TestCase): |
| 148 | 157 | |
| 149 | 158 | class GenerateAndParseConfig(unittest.TestCase): |
| 150 | 159 | """Test that we can serialize and deserialize a config.toml file""" |
| 160 | ||
| 151 | 161 | def test_no_args(self): |
| 152 | 162 | build = serialize_and_parse([]) |
| 153 | self.assertEqual(build.get_toml("profile"), 'dist') | |
| 163 | self.assertEqual(build.get_toml("profile"), "dist") | |
| 154 | 164 | self.assertIsNone(build.get_toml("llvm.download-ci-llvm")) |
| 155 | 165 | |
| 156 | 166 | def test_set_section(self): |
| 157 | 167 | 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") | |
| 159 | 169 | |
| 160 | 170 | def test_set_target(self): |
| 161 | 171 | 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 | ) | |
| 163 | 175 | |
| 164 | 176 | def test_set_top_level(self): |
| 165 | 177 | build = serialize_and_parse(["--set", "profile=compiler"]) |
| 166 | self.assertEqual(build.get_toml("profile"), 'compiler') | |
| 178 | self.assertEqual(build.get_toml("profile"), "compiler") | |
| 167 | 179 | |
| 168 | 180 | def test_set_codegen_backends(self): |
| 169 | 181 | 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 | ) | |
| 171 | 185 | 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 | ) | |
| 173 | 189 | build = serialize_and_parse(["--enable-full-tools"]) |
| 174 | 190 | self.assertNotEqual(build.config_toml.find("codegen-backends = ['llvm']"), -1) |
| 175 | 191 | |
| ... | ... | @@ -223,7 +239,7 @@ class BuildBootstrap(unittest.TestCase): |
| 223 | 239 | self.assertTrue("--timings" in args) |
| 224 | 240 | |
| 225 | 241 | def test_warnings(self): |
| 226 | for toml_warnings in ['false', 'true', None]: | |
| 242 | for toml_warnings in ["false", "true", None]: | |
| 227 | 243 | configure_args = [] |
| 228 | 244 | if toml_warnings is not None: |
| 229 | 245 | 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 |
| 6 | 6 | import shlex |
| 7 | 7 | import sys |
| 8 | 8 | import os |
| 9 | ||
| 9 | 10 | rust_dir = os.path.dirname(os.path.abspath(__file__)) |
| 10 | 11 | rust_dir = os.path.dirname(rust_dir) |
| 11 | 12 | rust_dir = os.path.dirname(rust_dir) |
| 12 | 13 | sys.path.append(os.path.join(rust_dir, "src", "bootstrap")) |
| 13 | import bootstrap # noqa: E402 | |
| 14 | import bootstrap # noqa: E402 | |
| 14 | 15 | |
| 15 | 16 | |
| 16 | 17 | class Option(object): |
| ... | ... | @@ -32,26 +33,62 @@ def v(*args): |
| 32 | 33 | options.append(Option(*args, value=True)) |
| 33 | 34 | |
| 34 | 35 | |
| 35 | o("debug", "rust.debug", "enables debugging environment; does not affect optimization of bootstrapped code") | |
| 36 | o( | |
| 37 | "debug", | |
| 38 | "rust.debug", | |
| 39 | "enables debugging environment; does not affect optimization of bootstrapped code", | |
| 40 | ) | |
| 36 | 41 | o("docs", "build.docs", "build standard library documentation") |
| 37 | 42 | o("compiler-docs", "build.compiler-docs", "build compiler documentation") |
| 38 | 43 | o("optimize-tests", "rust.optimize-tests", "build tests with optimizations") |
| 39 | 44 | o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests") |
| 40 | o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds") | |
| 45 | o( | |
| 46 | "ccache", | |
| 47 | "llvm.ccache", | |
| 48 | "invoke gcc/clang via ccache to reuse object files between builds", | |
| 49 | ) | |
| 41 | 50 | o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds") |
| 42 | 51 | o("local-rust", None, "use an installed rustc rather than downloading a snapshot") |
| 43 | 52 | v("local-rust-root", None, "set prefix for local rust binary") |
| 44 | o("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") | |
| 45 | o("llvm-static-stdcpp", "llvm.static-libstdcpp", "statically link to libstdc++ for LLVM") | |
| 46 | o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)") | |
| 53 | o( | |
| 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 | ) | |
| 58 | o( | |
| 59 | "llvm-static-stdcpp", | |
| 60 | "llvm.static-libstdcpp", | |
| 61 | "statically link to libstdc++ for LLVM", | |
| 62 | ) | |
| 63 | o( | |
| 64 | "llvm-link-shared", | |
| 65 | "llvm.link-shared", | |
| 66 | "prefer shared linking to LLVM (llvm-config --link-shared)", | |
| 67 | ) | |
| 47 | 68 | o("rpath", "rust.rpath", "build rpaths into rustc itself") |
| 48 | 69 | o("codegen-tests", "rust.codegen-tests", "run the tests/codegen tests") |
| 49 | o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)") | |
| 70 | o( | |
| 71 | "ninja", | |
| 72 | "llvm.ninja", | |
| 73 | "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)", | |
| 74 | ) | |
| 50 | 75 | o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date") |
| 51 | 76 | o("vendor", "build.vendor", "enable usage of vendored Rust crates") |
| 52 | o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, dfsan, lsan, msan, tsan, hwasan)") | |
| 53 | o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball") | |
| 54 | o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo") | |
| 77 | o( | |
| 78 | "sanitizers", | |
| 79 | "build.sanitizers", | |
| 80 | "build the sanitizer runtimes (asan, dfsan, lsan, msan, tsan, hwasan)", | |
| 81 | ) | |
| 82 | o( | |
| 83 | "dist-src", | |
| 84 | "rust.dist-src", | |
| 85 | "when building tarballs enables building a source tarball", | |
| 86 | ) | |
| 87 | o( | |
| 88 | "cargo-native-static", | |
| 89 | "build.cargo-native-static", | |
| 90 | "static native libraries in cargo", | |
| 91 | ) | |
| 55 | 92 | o("profiler", "build.profiler", "build the profiler runtime") |
| 56 | 93 | o("full-tools", None, "enable all tools") |
| 57 | 94 | o("lld", "rust.lld", "build lld") |
| ... | ... | @@ -59,7 +96,11 @@ o("llvm-bitcode-linker", "rust.llvm-bitcode-linker", "build llvm bitcode linker" |
| 59 | 96 | o("clang", "llvm.clang", "build clang") |
| 60 | 97 | o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++") |
| 61 | 98 | o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard") |
| 62 | o("patch-binaries-for-nix", "build.patch-binaries-for-nix", "whether patch binaries for usage with Nix toolchains") | |
| 99 | o( | |
| 100 | "patch-binaries-for-nix", | |
| 101 | "build.patch-binaries-for-nix", | |
| 102 | "whether patch binaries for usage with Nix toolchains", | |
| 103 | ) | |
| 63 | 104 | o("new-symbol-mangling", "rust.new-symbol-mangling", "use symbol-mangling-version v0") |
| 64 | 105 | |
| 65 | 106 | v("llvm-cflags", "llvm.cflags", "build LLVM with these extra compiler flags") |
| ... | ... | @@ -76,16 +117,48 @@ o("llvm-enzyme", "llvm.enzyme", "build LLVM with enzyme") |
| 76 | 117 | o("llvm-offload", "llvm.offload", "build LLVM with gpu offload support") |
| 77 | 118 | o("llvm-plugins", "llvm.plugins", "build LLVM with plugin interface") |
| 78 | 119 | o("debug-assertions", "rust.debug-assertions", "build with debugging assertions") |
| 79 | o("debug-assertions-std", "rust.debug-assertions-std", "build the standard library with debugging assertions") | |
| 120 | o( | |
| 121 | "debug-assertions-std", | |
| 122 | "rust.debug-assertions-std", | |
| 123 | "build the standard library with debugging assertions", | |
| 124 | ) | |
| 80 | 125 | o("overflow-checks", "rust.overflow-checks", "build with overflow checks") |
| 81 | o("overflow-checks-std", "rust.overflow-checks-std", "build the standard library with overflow checks") | |
| 82 | o("llvm-release-debuginfo", "llvm.release-debuginfo", "build LLVM with debugger metadata") | |
| 126 | o( | |
| 127 | "overflow-checks-std", | |
| 128 | "rust.overflow-checks-std", | |
| 129 | "build the standard library with overflow checks", | |
| 130 | ) | |
| 131 | o( | |
| 132 | "llvm-release-debuginfo", | |
| 133 | "llvm.release-debuginfo", | |
| 134 | "build LLVM with debugger metadata", | |
| 135 | ) | |
| 83 | 136 | v("debuginfo-level", "rust.debuginfo-level", "debuginfo level for Rust code") |
| 84 | v("debuginfo-level-rustc", "rust.debuginfo-level-rustc", "debuginfo level for the compiler") | |
| 85 | v("debuginfo-level-std", "rust.debuginfo-level-std", "debuginfo level for the standard library") | |
| 86 | v("debuginfo-level-tools", "rust.debuginfo-level-tools", "debuginfo level for the tools") | |
| 87 | v("debuginfo-level-tests", "rust.debuginfo-level-tests", "debuginfo level for the test suites run with compiletest") | |
| 88 | v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file") | |
| 137 | v( | |
| 138 | "debuginfo-level-rustc", | |
| 139 | "rust.debuginfo-level-rustc", | |
| 140 | "debuginfo level for the compiler", | |
| 141 | ) | |
| 142 | v( | |
| 143 | "debuginfo-level-std", | |
| 144 | "rust.debuginfo-level-std", | |
| 145 | "debuginfo level for the standard library", | |
| 146 | ) | |
| 147 | v( | |
| 148 | "debuginfo-level-tools", | |
| 149 | "rust.debuginfo-level-tools", | |
| 150 | "debuginfo level for the tools", | |
| 151 | ) | |
| 152 | v( | |
| 153 | "debuginfo-level-tests", | |
| 154 | "rust.debuginfo-level-tests", | |
| 155 | "debuginfo level for the test suites run with compiletest", | |
| 156 | ) | |
| 157 | v( | |
| 158 | "save-toolstates", | |
| 159 | "rust.save-toolstates", | |
| 160 | "save build and test status of external tools into this file", | |
| 161 | ) | |
| 89 | 162 | |
| 90 | 163 | v("prefix", "install.prefix", "set installation prefix") |
| 91 | 164 | v("localstatedir", "install.localstatedir", "local state directory") |
| ... | ... | @@ -102,50 +175,117 @@ v("llvm-config", None, "set path to llvm-config") |
| 102 | 175 | v("llvm-filecheck", None, "set path to LLVM's FileCheck utility") |
| 103 | 176 | v("python", "build.python", "set path to python") |
| 104 | 177 | v("android-ndk", "build.android-ndk", "set path to Android NDK") |
| 105 | v("musl-root", "target.x86_64-unknown-linux-musl.musl-root", | |
| 106 | "MUSL root installation directory (deprecated)") | |
| 107 | v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root", | |
| 108 | "x86_64-unknown-linux-musl install directory") | |
| 109 | v("musl-root-i586", "target.i586-unknown-linux-musl.musl-root", | |
| 110 | "i586-unknown-linux-musl install directory") | |
| 111 | v("musl-root-i686", "target.i686-unknown-linux-musl.musl-root", | |
| 112 | "i686-unknown-linux-musl install directory") | |
| 113 | v("musl-root-arm", "target.arm-unknown-linux-musleabi.musl-root", | |
| 114 | "arm-unknown-linux-musleabi install directory") | |
| 115 | v("musl-root-armhf", "target.arm-unknown-linux-musleabihf.musl-root", | |
| 116 | "arm-unknown-linux-musleabihf install directory") | |
| 117 | v("musl-root-armv5te", "target.armv5te-unknown-linux-musleabi.musl-root", | |
| 118 | "armv5te-unknown-linux-musleabi install directory") | |
| 119 | v("musl-root-armv7", "target.armv7-unknown-linux-musleabi.musl-root", | |
| 120 | "armv7-unknown-linux-musleabi install directory") | |
| 121 | v("musl-root-armv7hf", "target.armv7-unknown-linux-musleabihf.musl-root", | |
| 122 | "armv7-unknown-linux-musleabihf install directory") | |
| 123 | v("musl-root-aarch64", "target.aarch64-unknown-linux-musl.musl-root", | |
| 124 | "aarch64-unknown-linux-musl install directory") | |
| 125 | v("musl-root-mips", "target.mips-unknown-linux-musl.musl-root", | |
| 126 | "mips-unknown-linux-musl install directory") | |
| 127 | v("musl-root-mipsel", "target.mipsel-unknown-linux-musl.musl-root", | |
| 128 | "mipsel-unknown-linux-musl install directory") | |
| 129 | v("musl-root-mips64", "target.mips64-unknown-linux-muslabi64.musl-root", | |
| 130 | "mips64-unknown-linux-muslabi64 install directory") | |
| 131 | v("musl-root-mips64el", "target.mips64el-unknown-linux-muslabi64.musl-root", | |
| 132 | "mips64el-unknown-linux-muslabi64 install directory") | |
| 133 | v("musl-root-riscv32gc", "target.riscv32gc-unknown-linux-musl.musl-root", | |
| 134 | "riscv32gc-unknown-linux-musl install directory") | |
| 135 | v("musl-root-riscv64gc", "target.riscv64gc-unknown-linux-musl.musl-root", | |
| 136 | "riscv64gc-unknown-linux-musl install directory") | |
| 137 | v("musl-root-loongarch64", "target.loongarch64-unknown-linux-musl.musl-root", | |
| 138 | "loongarch64-unknown-linux-musl install directory") | |
| 139 | v("qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs", | |
| 140 | "rootfs in qemu testing, you probably don't want to use this") | |
| 141 | v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs", | |
| 142 | "rootfs in qemu testing, you probably don't want to use this") | |
| 143 | v("qemu-riscv64-rootfs", "target.riscv64gc-unknown-linux-gnu.qemu-rootfs", | |
| 144 | "rootfs in qemu testing, you probably don't want to use this") | |
| 145 | v("experimental-targets", "llvm.experimental-targets", | |
| 146 | "experimental LLVM targets to build") | |
| 178 | v( | |
| 179 | "musl-root", | |
| 180 | "target.x86_64-unknown-linux-musl.musl-root", | |
| 181 | "MUSL root installation directory (deprecated)", | |
| 182 | ) | |
| 183 | v( | |
| 184 | "musl-root-x86_64", | |
| 185 | "target.x86_64-unknown-linux-musl.musl-root", | |
| 186 | "x86_64-unknown-linux-musl install directory", | |
| 187 | ) | |
| 188 | v( | |
| 189 | "musl-root-i586", | |
| 190 | "target.i586-unknown-linux-musl.musl-root", | |
| 191 | "i586-unknown-linux-musl install directory", | |
| 192 | ) | |
| 193 | v( | |
| 194 | "musl-root-i686", | |
| 195 | "target.i686-unknown-linux-musl.musl-root", | |
| 196 | "i686-unknown-linux-musl install directory", | |
| 197 | ) | |
| 198 | v( | |
| 199 | "musl-root-arm", | |
| 200 | "target.arm-unknown-linux-musleabi.musl-root", | |
| 201 | "arm-unknown-linux-musleabi install directory", | |
| 202 | ) | |
| 203 | v( | |
| 204 | "musl-root-armhf", | |
| 205 | "target.arm-unknown-linux-musleabihf.musl-root", | |
| 206 | "arm-unknown-linux-musleabihf install directory", | |
| 207 | ) | |
| 208 | v( | |
| 209 | "musl-root-armv5te", | |
| 210 | "target.armv5te-unknown-linux-musleabi.musl-root", | |
| 211 | "armv5te-unknown-linux-musleabi install directory", | |
| 212 | ) | |
| 213 | v( | |
| 214 | "musl-root-armv7", | |
| 215 | "target.armv7-unknown-linux-musleabi.musl-root", | |
| 216 | "armv7-unknown-linux-musleabi install directory", | |
| 217 | ) | |
| 218 | v( | |
| 219 | "musl-root-armv7hf", | |
| 220 | "target.armv7-unknown-linux-musleabihf.musl-root", | |
| 221 | "armv7-unknown-linux-musleabihf install directory", | |
| 222 | ) | |
| 223 | v( | |
| 224 | "musl-root-aarch64", | |
| 225 | "target.aarch64-unknown-linux-musl.musl-root", | |
| 226 | "aarch64-unknown-linux-musl install directory", | |
| 227 | ) | |
| 228 | v( | |
| 229 | "musl-root-mips", | |
| 230 | "target.mips-unknown-linux-musl.musl-root", | |
| 231 | "mips-unknown-linux-musl install directory", | |
| 232 | ) | |
| 233 | v( | |
| 234 | "musl-root-mipsel", | |
| 235 | "target.mipsel-unknown-linux-musl.musl-root", | |
| 236 | "mipsel-unknown-linux-musl install directory", | |
| 237 | ) | |
| 238 | v( | |
| 239 | "musl-root-mips64", | |
| 240 | "target.mips64-unknown-linux-muslabi64.musl-root", | |
| 241 | "mips64-unknown-linux-muslabi64 install directory", | |
| 242 | ) | |
| 243 | v( | |
| 244 | "musl-root-mips64el", | |
| 245 | "target.mips64el-unknown-linux-muslabi64.musl-root", | |
| 246 | "mips64el-unknown-linux-muslabi64 install directory", | |
| 247 | ) | |
| 248 | v( | |
| 249 | "musl-root-riscv32gc", | |
| 250 | "target.riscv32gc-unknown-linux-musl.musl-root", | |
| 251 | "riscv32gc-unknown-linux-musl install directory", | |
| 252 | ) | |
| 253 | v( | |
| 254 | "musl-root-riscv64gc", | |
| 255 | "target.riscv64gc-unknown-linux-musl.musl-root", | |
| 256 | "riscv64gc-unknown-linux-musl install directory", | |
| 257 | ) | |
| 258 | v( | |
| 259 | "musl-root-loongarch64", | |
| 260 | "target.loongarch64-unknown-linux-musl.musl-root", | |
| 261 | "loongarch64-unknown-linux-musl install directory", | |
| 262 | ) | |
| 263 | v( | |
| 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 | ) | |
| 268 | v( | |
| 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 | ) | |
| 273 | v( | |
| 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 | ) | |
| 278 | v( | |
| 279 | "experimental-targets", | |
| 280 | "llvm.experimental-targets", | |
| 281 | "experimental LLVM targets to build", | |
| 282 | ) | |
| 147 | 283 | v("release-channel", "rust.channel", "the name of the release channel to build") |
| 148 | v("release-description", "rust.description", "optional descriptive string for version output") | |
| 284 | v( | |
| 285 | "release-description", | |
| 286 | "rust.description", | |
| 287 | "optional descriptive string for version output", | |
| 288 | ) | |
| 149 | 289 | v("dist-compression-formats", None, "List of compression formats to use") |
| 150 | 290 | |
| 151 | 291 | # Used on systems where "cc" is unavailable |
| ... | ... | @@ -154,7 +294,11 @@ v("default-linker", "rust.default-linker", "the default linker") |
| 154 | 294 | # Many of these are saved below during the "writing configuration" step |
| 155 | 295 | # (others are conditionally saved). |
| 156 | 296 | o("manage-submodules", "build.submodules", "let the build manage the git submodules") |
| 157 | o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two (not recommended except for testing reproducible builds)") | |
| 297 | o( | |
| 298 | "full-bootstrap", | |
| 299 | "build.full-bootstrap", | |
| 300 | "build three compilers instead of two (not recommended except for testing reproducible builds)", | |
| 301 | ) | |
| 158 | 302 | o("extended", "build.extended", "build an extended rust tool set") |
| 159 | 303 | |
| 160 | 304 | v("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") |
| 165 | 309 | v("target", None, "List of GNUs ./configure syntax LLVM target triples") |
| 166 | 310 | |
| 167 | 311 | # Options specific to this configure script |
| 168 | o("option-checking", None, "complain about unrecognized options in this configure script") | |
| 169 | o("verbose-configure", None, "don't truncate options when printing them in this configure script") | |
| 312 | o( | |
| 313 | "option-checking", | |
| 314 | None, | |
| 315 | "complain about unrecognized options in this configure script", | |
| 316 | ) | |
| 317 | o( | |
| 318 | "verbose-configure", | |
| 319 | None, | |
| 320 | "don't truncate options when printing them in this configure script", | |
| 321 | ) | |
| 170 | 322 | v("set", None, "set arbitrary key/value pairs in TOML configuration") |
| 171 | 323 | |
| 172 | 324 | |
| ... | ... | @@ -178,39 +330,42 @@ def err(msg): |
| 178 | 330 | print("\nconfigure: ERROR: " + msg + "\n") |
| 179 | 331 | sys.exit(1) |
| 180 | 332 | |
| 333 | ||
| 181 | 334 | def is_value_list(key): |
| 182 | 335 | 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"): | |
| 184 | 337 | return True |
| 185 | 338 | return False |
| 186 | 339 | |
| 187 | if '--help' in sys.argv or '-h' in sys.argv: | |
| 188 | print('Usage: ./configure [options]') | |
| 189 | print('') | |
| 190 | print('Options') | |
| 340 | ||
| 341 | if "--help" in sys.argv or "-h" in sys.argv: | |
| 342 | print("Usage: ./configure [options]") | |
| 343 | print("") | |
| 344 | print("Options") | |
| 191 | 345 | for option in options: |
| 192 | if 'android' in option.name: | |
| 346 | if "android" in option.name: | |
| 193 | 347 | # no one needs to know about these obscure options |
| 194 | 348 | continue |
| 195 | 349 | 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)) | |
| 197 | 351 | 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") | |
| 210 | 364 | sys.exit(0) |
| 211 | 365 | |
| 212 | 366 | VERBOSE = False |
| 213 | 367 | |
| 368 | ||
| 214 | 369 | # Parse all command line arguments into one of these three lists, handling |
| 215 | 370 | # boolean and value-based options separately |
| 216 | 371 | def parse_args(args): |
| ... | ... | @@ -222,7 +377,7 @@ def parse_args(args): |
| 222 | 377 | while i < len(args): |
| 223 | 378 | arg = args[i] |
| 224 | 379 | i += 1 |
| 225 | if not arg.startswith('--'): | |
| 380 | if not arg.startswith("--"): | |
| 226 | 381 | unknown_args.append(arg) |
| 227 | 382 | continue |
| 228 | 383 | |
| ... | ... | @@ -230,7 +385,7 @@ def parse_args(args): |
| 230 | 385 | for option in options: |
| 231 | 386 | value = None |
| 232 | 387 | if option.value: |
| 233 | keyval = arg[2:].split('=', 1) | |
| 388 | keyval = arg[2:].split("=", 1) | |
| 234 | 389 | key = keyval[0] |
| 235 | 390 | if option.name != key: |
| 236 | 391 | continue |
| ... | ... | @@ -244,9 +399,9 @@ def parse_args(args): |
| 244 | 399 | need_value_args.append(arg) |
| 245 | 400 | continue |
| 246 | 401 | else: |
| 247 | if arg[2:] == 'enable-' + option.name: | |
| 402 | if arg[2:] == "enable-" + option.name: | |
| 248 | 403 | value = True |
| 249 | elif arg[2:] == 'disable-' + option.name: | |
| 404 | elif arg[2:] == "disable-" + option.name: | |
| 250 | 405 | value = False |
| 251 | 406 | else: |
| 252 | 407 | continue |
| ... | ... | @@ -263,8 +418,9 @@ def parse_args(args): |
| 263 | 418 | # NOTE: here and a few other places, we use [-1] to apply the *last* value |
| 264 | 419 | # passed. But if option-checking is enabled, then the known_args loop will |
| 265 | 420 | # 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 | ) | |
| 268 | 424 | if option_checking: |
| 269 | 425 | if len(unknown_args) > 0: |
| 270 | 426 | err("Option '" + unknown_args[0] + "' is not recognized") |
| ... | ... | @@ -272,18 +428,18 @@ def parse_args(args): |
| 272 | 428 | err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0])) |
| 273 | 429 | |
| 274 | 430 | global VERBOSE |
| 275 | VERBOSE = 'verbose-configure' in known_args | |
| 431 | VERBOSE = "verbose-configure" in known_args | |
| 276 | 432 | |
| 277 | 433 | config = {} |
| 278 | 434 | |
| 279 | set('build.configure-args', args, config) | |
| 435 | set("build.configure-args", args, config) | |
| 280 | 436 | apply_args(known_args, option_checking, config) |
| 281 | 437 | return parse_example_config(known_args, config) |
| 282 | 438 | |
| 283 | 439 | |
| 284 | 440 | def 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] | |
| 287 | 443 | return bootstrap.default_build_triple(verbose=False) |
| 288 | 444 | |
| 289 | 445 | |
| ... | ... | @@ -291,7 +447,7 @@ def set(key, value, config): |
| 291 | 447 | if isinstance(value, list): |
| 292 | 448 | # Remove empty values, which value.split(',') tends to generate and |
| 293 | 449 | # 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] | |
| 295 | 451 | |
| 296 | 452 | s = "{:20} := {}".format(key, value) |
| 297 | 453 | if len(s) < 70 or VERBOSE: |
| ... | ... | @@ -310,7 +466,7 @@ def set(key, value, config): |
| 310 | 466 | for i, part in enumerate(parts): |
| 311 | 467 | if i == len(parts) - 1: |
| 312 | 468 | if is_value_list(part) and isinstance(value, str): |
| 313 | value = value.split(',') | |
| 469 | value = value.split(",") | |
| 314 | 470 | arr[part] = value |
| 315 | 471 | else: |
| 316 | 472 | if part not in arr: |
| ... | ... | @@ -321,9 +477,9 @@ def set(key, value, config): |
| 321 | 477 | def apply_args(known_args, option_checking, config): |
| 322 | 478 | for key in known_args: |
| 323 | 479 | # The `set` option is special and can be passed a bunch of times |
| 324 | if key == 'set': | |
| 480 | if key == "set": | |
| 325 | 481 | for _option, value in known_args[key]: |
| 326 | keyval = value.split('=', 1) | |
| 482 | keyval = value.split("=", 1) | |
| 327 | 483 | if len(keyval) == 1 or keyval[1] == "true": |
| 328 | 484 | value = True |
| 329 | 485 | elif keyval[1] == "false": |
| ... | ... | @@ -348,50 +504,55 @@ def apply_args(known_args, option_checking, config): |
| 348 | 504 | # that here. |
| 349 | 505 | build_triple = build(known_args) |
| 350 | 506 | |
| 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) | |
| 357 | 513 | 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) | |
| 361 | 517 | 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"]: | |
| 388 | 548 | # this was handled above |
| 389 | 549 | 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) | |
| 392 | 552 | else: |
| 393 | 553 | raise RuntimeError("unhandled option {}".format(option.name)) |
| 394 | 554 | |
| 555 | ||
| 395 | 556 | # "Parse" the `config.example.toml` file into the various sections, and we'll |
| 396 | 557 | # use this as a template of a `config.toml` to write out which preserves |
| 397 | 558 | # all the various comments and whatnot. |
| ... | ... | @@ -406,20 +567,22 @@ def parse_example_config(known_args, config): |
| 406 | 567 | targets = {} |
| 407 | 568 | top_level_keys = [] |
| 408 | 569 | |
| 409 | with open(rust_dir + '/config.example.toml') as example_config: | |
| 570 | with open(rust_dir + "/config.example.toml") as example_config: | |
| 410 | 571 | example_lines = example_config.read().split("\n") |
| 411 | 572 | for line in example_lines: |
| 412 | 573 | 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(" #") | |
| 416 | 577 | top_level_keys.append(top_level_key) |
| 417 | if line.startswith('['): | |
| 578 | if line.startswith("["): | |
| 418 | 579 | 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 | ) | |
| 423 | 586 | sections[cur_section] = [line] |
| 424 | 587 | section_order.append(cur_section) |
| 425 | 588 | else: |
| ... | ... | @@ -428,22 +591,25 @@ def parse_example_config(known_args, config): |
| 428 | 591 | # Fill out the `targets` array by giving all configured targets a copy of the |
| 429 | 592 | # `target` section we just loaded from the example config |
| 430 | 593 | 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"]: | |
| 438 | 601 | configured_targets.append(target) |
| 439 | 602 | for target in configured_targets: |
| 440 | targets[target] = sections['target'][:] | |
| 603 | targets[target] = sections["target"][:] | |
| 441 | 604 | # 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. |
| 442 | 605 | # 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 | ) | |
| 444 | 610 | |
| 445 | if 'profile' not in config: | |
| 446 | set('profile', 'dist', config) | |
| 611 | if "profile" not in config: | |
| 612 | set("profile", "dist", config) | |
| 447 | 613 | configure_file(sections, top_level_keys, targets, config) |
| 448 | 614 | return section_order, sections, targets |
| 449 | 615 | |
| ... | ... | @@ -467,7 +633,7 @@ def to_toml(value): |
| 467 | 633 | else: |
| 468 | 634 | return "false" |
| 469 | 635 | elif isinstance(value, list): |
| 470 | return '[' + ', '.join(map(to_toml, value)) + ']' | |
| 636 | return "[" + ", ".join(map(to_toml, value)) + "]" | |
| 471 | 637 | elif isinstance(value, str): |
| 472 | 638 | # Don't put quotes around numeric values |
| 473 | 639 | if is_number(value): |
| ... | ... | @@ -475,9 +641,18 @@ def to_toml(value): |
| 475 | 641 | else: |
| 476 | 642 | return "'" + value + "'" |
| 477 | 643 | 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 | ) | |
| 479 | 654 | else: |
| 480 | raise RuntimeError('no toml') | |
| 655 | raise RuntimeError("no toml") | |
| 481 | 656 | |
| 482 | 657 | |
| 483 | 658 | def configure_section(lines, config): |
| ... | ... | @@ -485,7 +660,7 @@ def configure_section(lines, config): |
| 485 | 660 | value = config[key] |
| 486 | 661 | found = False |
| 487 | 662 | for i, line in enumerate(lines): |
| 488 | if not line.startswith('#' + key + ' = '): | |
| 663 | if not line.startswith("#" + key + " = "): | |
| 489 | 664 | continue |
| 490 | 665 | found = True |
| 491 | 666 | lines[i] = "{} = {}".format(key, to_toml(value)) |
| ... | ... | @@ -501,7 +676,9 @@ def configure_section(lines, config): |
| 501 | 676 | |
| 502 | 677 | def configure_top_level_key(lines, top_level_key, value): |
| 503 | 678 | 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 | ): | |
| 505 | 682 | lines[i] = "{} = {}".format(top_level_key, to_toml(value)) |
| 506 | 683 | return |
| 507 | 684 | |
| ... | ... | @@ -512,11 +689,13 @@ def configure_top_level_key(lines, top_level_key, value): |
| 512 | 689 | def configure_file(sections, top_level_keys, targets, config): |
| 513 | 690 | for section_key, section_config in config.items(): |
| 514 | 691 | 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 | ) | |
| 516 | 695 | if section_key in top_level_keys: |
| 517 | 696 | configure_top_level_key(sections[None], section_key, section_config) |
| 518 | 697 | |
| 519 | elif section_key == 'target': | |
| 698 | elif section_key == "target": | |
| 520 | 699 | for target in section_config: |
| 521 | 700 | configure_section(targets[target], section_config[target]) |
| 522 | 701 | else: |
| ... | ... | @@ -536,18 +715,19 @@ def write_uncommented(target, f): |
| 536 | 715 | block = [] |
| 537 | 716 | is_comment = True |
| 538 | 717 | continue |
| 539 | is_comment = is_comment and line.startswith('#') | |
| 718 | is_comment = is_comment and line.startswith("#") | |
| 540 | 719 | return f |
| 541 | 720 | |
| 542 | 721 | |
| 543 | 722 | def write_config_toml(writer, section_order, targets, sections): |
| 544 | 723 | for section in section_order: |
| 545 | if section == 'target': | |
| 724 | if section == "target": | |
| 546 | 725 | for target in targets: |
| 547 | 726 | writer = write_uncommented(targets[target], writer) |
| 548 | 727 | else: |
| 549 | 728 | writer = write_uncommented(sections[section], writer) |
| 550 | 729 | |
| 730 | ||
| 551 | 731 | def quit_if_file_exists(file): |
| 552 | 732 | if os.path.isfile(file): |
| 553 | 733 | msg = "Existing '{}' detected. Exiting".format(file) |
| ... | ... | @@ -559,9 +739,10 @@ def quit_if_file_exists(file): |
| 559 | 739 | |
| 560 | 740 | err(msg) |
| 561 | 741 | |
| 742 | ||
| 562 | 743 | if __name__ == "__main__": |
| 563 | 744 | # 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") | |
| 565 | 746 | |
| 566 | 747 | if "GITHUB_ACTIONS" in os.environ: |
| 567 | 748 | print("::group::Configure the build") |
| ... | ... | @@ -575,13 +756,13 @@ if __name__ == "__main__": |
| 575 | 756 | # order that we read it in. |
| 576 | 757 | p("") |
| 577 | 758 | p("writing `config.toml` in current directory") |
| 578 | with bootstrap.output('config.toml') as f: | |
| 759 | with bootstrap.output("config.toml") as f: | |
| 579 | 760 | write_config_toml(f, section_order, targets, sections) |
| 580 | 761 | |
| 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") | |
| 583 | 764 | contents = open(contents).read() |
| 584 | contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/') | |
| 765 | contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + "/") | |
| 585 | 766 | contents = contents.replace("$(CFG_PYTHON)", sys.executable) |
| 586 | 767 | f.write(contents) |
| 587 | 768 |
src/ci/cpu-usage-over-time.py+25-11| ... | ... | @@ -40,12 +40,13 @@ import time |
| 40 | 40 | # Python 3.3 changed the value of `sys.platform` on Linux from "linux2" to just |
| 41 | 41 | # "linux". We check here with `.startswith` to keep compatibility with older |
| 42 | 42 | # Python versions (especially Python 2.7). |
| 43 | if sys.platform.startswith('linux'): | |
| 43 | if sys.platform.startswith("linux"): | |
| 44 | ||
| 44 | 45 | class State: |
| 45 | 46 | def __init__(self): |
| 46 | with open('/proc/stat', 'r') as file: | |
| 47 | with open("/proc/stat", "r") as file: | |
| 47 | 48 | data = file.readline().split() |
| 48 | if data[0] != 'cpu': | |
| 49 | if data[0] != "cpu": | |
| 49 | 50 | raise Exception('did not start with "cpu"') |
| 50 | 51 | self.user = int(data[1]) |
| 51 | 52 | self.nice = int(data[2]) |
| ... | ... | @@ -69,10 +70,21 @@ if sys.platform.startswith('linux'): |
| 69 | 70 | steal = self.steal - prev.steal |
| 70 | 71 | guest = self.guest - prev.guest |
| 71 | 72 | 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 | ) | |
| 73 | 85 | return float(idle) / float(total) * 100 |
| 74 | 86 | |
| 75 | elif sys.platform == 'win32': | |
| 87 | elif sys.platform == "win32": | |
| 76 | 88 | from ctypes.wintypes import DWORD |
| 77 | 89 | from ctypes import Structure, windll, WinError, GetLastError, byref |
| 78 | 90 | |
| ... | ... | @@ -104,9 +116,10 @@ elif sys.platform == 'win32': |
| 104 | 116 | kernel = self.kernel - prev.kernel |
| 105 | 117 | return float(idle) / float(user + kernel) * 100 |
| 106 | 118 | |
| 107 | elif sys.platform == 'darwin': | |
| 119 | elif sys.platform == "darwin": | |
| 108 | 120 | from ctypes import * |
| 109 | libc = cdll.LoadLibrary('/usr/lib/libc.dylib') | |
| 121 | ||
| 122 | libc = cdll.LoadLibrary("/usr/lib/libc.dylib") | |
| 110 | 123 | |
| 111 | 124 | class host_cpu_load_info_data_t(Structure): |
| 112 | 125 | _fields_ = [("cpu_ticks", c_uint * 4)] |
| ... | ... | @@ -116,7 +129,7 @@ elif sys.platform == 'darwin': |
| 116 | 129 | c_uint, |
| 117 | 130 | c_int, |
| 118 | 131 | POINTER(host_cpu_load_info_data_t), |
| 119 | POINTER(c_int) | |
| 132 | POINTER(c_int), | |
| 120 | 133 | ] |
| 121 | 134 | host_statistics.restype = c_int |
| 122 | 135 | |
| ... | ... | @@ -124,13 +137,14 @@ elif sys.platform == 'darwin': |
| 124 | 137 | CPU_STATE_SYSTEM = 1 |
| 125 | 138 | CPU_STATE_IDLE = 2 |
| 126 | 139 | CPU_STATE_NICE = 3 |
| 140 | ||
| 127 | 141 | class State: |
| 128 | 142 | def __init__(self): |
| 129 | 143 | 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 | |
| 131 | 145 | err = libc.host_statistics( |
| 132 | 146 | libc.mach_host_self(), |
| 133 | c_int(3), # HOST_CPU_LOAD_INFO | |
| 147 | c_int(3), # HOST_CPU_LOAD_INFO | |
| 134 | 148 | byref(stats), |
| 135 | 149 | byref(count), |
| 136 | 150 | ) |
| ... | ... | @@ -148,7 +162,7 @@ elif sys.platform == 'darwin': |
| 148 | 162 | return float(idle) / float(user + system + idle + nice) * 100.0 |
| 149 | 163 | |
| 150 | 164 | else: |
| 151 | print('unknown platform', sys.platform) | |
| 165 | print("unknown platform", sys.platform) | |
| 152 | 166 | sys.exit(1) |
| 153 | 167 | |
| 154 | 168 | cur_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 |
| 27 | 27 | RUN echo "optimize = false" >> /config/nopt-std-config.toml |
| 28 | 28 | |
| 29 | 29 | ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests |
| 30 | ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \ | |
| 31 | && python3 ../x.py --stage 2 test | |
| 30 | ARG SCRIPT_ARG | |
| 31 | ENV SCRIPT=${SCRIPT_ARG} |
src/ci/docker/host-x86_64/i686-gnu/Dockerfile+2-7| ... | ... | @@ -24,10 +24,5 @@ COPY scripts/sccache.sh /scripts/ |
| 24 | 24 | RUN sh /scripts/sccache.sh |
| 25 | 25 | |
| 26 | 26 | ENV 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. | |
| 29 | ENV 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 | |
| 27 | ARG SCRIPT_ARG | |
| 28 | ENV SCRIPT=${SCRIPT_ARG} |
src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py+76-73| ... | ... | @@ -8,78 +8,79 @@ import tempfile |
| 8 | 8 | |
| 9 | 9 | from pathlib import Path |
| 10 | 10 | |
| 11 | TARGET_AARCH64 = 'aarch64-unknown-uefi' | |
| 12 | TARGET_I686 = 'i686-unknown-uefi' | |
| 13 | TARGET_X86_64 = 'x86_64-unknown-uefi' | |
| 11 | TARGET_AARCH64 = "aarch64-unknown-uefi" | |
| 12 | TARGET_I686 = "i686-unknown-uefi" | |
| 13 | TARGET_X86_64 = "x86_64-unknown-uefi" | |
| 14 | ||
| 14 | 15 | |
| 15 | 16 | def run(*cmd, capture=False, check=True, env=None, timeout=None): |
| 16 | 17 | """Print and run a command, optionally capturing the output.""" |
| 17 | 18 | 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 | ||
| 25 | 24 | |
| 26 | 25 | def build_and_run(tmp_dir, target): |
| 27 | 26 | 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" | |
| 35 | 34 | 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" | |
| 40 | 39 | # The i686 target intentionally uses 64-bit qemu; the important |
| 41 | 40 | # 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" | |
| 45 | 44 | 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" | |
| 53 | 52 | else: |
| 54 | raise KeyError('invalid target') | |
| 53 | raise KeyError("invalid target") | |
| 55 | 54 | |
| 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" | |
| 59 | 58 | |
| 60 | 59 | env = dict(os.environ) |
| 61 | env['PATH'] = '{}:{}:{}'.format(stage2, stage0, env['PATH']) | |
| 60 | env["PATH"] = "{}:{}:{}".format(stage2, stage0, env["PATH"]) | |
| 62 | 61 | |
| 63 | 62 | # 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) | |
| 66 | 65 | |
| 67 | 66 | # 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", | |
| 73 | 73 | target, |
| 74 | env=env) | |
| 74 | env=env, | |
| 75 | ) | |
| 75 | 76 | |
| 76 | 77 | # 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" | |
| 79 | 80 | os.makedirs(boot, exist_ok=True) |
| 80 | 81 | |
| 81 | 82 | # 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" | |
| 83 | 84 | shutil.copy(src_exe_path, boot / boot_file_name) |
| 84 | 85 | print(src_exe_path, boot / boot_file_name) |
| 85 | 86 | |
| ... | ... | @@ -89,37 +90,39 @@ def build_and_run(tmp_dir, target): |
| 89 | 90 | |
| 90 | 91 | # Make a writable copy of the vars file. aarch64 doesn't boot |
| 91 | 92 | # correctly with read-only vars. |
| 92 | ovmf_rw_vars = Path(tmp_dir) / 'vars.fd' | |
| 93 | ovmf_rw_vars = Path(tmp_dir) / "vars.fd" | |
| 93 | 94 | shutil.copy(ovmf_vars, ovmf_rw_vars) |
| 94 | 95 | |
| 95 | 96 | # 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") | |
| 118 | 121 | else: |
| 119 | print('unexpected VM output:') | |
| 120 | print('---start---') | |
| 122 | print("unexpected VM output:") | |
| 123 | print("---start---") | |
| 121 | 124 | print(output) |
| 122 | print('---end---') | |
| 125 | print("---end---") | |
| 123 | 126 | sys.exit(1) |
| 124 | 127 | |
| 125 | 128 |
src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | 0.18.1 | |
| \ No newline at end of file | ||
| 1 | 0.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 |
| 105 | 105 | # It seems that it cannot be the same as $IMAGE_TAG, otherwise it overwrites the cache |
| 106 | 106 | CACHE_IMAGE_TAG=${REGISTRY}/${REGISTRY_USERNAME}/rust-ci-cache:${cksum} |
| 107 | 107 | |
| 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 | ||
| 108 | 125 | # On non-CI jobs, we try to download a pre-built image from the rust-lang-ci |
| 109 | 126 | # ghcr.io registry. If it is not possible, we fall back to building the image |
| 110 | 127 | # locally. |
| ... | ... | @@ -115,7 +132,7 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then |
| 115 | 132 | docker tag "${IMAGE_TAG}" rust-ci |
| 116 | 133 | else |
| 117 | 134 | echo "Building local Docker image" |
| 118 | retry docker build --rm -t rust-ci -f "$dockerfile" "$context" | |
| 135 | retry docker "${build_args[@]}" | |
| 119 | 136 | fi |
| 120 | 137 | # On PR CI jobs, we don't have permissions to write to the registry cache, |
| 121 | 138 | # but we can still read from it. |
| ... | ... | @@ -127,13 +144,9 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then |
| 127 | 144 | # Build the image using registry caching backend |
| 128 | 145 | retry docker \ |
| 129 | 146 | buildx \ |
| 130 | build \ | |
| 131 | --rm \ | |
| 132 | -t rust-ci \ | |
| 133 | -f "$dockerfile" \ | |
| 147 | "${build_args[@]}" \ | |
| 134 | 148 | --cache-from type=registry,ref=${CACHE_IMAGE_TAG} \ |
| 135 | --output=type=docker \ | |
| 136 | "$context" | |
| 149 | --output=type=docker | |
| 137 | 150 | # On auto/try builds, we can also write to the cache. |
| 138 | 151 | else |
| 139 | 152 | # 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 |
| 147 | 160 | # Build the image using registry caching backend |
| 148 | 161 | retry docker \ |
| 149 | 162 | buildx \ |
| 150 | build \ | |
| 151 | --rm \ | |
| 152 | -t rust-ci \ | |
| 153 | -f "$dockerfile" \ | |
| 163 | "${build_args[@]}" \ | |
| 154 | 164 | --cache-from type=registry,ref=${CACHE_IMAGE_TAG} \ |
| 155 | 165 | --cache-to type=registry,ref=${CACHE_IMAGE_TAG},compression=zstd \ |
| 156 | --output=type=docker \ | |
| 157 | "$context" | |
| 166 | --output=type=docker | |
| 158 | 167 | |
| 159 | 168 | # Print images for debugging purposes |
| 160 | 169 | docker images |
src/ci/docker/scripts/android-sdk-manager.py+47-13| ... | ... | @@ -35,6 +35,7 @@ MIRROR_BUCKET = "rust-lang-ci-mirrors" |
| 35 | 35 | MIRROR_BUCKET_REGION = "us-west-1" |
| 36 | 36 | MIRROR_BASE_DIR = "rustc/android/" |
| 37 | 37 | |
| 38 | ||
| 38 | 39 | class Package: |
| 39 | 40 | def __init__(self, path, url, sha1, deps=None): |
| 40 | 41 | if deps is None: |
| ... | ... | @@ -53,18 +54,25 @@ class Package: |
| 53 | 54 | sha1 = hashlib.sha1(f.read()).hexdigest() |
| 54 | 55 | if sha1 != self.sha1: |
| 55 | 56 | 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)" | |
| 58 | 64 | ) |
| 59 | 65 | return file |
| 60 | 66 | |
| 61 | 67 | 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 | ||
| 63 | 70 | |
| 64 | 71 | def fetch_url(url): |
| 65 | 72 | page = urllib.request.urlopen(url) |
| 66 | 73 | return page.read() |
| 67 | 74 | |
| 75 | ||
| 68 | 76 | def fetch_repository(base, repo_url): |
| 69 | 77 | packages = {} |
| 70 | 78 | root = ET.fromstring(fetch_url(base + repo_url)) |
| ... | ... | @@ -92,12 +100,14 @@ def fetch_repository(base, repo_url): |
| 92 | 100 | |
| 93 | 101 | return packages |
| 94 | 102 | |
| 103 | ||
| 95 | 104 | def fetch_repositories(): |
| 96 | 105 | packages = {} |
| 97 | 106 | for repo in REPOSITORIES: |
| 98 | 107 | packages.update(fetch_repository(BASE_REPOSITORY, repo)) |
| 99 | 108 | return packages |
| 100 | 109 | |
| 110 | ||
| 101 | 111 | class Lockfile: |
| 102 | 112 | def __init__(self, path): |
| 103 | 113 | self.path = path |
| ... | ... | @@ -123,6 +133,7 @@ class Lockfile: |
| 123 | 133 | for package in packages: |
| 124 | 134 | f.write(package.path + " " + package.url + " " + package.sha1 + "\n") |
| 125 | 135 | |
| 136 | ||
| 126 | 137 | def cli_add_to_lockfile(args): |
| 127 | 138 | lockfile = Lockfile(args.lockfile) |
| 128 | 139 | packages = fetch_repositories() |
| ... | ... | @@ -130,28 +141,49 @@ def cli_add_to_lockfile(args): |
| 130 | 141 | lockfile.add(packages, package) |
| 131 | 142 | lockfile.save() |
| 132 | 143 | |
| 144 | ||
| 133 | 145 | def cli_update_mirror(args): |
| 134 | 146 | lockfile = Lockfile(args.lockfile) |
| 135 | 147 | for package in lockfile.packages.values(): |
| 136 | 148 | 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 | ||
| 142 | 161 | |
| 143 | 162 | def cli_install(args): |
| 144 | 163 | lockfile = Lockfile(args.lockfile) |
| 145 | 164 | for package in lockfile.packages.values(): |
| 146 | 165 | # 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 | ) | |
| 149 | 174 | downloaded = package.download(url) |
| 150 | 175 | # Extract the file in a temporary directory |
| 151 | 176 | 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 | ) | |
| 155 | 187 | # Figure out the prefix used in the zip |
| 156 | 188 | subdirs = [d for d in os.listdir(extract_dir) if not d.startswith(".")] |
| 157 | 189 | if len(subdirs) != 1: |
| ... | ... | @@ -162,6 +194,7 @@ def cli_install(args): |
| 162 | 194 | os.rename(os.path.join(extract_dir, subdirs[0]), dest) |
| 163 | 195 | os.unlink(downloaded) |
| 164 | 196 | |
| 197 | ||
| 165 | 198 | def cli(): |
| 166 | 199 | parser = argparse.ArgumentParser() |
| 167 | 200 | subparsers = parser.add_subparsers() |
| ... | ... | @@ -187,5 +220,6 @@ def cli(): |
| 187 | 220 | exit(1) |
| 188 | 221 | args.func(args) |
| 189 | 222 | |
| 223 | ||
| 190 | 224 | if __name__ == "__main__": |
| 191 | 225 | cli() |
src/ci/docker/scripts/fuchsia-test-runner.py+3-5| ... | ... | @@ -588,7 +588,7 @@ class TestEnvironment: |
| 588 | 588 | "--repo-path", |
| 589 | 589 | self.repo_dir(), |
| 590 | 590 | "--repository", |
| 591 | self.TEST_REPO_NAME | |
| 591 | self.TEST_REPO_NAME, | |
| 592 | 592 | ], |
| 593 | 593 | env=ffx_env, |
| 594 | 594 | stdout_handler=self.subprocess_logger.debug, |
| ... | ... | @@ -619,9 +619,7 @@ class TestEnvironment: |
| 619 | 619 | # `facet` statement required for TCP testing via |
| 620 | 620 | # protocol `fuchsia.posix.socket.Provider`. See |
| 621 | 621 | # 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] = """ | |
| 625 | 623 | {{ |
| 626 | 624 | program: {{ |
| 627 | 625 | runner: "elf_test_runner", |
| ... | ... | @@ -994,7 +992,7 @@ class TestEnvironment: |
| 994 | 992 | "repository", |
| 995 | 993 | "server", |
| 996 | 994 | "stop", |
| 997 | self.TEST_REPO_NAME | |
| 995 | self.TEST_REPO_NAME, | |
| 998 | 996 | ], |
| 999 | 997 | env=self.ffx_cmd_env(), |
| 1000 | 998 | stdout_handler=self.subprocess_logger.debug, |
src/ci/docker/scripts/rfl-build.sh+2-2| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | set -euo pipefail |
| 4 | 4 | |
| 5 | LINUX_VERSION=28e848386b92645f93b9f2fdba5882c3ca7fb3e2 | |
| 5 | LINUX_VERSION=v6.13-rc1 | |
| 6 | 6 | |
| 7 | 7 | # Build rustc, rustdoc, cargo, clippy-driver and rustfmt |
| 8 | 8 | ../x.py build --stage 2 library rustdoc clippy rustfmt |
| ... | ... | @@ -64,7 +64,7 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ |
| 64 | 64 | |
| 65 | 65 | BUILD_TARGETS=" |
| 66 | 66 | samples/rust/rust_minimal.o |
| 67 | samples/rust/rust_print.o | |
| 67 | samples/rust/rust_print_main.o | |
| 68 | 68 | drivers/net/phy/ax88796b_rust.o |
| 69 | 69 | rust/doctests_kernel_generated.o |
| 70 | 70 | " |
src/ci/github-actions/calculate-job-matrix.py+9-4| ... | ... | @@ -7,6 +7,7 @@ be executed on CI. |
| 7 | 7 | It reads job definitions from `src/ci/github-actions/jobs.yml` |
| 8 | 8 | and filters them based on the event that happened on CI. |
| 9 | 9 | """ |
| 10 | ||
| 10 | 11 | import dataclasses |
| 11 | 12 | import json |
| 12 | 13 | import logging |
| ... | ... | @@ -94,7 +95,7 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: |
| 94 | 95 | try_build = ctx.ref in ( |
| 95 | 96 | "refs/heads/try", |
| 96 | 97 | "refs/heads/try-perf", |
| 97 | "refs/heads/automation/bors/try" | |
| 98 | "refs/heads/automation/bors/try", | |
| 98 | 99 | ) |
| 99 | 100 | |
| 100 | 101 | # Unrolled branch from a rollup for testing perf |
| ... | ... | @@ -135,11 +136,15 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[ |
| 135 | 136 | continue |
| 136 | 137 | jobs.append(job[0]) |
| 137 | 138 | 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 | ) | |
| 139 | 142 | |
| 140 | 143 | return add_base_env(name_jobs(jobs, "try"), job_data["envs"]["try"]) |
| 141 | 144 | 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 | ) | |
| 143 | 148 | |
| 144 | 149 | return [] |
| 145 | 150 | |
| ... | ... | @@ -161,7 +166,7 @@ def get_github_ctx() -> GitHubCtx: |
| 161 | 166 | event_name=event_name, |
| 162 | 167 | ref=os.environ["GITHUB_REF"], |
| 163 | 168 | repository=os.environ["GITHUB_REPOSITORY"], |
| 164 | commit_message=commit_message | |
| 169 | commit_message=commit_message, | |
| 165 | 170 | ) |
| 166 | 171 | |
| 167 | 172 |
src/ci/github-actions/jobs.yml+51-4| ... | ... | @@ -58,6 +58,22 @@ envs: |
| 58 | 58 | NO_DEBUG_ASSERTIONS: 1 |
| 59 | 59 | NO_OVERFLOW_CHECKS: 1 |
| 60 | 60 | |
| 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 | ||
| 61 | 77 | production: |
| 62 | 78 | &production |
| 63 | 79 | DEPLOY_BUCKET: rust-lang-ci2 |
| ... | ... | @@ -212,11 +228,42 @@ auto: |
| 212 | 228 | - image: dist-x86_64-netbsd |
| 213 | 229 | <<: *job-linux-4c |
| 214 | 230 | |
| 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 | |
| 217 | 238 | |
| 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 | |
| 220 | 267 | |
| 221 | 268 | - image: mingw-check |
| 222 | 269 | <<: *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> |
| 19 | 19 | |
| 20 | 20 | `path-to-CPU-usage-CSV` is a path to a CSV generated by the `src/ci/cpu-usage-over-time.py` script. |
| 21 | 21 | """ |
| 22 | ||
| 22 | 23 | import argparse |
| 23 | 24 | import csv |
| 24 | 25 | import os |
| ... | ... | @@ -31,7 +32,7 @@ from typing import List |
| 31 | 32 | def load_cpu_usage(path: Path) -> List[float]: |
| 32 | 33 | usage = [] |
| 33 | 34 | with open(path) as f: |
| 34 | reader = csv.reader(f, delimiter=',') | |
| 35 | reader = csv.reader(f, delimiter=",") | |
| 35 | 36 | for row in reader: |
| 36 | 37 | # The log might contain incomplete rows or some Python exception |
| 37 | 38 | if len(row) == 2: |
| ... | ... | @@ -50,25 +51,21 @@ def upload_datadog_measure(name: str, value: float): |
| 50 | 51 | print(f"Metric {name}: {value:.4f}") |
| 51 | 52 | |
| 52 | 53 | 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 | ): | |
| 54 | 57 | # Due to weird interaction of MSYS2 and Python, we need to use an absolute path, |
| 55 | 58 | # and also specify the ".cmd" at the end. See https://github.com/rust-lang/rust/pull/125771. |
| 56 | 59 | datadog_cmd = "C:\\npm\\prefix\\datadog-ci.cmd" |
| 57 | 60 | |
| 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, | |
| 65 | 64 | ) |
| 66 | 65 | |
| 67 | 66 | |
| 68 | 67 | if __name__ == "__main__": |
| 69 | parser = argparse.ArgumentParser( | |
| 70 | prog="DataDog metric uploader" | |
| 71 | ) | |
| 68 | parser = argparse.ArgumentParser(prog="DataDog metric uploader") | |
| 72 | 69 | parser.add_argument("cpu-usage-history-csv") |
| 73 | 70 | args = parser.parse_args() |
| 74 | 71 |
src/etc/dec2flt_table.py+24-22| ... | ... | @@ -13,6 +13,7 @@ i.e., within 0.5 ULP of the true value. |
| 13 | 13 | Adapted from Daniel Lemire's fast_float ``table_generation.py``, |
| 14 | 14 | available here: <https://github.com/fastfloat/fast_float/blob/main/script/table_generation.py>. |
| 15 | 15 | """ |
| 16 | ||
| 16 | 17 | from __future__ import print_function |
| 17 | 18 | from math import ceil, floor, log |
| 18 | 19 | from collections import deque |
| ... | ... | @@ -34,6 +35,7 @@ STATIC_WARNING = """ |
| 34 | 35 | // the final binary. |
| 35 | 36 | """ |
| 36 | 37 | |
| 38 | ||
| 37 | 39 | def main(): |
| 38 | 40 | min_exp = minimum_exponent(10) |
| 39 | 41 | max_exp = maximum_exponent(10) |
| ... | ... | @@ -41,10 +43,10 @@ def main(): |
| 41 | 43 | |
| 42 | 44 | print(HEADER.strip()) |
| 43 | 45 | 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;") | |
| 48 | 50 | print() |
| 49 | 51 | print_proper_powers(min_exp, max_exp, bias) |
| 50 | 52 | |
| ... | ... | @@ -54,7 +56,7 @@ def minimum_exponent(base): |
| 54 | 56 | |
| 55 | 57 | |
| 56 | 58 | def maximum_exponent(base): |
| 57 | return floor(log(1.7976931348623157e+308, base)) | |
| 59 | return floor(log(1.7976931348623157e308, base)) | |
| 58 | 60 | |
| 59 | 61 | |
| 60 | 62 | def print_proper_powers(min_exp, max_exp, bias): |
| ... | ... | @@ -64,46 +66,46 @@ def print_proper_powers(min_exp, max_exp, bias): |
| 64 | 66 | # 2^(2b)/(5^−q) with b=64 + int(math.ceil(log2(5^−q))) |
| 65 | 67 | powers = [] |
| 66 | 68 | for q in range(min_exp, 0): |
| 67 | power5 = 5 ** -q | |
| 69 | power5 = 5**-q | |
| 68 | 70 | z = 0 |
| 69 | 71 | while (1 << z) < power5: |
| 70 | 72 | z += 1 |
| 71 | 73 | if q >= -27: |
| 72 | 74 | b = z + 127 |
| 73 | c = 2 ** b // power5 + 1 | |
| 75 | c = 2**b // power5 + 1 | |
| 74 | 76 | powers.append((c, q)) |
| 75 | 77 | else: |
| 76 | 78 | b = 2 * z + 2 * 64 |
| 77 | c = 2 ** b // power5 + 1 | |
| 79 | c = 2**b // power5 + 1 | |
| 78 | 80 | # truncate |
| 79 | while c >= (1<<128): | |
| 81 | while c >= (1 << 128): | |
| 80 | 82 | c //= 2 |
| 81 | 83 | powers.append((c, q)) |
| 82 | 84 | |
| 83 | 85 | # Add positive exponents |
| 84 | 86 | for q in range(0, max_exp + 1): |
| 85 | power5 = 5 ** q | |
| 87 | power5 = 5**q | |
| 86 | 88 | # move the most significant bit in position |
| 87 | while power5 < (1<<127): | |
| 89 | while power5 < (1 << 127): | |
| 88 | 90 | power5 *= 2 |
| 89 | 91 | # *truncate* |
| 90 | while power5 >= (1<<128): | |
| 92 | while power5 >= (1 << 128): | |
| 91 | 93 | power5 //= 2 |
| 92 | 94 | powers.append((power5, q)) |
| 93 | 95 | |
| 94 | 96 | # Print the powers. |
| 95 | 97 | 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)) | |
| 99 | 101 | 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("];") | |
| 106 | 108 | |
| 107 | 109 | |
| 108 | if __name__ == '__main__': | |
| 110 | if __name__ == "__main__": | |
| 109 | 111 | main() |
src/etc/gdb_load_rust_pretty_printers.py+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | # Add this folder to the python sys path; GDB Python-interpreter will now find modules in this path |
| 2 | 2 | import sys |
| 3 | 3 | from os import path |
| 4 | ||
| 4 | 5 | self_dir = path.dirname(path.realpath(__file__)) |
| 5 | 6 | sys.path.append(self_dir) |
| 6 | 7 |
src/etc/gdb_lookup.py+5-2| ... | ... | @@ -6,8 +6,11 @@ from gdb_providers import * |
| 6 | 6 | from rust_types import * |
| 7 | 7 | |
| 8 | 8 | |
| 9 | _gdb_version_matched = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION) | |
| 10 | gdb_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) | |
| 10 | gdb_version = ( | |
| 11 | [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else [] | |
| 12 | ) | |
| 13 | ||
| 11 | 14 | |
| 12 | 15 | def register_printers(objfile): |
| 13 | 16 | 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): |
| 21 | 21 | # GDB 14 has a tag class that indicates that extension methods are ok |
| 22 | 22 | # to call. Use of this tag only requires that printers hide local |
| 23 | 23 | # attributes and methods by prefixing them with "_". |
| 24 | if hasattr(gdb, 'ValuePrinter'): | |
| 24 | if hasattr(gdb, "ValuePrinter"): | |
| 25 | 25 | printer_base = gdb.ValuePrinter |
| 26 | 26 | else: |
| 27 | 27 | printer_base = object |
| ... | ... | @@ -98,7 +98,7 @@ class StdStrProvider(printer_base): |
| 98 | 98 | |
| 99 | 99 | |
| 100 | 100 | def _enumerate_array_elements(element_ptrs): |
| 101 | for (i, element_ptr) in enumerate(element_ptrs): | |
| 101 | for i, element_ptr in enumerate(element_ptrs): | |
| 102 | 102 | key = "[{}]".format(i) |
| 103 | 103 | element = element_ptr.dereference() |
| 104 | 104 | |
| ... | ... | @@ -173,7 +173,8 @@ class StdVecDequeProvider(printer_base): |
| 173 | 173 | |
| 174 | 174 | def children(self): |
| 175 | 175 | 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) | |
| 177 | 178 | ) |
| 178 | 179 | |
| 179 | 180 | @staticmethod |
| ... | ... | @@ -270,7 +271,9 @@ def children_of_btree_map(map): |
| 270 | 271 | # Yields each key/value pair in the node and in any child nodes. |
| 271 | 272 | def children_of_node(node_ptr, height): |
| 272 | 273 | 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 | ) | |
| 274 | 277 | internal_type = gdb.lookup_type(internal_type_name) |
| 275 | 278 | return node.cast(internal_type.pointer()) |
| 276 | 279 | |
| ... | ... | @@ -293,8 +296,16 @@ def children_of_btree_map(map): |
| 293 | 296 | # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays. |
| 294 | 297 | key_type_size = keys.type.sizeof |
| 295 | 298 | 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 | ) | |
| 298 | 309 | yield key, val |
| 299 | 310 | |
| 300 | 311 | if map["length"] > 0: |
| ... | ... | @@ -352,7 +363,7 @@ class StdOldHashMapProvider(printer_base): |
| 352 | 363 | self._hashes = self._table["hashes"] |
| 353 | 364 | self._hash_uint_type = self._hashes.type |
| 354 | 365 | self._hash_uint_size = self._hashes.type.sizeof |
| 355 | self._modulo = 2 ** self._hash_uint_size | |
| 366 | self._modulo = 2**self._hash_uint_size | |
| 356 | 367 | self._data_ptr = self._hashes[ZERO_FIELD]["pointer"] |
| 357 | 368 | |
| 358 | 369 | self._capacity_mask = int(self._table["capacity_mask"]) |
| ... | ... | @@ -382,8 +393,14 @@ class StdOldHashMapProvider(printer_base): |
| 382 | 393 | |
| 383 | 394 | hashes = self._hash_uint_size * self._capacity |
| 384 | 395 | 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 | |
| 387 | 404 | |
| 388 | 405 | pairs_offset = hashes + len_rounded_up |
| 389 | 406 | 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 |
| 12 | 12 | import stat |
| 13 | 13 | |
| 14 | 14 | TEST_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 | ) | |
| 16 | 17 | |
| 17 | 18 | TEMPLATE = """\ |
| 18 | 19 | // 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) |
| 56 | 57 | |
| 57 | 58 | |
| 58 | 59 | def 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)) | |
| 65 | 71 | code = string.format(traits=all_traits, errors=errors) |
| 66 | 72 | return TEMPLATE.format(error_deriving=error_deriving, code=code) |
| 67 | 73 | |
| 68 | 74 | |
| 69 | 75 | def 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) | |
| 71 | 77 | |
| 72 | 78 | # set write permission if file exists, so it can be changed |
| 73 | 79 | if os.path.exists(test_file): |
| 74 | 80 | os.chmod(test_file, stat.S_IWUSR) |
| 75 | 81 | |
| 76 | with open(test_file, 'w') as f: | |
| 82 | with open(test_file, "w") as f: | |
| 77 | 83 | f.write(string) |
| 78 | 84 | |
| 79 | 85 | # 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) | |
| 81 | 87 | |
| 82 | 88 | |
| 83 | 89 | ENUM = 1 |
| ... | ... | @@ -85,29 +91,31 @@ STRUCT = 2 |
| 85 | 91 | ALL = STRUCT | ENUM |
| 86 | 92 | |
| 87 | 93 | traits = { |
| 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 | |
| 93 | 98 | } |
| 94 | 99 | |
| 95 | for (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)]: | |
| 100 | for 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 | ]: | |
| 102 | 109 | traits[trait] = (ALL, supers, errs) |
| 103 | 110 | |
| 104 | for (trait, (types, super_traits, error_count)) in traits.items(): | |
| 111 | for trait, (types, super_traits, error_count) in traits.items(): | |
| 112 | ||
| 105 | 113 | def mk(ty, t=trait, st=super_traits, ec=error_count): |
| 106 | 114 | return create_test_case(ty, t, st, ec) |
| 107 | 115 | |
| 108 | 116 | 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)) | |
| 111 | 119 | 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() { |
| 22 | 22 | } |
| 23 | 23 | """ |
| 24 | 24 | |
| 25 | test_dir = os.path.abspath( | |
| 26 | os.path.join(os.path.dirname(__file__), '../test/ui/parser') | |
| 27 | ) | |
| 25 | test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../test/ui/parser")) | |
| 28 | 26 | |
| 29 | 27 | for 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) | |
| 31 | 29 | |
| 32 | 30 | # set write permission if file exists, so it can be changed |
| 33 | 31 | if os.path.exists(test_file): |
| 34 | 32 | os.chmod(test_file, stat.S_IWUSR) |
| 35 | 33 | |
| 36 | with open(test_file, 'wt') as f: | |
| 34 | with open(test_file, "wt") as f: | |
| 37 | 35 | f.write(template % (kw, kw, kw)) |
| 38 | 36 | |
| 39 | 37 | # mark file read-only |
src/etc/htmldocck.py+151-109| ... | ... | @@ -127,6 +127,7 @@ import os.path |
| 127 | 127 | import re |
| 128 | 128 | import shlex |
| 129 | 129 | from collections import namedtuple |
| 130 | ||
| 130 | 131 | try: |
| 131 | 132 | from html.parser import HTMLParser |
| 132 | 133 | except ImportError: |
| ... | ... | @@ -142,12 +143,28 @@ except ImportError: |
| 142 | 143 | from htmlentitydefs import name2codepoint |
| 143 | 144 | |
| 144 | 145 | # "void elements" (no closing tag) from the HTML Standard section 12.1.2 |
| 145 | VOID_ELEMENTS = {'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', | |
| 146 | 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'} | |
| 146 | VOID_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 | } | |
| 147 | 164 | |
| 148 | 165 | # Python 2 -> 3 compatibility |
| 149 | 166 | try: |
| 150 | unichr # noqa: B018 FIXME: py2 | |
| 167 | unichr # noqa: B018 FIXME: py2 | |
| 151 | 168 | except NameError: |
| 152 | 169 | unichr = chr |
| 153 | 170 | |
| ... | ... | @@ -158,18 +175,20 @@ channel = os.environ["DOC_RUST_LANG_ORG_CHANNEL"] |
| 158 | 175 | rust_test_path = None |
| 159 | 176 | bless = None |
| 160 | 177 | |
| 178 | ||
| 161 | 179 | class CustomHTMLParser(HTMLParser): |
| 162 | 180 | """simplified HTML parser. |
| 163 | 181 | |
| 164 | 182 | this is possible because we are dealing with very regular HTML from |
| 165 | 183 | rustdoc; we only have to deal with i) void elements and ii) empty |
| 166 | 184 | attributes.""" |
| 185 | ||
| 167 | 186 | def __init__(self, target=None): |
| 168 | 187 | HTMLParser.__init__(self) |
| 169 | 188 | self.__builder = target or ET.TreeBuilder() |
| 170 | 189 | |
| 171 | 190 | 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} | |
| 173 | 192 | self.__builder.start(tag, attrs) |
| 174 | 193 | if tag in VOID_ELEMENTS: |
| 175 | 194 | self.__builder.end(tag) |
| ... | ... | @@ -178,7 +197,7 @@ class CustomHTMLParser(HTMLParser): |
| 178 | 197 | self.__builder.end(tag) |
| 179 | 198 | |
| 180 | 199 | 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} | |
| 182 | 201 | self.__builder.start(tag, attrs) |
| 183 | 202 | self.__builder.end(tag) |
| 184 | 203 | |
| ... | ... | @@ -189,7 +208,7 @@ class CustomHTMLParser(HTMLParser): |
| 189 | 208 | self.__builder.data(unichr(name2codepoint[name])) |
| 190 | 209 | |
| 191 | 210 | 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) | |
| 193 | 212 | self.__builder.data(unichr(code)) |
| 194 | 213 | |
| 195 | 214 | def close(self): |
| ... | ... | @@ -197,7 +216,7 @@ class CustomHTMLParser(HTMLParser): |
| 197 | 216 | return self.__builder.close() |
| 198 | 217 | |
| 199 | 218 | |
| 200 | Command = namedtuple('Command', 'negated cmd args lineno context') | |
| 219 | Command = namedtuple("Command", "negated cmd args lineno context") | |
| 201 | 220 | |
| 202 | 221 | |
| 203 | 222 | class FailedCheck(Exception): |
| ... | ... | @@ -216,17 +235,17 @@ def concat_multi_lines(f): |
| 216 | 235 | concatenated.""" |
| 217 | 236 | lastline = None # set to the last line when the last line has a backslash |
| 218 | 237 | firstlineno = None |
| 219 | catenated = '' | |
| 238 | catenated = "" | |
| 220 | 239 | for lineno, line in enumerate(f): |
| 221 | line = line.rstrip('\r\n') | |
| 240 | line = line.rstrip("\r\n") | |
| 222 | 241 | |
| 223 | 242 | # strip the common prefix from the current line if needed |
| 224 | 243 | if lastline is not None: |
| 225 | 244 | common_prefix = os.path.commonprefix([line, lastline]) |
| 226 | line = line[len(common_prefix):].lstrip() | |
| 245 | line = line[len(common_prefix) :].lstrip() | |
| 227 | 246 | |
| 228 | 247 | firstlineno = firstlineno or lineno |
| 229 | if line.endswith('\\'): | |
| 248 | if line.endswith("\\"): | |
| 230 | 249 | if lastline is None: |
| 231 | 250 | lastline = line[:-1] |
| 232 | 251 | catenated += line[:-1] |
| ... | ... | @@ -234,10 +253,10 @@ def concat_multi_lines(f): |
| 234 | 253 | yield firstlineno, catenated + line |
| 235 | 254 | lastline = None |
| 236 | 255 | firstlineno = None |
| 237 | catenated = '' | |
| 256 | catenated = "" | |
| 238 | 257 | |
| 239 | 258 | 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") | |
| 241 | 260 | |
| 242 | 261 | |
| 243 | 262 | def get_known_directive_names(): |
| ... | ... | @@ -253,12 +272,12 @@ def get_known_directive_names(): |
| 253 | 272 | "tools/compiletest/src/directive-list.rs", |
| 254 | 273 | ), |
| 255 | 274 | "r", |
| 256 | encoding="utf8" | |
| 275 | encoding="utf8", | |
| 257 | 276 | ) as fd: |
| 258 | 277 | content = fd.read() |
| 259 | 278 | 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") | |
| 262 | 281 | if filter_line(line) |
| 263 | 282 | ] |
| 264 | 283 | |
| ... | ... | @@ -269,35 +288,42 @@ def get_known_directive_names(): |
| 269 | 288 | # See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>. |
| 270 | 289 | KNOWN_DIRECTIVE_NAMES = get_known_directive_names() |
| 271 | 290 | |
| 272 | LINE_PATTERN = re.compile(r''' | |
| 291 | LINE_PATTERN = re.compile( | |
| 292 | r""" | |
| 273 | 293 | //@\s+ |
| 274 | 294 | (?P<negated>!?)(?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*) |
| 275 | 295 | (?P<args>.*)$ |
| 276 | ''', re.X | re.UNICODE) | |
| 296 | """, | |
| 297 | re.X | re.UNICODE, | |
| 298 | ) | |
| 277 | 299 | |
| 278 | 300 | |
| 279 | 301 | def get_commands(template): |
| 280 | with io.open(template, encoding='utf-8') as f: | |
| 302 | with io.open(template, encoding="utf-8") as f: | |
| 281 | 303 | for lineno, line in concat_multi_lines(f): |
| 282 | 304 | m = LINE_PATTERN.search(line) |
| 283 | 305 | if not m: |
| 284 | 306 | continue |
| 285 | 307 | |
| 286 | cmd = m.group('cmd') | |
| 287 | negated = (m.group('negated') == '!') | |
| 308 | cmd = m.group("cmd") | |
| 309 | negated = m.group("negated") == "!" | |
| 288 | 310 | if not negated and cmd in KNOWN_DIRECTIVE_NAMES: |
| 289 | 311 | continue |
| 290 | args = m.group('args') | |
| 312 | args = m.group("args") | |
| 291 | 313 | if args and not args[:1].isspace(): |
| 292 | print_err(lineno, line, 'Invalid template syntax') | |
| 314 | print_err(lineno, line, "Invalid template syntax") | |
| 293 | 315 | continue |
| 294 | 316 | try: |
| 295 | 317 | args = shlex.split(args) |
| 296 | 318 | 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 | ] | |
| 298 | 322 | except Exception as exc: |
| 299 | 323 | 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 | ) | |
| 301 | 327 | |
| 302 | 328 | |
| 303 | 329 | def _flatten(node, acc): |
| ... | ... | @@ -312,22 +338,24 @@ def _flatten(node, acc): |
| 312 | 338 | def flatten(node): |
| 313 | 339 | acc = [] |
| 314 | 340 | _flatten(node, acc) |
| 315 | return ''.join(acc) | |
| 341 | return "".join(acc) | |
| 316 | 342 | |
| 317 | 343 | |
| 318 | 344 | def make_xml(text): |
| 319 | xml = ET.XML('<xml>%s</xml>' % text) | |
| 345 | xml = ET.XML("<xml>%s</xml>" % text) | |
| 320 | 346 | return xml |
| 321 | 347 | |
| 322 | 348 | |
| 323 | 349 | def normalize_xpath(path): |
| 324 | 350 | 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(".//"): | |
| 328 | 354 | return path |
| 329 | 355 | 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 | ) | |
| 331 | 359 | |
| 332 | 360 | |
| 333 | 361 | class CachedFiles(object): |
| ... | ... | @@ -338,12 +366,12 @@ class CachedFiles(object): |
| 338 | 366 | self.last_path = None |
| 339 | 367 | |
| 340 | 368 | def resolve_path(self, path): |
| 341 | if path != '-': | |
| 369 | if path != "-": | |
| 342 | 370 | path = os.path.normpath(path) |
| 343 | 371 | self.last_path = path |
| 344 | 372 | return path |
| 345 | 373 | 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") | |
| 347 | 375 | else: |
| 348 | 376 | return self.last_path |
| 349 | 377 | |
| ... | ... | @@ -356,10 +384,10 @@ class CachedFiles(object): |
| 356 | 384 | return self.files[path] |
| 357 | 385 | |
| 358 | 386 | 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)) | |
| 361 | 389 | |
| 362 | with io.open(abspath, encoding='utf-8') as f: | |
| 390 | with io.open(abspath, encoding="utf-8") as f: | |
| 363 | 391 | data = f.read() |
| 364 | 392 | self.files[path] = data |
| 365 | 393 | return data |
| ... | ... | @@ -370,15 +398,15 @@ class CachedFiles(object): |
| 370 | 398 | return self.trees[path] |
| 371 | 399 | |
| 372 | 400 | 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)) | |
| 375 | 403 | |
| 376 | with io.open(abspath, encoding='utf-8') as f: | |
| 404 | with io.open(abspath, encoding="utf-8") as f: | |
| 377 | 405 | try: |
| 378 | 406 | tree = ET.fromstringlist(f.readlines(), CustomHTMLParser()) |
| 379 | 407 | 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) | |
| 382 | 410 | ) |
| 383 | 411 | self.trees[path] = tree |
| 384 | 412 | return self.trees[path] |
| ... | ... | @@ -386,8 +414,8 @@ class CachedFiles(object): |
| 386 | 414 | def get_dir(self, path): |
| 387 | 415 | path = self.resolve_path(path) |
| 388 | 416 | 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)) | |
| 391 | 419 | |
| 392 | 420 | |
| 393 | 421 | def check_string(data, pat, regexp): |
| ... | ... | @@ -397,8 +425,8 @@ def check_string(data, pat, regexp): |
| 397 | 425 | elif regexp: |
| 398 | 426 | return re.search(pat, data, flags=re.UNICODE) is not None |
| 399 | 427 | else: |
| 400 | data = ' '.join(data.split()) | |
| 401 | pat = ' '.join(pat.split()) | |
| 428 | data = " ".join(data.split()) | |
| 429 | pat = " ".join(pat.split()) | |
| 402 | 430 | return pat in data |
| 403 | 431 | |
| 404 | 432 | |
| ... | ... | @@ -444,19 +472,19 @@ def get_tree_count(tree, path): |
| 444 | 472 | |
| 445 | 473 | |
| 446 | 474 | def 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") | |
| 449 | 477 | try: |
| 450 | with open(snapshot_path, 'r') as snapshot_file: | |
| 478 | with open(snapshot_path, "r") as snapshot_file: | |
| 451 | 479 | expected_str = snapshot_file.read().replace("{{channel}}", channel) |
| 452 | 480 | except FileNotFoundError: |
| 453 | 481 | if bless: |
| 454 | 482 | expected_str = None |
| 455 | 483 | else: |
| 456 | raise FailedCheck('No saved snapshot value') # noqa: B904 FIXME: py2 | |
| 484 | raise FailedCheck("No saved snapshot value") # noqa: B904 FIXME: py2 | |
| 457 | 485 | |
| 458 | 486 | 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") | |
| 460 | 488 | else: |
| 461 | 489 | actual_str = flatten(actual_tree) |
| 462 | 490 | |
| ... | ... | @@ -464,64 +492,66 @@ def check_snapshot(snapshot_name, actual_tree, normalize_to_text): |
| 464 | 492 | # 1. Is --bless |
| 465 | 493 | # 2. Are actual and expected tree different |
| 466 | 494 | # 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 | ): | |
| 472 | 503 | if bless: |
| 473 | with open(snapshot_path, 'w') as snapshot_file: | |
| 504 | with open(snapshot_path, "w") as snapshot_file: | |
| 474 | 505 | actual_str = actual_str.replace(channel, "{{channel}}") |
| 475 | 506 | snapshot_file.write(actual_str) |
| 476 | 507 | else: |
| 477 | print('--- expected ---\n') | |
| 508 | print("--- expected ---\n") | |
| 478 | 509 | print(expected_str) |
| 479 | print('\n\n--- actual ---\n') | |
| 510 | print("\n\n--- actual ---\n") | |
| 480 | 511 | print(actual_str) |
| 481 | 512 | print() |
| 482 | raise FailedCheck('Actual snapshot value is different than expected') | |
| 513 | raise FailedCheck("Actual snapshot value is different than expected") | |
| 483 | 514 | |
| 484 | 515 | |
| 485 | 516 | # Adapted from https://github.com/formencode/formencode/blob/3a1ba9de2fdd494dd945510a4568a3afeddb0b2e/formencode/doctest_xml_compare.py#L72-L120 |
| 486 | 517 | def compare_tree(x1, x2, reporter=None): |
| 487 | 518 | if x1.tag != x2.tag: |
| 488 | 519 | 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)) | |
| 490 | 521 | return False |
| 491 | 522 | for name, value in x1.attrib.items(): |
| 492 | 523 | if x2.attrib.get(name) != value: |
| 493 | 524 | 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 | ) | |
| 496 | 529 | return False |
| 497 | 530 | for name in x2.attrib: |
| 498 | 531 | if name not in x1.attrib: |
| 499 | 532 | 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) | |
| 502 | 534 | return False |
| 503 | 535 | if not text_compare(x1.text, x2.text): |
| 504 | 536 | if reporter: |
| 505 | reporter('text: %r != %r' % (x1.text, x2.text)) | |
| 537 | reporter("text: %r != %r" % (x1.text, x2.text)) | |
| 506 | 538 | return False |
| 507 | 539 | if not text_compare(x1.tail, x2.tail): |
| 508 | 540 | if reporter: |
| 509 | reporter('tail: %r != %r' % (x1.tail, x2.tail)) | |
| 541 | reporter("tail: %r != %r" % (x1.tail, x2.tail)) | |
| 510 | 542 | return False |
| 511 | 543 | cl1 = list(x1) |
| 512 | 544 | cl2 = list(x2) |
| 513 | 545 | if len(cl1) != len(cl2): |
| 514 | 546 | 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))) | |
| 517 | 548 | return False |
| 518 | 549 | i = 0 |
| 519 | 550 | for c1, c2 in zip(cl1, cl2): |
| 520 | 551 | i += 1 |
| 521 | 552 | if not compare_tree(c1, c2, reporter=reporter): |
| 522 | 553 | 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)) | |
| 525 | 555 | return False |
| 526 | 556 | return True |
| 527 | 557 | |
| ... | ... | @@ -529,14 +559,14 @@ def compare_tree(x1, x2, reporter=None): |
| 529 | 559 | def text_compare(t1, t2): |
| 530 | 560 | if not t1 and not t2: |
| 531 | 561 | return True |
| 532 | if t1 == '*' or t2 == '*': | |
| 562 | if t1 == "*" or t2 == "*": | |
| 533 | 563 | return True |
| 534 | return (t1 or '').strip() == (t2 or '').strip() | |
| 564 | return (t1 or "").strip() == (t2 or "").strip() | |
| 535 | 565 | |
| 536 | 566 | |
| 537 | 567 | def stderr(*args): |
| 538 | 568 | if sys.version_info.major < 3: |
| 539 | file = codecs.getwriter('utf-8')(sys.stderr) | |
| 569 | file = codecs.getwriter("utf-8")(sys.stderr) | |
| 540 | 570 | else: |
| 541 | 571 | file = sys.stderr |
| 542 | 572 | |
| ... | ... | @@ -556,21 +586,25 @@ def print_err(lineno, context, err, message=None): |
| 556 | 586 | |
| 557 | 587 | def get_nb_matching_elements(cache, c, regexp, stop_at_first): |
| 558 | 588 | tree = cache.get_tree(c.args[0]) |
| 559 | pat, sep, attr = c.args[1].partition('/@') | |
| 589 | pat, sep, attr = c.args[1].partition("/@") | |
| 560 | 590 | if sep: # attribute |
| 561 | 591 | tree = cache.get_tree(c.args[0]) |
| 562 | 592 | return check_tree_attr(tree, pat, attr, c.args[2], False) |
| 563 | 593 | else: # normalized text |
| 564 | 594 | pat = c.args[1] |
| 565 | if pat.endswith('/text()'): | |
| 595 | if pat.endswith("/text()"): | |
| 566 | 596 | 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 | ) | |
| 568 | 600 | |
| 569 | 601 | |
| 570 | 602 | def check_files_in_folder(c, cache, folder, files): |
| 571 | 603 | 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 | ) | |
| 574 | 608 | |
| 575 | 609 | folder = cache.get_absolute_path(folder) |
| 576 | 610 | |
| ... | ... | @@ -592,12 +626,18 @@ def check_files_in_folder(c, cache, folder, files): |
| 592 | 626 | |
| 593 | 627 | error = 0 |
| 594 | 628 | 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 | ) | |
| 597 | 634 | error += 1 |
| 598 | 635 | 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 | ) | |
| 601 | 641 | error += 1 |
| 602 | 642 | return error == 0 |
| 603 | 643 | |
| ... | ... | @@ -608,11 +648,11 @@ ERR_COUNT = 0 |
| 608 | 648 | def check_command(c, cache): |
| 609 | 649 | try: |
| 610 | 650 | 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") | |
| 613 | 653 | |
| 614 | 654 | # 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: | |
| 616 | 656 | try: |
| 617 | 657 | cache.get_file(c.args[0]) |
| 618 | 658 | ret = True |
| ... | ... | @@ -620,24 +660,24 @@ def check_command(c, cache): |
| 620 | 660 | cerr = str(err) |
| 621 | 661 | ret = False |
| 622 | 662 | # 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: | |
| 624 | 664 | cerr = "`PATTERN` did not match" |
| 625 | 665 | ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp) |
| 626 | 666 | # 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: | |
| 628 | 668 | cerr = "`XPATH PATTERN` did not match" |
| 629 | 669 | ret = get_nb_matching_elements(cache, c, regexp, True) != 0 |
| 630 | 670 | else: |
| 631 | raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd)) | |
| 671 | raise InvalidCheck("Invalid number of {} arguments".format(c.cmd)) | |
| 632 | 672 | |
| 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> | |
| 635 | 675 | raise InvalidCheck("Invalid number of {} arguments".format(c.cmd)) |
| 636 | 676 | elif c.negated: |
| 637 | 677 | raise InvalidCheck("{} doesn't support negative check".format(c.cmd)) |
| 638 | 678 | ret = check_files_in_folder(c, cache, c.args[0], c.args[1]) |
| 639 | 679 | |
| 640 | elif c.cmd == 'count': # count test | |
| 680 | elif c.cmd == "count": # count test | |
| 641 | 681 | if len(c.args) == 3: # count <path> <pat> <count> = count test |
| 642 | 682 | expected = int(c.args[2]) |
| 643 | 683 | found = get_tree_count(cache.get_tree(c.args[0]), c.args[1]) |
| ... | ... | @@ -649,15 +689,15 @@ def check_command(c, cache): |
| 649 | 689 | cerr = "Expected {} occurrences but found {}".format(expected, found) |
| 650 | 690 | ret = found == expected |
| 651 | 691 | else: |
| 652 | raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd)) | |
| 692 | raise InvalidCheck("Invalid number of {} arguments".format(c.cmd)) | |
| 653 | 693 | |
| 654 | elif c.cmd == 'snapshot': # snapshot test | |
| 694 | elif c.cmd == "snapshot": # snapshot test | |
| 655 | 695 | if len(c.args) == 3: # snapshot <snapshot-name> <html-path> <xpath> |
| 656 | 696 | [snapshot_name, html_path, pattern] = c.args |
| 657 | 697 | tree = cache.get_tree(html_path) |
| 658 | 698 | xpath = normalize_xpath(pattern) |
| 659 | 699 | normalize_to_text = False |
| 660 | if xpath.endswith('/text()'): | |
| 700 | if xpath.endswith("/text()"): | |
| 661 | 701 | xpath = xpath[:-7] |
| 662 | 702 | normalize_to_text = True |
| 663 | 703 | |
| ... | ... | @@ -671,13 +711,15 @@ def check_command(c, cache): |
| 671 | 711 | cerr = str(err) |
| 672 | 712 | ret = False |
| 673 | 713 | elif len(subtrees) == 0: |
| 674 | raise FailedCheck('XPATH did not match') | |
| 714 | raise FailedCheck("XPATH did not match") | |
| 675 | 715 | 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 | ) | |
| 677 | 719 | else: |
| 678 | raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd)) | |
| 720 | raise InvalidCheck("Invalid number of {} arguments".format(c.cmd)) | |
| 679 | 721 | |
| 680 | elif c.cmd == 'has-dir': # has-dir test | |
| 722 | elif c.cmd == "has-dir": # has-dir test | |
| 681 | 723 | if len(c.args) == 1: # has-dir <path> = has-dir test |
| 682 | 724 | try: |
| 683 | 725 | cache.get_dir(c.args[0]) |
| ... | ... | @@ -686,22 +728,22 @@ def check_command(c, cache): |
| 686 | 728 | cerr = str(err) |
| 687 | 729 | ret = False |
| 688 | 730 | else: |
| 689 | raise InvalidCheck('Invalid number of {} arguments'.format(c.cmd)) | |
| 731 | raise InvalidCheck("Invalid number of {} arguments".format(c.cmd)) | |
| 690 | 732 | |
| 691 | elif c.cmd == 'valid-html': | |
| 692 | raise InvalidCheck('Unimplemented valid-html') | |
| 733 | elif c.cmd == "valid-html": | |
| 734 | raise InvalidCheck("Unimplemented valid-html") | |
| 693 | 735 | |
| 694 | elif c.cmd == 'valid-links': | |
| 695 | raise InvalidCheck('Unimplemented valid-links') | |
| 736 | elif c.cmd == "valid-links": | |
| 737 | raise InvalidCheck("Unimplemented valid-links") | |
| 696 | 738 | |
| 697 | 739 | else: |
| 698 | raise InvalidCheck('Unrecognized {}'.format(c.cmd)) | |
| 740 | raise InvalidCheck("Unrecognized {}".format(c.cmd)) | |
| 699 | 741 | |
| 700 | 742 | if ret == c.negated: |
| 701 | 743 | raise FailedCheck(cerr) |
| 702 | 744 | |
| 703 | 745 | 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) | |
| 705 | 747 | print_err(c.lineno, c.context, str(err), message) |
| 706 | 748 | except InvalidCheck as err: |
| 707 | 749 | print_err(c.lineno, c.context, str(err)) |
| ... | ... | @@ -713,18 +755,18 @@ def check(target, commands): |
| 713 | 755 | check_command(c, cache) |
| 714 | 756 | |
| 715 | 757 | |
| 716 | if __name__ == '__main__': | |
| 758 | if __name__ == "__main__": | |
| 717 | 759 | 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])) | |
| 719 | 761 | raise SystemExit(1) |
| 720 | 762 | |
| 721 | 763 | 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": | |
| 723 | 765 | bless = True |
| 724 | 766 | else: |
| 725 | 767 | # We only support `--bless` at the end of the arguments. |
| 726 | 768 | # This assert is to prevent silent failures. |
| 727 | assert '--bless' not in sys.argv | |
| 769 | assert "--bless" not in sys.argv | |
| 728 | 770 | bless = False |
| 729 | 771 | check(sys.argv[1], get_commands(rust_test_path)) |
| 730 | 772 | if ERR_COUNT: |
src/etc/lldb_batchmode.py+48-21| ... | ... | @@ -45,7 +45,7 @@ def normalize_whitespace(s): |
| 45 | 45 | |
| 46 | 46 | def breakpoint_callback(frame, bp_loc, dict): |
| 47 | 47 | """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""" | |
| 49 | 49 | |
| 50 | 50 | # HACK(eddyb) print a newline to avoid continuing an unfinished line. |
| 51 | 51 | print("") |
| ... | ... | @@ -79,7 +79,7 @@ def execute_command(command_interpreter, command): |
| 79 | 79 | |
| 80 | 80 | if res.Succeeded(): |
| 81 | 81 | if res.HasResult(): |
| 82 | print(normalize_whitespace(res.GetOutput() or ''), end='\n') | |
| 82 | print(normalize_whitespace(res.GetOutput() or ""), end="\n") | |
| 83 | 83 | |
| 84 | 84 | # If the command introduced any breakpoints, make sure to register |
| 85 | 85 | # them with the breakpoint |
| ... | ... | @@ -89,20 +89,32 @@ def execute_command(command_interpreter, command): |
| 89 | 89 | breakpoint_id = new_breakpoints.pop() |
| 90 | 90 | |
| 91 | 91 | 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 | ) | |
| 94 | 96 | 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 | ) | |
| 98 | 104 | command_interpreter.HandleCommand(callback_command, res) |
| 99 | 105 | 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 | ) | |
| 102 | 110 | registered_breakpoints.add(breakpoint_id) |
| 103 | 111 | 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 | ) | |
| 106 | 118 | else: |
| 107 | 119 | print(res.GetError()) |
| 108 | 120 | |
| ... | ... | @@ -117,14 +129,16 @@ def start_breakpoint_listener(target): |
| 117 | 129 | try: |
| 118 | 130 | while True: |
| 119 | 131 | 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 | ): | |
| 123 | 137 | global new_breakpoints |
| 124 | 138 | breakpoint = lldb.SBBreakpoint.GetBreakpointFromEvent(event) |
| 125 | 139 | print_debug("breakpoint added, id = " + str(breakpoint.id)) |
| 126 | 140 | new_breakpoints.append(breakpoint.id) |
| 127 | except BaseException: # explicitly catch ctrl+c/sysexit | |
| 141 | except BaseException: # explicitly catch ctrl+c/sysexit | |
| 128 | 142 | print_debug("breakpoint listener shutting down") |
| 129 | 143 | |
| 130 | 144 | # Start the listener and let it run as a daemon |
| ... | ... | @@ -133,7 +147,9 @@ def start_breakpoint_listener(target): |
| 133 | 147 | listener_thread.start() |
| 134 | 148 | |
| 135 | 149 | # Register the listener with the target |
| 136 | target.GetBroadcaster().AddListener(listener, lldb.SBTarget.eBroadcastBitBreakpointChanged) | |
| 150 | target.GetBroadcaster().AddListener( | |
| 151 | listener, lldb.SBTarget.eBroadcastBitBreakpointChanged | |
| 152 | ) | |
| 137 | 153 | |
| 138 | 154 | |
| 139 | 155 | def start_watchdog(): |
| ... | ... | @@ -159,6 +175,7 @@ def start_watchdog(): |
| 159 | 175 | watchdog_thread.daemon = True |
| 160 | 176 | watchdog_thread.start() |
| 161 | 177 | |
| 178 | ||
| 162 | 179 | #################################################################################################### |
| 163 | 180 | # ~main |
| 164 | 181 | #################################################################################################### |
| ... | ... | @@ -193,8 +210,14 @@ target_error = lldb.SBError() |
| 193 | 210 | target = debugger.CreateTarget(target_path, None, None, True, target_error) |
| 194 | 211 | |
| 195 | 212 | if 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 | ) | |
| 198 | 221 | sys.exit(1) |
| 199 | 222 | |
| 200 | 223 | |
| ... | ... | @@ -204,15 +227,19 @@ start_breakpoint_listener(target) |
| 204 | 227 | command_interpreter = debugger.GetCommandInterpreter() |
| 205 | 228 | |
| 206 | 229 | try: |
| 207 | script_file = open(script_path, 'r') | |
| 230 | script_file = open(script_path, "r") | |
| 208 | 231 | |
| 209 | 232 | for line in script_file: |
| 210 | 233 | 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 | ): | |
| 212 | 239 | # Before starting to run the program, let the thread sleep a bit, so all |
| 213 | 240 | # breakpoint added events can be processed |
| 214 | 241 | time.sleep(0.5) |
| 215 | if command != '': | |
| 242 | if command != "": | |
| 216 | 243 | execute_command(command_interpreter, command) |
| 217 | 244 | |
| 218 | 245 | except IOError as e: |
src/etc/lldb_providers.py+131-55| ... | ... | @@ -1,7 +1,12 @@ |
| 1 | 1 | import sys |
| 2 | 2 | |
| 3 | from lldb import SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \ | |
| 4 | eBasicTypeUnsignedChar | |
| 3 | from lldb import ( | |
| 4 | SBData, | |
| 5 | SBError, | |
| 6 | eBasicTypeLong, | |
| 7 | eBasicTypeUnsignedLong, | |
| 8 | eBasicTypeUnsignedChar, | |
| 9 | ) | |
| 5 | 10 | |
| 6 | 11 | # from lldb.formatters import Logger |
| 7 | 12 | |
| ... | ... | @@ -50,13 +55,17 @@ class ValueBuilder: |
| 50 | 55 | def from_int(self, name, value): |
| 51 | 56 | # type: (str, int) -> SBValue |
| 52 | 57 | 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 | ) | |
| 54 | 61 | return self.valobj.CreateValueFromData(name, data, type) |
| 55 | 62 | |
| 56 | 63 | def from_uint(self, name, value): |
| 57 | 64 | # type: (str, int) -> SBValue |
| 58 | 65 | 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 | ) | |
| 60 | 69 | return self.valobj.CreateValueFromData(name, data, type) |
| 61 | 70 | |
| 62 | 71 | |
| ... | ... | @@ -127,13 +136,17 @@ class EmptySyntheticProvider: |
| 127 | 136 | |
| 128 | 137 | def SizeSummaryProvider(valobj, dict): |
| 129 | 138 | # type: (SBValue, dict) -> str |
| 130 | return 'size=' + str(valobj.GetNumChildren()) | |
| 139 | return "size=" + str(valobj.GetNumChildren()) | |
| 131 | 140 | |
| 132 | 141 | |
| 133 | 142 | def vec_to_string(vec): |
| 134 | 143 | length = vec.GetNumChildren() |
| 135 | 144 | 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 | ) | |
| 137 | 150 | |
| 138 | 151 | |
| 139 | 152 | def StdStringSummaryProvider(valobj, dict): |
| ... | ... | @@ -172,7 +185,7 @@ def StdStrSummaryProvider(valobj, dict): |
| 172 | 185 | error = SBError() |
| 173 | 186 | process = data_ptr.GetProcess() |
| 174 | 187 | 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 | |
| 176 | 189 | return '"%s"' % data |
| 177 | 190 | |
| 178 | 191 | |
| ... | ... | @@ -199,9 +212,9 @@ def StdPathSummaryProvider(valobj, dict): |
| 199 | 212 | data = process.ReadMemory(start, length, error) |
| 200 | 213 | if PY3: |
| 201 | 214 | try: |
| 202 | data = data.decode(encoding='UTF-8') | |
| 215 | data = data.decode(encoding="UTF-8") | |
| 203 | 216 | except UnicodeDecodeError: |
| 204 | return '%r' % data | |
| 217 | return "%r" % data | |
| 205 | 218 | return '"%s"' % data |
| 206 | 219 | |
| 207 | 220 | |
| ... | ... | @@ -250,8 +263,10 @@ class StructSyntheticProvider: |
| 250 | 263 | # type: () -> bool |
| 251 | 264 | return True |
| 252 | 265 | |
| 266 | ||
| 253 | 267 | class ClangEncodedEnumProvider: |
| 254 | 268 | """Pretty-printer for 'clang-encoded' enums support implemented in LLDB""" |
| 269 | ||
| 255 | 270 | DISCRIMINANT_MEMBER_NAME = "$discr$" |
| 256 | 271 | VALUE_MEMBER_NAME = "value" |
| 257 | 272 | |
| ... | ... | @@ -260,7 +275,7 @@ class ClangEncodedEnumProvider: |
| 260 | 275 | self.update() |
| 261 | 276 | |
| 262 | 277 | def has_children(self): |
| 263 | return True | |
| 278 | return True | |
| 264 | 279 | |
| 265 | 280 | def num_children(self): |
| 266 | 281 | if self.is_default: |
| ... | ... | @@ -276,25 +291,32 @@ class ClangEncodedEnumProvider: |
| 276 | 291 | |
| 277 | 292 | def get_child_at_index(self, index): |
| 278 | 293 | if index == 0: |
| 279 | return self.variant.GetChildMemberWithName(ClangEncodedEnumProvider.VALUE_MEMBER_NAME) | |
| 294 | return self.variant.GetChildMemberWithName( | |
| 295 | ClangEncodedEnumProvider.VALUE_MEMBER_NAME | |
| 296 | ) | |
| 280 | 297 | if index == 1: |
| 281 | 298 | return self.variant.GetChildMemberWithName( |
| 282 | ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME) | |
| 283 | ||
| 299 | ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME | |
| 300 | ) | |
| 284 | 301 | |
| 285 | 302 | def update(self): |
| 286 | 303 | all_variants = self.valobj.GetChildAtIndex(0) |
| 287 | 304 | index = self._getCurrentVariantIndex(all_variants) |
| 288 | 305 | 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 | ) | |
| 291 | 312 | |
| 292 | 313 | def _getCurrentVariantIndex(self, all_variants): |
| 293 | 314 | default_index = 0 |
| 294 | 315 | for i in range(all_variants.GetNumChildren()): |
| 295 | 316 | variant = all_variants.GetChildAtIndex(i) |
| 296 | 317 | discr = variant.GetChildMemberWithName( |
| 297 | ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME) | |
| 318 | ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME | |
| 319 | ) | |
| 298 | 320 | if discr.IsValid(): |
| 299 | 321 | discr_unsigned_value = discr.GetValueAsUnsigned() |
| 300 | 322 | if variant.GetName() == f"$variant${discr_unsigned_value}": |
| ... | ... | @@ -303,6 +325,7 @@ class ClangEncodedEnumProvider: |
| 303 | 325 | default_index = i |
| 304 | 326 | return default_index |
| 305 | 327 | |
| 328 | ||
| 306 | 329 | class TupleSyntheticProvider: |
| 307 | 330 | """Pretty-printer for tuples and tuple enum variants""" |
| 308 | 331 | |
| ... | ... | @@ -336,7 +359,9 @@ class TupleSyntheticProvider: |
| 336 | 359 | else: |
| 337 | 360 | field = self.type.GetFieldAtIndex(index) |
| 338 | 361 | 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 | ) | |
| 340 | 365 | |
| 341 | 366 | def update(self): |
| 342 | 367 | # type: () -> None |
| ... | ... | @@ -373,7 +398,7 @@ class StdVecSyntheticProvider: |
| 373 | 398 | |
| 374 | 399 | def get_child_index(self, name): |
| 375 | 400 | # type: (str) -> int |
| 376 | index = name.lstrip('[').rstrip(']') | |
| 401 | index = name.lstrip("[").rstrip("]") | |
| 377 | 402 | if index.isdigit(): |
| 378 | 403 | return int(index) |
| 379 | 404 | else: |
| ... | ... | @@ -383,15 +408,21 @@ class StdVecSyntheticProvider: |
| 383 | 408 | # type: (int) -> SBValue |
| 384 | 409 | start = self.data_ptr.GetValueAsUnsigned() |
| 385 | 410 | 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 | ) | |
| 387 | 414 | return element |
| 388 | 415 | |
| 389 | 416 | def update(self): |
| 390 | 417 | # type: () -> None |
| 391 | 418 | 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 | ) | |
| 393 | 422 | |
| 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 | ) | |
| 395 | 426 | |
| 396 | 427 | self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) |
| 397 | 428 | self.element_type_size = self.element_type.GetByteSize() |
| ... | ... | @@ -412,7 +443,7 @@ class StdSliceSyntheticProvider: |
| 412 | 443 | |
| 413 | 444 | def get_child_index(self, name): |
| 414 | 445 | # type: (str) -> int |
| 415 | index = name.lstrip('[').rstrip(']') | |
| 446 | index = name.lstrip("[").rstrip("]") | |
| 416 | 447 | if index.isdigit(): |
| 417 | 448 | return int(index) |
| 418 | 449 | else: |
| ... | ... | @@ -422,7 +453,9 @@ class StdSliceSyntheticProvider: |
| 422 | 453 | # type: (int) -> SBValue |
| 423 | 454 | start = self.data_ptr.GetValueAsUnsigned() |
| 424 | 455 | 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 | ) | |
| 426 | 459 | return element |
| 427 | 460 | |
| 428 | 461 | def update(self): |
| ... | ... | @@ -457,7 +490,7 @@ class StdVecDequeSyntheticProvider: |
| 457 | 490 | |
| 458 | 491 | def get_child_index(self, name): |
| 459 | 492 | # type: (str) -> int |
| 460 | index = name.lstrip('[').rstrip(']') | |
| 493 | index = name.lstrip("[").rstrip("]") | |
| 461 | 494 | if index.isdigit() and int(index) < self.size: |
| 462 | 495 | return int(index) |
| 463 | 496 | else: |
| ... | ... | @@ -467,20 +500,26 @@ class StdVecDequeSyntheticProvider: |
| 467 | 500 | # type: (int) -> SBValue |
| 468 | 501 | start = self.data_ptr.GetValueAsUnsigned() |
| 469 | 502 | 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 | ) | |
| 471 | 506 | return element |
| 472 | 507 | |
| 473 | 508 | def update(self): |
| 474 | 509 | # type: () -> None |
| 475 | 510 | self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned() |
| 476 | 511 | 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 | ) | |
| 478 | 515 | cap = self.buf.GetChildMemberWithName("cap") |
| 479 | 516 | if cap.GetType().num_fields == 1: |
| 480 | 517 | cap = cap.GetChildAtIndex(0) |
| 481 | 518 | self.cap = cap.GetValueAsUnsigned() |
| 482 | 519 | |
| 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 | ) | |
| 484 | 523 | |
| 485 | 524 | self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) |
| 486 | 525 | self.element_type_size = self.element_type.GetByteSize() |
| ... | ... | @@ -510,7 +549,7 @@ class StdOldHashMapSyntheticProvider: |
| 510 | 549 | |
| 511 | 550 | def get_child_index(self, name): |
| 512 | 551 | # type: (str) -> int |
| 513 | index = name.lstrip('[').rstrip(']') | |
| 552 | index = name.lstrip("[").rstrip("]") | |
| 514 | 553 | if index.isdigit(): |
| 515 | 554 | return int(index) |
| 516 | 555 | else: |
| ... | ... | @@ -525,8 +564,14 @@ class StdOldHashMapSyntheticProvider: |
| 525 | 564 | hashes = self.hash_uint_size * self.capacity |
| 526 | 565 | align = self.pair_type_size |
| 527 | 566 | # 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 | |
| 530 | 575 | # len_rounded_up = ((hashes + align - 1) & ~(align - 1)) - hashes |
| 531 | 576 | |
| 532 | 577 | pairs_offset = hashes + len_rounded_up |
| ... | ... | @@ -535,12 +580,16 @@ class StdOldHashMapSyntheticProvider: |
| 535 | 580 | table_index = self.valid_indices[index] |
| 536 | 581 | idx = table_index & self.capacity_mask |
| 537 | 582 | 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 | ) | |
| 539 | 586 | if self.show_values: |
| 540 | 587 | return element |
| 541 | 588 | else: |
| 542 | 589 | 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 | ) | |
| 544 | 593 | |
| 545 | 594 | def update(self): |
| 546 | 595 | # type: () -> None |
| ... | ... | @@ -551,10 +600,12 @@ class StdOldHashMapSyntheticProvider: |
| 551 | 600 | self.hashes = self.table.GetChildMemberWithName("hashes") |
| 552 | 601 | self.hash_uint_type = self.hashes.GetType() |
| 553 | 602 | self.hash_uint_size = self.hashes.GetType().GetByteSize() |
| 554 | self.modulo = 2 ** self.hash_uint_size | |
| 603 | self.modulo = 2**self.hash_uint_size | |
| 555 | 604 | self.data_ptr = self.hashes.GetChildAtIndex(0).GetChildAtIndex(0) |
| 556 | 605 | |
| 557 | self.capacity_mask = self.table.GetChildMemberWithName("capacity_mask").GetValueAsUnsigned() | |
| 606 | self.capacity_mask = self.table.GetChildMemberWithName( | |
| 607 | "capacity_mask" | |
| 608 | ).GetValueAsUnsigned() | |
| 558 | 609 | self.capacity = (self.capacity_mask + 1) % self.modulo |
| 559 | 610 | |
| 560 | 611 | marker = self.table.GetChildMemberWithName("marker").GetType() # type: SBType |
| ... | ... | @@ -564,8 +615,9 @@ class StdOldHashMapSyntheticProvider: |
| 564 | 615 | self.valid_indices = [] |
| 565 | 616 | for idx in range(self.capacity): |
| 566 | 617 | 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 | ) | |
| 569 | 621 | hash_ptr = hash_uint.GetChildAtIndex(0).GetChildAtIndex(0) |
| 570 | 622 | if hash_ptr.GetValueAsUnsigned() != 0: |
| 571 | 623 | self.valid_indices.append(idx) |
| ... | ... | @@ -592,7 +644,7 @@ class StdHashMapSyntheticProvider: |
| 592 | 644 | |
| 593 | 645 | def get_child_index(self, name): |
| 594 | 646 | # type: (str) -> int |
| 595 | index = name.lstrip('[').rstrip(']') | |
| 647 | index = name.lstrip("[").rstrip("]") | |
| 596 | 648 | if index.isdigit(): |
| 597 | 649 | return int(index) |
| 598 | 650 | else: |
| ... | ... | @@ -605,19 +657,25 @@ class StdHashMapSyntheticProvider: |
| 605 | 657 | if self.new_layout: |
| 606 | 658 | idx = -(idx + 1) |
| 607 | 659 | 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 | ) | |
| 609 | 663 | if self.show_values: |
| 610 | 664 | return element |
| 611 | 665 | else: |
| 612 | 666 | 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 | ) | |
| 614 | 670 | |
| 615 | 671 | def update(self): |
| 616 | 672 | # type: () -> None |
| 617 | 673 | table = self.table() |
| 618 | 674 | inner_table = table.GetChildMemberWithName("table") |
| 619 | 675 | |
| 620 | capacity = inner_table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 | |
| 676 | capacity = ( | |
| 677 | inner_table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 | |
| 678 | ) | |
| 621 | 679 | ctrl = inner_table.GetChildMemberWithName("ctrl").GetChildAtIndex(0) |
| 622 | 680 | |
| 623 | 681 | self.size = inner_table.GetChildMemberWithName("items").GetValueAsUnsigned() |
| ... | ... | @@ -630,16 +688,21 @@ class StdHashMapSyntheticProvider: |
| 630 | 688 | if self.new_layout: |
| 631 | 689 | self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType()) |
| 632 | 690 | else: |
| 633 | self.data_ptr = inner_table.GetChildMemberWithName("data").GetChildAtIndex(0) | |
| 691 | self.data_ptr = inner_table.GetChildMemberWithName("data").GetChildAtIndex( | |
| 692 | 0 | |
| 693 | ) | |
| 634 | 694 | |
| 635 | 695 | 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 | ) | |
| 637 | 699 | |
| 638 | 700 | self.valid_indices = [] |
| 639 | 701 | for idx in range(capacity): |
| 640 | 702 | 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() | |
| 643 | 706 | is_present = value & 128 == 0 |
| 644 | 707 | if is_present: |
| 645 | 708 | self.valid_indices.append(idx) |
| ... | ... | @@ -691,10 +754,16 @@ class StdRcSyntheticProvider: |
| 691 | 754 | |
| 692 | 755 | self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value") |
| 693 | 756 | |
| 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 | ) | |
| 698 | 767 | |
| 699 | 768 | self.value_builder = ValueBuilder(valobj) |
| 700 | 769 | |
| ... | ... | @@ -772,7 +841,9 @@ class StdCellSyntheticProvider: |
| 772 | 841 | def StdRefSummaryProvider(valobj, dict): |
| 773 | 842 | # type: (SBValue, dict) -> str |
| 774 | 843 | 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 | ) | |
| 776 | 847 | |
| 777 | 848 | |
| 778 | 849 | class StdRefSyntheticProvider: |
| ... | ... | @@ -785,11 +856,16 @@ class StdRefSyntheticProvider: |
| 785 | 856 | borrow = valobj.GetChildMemberWithName("borrow") |
| 786 | 857 | value = valobj.GetChildMemberWithName("value") |
| 787 | 858 | if is_cell: |
| 788 | self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName("value") | |
| 859 | self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName( | |
| 860 | "value" | |
| 861 | ) | |
| 789 | 862 | self.value = value.GetChildMemberWithName("value") |
| 790 | 863 | 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 | ) | |
| 793 | 869 | self.value = value.Dereference() |
| 794 | 870 | |
| 795 | 871 | self.value_builder = ValueBuilder(valobj) |
| ... | ... | @@ -832,7 +908,7 @@ def StdNonZeroNumberSummaryProvider(valobj, _dict): |
| 832 | 908 | |
| 833 | 909 | # FIXME: Avoid printing as character literal, |
| 834 | 910 | # 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()) | |
| 837 | 913 | 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<.+>$") |
| 54 | 54 | STD_REF_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefCell<.+>$") |
| 55 | 55 | STD_NONZERO_NUMBER_REGEX = re.compile(r"^(core::([a-z_]+::)+)NonZero<.+>$") |
| 56 | 56 | STD_PATHBUF_REGEX = re.compile(r"^(std::([a-z_]+::)+)PathBuf$") |
| 57 | STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$") | |
| 57 | STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$") | |
| 58 | 58 | |
| 59 | 59 | TUPLE_ITEM_REGEX = re.compile(r"__\d+$") |
| 60 | 60 | |
| ... | ... | @@ -84,6 +84,7 @@ STD_TYPE_TO_REGEX = { |
| 84 | 84 | RustType.STD_PATH: STD_PATH_REGEX, |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | ||
| 87 | 88 | def is_tuple_fields(fields): |
| 88 | 89 | # type: (list) -> bool |
| 89 | 90 | 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; |
| 32 | 32 | use std::ops::{ControlFlow, Range}; |
| 33 | 33 | use std::path::PathBuf; |
| 34 | 34 | use std::str::{self, CharIndices}; |
| 35 | use std::sync::atomic::AtomicUsize; | |
| 36 | use std::sync::{Arc, Weak}; | |
| 35 | 37 | |
| 36 | 38 | use pulldown_cmark::{ |
| 37 | 39 | BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html, |
| ... | ... | @@ -1301,8 +1303,20 @@ impl LangString { |
| 1301 | 1303 | } |
| 1302 | 1304 | } |
| 1303 | 1305 | |
| 1304 | impl Markdown<'_> { | |
| 1306 | impl<'a> Markdown<'a> { | |
| 1305 | 1307 | 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>>> { | |
| 1306 | 1320 | let Markdown { |
| 1307 | 1321 | content: md, |
| 1308 | 1322 | links, |
| ... | ... | @@ -1313,32 +1327,72 @@ impl Markdown<'_> { |
| 1313 | 1327 | heading_offset, |
| 1314 | 1328 | } = self; |
| 1315 | 1329 | |
| 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<'_>| { | |
| 1321 | 1331 | links |
| 1322 | 1332 | .iter() |
| 1323 | 1333 | .find(|link| *link.original_text == *broken_link.reference) |
| 1324 | 1334 | .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) |
| 1325 | 1335 | }; |
| 1326 | 1336 | |
| 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)); | |
| 1328 | 1338 | let p = p.into_offset_iter(); |
| 1329 | 1339 | |
| 1330 | let mut s = String::with_capacity(md.len() * 3 / 2); | |
| 1331 | ||
| 1332 | 1340 | ids.handle_footnotes(|ids, existing_footnotes| { |
| 1333 | 1341 | let p = HeadingLinks::new(p, None, ids, heading_offset); |
| 1334 | 1342 | let p = footnotes::Footnotes::new(p, existing_footnotes); |
| 1335 | 1343 | let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); |
| 1336 | 1344 | 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 | } | |
| 1340 | 1348 | |
| 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)) } | |
| 1342 | 1396 | } |
| 1343 | 1397 | } |
| 1344 | 1398 | |
| ... | ... | @@ -1882,7 +1936,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust |
| 1882 | 1936 | #[derive(Clone, Default, Debug)] |
| 1883 | 1937 | pub struct IdMap { |
| 1884 | 1938 | map: FxHashMap<String, usize>, |
| 1885 | existing_footnotes: usize, | |
| 1939 | existing_footnotes: Arc<AtomicUsize>, | |
| 1886 | 1940 | } |
| 1887 | 1941 | |
| 1888 | 1942 | fn is_default_id(id: &str) -> bool { |
| ... | ... | @@ -1942,7 +1996,7 @@ fn is_default_id(id: &str) -> bool { |
| 1942 | 1996 | |
| 1943 | 1997 | impl IdMap { |
| 1944 | 1998 | pub fn new() -> Self { |
| 1945 | IdMap { map: FxHashMap::default(), existing_footnotes: 0 } | |
| 1999 | IdMap { map: FxHashMap::default(), existing_footnotes: Arc::new(AtomicUsize::new(0)) } | |
| 1946 | 2000 | } |
| 1947 | 2001 | |
| 1948 | 2002 | pub(crate) fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String { |
| ... | ... | @@ -1970,15 +2024,17 @@ impl IdMap { |
| 1970 | 2024 | |
| 1971 | 2025 | /// Method to handle `existing_footnotes` increment automatically (to prevent forgetting |
| 1972 | 2026 | /// 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); | |
| 1975 | 2032 | |
| 1976 | closure(self, &mut existing_footnotes); | |
| 1977 | self.existing_footnotes = existing_footnotes; | |
| 2033 | closure(self, existing_footnotes) | |
| 1978 | 2034 | } |
| 1979 | 2035 | |
| 1980 | 2036 | pub(crate) fn clear(&mut self) { |
| 1981 | 2037 | self.map.clear(); |
| 1982 | self.existing_footnotes = 0; | |
| 2038 | self.existing_footnotes = Arc::new(AtomicUsize::new(0)); | |
| 1983 | 2039 | } |
| 1984 | 2040 | } |
src/librustdoc/html/markdown/footnotes.rs+16-9| ... | ... | @@ -1,5 +1,8 @@ |
| 1 | 1 | //! Markdown footnote handling. |
| 2 | ||
| 2 | 3 | use std::fmt::Write as _; |
| 4 | use std::sync::atomic::{AtomicUsize, Ordering}; | |
| 5 | use std::sync::{Arc, Weak}; | |
| 3 | 6 | |
| 4 | 7 | use pulldown_cmark::{CowStr, Event, Tag, TagEnd, html}; |
| 5 | 8 | use rustc_data_structures::fx::FxIndexMap; |
| ... | ... | @@ -8,10 +11,11 @@ use super::SpannedEvent; |
| 8 | 11 | |
| 9 | 12 | /// Moves all footnote definitions to the end and add back links to the |
| 10 | 13 | /// references. |
| 11 | pub(super) struct Footnotes<'a, 'b, I> { | |
| 14 | pub(super) struct Footnotes<'a, I> { | |
| 12 | 15 | inner: I, |
| 13 | 16 | footnotes: FxIndexMap<String, FootnoteDef<'a>>, |
| 14 | existing_footnotes: &'b mut usize, | |
| 17 | existing_footnotes: Arc<AtomicUsize>, | |
| 18 | start_id: usize, | |
| 15 | 19 | } |
| 16 | 20 | |
| 17 | 21 | /// The definition of a single footnote. |
| ... | ... | @@ -21,13 +25,16 @@ struct FootnoteDef<'a> { |
| 21 | 25 | id: usize, |
| 22 | 26 | } |
| 23 | 27 | |
| 24 | impl<'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 } | |
| 28 | impl<'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 } | |
| 27 | 34 | } |
| 28 | 35 | |
| 29 | 36 | 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; | |
| 31 | 38 | let key = key.to_owned(); |
| 32 | 39 | let FootnoteDef { content, id } = |
| 33 | 40 | 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> { |
| 44 | 51 | id, |
| 45 | 52 | // Although the ID count is for the whole page, the footnote reference |
| 46 | 53 | // are local to the item so we make this ID "local" when displayed. |
| 47 | id - *self.existing_footnotes | |
| 54 | id - self.start_id | |
| 48 | 55 | ); |
| 49 | 56 | Event::Html(reference.into()) |
| 50 | 57 | } |
| ... | ... | @@ -64,7 +71,7 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> { |
| 64 | 71 | } |
| 65 | 72 | } |
| 66 | 73 | |
| 67 | impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I> { | |
| 74 | impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> { | |
| 68 | 75 | type Item = SpannedEvent<'a>; |
| 69 | 76 | |
| 70 | 77 | fn next(&mut self) -> Option<Self::Item> { |
| ... | ... | @@ -87,7 +94,7 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I> |
| 87 | 94 | // After all the markdown is emmited, emit an <hr> then all the footnotes |
| 88 | 95 | // in a list. |
| 89 | 96 | 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); | |
| 91 | 98 | let defs_html = render_footnotes_defs(defs); |
| 92 | 99 | return Some((Event::Html(defs_html.into()), 0..0)); |
| 93 | 100 | } else { |
src/librustdoc/html/render/mod.rs+28-17| ... | ... | @@ -1904,7 +1904,6 @@ fn render_impl( |
| 1904 | 1904 | } |
| 1905 | 1905 | } |
| 1906 | 1906 | |
| 1907 | let trait_is_none = trait_.is_none(); | |
| 1908 | 1907 | // If we've implemented a trait, then also emit documentation for all |
| 1909 | 1908 | // default items which weren't overridden in the implementation block. |
| 1910 | 1909 | // We don't emit documentation for default items if they appear in the |
| ... | ... | @@ -1936,6 +1935,23 @@ fn render_impl( |
| 1936 | 1935 | if rendering_params.toggle_open_by_default { " open" } else { "" } |
| 1937 | 1936 | ); |
| 1938 | 1937 | } |
| 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)); | |
| 1939 | 1955 | render_impl_summary( |
| 1940 | 1956 | w, |
| 1941 | 1957 | cx, |
| ... | ... | @@ -1944,33 +1960,23 @@ fn render_impl( |
| 1944 | 1960 | rendering_params.show_def_docs, |
| 1945 | 1961 | use_absolute, |
| 1946 | 1962 | aliases, |
| 1963 | &before_dox, | |
| 1947 | 1964 | ); |
| 1948 | 1965 | if toggled { |
| 1949 | 1966 | w.write_str("</summary>"); |
| 1950 | 1967 | } |
| 1951 | 1968 | |
| 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() { | |
| 1954 | 1971 | w.write_str( |
| 1955 | 1972 | "<div class=\"item-info\">\ |
| 1956 | 1973 | <div class=\"stab empty-impl\">This impl block contains no items.</div>\ |
| 1957 | 1974 | </div>", |
| 1958 | 1975 | ); |
| 1959 | 1976 | } |
| 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 | } | |
| 1974 | 1980 | } |
| 1975 | 1981 | if !default_impl_items.is_empty() || !impl_items.is_empty() { |
| 1976 | 1982 | w.write_str("<div class=\"impl-items\">"); |
| ... | ... | @@ -2031,6 +2037,7 @@ pub(crate) fn render_impl_summary( |
| 2031 | 2037 | // This argument is used to reference same type with different paths to avoid duplication |
| 2032 | 2038 | // in documentation pages for trait with automatic implementations like "Send" and "Sync". |
| 2033 | 2039 | aliases: &[String], |
| 2040 | doc: &Option<String>, | |
| 2034 | 2041 | ) { |
| 2035 | 2042 | let inner_impl = i.inner_impl(); |
| 2036 | 2043 | 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( |
| 2082 | 2089 | ); |
| 2083 | 2090 | } |
| 2084 | 2091 | |
| 2092 | if let Some(doc) = doc { | |
| 2093 | write!(w, "<div class=\"docblock\">{doc}</div>"); | |
| 2094 | } | |
| 2095 | ||
| 2085 | 2096 | w.write_str("</section>"); |
| 2086 | 2097 | } |
| 2087 | 2098 |
src/librustdoc/html/static/css/rustdoc.css+33| ... | ... | @@ -2210,6 +2210,39 @@ details.toggle[open] > summary::after { |
| 2210 | 2210 | 	content: "Collapse"; |
| 2211 | 2211 | } |
| 2212 | 2212 | |
| 2213 | details.toggle:not([open]) > summary .docblock { | |
| 2214 | 	max-height: calc(1.5em + 0.75em); | |
| 2215 | 	overflow-y: hidden; | |
| 2216 | } | |
| 2217 | details.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 | } | |
| 2225 | details.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 | } | |
| 2237 | details.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 | ||
| 2242 | details.toggle > summary .docblock { | |
| 2243 | 	margin-top: 0.75em; | |
| 2244 | } | |
| 2245 | ||
| 2213 | 2246 | /* This is needed in docblocks to have the "â–¶" element to be on the same line. */ |
| 2214 | 2247 | .docblock summary > * { |
| 2215 | 2248 | 	display: inline-block; |
src/tools/publish_toolstate.py+125-101| ... | ... | @@ -14,6 +14,7 @@ import json |
| 14 | 14 | import datetime |
| 15 | 15 | import collections |
| 16 | 16 | import textwrap |
| 17 | ||
| 17 | 18 | try: |
| 18 | 19 | import urllib2 |
| 19 | 20 | from urllib2 import HTTPError |
| ... | ... | @@ -21,7 +22,7 @@ except ImportError: |
| 21 | 22 | import urllib.request as urllib2 |
| 22 | 23 | from urllib.error import HTTPError |
| 23 | 24 | try: |
| 24 | import typing # noqa: F401 FIXME: py2 | |
| 25 | import typing # noqa: F401 FIXME: py2 | |
| 25 | 26 | except ImportError: |
| 26 | 27 | pass |
| 27 | 28 | |
| ... | ... | @@ -29,40 +30,41 @@ except ImportError: |
| 29 | 30 | # These should be collaborators of the rust-lang/rust repository (with at least |
| 30 | 31 | # read privileges on it). CI will fail otherwise. |
| 31 | 32 | MAINTAINERS = { |
| 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"}, | |
| 39 | 40 | } |
| 40 | 41 | |
| 41 | 42 | LABELS = { |
| 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"], | |
| 49 | 50 | } |
| 50 | 51 | |
| 51 | 52 | REPOS = { |
| 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", | |
| 59 | 60 | } |
| 60 | 61 | |
| 62 | ||
| 61 | 63 | def load_json_from_response(resp): |
| 62 | 64 | # type: (typing.Any) -> typing.Any |
| 63 | 65 | content = resp.read() |
| 64 | 66 | if isinstance(content, bytes): |
| 65 | content_str = content.decode('utf-8') | |
| 67 | content_str = content.decode("utf-8") | |
| 66 | 68 | else: |
| 67 | 69 | print("Refusing to decode " + str(type(content)) + " to str") |
| 68 | 70 | return json.loads(content_str) |
| ... | ... | @@ -70,11 +72,10 @@ def load_json_from_response(resp): |
| 70 | 72 | |
| 71 | 73 | def read_current_status(current_commit, path): |
| 72 | 74 | # 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: | |
| 76 | 77 | for line in f: |
| 77 | (commit, status) = line.split('\t', 1) | |
| 78 | (commit, status) = line.split("\t", 1) | |
| 78 | 79 | if commit == current_commit: |
| 79 | 80 | return json.loads(status) |
| 80 | 81 | return {} |
| ... | ... | @@ -82,12 +83,12 @@ def read_current_status(current_commit, path): |
| 82 | 83 | |
| 83 | 84 | def gh_url(): |
| 84 | 85 | # type: () -> str |
| 85 | return os.environ['TOOLSTATE_ISSUES_API_URL'] | |
| 86 | return os.environ["TOOLSTATE_ISSUES_API_URL"] | |
| 86 | 87 | |
| 87 | 88 | |
| 88 | 89 | def maybe_remove_mention(message): |
| 89 | 90 | # 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: | |
| 91 | 92 | return message.replace("@", "") |
| 92 | 93 | return message |
| 93 | 94 | |
| ... | ... | @@ -102,36 +103,45 @@ def issue( |
| 102 | 103 | github_token, |
| 103 | 104 | ): |
| 104 | 105 | # 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" | |
| 108 | 109 | 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("""\ | |
| 112 | 115 | Hello, this is your friendly neighborhood mergebot. |
| 113 | 116 | After merging PR {}, I observed that the tool {} {}. |
| 114 | 117 | A follow-up PR to the repository {} is needed to fix the fallout. |
| 115 | 118 | |
| 116 | 119 | cc @{}, do you think you would have time to do the follow-up work? |
| 117 | 120 | 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, | |
| 133 | 132 | } |
| 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 | ) | |
| 135 | 145 | response.read() |
| 136 | 146 | |
| 137 | 147 | |
| ... | ... | @@ -145,27 +155,26 @@ def update_latest( |
| 145 | 155 | github_token, |
| 146 | 156 | ): |
| 147 | 157 | # 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: | |
| 151 | 160 | latest = json.load(f, object_pairs_hook=collections.OrderedDict) |
| 152 | 161 | |
| 153 | 162 | 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"] | |
| 156 | 165 | } |
| 157 | 166 | |
| 158 | slug = 'rust-lang/rust' | |
| 159 | message = textwrap.dedent('''\ | |
| 167 | slug = "rust-lang/rust" | |
| 168 | message = textwrap.dedent("""\ | |
| 160 | 169 | 📣 Toolstate changed by {}! |
| 161 | 170 | |
| 162 | 171 | Tested on commit {}@{}. |
| 163 | 172 | Direct link to PR: <{}> |
| 164 | 173 | |
| 165 | ''').format(relevant_pr_number, slug, current_commit, relevant_pr_url) | |
| 174 | """).format(relevant_pr_number, slug, current_commit, relevant_pr_url) | |
| 166 | 175 | anything_changed = False |
| 167 | 176 | for status in latest: |
| 168 | tool = status['tool'] | |
| 177 | tool = status["tool"] | |
| 169 | 178 | changed = False |
| 170 | 179 | create_issue_for_status = None # set to the status that caused the issue |
| 171 | 180 | |
| ... | ... | @@ -173,57 +182,70 @@ def update_latest( |
| 173 | 182 | old = status[os_] |
| 174 | 183 | new = s.get(tool, old) |
| 175 | 184 | status[os_] = new |
| 176 | maintainers = ' '.join('@'+name for name in MAINTAINERS.get(tool, ())) | |
| 185 | maintainers = " ".join("@" + name for name in MAINTAINERS.get(tool, ())) | |
| 177 | 186 | # comparing the strings, but they are ordered appropriately: |
| 178 | 187 | # "test-pass" > "test-fail" > "build-fail" |
| 179 | 188 | if new > old: |
| 180 | 189 | # things got fixed or at least the status quo improved |
| 181 | 190 | 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 | ) | |
| 184 | 194 | elif new < old: |
| 185 | 195 | # tests or builds are failing and were not failing before |
| 186 | 196 | 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) | |
| 191 | 199 | # See if we need to create an issue. |
| 192 | 200 | # Create issue if things no longer build. |
| 193 | 201 | # (No issue for mere test failures to avoid spurious issues.) |
| 194 | if new == 'build-fail': | |
| 202 | if new == "build-fail": | |
| 195 | 203 | create_issue_for_status = new |
| 196 | 204 | |
| 197 | 205 | if create_issue_for_status is not None: |
| 198 | 206 | try: |
| 199 | 207 | 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, []), | |
| 202 | 214 | github_token, |
| 203 | 215 | ) |
| 204 | 216 | except HTTPError as e: |
| 205 | 217 | # network errors will simply end up not creating an issue, but that's better |
| 206 | 218 | # 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 | ) | |
| 209 | 224 | 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 | ) | |
| 211 | 230 | 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 | ) | |
| 214 | 236 | raise |
| 215 | 237 | |
| 216 | 238 | if changed: |
| 217 | status['commit'] = current_commit | |
| 218 | status['datetime'] = current_datetime | |
| 239 | status["commit"] = current_commit | |
| 240 | status["datetime"] = current_datetime | |
| 219 | 241 | anything_changed = True |
| 220 | 242 | |
| 221 | 243 | if not anything_changed: |
| 222 | return '' | |
| 244 | return "" | |
| 223 | 245 | |
| 224 | 246 | f.seek(0) |
| 225 | 247 | f.truncate(0) |
| 226 | json.dump(latest, f, indent=4, separators=(',', ': ')) | |
| 248 | json.dump(latest, f, indent=4, separators=(",", ": ")) | |
| 227 | 249 | return message |
| 228 | 250 | |
| 229 | 251 | |
| ... | ... | @@ -231,12 +253,12 @@ def update_latest( |
| 231 | 253 | # There are variables declared within that are implicitly global; it is unknown |
| 232 | 254 | # which ones precisely but at least this is true for `github_token`. |
| 233 | 255 | try: |
| 234 | if __name__ != '__main__': | |
| 256 | if __name__ != "__main__": | |
| 235 | 257 | exit(0) |
| 236 | 258 | |
| 237 | 259 | cur_commit = sys.argv[1] |
| 238 | 260 | cur_datetime = datetime.datetime.now(datetime.timezone.utc).strftime( |
| 239 | '%Y-%m-%dT%H:%M:%SZ' | |
| 261 | "%Y-%m-%dT%H:%M:%SZ" | |
| 240 | 262 | ) |
| 241 | 263 | cur_commit_msg = sys.argv[2] |
| 242 | 264 | save_message_to_path = sys.argv[3] |
| ... | ... | @@ -244,21 +266,21 @@ try: |
| 244 | 266 | |
| 245 | 267 | # assume that PR authors are also owners of the repo where the branch lives |
| 246 | 268 | relevant_pr_match = re.search( |
| 247 | r'Auto merge of #([0-9]+) - ([^:]+):[^,]+, r=(\S+)', | |
| 269 | r"Auto merge of #([0-9]+) - ([^:]+):[^,]+, r=(\S+)", | |
| 248 | 270 | cur_commit_msg, |
| 249 | 271 | ) |
| 250 | 272 | if relevant_pr_match: |
| 251 | 273 | number = relevant_pr_match.group(1) |
| 252 | 274 | 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 | |
| 255 | 277 | pr_reviewer = relevant_pr_match.group(3) |
| 256 | 278 | 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" | |
| 262 | 284 | |
| 263 | 285 | message = update_latest( |
| 264 | 286 | cur_commit, |
| ... | ... | @@ -270,28 +292,30 @@ try: |
| 270 | 292 | github_token, |
| 271 | 293 | ) |
| 272 | 294 | if not message: |
| 273 | print('<Nothing changed>') | |
| 295 | print("<Nothing changed>") | |
| 274 | 296 | sys.exit(0) |
| 275 | 297 | |
| 276 | 298 | print(message) |
| 277 | 299 | |
| 278 | 300 | if not github_token: |
| 279 | print('Dry run only, not committing anything') | |
| 301 | print("Dry run only, not committing anything") | |
| 280 | 302 | sys.exit(0) |
| 281 | 303 | |
| 282 | with open(save_message_to_path, 'w') as f: | |
| 304 | with open(save_message_to_path, "w") as f: | |
| 283 | 305 | f.write(message) |
| 284 | 306 | |
| 285 | 307 | # 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 | ) | |
| 295 | 319 | response.read() |
| 296 | 320 | except HTTPError as e: |
| 297 | 321 | 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 | |
| 3 | extend-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 @@ |
| 6 | 6 | # Note: this generation step should be run with the oldest supported python |
| 7 | 7 | # version (currently 3.9) to ensure backward compatibility |
| 8 | 8 | |
| 9 | black==24.4.2 | |
| 10 | 9 | ruff==0.4.9 |
| 11 | 10 | clang-format==18.1.7 |
src/tools/tidy/config/requirements.txt-52| ... | ... | @@ -4,30 +4,6 @@ |
| 4 | 4 | # |
| 5 | 5 | # pip-compile --generate-hashes --strip-extras src/tools/tidy/config/requirements.in |
| 6 | 6 | # |
| 7 | black==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 | |
| 31 | 7 | clang-format==18.1.7 \ |
| 32 | 8 | --hash=sha256:035204410f65d03f98cb81c9c39d6d193f9987917cc88de9d0dbd01f2aa9c302 \ |
| 33 | 9 | --hash=sha256:05c482a854287a5d21f7567186c0bd4b8dbd4a871751e655a45849185f30b931 \ |
| ... | ... | @@ -45,26 +21,6 @@ clang-format==18.1.7 \ |
| 45 | 21 | --hash=sha256:f4f77ac0f4f9a659213fedda0f2d216886c410132e6e7dd4b13f92b34e925554 \ |
| 46 | 22 | --hash=sha256:f935d34152a2e11e55120eb9182862f432bc9789ab819f680c9f6db4edebf9e3 |
| 47 | 23 | # via -r src/tools/tidy/config/requirements.in |
| 48 | click==8.1.3 \ | |
| 49 | --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \ | |
| 50 | --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48 | |
| 51 | # via black | |
| 52 | mypy-extensions==1.0.0 \ | |
| 53 | --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ | |
| 54 | --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 | |
| 55 | # via black | |
| 56 | packaging==23.1 \ | |
| 57 | --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ | |
| 58 | --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f | |
| 59 | # via black | |
| 60 | pathspec==0.11.1 \ | |
| 61 | --hash=sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687 \ | |
| 62 | --hash=sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293 | |
| 63 | # via black | |
| 64 | platformdirs==4.2.2 \ | |
| 65 | --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ | |
| 66 | --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 | |
| 67 | # via black | |
| 68 | 24 | ruff==0.4.9 \ |
| 69 | 25 | --hash=sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893 \ |
| 70 | 26 | --hash=sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff \ |
| ... | ... | @@ -84,11 +40,3 @@ ruff==0.4.9 \ |
| 84 | 40 | --hash=sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde \ |
| 85 | 41 | --hash=sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3 |
| 86 | 42 | # via -r src/tools/tidy/config/requirements.in |
| 87 | tomli==2.0.1 \ | |
| 88 | --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ | |
| 89 | --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f | |
| 90 | # via black | |
| 91 | typing-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 = [ |
| 19 | 19 | "src/tools/enzyme/", |
| 20 | 20 | "src/tools/rustc-perf/", |
| 21 | 21 | "src/gcc/", |
| 22 | "compiler/rustc_codegen_gcc", | |
| 23 | "src/tools/clippy", | |
| 24 | "src/tools/miri", | |
| 22 | 25 | # Hack: CI runs from a subdirectory under the main checkout |
| 23 | 26 | "../src/doc/nomicon/", |
| 24 | 27 | "../src/tools/cargo/", |
| ... | ... | @@ -34,6 +37,9 @@ extend-exclude = [ |
| 34 | 37 | "../src/tools/enzyme/", |
| 35 | 38 | "../src/tools/rustc-perf/", |
| 36 | 39 | "../src/gcc/", |
| 40 | "../compiler/rustc_codegen_gcc", | |
| 41 | "../src/tools/clippy", | |
| 42 | "../src/tools/miri", | |
| 37 | 43 | ] |
| 38 | 44 | |
| 39 | 45 | [lint] |
src/tools/tidy/src/ext_tool_checks.rs+16-14| ... | ... | @@ -32,9 +32,8 @@ const REL_PY_PATH: &[&str] = &["Scripts", "python3.exe"]; |
| 32 | 32 | const REL_PY_PATH: &[&str] = &["bin", "python3"]; |
| 33 | 33 | |
| 34 | 34 | const RUFF_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "ruff.toml"]; |
| 35 | const BLACK_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "black.toml"]; | |
| 36 | 35 | /// Location within build directory |
| 37 | const RUFF_CACH_PATH: &[&str] = &["cache", "ruff_cache"]; | |
| 36 | const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"]; | |
| 38 | 37 | const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"]; |
| 39 | 38 | |
| 40 | 39 | pub fn check( |
| ... | ... | @@ -96,7 +95,7 @@ fn check_impl( |
| 96 | 95 | let mut cfg_path = root_path.to_owned(); |
| 97 | 96 | cfg_path.extend(RUFF_CONFIG_PATH); |
| 98 | 97 | let mut cache_dir = outdir.to_owned(); |
| 99 | cache_dir.extend(RUFF_CACH_PATH); | |
| 98 | cache_dir.extend(RUFF_CACHE_PATH); | |
| 100 | 99 | |
| 101 | 100 | cfg_args_ruff.extend([ |
| 102 | 101 | "--config".as_ref(), |
| ... | ... | @@ -124,33 +123,36 @@ fn check_impl( |
| 124 | 123 | } |
| 125 | 124 | |
| 126 | 125 | 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(); | |
| 129 | 128 | |
| 130 | 129 | if bless { |
| 131 | 130 | eprintln!("formatting python files"); |
| 132 | 131 | } else { |
| 133 | 132 | eprintln!("checking python file formatting"); |
| 134 | cfg_args_black.push("--check".as_ref()); | |
| 133 | cfg_args_ruff.push("--check".as_ref()); | |
| 135 | 134 | } |
| 136 | 135 | |
| 137 | 136 | 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); | |
| 139 | 140 | |
| 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()]); | |
| 141 | 142 | |
| 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()); | |
| 144 | 145 | } |
| 145 | 146 | |
| 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); | |
| 148 | 150 | |
| 149 | 151 | if res.is_err() && show_diff { |
| 150 | 152 | eprintln!("\npython formatting does not match! Printing diff:"); |
| 151 | 153 | |
| 152 | 154 | 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); | |
| 154 | 156 | } |
| 155 | 157 | // Rethrow error |
| 156 | 158 | let _ = res?; |
| ... | ... | @@ -445,7 +447,7 @@ fn shellcheck_runner(args: &[&OsStr]) -> Result<(), Error> { |
| 445 | 447 | } |
| 446 | 448 | |
| 447 | 449 | 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")) } | |
| 449 | 451 | } |
| 450 | 452 | |
| 451 | 453 | /// 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( |
| 57 | 57 | // CHECK: [[IS_RUST_EXN_I8:%.*]] = zext i1 [[IS_RUST_EXN]] to i8 |
| 58 | 58 | |
| 59 | 59 | // 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]] | |
| 61 | 61 | // CHECK: store i8 [[IS_RUST_EXN_I8]], ptr [[IS_RUST_SLOT]] |
| 62 | 62 | |
| 63 | 63 | // 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 { |
| 12 | 12 | // CHECK-LABEL: @branchy( |
| 13 | 13 | // CHECK-NEXT: start: |
| 14 | 14 | // 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]] | |
| 16 | 16 | // CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]] |
| 17 | 17 | // CHECK-NEXT: ret i64 [[SWITCH_LOAD]] |
| 18 | 18 | match input % 4 { |
tests/codegen/issues/issue-122805.rs+7-7| ... | ... | @@ -17,19 +17,19 @@ |
| 17 | 17 | // CHECK-LABEL: define{{.*}}void @convert( |
| 18 | 18 | // CHECK-NOT: shufflevector |
| 19 | 19 | // OPT2: store i16 |
| 20 | // OPT2-NEXT: getelementptr inbounds i8, {{.+}} 2 | |
| 20 | // OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 2 | |
| 21 | 21 | // OPT2-NEXT: store i16 |
| 22 | // OPT2-NEXT: getelementptr inbounds i8, {{.+}} 4 | |
| 22 | // OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 4 | |
| 23 | 23 | // OPT2-NEXT: store i16 |
| 24 | // OPT2-NEXT: getelementptr inbounds i8, {{.+}} 6 | |
| 24 | // OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 6 | |
| 25 | 25 | // OPT2-NEXT: store i16 |
| 26 | // OPT2-NEXT: getelementptr inbounds i8, {{.+}} 8 | |
| 26 | // OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 8 | |
| 27 | 27 | // OPT2-NEXT: store i16 |
| 28 | // OPT2-NEXT: getelementptr inbounds i8, {{.+}} 10 | |
| 28 | // OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 10 | |
| 29 | 29 | // OPT2-NEXT: store i16 |
| 30 | // OPT2-NEXT: getelementptr inbounds i8, {{.+}} 12 | |
| 30 | // OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 12 | |
| 31 | 31 | // OPT2-NEXT: store i16 |
| 32 | // OPT2-NEXT: getelementptr inbounds i8, {{.+}} 14 | |
| 32 | // OPT2-NEXT: getelementptr inbounds{{( nuw)?}} i8, {{.+}} 14 | |
| 33 | 33 | // OPT2-NEXT: store i16 |
| 34 | 34 | // OPT3LINX64: load <8 x i16> |
| 35 | 35 | // OPT3LINX64-NEXT: call <8 x i16> @llvm.bswap |
tests/codegen/slice-iter-nonnull.rs+6-6| ... | ... | @@ -14,7 +14,7 @@ |
| 14 | 14 | // CHECK-LABEL: @slice_iter_next( |
| 15 | 15 | #[no_mangle] |
| 16 | 16 | pub 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}} | |
| 18 | 18 | // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]] |
| 19 | 19 | // CHECK-SAME: !nonnull |
| 20 | 20 | // CHECK-SAME: !noundef |
| ... | ... | @@ -31,7 +31,7 @@ pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32 |
| 31 | 31 | // CHECK-LABEL: @slice_iter_next_back( |
| 32 | 32 | #[no_mangle] |
| 33 | 33 | pub 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}} | |
| 35 | 35 | // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]] |
| 36 | 36 | // CHECK-SAME: !nonnull |
| 37 | 37 | // CHECK-SAME: !noundef |
| ... | ... | @@ -55,7 +55,7 @@ pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&' |
| 55 | 55 | #[no_mangle] |
| 56 | 56 | pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> { |
| 57 | 57 | // CHECK-NOT: slice |
| 58 | // CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1 | |
| 58 | // CHECK: %[[END:.+]] = getelementptr inbounds{{( nuw)?}} i32{{.+}} %slice.0{{.+}} %slice.1 | |
| 59 | 59 | // CHECK-NOT: slice |
| 60 | 60 | // CHECK: insertvalue {{.+}} ptr %slice.0, 0 |
| 61 | 61 | // CHECK-NOT: slice |
| ... | ... | @@ -70,7 +70,7 @@ pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> { |
| 70 | 70 | #[no_mangle] |
| 71 | 71 | pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> { |
| 72 | 72 | // CHECK-NOT: slice |
| 73 | // CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1 | |
| 73 | // CHECK: %[[END:.+]] = getelementptr inbounds{{( nuw)?}} i32{{.+}} %slice.0{{.+}} %slice.1 | |
| 74 | 74 | // CHECK-NOT: slice |
| 75 | 75 | // CHECK: insertvalue {{.+}} ptr %slice.0, 0 |
| 76 | 76 | // CHECK-NOT: slice |
| ... | ... | @@ -83,7 +83,7 @@ pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> { |
| 83 | 83 | // CHECK-LABEL: @slice_iter_is_empty |
| 84 | 84 | #[no_mangle] |
| 85 | 85 | pub 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}} | |
| 87 | 87 | // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]] |
| 88 | 88 | // CHECK-SAME: !nonnull |
| 89 | 89 | // CHECK-SAME: !noundef |
| ... | ... | @@ -99,7 +99,7 @@ pub fn slice_iter_is_empty(it: &std::slice::Iter<'_, u32>) -> bool { |
| 99 | 99 | // CHECK-LABEL: @slice_iter_len |
| 100 | 100 | #[no_mangle] |
| 101 | 101 | pub 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}} | |
| 103 | 103 | // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]] |
| 104 | 104 | // CHECK-SAME: !nonnull |
| 105 | 105 | // CHECK-SAME: !noundef |
tests/debuginfo/auxiliary/dependency-with-embedded-visualizers.py+3| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | import gdb |
| 2 | 2 | |
| 3 | ||
| 3 | 4 | class PersonPrinter: |
| 4 | 5 | "Print a Person" |
| 5 | 6 | |
| ... | ... | @@ -11,6 +12,7 @@ class PersonPrinter: |
| 11 | 12 | def to_string(self): |
| 12 | 13 | return "{} is {} years old.".format(self.name, self.age) |
| 13 | 14 | |
| 15 | ||
| 14 | 16 | def lookup(val): |
| 15 | 17 | lookup_tag = val.type.tag |
| 16 | 18 | if lookup_tag is None: |
| ... | ... | @@ -20,4 +22,5 @@ def lookup(val): |
| 20 | 22 | |
| 21 | 23 | return None |
| 22 | 24 | |
| 25 | ||
| 23 | 26 | gdb.current_objfile().pretty_printers.append(lookup) |
tests/debuginfo/embedded-visualizer-point.py+3| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | import gdb |
| 2 | 2 | |
| 3 | ||
| 3 | 4 | class PointPrinter: |
| 4 | 5 | "Print a Point" |
| 5 | 6 | |
| ... | ... | @@ -11,6 +12,7 @@ class PointPrinter: |
| 11 | 12 | def to_string(self): |
| 12 | 13 | return "({}, {})".format(self.x, self.y) |
| 13 | 14 | |
| 15 | ||
| 14 | 16 | def lookup(val): |
| 15 | 17 | lookup_tag = val.type.tag |
| 16 | 18 | if lookup_tag is None: |
| ... | ... | @@ -20,4 +22,5 @@ def lookup(val): |
| 20 | 22 | |
| 21 | 23 | return None |
| 22 | 24 | |
| 25 | ||
| 23 | 26 | gdb.current_objfile().pretty_printers.append(lookup) |
tests/debuginfo/embedded-visualizer.py+3| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | import gdb |
| 2 | 2 | |
| 3 | ||
| 3 | 4 | class LinePrinter: |
| 4 | 5 | "Print a Line" |
| 5 | 6 | |
| ... | ... | @@ -11,6 +12,7 @@ class LinePrinter: |
| 11 | 12 | def to_string(self): |
| 12 | 13 | return "({}, {})".format(self.a, self.b) |
| 13 | 14 | |
| 15 | ||
| 14 | 16 | def lookup(val): |
| 15 | 17 | lookup_tag = val.type.tag |
| 16 | 18 | if lookup_tag is None: |
| ... | ... | @@ -20,4 +22,5 @@ def lookup(val): |
| 20 | 22 | |
| 21 | 23 | return None |
| 22 | 24 | |
| 25 | ||
| 23 | 26 | gdb.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"}) |
| 10 | 10 | |
| 11 | 11 | // Checking it works on other doc blocks as well... |
| 12 | 12 | |
| 13 | // Logically, the ".docblock" and the "<p>" should have the same scroll width. | |
| 14 | compare-elements-property: ( | |
| 15 | "#implementations-list > details .docblock", | |
| 16 | "#implementations-list > details .docblock > p", | |
| 17 | ["scrollWidth"], | |
| 18 | ) | |
| 19 | assert-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). | |
| 14 | assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 816}) | |
| 15 | assert-property: ("#implementations-list > details .docblock > p", {"scrollWidth": 835}) | |
| 20 | 16 | // However, since there is overflow in the <table>, its scroll width is bigger. |
| 21 | 17 | assert-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. | |
| 3 | go-to: "file://" + |DOC_PATH| + "/test_docs/struct.ImplDoc.html" | |
| 4 | ||
| 5 | set-window-size: (900, 600) | |
| 6 | ||
| 7 | define-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 | ||
| 27 | call-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. | |
| 29 | assert: (|impl_y| + |impl_height|) <= (|doc_y| + |doc_height|) | |
| 30 | ||
| 31 | call-function: ("compare-size-and-pos", {"nth_impl": 2}) | |
| 32 | // The second impl block has a short line. | |
| 33 | assert: (|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. | |
| 37 | call-function: ("compare-size-and-pos", {"nth_impl": 3}) | |
| 38 | assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|) | |
| 39 | call-function: ("compare-size-and-pos", {"nth_impl": 4}) | |
| 40 | assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|) | |
| 41 | call-function: ("compare-size-and-pos", {"nth_impl": 5}) | |
| 42 | assert: (|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" |
| 3 | 3 | |
| 4 | 4 | // The text is about 24px tall, so if there's a margin, then their position will be >24px apart |
| 5 | 5 | compare-elements-position-near-false: ( |
| 6 | "#implementations-list > .implementors-toggle > .docblock > p", | |
| 6 | "#implementations-list > .implementors-toggle .docblock > p", | |
| 7 | 7 | "#implementations-list > .implementors-toggle > .impl-items", |
| 8 | 8 | {"y": 24} |
| 9 | 9 | ) |
tests/rustdoc-gui/item-info-overflow.goml+1-1| ... | ... | @@ -16,7 +16,7 @@ assert-text: ( |
| 16 | 16 | go-to: "file://" + |DOC_PATH| + "/lib2/struct.LongItemInfo2.html" |
| 17 | 17 | compare-elements-property: ( |
| 18 | 18 | "#impl-SimpleTrait-for-LongItemInfo2 .item-info", |
| 19 | "#impl-SimpleTrait-for-LongItemInfo2 + .docblock", | |
| 19 | "#impl-SimpleTrait-for-LongItemInfo2 .docblock", | |
| 20 | 20 | ["scrollWidth"], |
| 21 | 21 | ) |
| 22 | 22 | assert-property: ( |
tests/rustdoc-gui/source-code-page-code-scroll.goml+2-2| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" |
| 3 | 3 | set-window-size: (800, 1000) |
| 4 | 4 | // "scrollWidth" should be superior than "clientWidth". |
| 5 | assert-property: ("body", {"scrollWidth": 1114, "clientWidth": 800}) | |
| 5 | assert-property: ("body", {"scrollWidth": 1776, "clientWidth": 800}) | |
| 6 | 6 | |
| 7 | 7 | // Both properties should be equal (ie, no scroll on the code block). |
| 8 | assert-property: (".example-wrap .rust", {"scrollWidth": 1000, "clientWidth": 1000}) | |
| 8 | assert-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 { |
| 652 | 652 | //! * [`FromBytes`](#a) indicates that a type may safely be converted from an arbitrary byte |
| 653 | 653 | //! sequence |
| 654 | 654 | } |
| 655 | ||
| 656 | pub 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 | |
| 662 | impl ImplDoc { | |
| 663 | pub fn bar() {} | |
| 664 | } | |
| 665 | ||
| 666 | /// bla | |
| 667 | /// | |
| 668 | /// bla | |
| 669 | impl 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 | | |
| 677 | impl ImplDoc { | |
| 678 | pub fn bar3() {} | |
| 679 | } | |
| 680 | ||
| 681 | /// # h1 | |
| 682 | /// | |
| 683 | /// bla | |
| 684 | impl ImplDoc { | |
| 685 | pub fn bar4() {} | |
| 686 | } | |
| 687 | ||
| 688 | /// * list | |
| 689 | /// * list | |
| 690 | /// * list | |
| 691 | impl ImplDoc { | |
| 692 | pub fn bar5() {} | |
| 693 | } |
tests/ui/codegen/target-cpus.rs+6| ... | ... | @@ -1,3 +1,9 @@ |
| 1 | 1 | //@ needs-llvm-components: webassembly |
| 2 | 2 | //@ compile-flags: --print=target-cpus --target=wasm32-unknown-unknown |
| 3 | 3 | //@ 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 | ||
| 5 | macro call($f:expr $(, $args:expr)* $(,)?) { | |
| 6 | ($f)($($args),*) | |
| 7 | } | |
| 8 | ||
| 9 | fn main() { | |
| 10 | become call!(f); | |
| 11 | } | |
| 12 | ||
| 13 | fn 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)] | |
| 5 | use std::num::Wrapping; | |
| 6 | use std::ops::{Not, Add, BitXorAssign}; | |
| 7 | ||
| 8 | // built-ins and overloaded operators are handled differently | |
| 9 | ||
| 10 | fn f(a: u64, b: u64) -> u64 { | |
| 11 | return a + b; //~ error: `become` does not support operators | |
| 12 | } | |
| 13 | ||
| 14 | fn g(a: String, b: &str) -> String { | |
| 15 | become (a).add(b); //~ error: `become` does not support operators | |
| 16 | } | |
| 17 | ||
| 18 | fn h(x: u64) -> u64 { | |
| 19 | return !x; //~ error: `become` does not support operators | |
| 20 | } | |
| 21 | ||
| 22 | fn i_do_not_know_any_more_letters(x: Wrapping<u32>) -> Wrapping<u32> { | |
| 23 | become (x).not(); //~ error: `become` does not support operators | |
| 24 | } | |
| 25 | ||
| 26 | fn 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 | ||
| 33 | fn a(a: &mut u8, _: u8) { | |
| 34 | return *a ^= 1; //~ error: `become` does not support operators | |
| 35 | } | |
| 36 | ||
| 37 | fn b(b: &mut Wrapping<u8>, _: u8) { | |
| 38 | become (*b).bitxor_assign(1); //~ error: `become` does not support operators | |
| 39 | } | |
| 40 | ||
| 41 | ||
| 42 | fn 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)] | |
| 5 | use std::num::Wrapping; | |
| 6 | use std::ops::{Not, Add, BitXorAssign}; | |
| 7 | ||
| 8 | // built-ins and overloaded operators are handled differently | |
| 9 | ||
| 10 | fn f(a: u64, b: u64) -> u64 { | |
| 11 | become a + b; //~ error: `become` does not support operators | |
| 12 | } | |
| 13 | ||
| 14 | fn g(a: String, b: &str) -> String { | |
| 15 | become a + b; //~ error: `become` does not support operators | |
| 16 | } | |
| 17 | ||
| 18 | fn h(x: u64) -> u64 { | |
| 19 | become !x; //~ error: `become` does not support operators | |
| 20 | } | |
| 21 | ||
| 22 | fn i_do_not_know_any_more_letters(x: Wrapping<u32>) -> Wrapping<u32> { | |
| 23 | become !x; //~ error: `become` does not support operators | |
| 24 | } | |
| 25 | ||
| 26 | fn 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 | ||
| 33 | fn a(a: &mut u8, _: u8) { | |
| 34 | become *a ^= 1; //~ error: `become` does not support operators | |
| 35 | } | |
| 36 | ||
| 37 | fn b(b: &mut Wrapping<u8>, _: u8) { | |
| 38 | become *b ^= 1; //~ error: `become` does not support operators | |
| 39 | } | |
| 40 | ||
| 41 | ||
| 42 | fn main() {} |
tests/ui/explicit-tail-calls/become-operator.stderr created+75| ... | ... | @@ -0,0 +1,75 @@ |
| 1 | error: `become` does not support operators | |
| 2 | --> $DIR/become-operator.rs:11:12 | |
| 3 | | | |
| 4 | LL | 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 | ||
| 11 | error: `become` does not support operators | |
| 12 | --> $DIR/become-operator.rs:15:12 | |
| 13 | | | |
| 14 | LL | become a + b; | |
| 15 | | ^^^^^ | |
| 16 | | | |
| 17 | help: try using the method directly | |
| 18 | | | |
| 19 | LL | become (a).add(b); | |
| 20 | | + ~~~~~~ + | |
| 21 | ||
| 22 | error: `become` does not support operators | |
| 23 | --> $DIR/become-operator.rs:19:12 | |
| 24 | | | |
| 25 | LL | become !x; | |
| 26 | | -------^^ | |
| 27 | | | | |
| 28 | | help: try using `return` instead: `return` | |
| 29 | | | |
| 30 | = note: using `become` on a builtin operator is not useful | |
| 31 | ||
| 32 | error: `become` does not support operators | |
| 33 | --> $DIR/become-operator.rs:23:12 | |
| 34 | | | |
| 35 | LL | become !x; | |
| 36 | | ^^ | |
| 37 | | | |
| 38 | help: try using the method directly | |
| 39 | | | |
| 40 | LL | become (x).not(); | |
| 41 | | ~ +++++++ | |
| 42 | ||
| 43 | error: `become` does not support operators | |
| 44 | --> $DIR/become-operator.rs:27:12 | |
| 45 | | | |
| 46 | LL | 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 | ||
| 53 | error: `become` does not support operators | |
| 54 | --> $DIR/become-operator.rs:34:12 | |
| 55 | | | |
| 56 | LL | 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 | ||
| 63 | error: `become` does not support operators | |
| 64 | --> $DIR/become-operator.rs:38:12 | |
| 65 | | | |
| 66 | LL | become *b ^= 1; | |
| 67 | | ^^^^^^^ | |
| 68 | | | |
| 69 | help: try using the method directly | |
| 70 | | | |
| 71 | LL | become (*b).bitxor_assign(1); | |
| 72 | | + ~~~~~~~~~~~~~~~~ + | |
| 73 | ||
| 74 | error: aborting due to 7 previous errors | |
| 75 |
tests/ui/explicit-tail-calls/become-outside.rs+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | //@ revisions: constant array |
| 2 | #![allow(incomplete_features)] | |
| 2 | #![expect(incomplete_features)] | |
| 3 | 3 | #![feature(explicit_tail_calls)] |
| 4 | 4 | |
| 5 | 5 | #[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 | ||
| 6 | fn f() -> u64 { | |
| 7 | return 1; //~ error: `become` requires a function call | |
| 8 | } | |
| 9 | ||
| 10 | fn g() { | |
| 11 | return { h() }; //~ error: `become` requires a function call | |
| 12 | } | |
| 13 | ||
| 14 | fn h() { | |
| 15 | return *&g(); //~ error: `become` requires a function call | |
| 16 | } | |
| 17 | ||
| 18 | fn 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 | ||
| 6 | fn f() -> u64 { | |
| 7 | become 1; //~ error: `become` requires a function call | |
| 8 | } | |
| 9 | ||
| 10 | fn g() { | |
| 11 | become { h() }; //~ error: `become` requires a function call | |
| 12 | } | |
| 13 | ||
| 14 | fn h() { | |
| 15 | become *&g(); //~ error: `become` requires a function call | |
| 16 | } | |
| 17 | ||
| 18 | fn main() {} |
tests/ui/explicit-tail-calls/become-uncallable.stderr created+44| ... | ... | @@ -0,0 +1,44 @@ |
| 1 | error: `become` requires a function call | |
| 2 | --> $DIR/become-uncallable.rs:7:12 | |
| 3 | | | |
| 4 | LL | become 1; | |
| 5 | | -------^ | |
| 6 | | | | |
| 7 | | help: try using `return` instead: `return` | |
| 8 | | | |
| 9 | note: not a function call | |
| 10 | --> $DIR/become-uncallable.rs:7:12 | |
| 11 | | | |
| 12 | LL | become 1; | |
| 13 | | ^ | |
| 14 | ||
| 15 | error: `become` requires a function call | |
| 16 | --> $DIR/become-uncallable.rs:11:12 | |
| 17 | | | |
| 18 | LL | become { h() }; | |
| 19 | | -------^^^^^^^ | |
| 20 | | | | |
| 21 | | help: try using `return` instead: `return` | |
| 22 | | | |
| 23 | note: not a function call | |
| 24 | --> $DIR/become-uncallable.rs:11:12 | |
| 25 | | | |
| 26 | LL | become { h() }; | |
| 27 | | ^^^^^^^ | |
| 28 | ||
| 29 | error: `become` requires a function call | |
| 30 | --> $DIR/become-uncallable.rs:15:12 | |
| 31 | | | |
| 32 | LL | become *&g(); | |
| 33 | | -------^^^^^ | |
| 34 | | | | |
| 35 | | help: try using `return` instead: `return` | |
| 36 | | | |
| 37 | note: not a function call | |
| 38 | --> $DIR/become-uncallable.rs:15:12 | |
| 39 | | | |
| 40 | LL | become *&g(); | |
| 41 | | ^^^^^ | |
| 42 | ||
| 43 | error: 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 | ||
| 5 | fn a() { | |
| 6 | become ((|| ()) as fn() -> _)(); | |
| 7 | //~^ ERROR: tail calling closures directly is not allowed | |
| 8 | } | |
| 9 | ||
| 10 | fn aa((): ()) { | |
| 11 | become ((|()| ()) as fn(_) -> _)(()); | |
| 12 | //~^ ERROR: tail calling closures directly is not allowed | |
| 13 | } | |
| 14 | ||
| 15 | fn aaa((): (), _: i32) { | |
| 16 | become ((|(), _| ()) as fn(_, _) -> _)((), 1); | |
| 17 | //~^ ERROR: tail calling closures directly is not allowed | |
| 18 | } | |
| 19 | ||
| 20 | fn v((): (), ((), ()): ((), ())) -> (((), ()), ()) { | |
| 21 | let f = |(), ((), ())| (((), ()), ()); | |
| 22 | become (f as fn(_, _) -> _)((), ((), ())); | |
| 23 | //~^ ERROR: tail calling closures directly is not allowed | |
| 24 | } | |
| 25 | ||
| 26 | fn 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 | ||
| 5 | fn a() { | |
| 6 | become (|| ())(); | |
| 7 | //~^ ERROR: tail calling closures directly is not allowed | |
| 8 | } | |
| 9 | ||
| 10 | fn aa((): ()) { | |
| 11 | become (|()| ())(()); | |
| 12 | //~^ ERROR: tail calling closures directly is not allowed | |
| 13 | } | |
| 14 | ||
| 15 | fn aaa((): (), _: i32) { | |
| 16 | become (|(), _| ())((), 1); | |
| 17 | //~^ ERROR: tail calling closures directly is not allowed | |
| 18 | } | |
| 19 | ||
| 20 | fn v((): (), ((), ()): ((), ())) -> (((), ()), ()) { | |
| 21 | let f = |(), ((), ())| (((), ()), ()); | |
| 22 | become f((), ((), ())); | |
| 23 | //~^ ERROR: tail calling closures directly is not allowed | |
| 24 | } | |
| 25 | ||
| 26 | fn main() { | |
| 27 | a(); | |
| 28 | aa(()); | |
| 29 | aaa((), 1); | |
| 30 | v((), ((), ())); | |
| 31 | } |
tests/ui/explicit-tail-calls/closure.stderr created+46| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | error: tail calling closures directly is not allowed | |
| 2 | --> $DIR/closure.rs:6:5 | |
| 3 | | | |
| 4 | LL | become (|| ())(); | |
| 5 | | ^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | help: try casting the closure to a function pointer type | |
| 8 | | | |
| 9 | LL | become ((|| ()) as fn() -> _)(); | |
| 10 | | + +++++++++++++ | |
| 11 | ||
| 12 | error: tail calling closures directly is not allowed | |
| 13 | --> $DIR/closure.rs:11:5 | |
| 14 | | | |
| 15 | LL | become (|()| ())(()); | |
| 16 | | ^^^^^^^^^^^^^^^^^^^^ | |
| 17 | | | |
| 18 | help: try casting the closure to a function pointer type | |
| 19 | | | |
| 20 | LL | become ((|()| ()) as fn(_) -> _)(()); | |
| 21 | | + ++++++++++++++ | |
| 22 | ||
| 23 | error: tail calling closures directly is not allowed | |
| 24 | --> $DIR/closure.rs:16:5 | |
| 25 | | | |
| 26 | LL | become (|(), _| ())((), 1); | |
| 27 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 28 | | | |
| 29 | help: try casting the closure to a function pointer type | |
| 30 | | | |
| 31 | LL | become ((|(), _| ()) as fn(_, _) -> _)((), 1); | |
| 32 | | + +++++++++++++++++ | |
| 33 | ||
| 34 | error: tail calling closures directly is not allowed | |
| 35 | --> $DIR/closure.rs:22:5 | |
| 36 | | | |
| 37 | LL | become f((), ((), ())); | |
| 38 | | ^^^^^^^^^^^^^^^^^^^^^^ | |
| 39 | | | |
| 40 | help: try casting the closure to a function pointer type | |
| 41 | | | |
| 42 | LL | become (f as fn(_, _) -> _)((), ((), ())); | |
| 43 | | + +++++++++++++++++ | |
| 44 | ||
| 45 | error: 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)] | |
| 2 | 2 | #![feature(explicit_tail_calls)] |
| 3 | 3 | |
| 4 | 4 | const 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)] | |
| 2 | 2 | #![feature(explicit_tail_calls)] |
| 3 | 3 | |
| 4 | 4 | pub const fn test(_: &Type) { |
tests/ui/explicit-tail-calls/ctfe-arg-good-borrow.rs+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | //@ check-pass |
| 2 | #![allow(incomplete_features)] | |
| 2 | #![expect(incomplete_features)] | |
| 3 | 3 | #![feature(explicit_tail_calls)] |
| 4 | 4 | |
| 5 | 5 | pub const fn test(x: &Type) { |
tests/ui/explicit-tail-calls/ctfe-arg-move.rs+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | //@ check-pass |
| 2 | #![allow(incomplete_features)] | |
| 2 | #![expect(incomplete_features)] | |
| 3 | 3 | #![feature(explicit_tail_calls)] |
| 4 | 4 | |
| 5 | 5 | pub const fn test(s: String) -> String { |
tests/ui/explicit-tail-calls/ctfe-collatz-multi-rec.rs+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | //@ run-pass |
| 2 | #![allow(incomplete_features)] | |
| 2 | #![expect(incomplete_features)] | |
| 3 | 3 | #![feature(explicit_tail_calls)] |
| 4 | 4 | |
| 5 | 5 | /// A very unnecessarily complicated "implementation" of the Collatz conjecture. |
tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | //@ revisions: become return |
| 2 | 2 | //@ [become] run-pass |
| 3 | #![allow(incomplete_features)] | |
| 3 | #![expect(incomplete_features)] | |
| 4 | 4 | #![feature(explicit_tail_calls)] |
| 5 | 5 | |
| 6 | 6 | // 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)] | |
| 2 | 2 | #![feature(explicit_tail_calls)] |
| 3 | 3 | |
| 4 | 4 | pub const fn f() { |
tests/ui/explicit-tail-calls/drop-order.rs+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | // FIXME(explicit_tail_calls): enable this test once rustc_codegen_ssa supports tail calls |
| 2 | 2 | //@ ignore-test: tail calls are not implemented in rustc_codegen_ssa yet, so this causes 🧊 |
| 3 | 3 | //@ run-pass |
| 4 | #![allow(incomplete_features)] | |
| 4 | #![expect(incomplete_features)] | |
| 5 | 5 | #![feature(explicit_tail_calls)] |
| 6 | 6 | use std::cell::RefCell; |
| 7 | 7 |
tests/ui/explicit-tail-calls/in-closure.rs created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | #![expect(incomplete_features)] | |
| 2 | #![feature(explicit_tail_calls)] | |
| 3 | ||
| 4 | fn main() { | |
| 5 | || become f(); //~ error: `become` is not allowed in closures | |
| 6 | } | |
| 7 | ||
| 8 | fn f() {} |
tests/ui/explicit-tail-calls/in-closure.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: `become` is not allowed in closures | |
| 2 | --> $DIR/in-closure.rs:5:8 | |
| 3 | | | |
| 4 | LL | || become f(); | |
| 5 | | ^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/explicit-tail-calls/return-lifetime-sub.rs+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | //@ check-pass |
| 2 | #![allow(incomplete_features)] | |
| 2 | #![expect(incomplete_features)] | |
| 3 | 3 | #![feature(explicit_tail_calls)] |
| 4 | 4 | |
| 5 | 5 | fn _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)] | |
| 2 | 2 | #![feature(explicit_tail_calls)] |
| 3 | 3 | |
| 4 | 4 | fn _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 | ||
| 5 | fn _f0((): ()) { | |
| 6 | become _g0(); //~ error: mismatched signatures | |
| 7 | } | |
| 8 | ||
| 9 | fn _g0() {} | |
| 10 | ||
| 11 | ||
| 12 | fn _f1() { | |
| 13 | become _g1(()); //~ error: mismatched signatures | |
| 14 | } | |
| 15 | ||
| 16 | fn _g1((): ()) {} | |
| 17 | ||
| 18 | ||
| 19 | extern "C" fn _f2() { | |
| 20 | become _g2(); //~ error: mismatched function ABIs | |
| 21 | } | |
| 22 | ||
| 23 | fn _g2() {} | |
| 24 | ||
| 25 | ||
| 26 | fn _f3() { | |
| 27 | become _g3(); //~ error: mismatched function ABIs | |
| 28 | } | |
| 29 | ||
| 30 | extern "C" fn _g3() {} | |
| 31 | ||
| 32 | ||
| 33 | fn main() {} |
tests/ui/explicit-tail-calls/signature-mismatch.stderr created+40| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | error: mismatched signatures | |
| 2 | --> $DIR/signature-mismatch.rs:6:5 | |
| 3 | | | |
| 4 | LL | 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 | ||
| 11 | error: mismatched signatures | |
| 12 | --> $DIR/signature-mismatch.rs:13:5 | |
| 13 | | | |
| 14 | LL | 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 | ||
| 21 | error: mismatched function ABIs | |
| 22 | --> $DIR/signature-mismatch.rs:20:5 | |
| 23 | | | |
| 24 | LL | 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 | ||
| 30 | error: mismatched function ABIs | |
| 31 | --> $DIR/signature-mismatch.rs:27:5 | |
| 32 | | | |
| 33 | LL | 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 | ||
| 39 | error: 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)] | |
| 2 | 2 | #![feature(explicit_tail_calls)] |
| 3 | 3 | |
| 4 | 4 | const fn f() { |
x.py+6-3| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | |
| 7 | 7 | # Parts of `bootstrap.py` use the `multiprocessing` module, so this entry point |
| 8 | 8 | # must use the normal `if __name__ == '__main__':` convention to avoid problems. |
| 9 | if __name__ == '__main__': | |
| 9 | if __name__ == "__main__": | |
| 10 | 10 | import os |
| 11 | 11 | import sys |
| 12 | 12 | import warnings |
| ... | ... | @@ -32,14 +32,16 @@ if __name__ == '__main__': |
| 32 | 32 | # soft deprecation of old python versions |
| 33 | 33 | skip_check = os.environ.get("RUST_IGNORE_OLD_PYTHON") == "1" |
| 34 | 34 | if not skip_check and (major < 3 or (major == 3 and minor < 6)): |
| 35 | msg = cleandoc(""" | |
| 35 | msg = cleandoc( | |
| 36 | """ | |
| 36 | 37 | Using python {}.{} but >= 3.6 is recommended. Your python version |
| 37 | 38 | should continue to work for the near future, but this will |
| 38 | 39 | eventually change. If python >= 3.6 is not available on your system, |
| 39 | 40 | please file an issue to help us understand timelines. |
| 40 | 41 | |
| 41 | 42 | This message can be suppressed by setting `RUST_IGNORE_OLD_PYTHON=1` |
| 42 | """.format(major, minor)) | |
| 43 | """.format(major, minor) | |
| 44 | ) | |
| 43 | 45 | warnings.warn(msg, stacklevel=1) |
| 44 | 46 | |
| 45 | 47 | rust_dir = os.path.dirname(os.path.abspath(__file__)) |
| ... | ... | @@ -47,4 +49,5 @@ if __name__ == '__main__': |
| 47 | 49 | sys.path.insert(0, os.path.join(rust_dir, "src", "bootstrap")) |
| 48 | 50 | |
| 49 | 51 | import bootstrap |
| 52 | ||
| 50 | 53 | bootstrap.main() |