From 6076e3811fd1329f6b781f681f11a0f951bc1316 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 20 Nov 2025 05:03:52 -0600 Subject: [PATCH 1/2] fmt: Use the same rustfmt.toml everywhere It isn't worth keeping a separate rustfmt.toml only for ctest, so switch to using the vertical formatting everywhere. --- src/rustfmt.toml => .rustfmt.toml | 1 - build.rs | 10 +++++++-- ci/ios/deploy_and_run_on_ios_simulator.rs | 10 +++++++-- ci/runtest-android.rs | 5 ++++- ctest-test/src/t1.rs | 9 +++++++- ctest-test/src/t2.rs | 5 ++++- ctest-test/tests/all.rs | 5 ++++- ctest/src/ast/function.rs | 6 ++++- ctest/src/ast/static_variable.rs | 5 ++++- ctest/src/ast/structure.rs | 5 ++++- ctest/src/ast/union.rs | 5 ++++- ctest/src/cdecl.rs | 5 ++++- ctest/src/ffi_items.rs | 13 ++++++++++- ctest/src/generator.rs | 27 +++++++++++++++++++---- ctest/src/lib.rs | 18 +++++++++++++-- ctest/src/macro_expansion.rs | 5 ++++- ctest/src/runner.rs | 18 ++++++++++++--- ctest/src/template.rs | 14 ++++++++++-- ctest/src/tests.rs | 12 ++++++++-- ctest/src/translator.rs | 12 ++++++++-- ctest/tests/basic.rs | 20 +++++++++++++---- libc-test/build.rs | 17 +++++++++++--- libc-test/tests/cmsg.rs | 9 +++++++- libc-test/tests/errqueue.rs | 6 ++++- libc-test/tests/makedev.rs | 6 ++++- libc-test/tests/style.rs | 5 ++++- libc-test/tests/style_lib/mod.rs | 23 ++++++++++++++----- rustfmt.toml | 5 ----- 28 files changed, 229 insertions(+), 52 deletions(-) rename src/rustfmt.toml => .rustfmt.toml (74%) delete mode 100644 rustfmt.toml diff --git a/src/rustfmt.toml b/.rustfmt.toml similarity index 74% rename from src/rustfmt.toml rename to .rustfmt.toml index 19e4b2fa60beb..42e6cff32e740 100644 --- a/src/rustfmt.toml +++ b/.rustfmt.toml @@ -1,4 +1,3 @@ -# Note that there is a separate top-level configuration for everything else edition = "2021" error_on_line_overflow = true group_imports = "StdExternalCrate" diff --git a/build.rs b/build.rs index 0a3d39d16abb6..0e9e0c58ea5cb 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,11 @@ -use std::process::{Command, Output}; -use std::{env, str}; +use std::process::{ + Command, + Output, +}; +use std::{ + env, + str, +}; // List of cfgs this build script is allowed to set. The list is needed to support check-cfg, as we // need to know all the possible cfgs that this script will set. If you need to set another cfg diff --git a/ci/ios/deploy_and_run_on_ios_simulator.rs b/ci/ios/deploy_and_run_on_ios_simulator.rs index 0398a9d3f888d..5c2d2fbad328d 100644 --- a/ci/ios/deploy_and_run_on_ios_simulator.rs +++ b/ci/ios/deploy_and_run_on_ios_simulator.rs @@ -6,11 +6,17 @@ // (https://github.com/snipsco/dinghy): cargo dinghy install, then cargo dinghy // test. -use std::fs::{self, File}; +use std::fs::{ + self, + File, +}; use std::io::Write; use std::path::Path; use std::process::Command; -use std::{env, process}; +use std::{ + env, + process, +}; macro_rules! t { ($e:expr) => { diff --git a/ci/runtest-android.rs b/ci/runtest-android.rs index 29b1a82f675c7..b88cd9c1b9140 100644 --- a/ci/runtest-android.rs +++ b/ci/runtest-android.rs @@ -1,5 +1,8 @@ use std::env; -use std::path::{Path, PathBuf}; +use std::path::{ + Path, + PathBuf, +}; use std::process::Command; fn main() { diff --git a/ctest-test/src/t1.rs b/ctest-test/src/t1.rs index 7d832907bc9b5..19f0560806c01 100644 --- a/ctest-test/src/t1.rs +++ b/ctest-test/src/t1.rs @@ -1,6 +1,13 @@ #![allow(dead_code)] -use std::ffi::{c_char, c_double, c_int, c_long, c_uint, c_void}; +use std::ffi::{ + c_char, + c_double, + c_int, + c_long, + c_uint, + c_void, +}; pub type T1Foo = i32; pub const T1S: *const c_char = b"foo\0".as_ptr().cast(); diff --git a/ctest-test/src/t2.rs b/ctest-test/src/t2.rs index 0639753bbeff5..67c00d6e9a3fa 100644 --- a/ctest-test/src/t2.rs +++ b/ctest-test/src/t2.rs @@ -1,6 +1,9 @@ #![allow(non_camel_case_types)] -use std::ffi::{c_char, c_int}; +use std::ffi::{ + c_char, + c_int, +}; pub type T2Foo = u32; pub type T2Bar = u32; diff --git a/ctest-test/tests/all.rs b/ctest-test/tests/all.rs index 638fe564c4b07..3f35ed7d12a30 100644 --- a/ctest-test/tests/all.rs +++ b/ctest-test/tests/all.rs @@ -3,7 +3,10 @@ use std::collections::HashSet; use std::env; -use std::process::{Command, ExitStatus}; +use std::process::{ + Command, + ExitStatus, +}; /// Create a command that starts in the `target/debug` or `target/release` directory. fn cmd(name: &str) -> Command { diff --git a/ctest/src/ast/function.rs b/ctest/src/ast/function.rs index 8722c2b82b740..e266a53efdbbd 100644 --- a/ctest/src/ast/function.rs +++ b/ctest/src/ast/function.rs @@ -1,4 +1,8 @@ -use crate::{Abi, BoxStr, Parameter}; +use crate::{ + Abi, + BoxStr, + Parameter, +}; /// Represents a function signature defined in Rust. /// diff --git a/ctest/src/ast/static_variable.rs b/ctest/src/ast/static_variable.rs index 6abc771664610..665bd06eb8e4b 100644 --- a/ctest/src/ast/static_variable.rs +++ b/ctest/src/ast/static_variable.rs @@ -1,4 +1,7 @@ -use crate::{Abi, BoxStr}; +use crate::{ + Abi, + BoxStr, +}; /// Represents a static variable in Rust. /// diff --git a/ctest/src/ast/structure.rs b/ctest/src/ast/structure.rs index cc3edecf84dee..1c935890d4d23 100644 --- a/ctest/src/ast/structure.rs +++ b/ctest/src/ast/structure.rs @@ -1,4 +1,7 @@ -use crate::{BoxStr, Field}; +use crate::{ + BoxStr, + Field, +}; /// Represents a struct defined in Rust. #[derive(Debug, Clone)] diff --git a/ctest/src/ast/union.rs b/ctest/src/ast/union.rs index 12eedf7e002a3..990d6db0efca5 100644 --- a/ctest/src/ast/union.rs +++ b/ctest/src/ast/union.rs @@ -1,4 +1,7 @@ -use crate::{BoxStr, Field}; +use crate::{ + BoxStr, + Field, +}; /// Represents a union defined in Rust. #[derive(Debug, Clone)] diff --git a/ctest/src/cdecl.rs b/ctest/src/cdecl.rs index 3d552d421697c..9fa5d15fa5843 100644 --- a/ctest/src/cdecl.rs +++ b/ctest/src/cdecl.rs @@ -10,7 +10,10 @@ pub(crate) enum Constness { } #[cfg_attr(not(test), expect(unused_imports))] -use Constness::{Const, Mut}; +use Constness::{ + Const, + Mut, +}; use crate::BoxStr; diff --git a/ctest/src/ffi_items.rs b/ctest/src/ffi_items.rs index 6f49ee19b695a..cc639692e6745 100644 --- a/ctest/src/ffi_items.rs +++ b/ctest/src/ffi_items.rs @@ -5,7 +5,18 @@ use std::ops::Deref; use syn::punctuated::Punctuated; use syn::visit::Visit; -use crate::{Abi, BoxStr, Const, Field, Fn, Parameter, Static, Struct, Type, Union}; +use crate::{ + Abi, + BoxStr, + Const, + Field, + Fn, + Parameter, + Static, + Struct, + Type, + Union, +}; /// Represents a collected set of top-level Rust items relevant to FFI generation or analysis. /// diff --git a/ctest/src/generator.rs b/ctest/src/generator.rs index 77e43f72954f0..47a865c0f7020 100644 --- a/ctest/src/generator.rs +++ b/ctest/src/generator.rs @@ -3,18 +3,37 @@ use std::env; use std::fs::File; use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::{ + Path, + PathBuf, +}; use askama::Template; use syn::visit::Visit; use thiserror::Error; use crate::ffi_items::FfiItems; -use crate::template::{CTestTemplate, RustTestTemplate}; +use crate::template::{ + CTestTemplate, + RustTestTemplate, +}; use crate::translator::translate_primitive_type; use crate::{ - BoxStr, Const, Field, Language, MapInput, Parameter, Result, Static, Struct, TranslationError, - Type, Union, VolatileItemKind, expand, get_build_target, + BoxStr, + Const, + Field, + Language, + MapInput, + Parameter, + Result, + Static, + Struct, + TranslationError, + Type, + Union, + VolatileItemKind, + expand, + get_build_target, }; /// A function that takes a mappable input and returns its mapping as `Some`, otherwise diff --git a/ctest/src/lib.rs b/ctest/src/lib.rs index 45dc41088d5d5..c03f9de54d524 100644 --- a/ctest/src/lib.rs +++ b/ctest/src/lib.rs @@ -22,10 +22,24 @@ mod translator; use std::env; -pub use ast::{Abi, Const, Field, Fn, Parameter, Static, Struct, Type, Union}; +pub use ast::{ + Abi, + Const, + Field, + Fn, + Parameter, + Static, + Struct, + Type, + Union, +}; pub use generator::TestGenerator; pub use macro_expansion::expand; -pub use runner::{__compile_test, __run_test, generate_test}; +pub use runner::{ + __compile_test, + __run_test, + generate_test, +}; pub use translator::TranslationError; use crate::generator::GenerationError; diff --git a/ctest/src/macro_expansion.rs b/ctest/src/macro_expansion.rs index 9c46e047e903e..fc5ff1e4b3121 100644 --- a/ctest/src/macro_expansion.rs +++ b/ctest/src/macro_expansion.rs @@ -3,7 +3,10 @@ use std::fs::canonicalize; use std::path::Path; use std::process::Command; -use crate::{EDITION, Result}; +use crate::{ + EDITION, + Result, +}; /// Use rustc to expand all macros and pretty print the crate into a single file. pub fn expand>( diff --git a/ctest/src/runner.rs b/ctest/src/runner.rs index d5983ea42654c..a5ec6dd3f2294 100644 --- a/ctest/src/runner.rs +++ b/ctest/src/runner.rs @@ -1,13 +1,25 @@ //! Generation, compilation, and running of tests. use std::env; -use std::fs::{File, canonicalize}; +use std::fs::{ + File, + canonicalize, +}; use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::{ + Path, + PathBuf, +}; use std::process::Command; use crate::generator::GenerationError; -use crate::{EDITION, Language, Result, TestGenerator, get_build_target}; +use crate::{ + EDITION, + Language, + Result, + TestGenerator, + get_build_target, +}; /// Generate all tests for the given crate and output the Rust side to a file. #[doc(hidden)] diff --git a/ctest/src/template.rs b/ctest/src/template.rs index 538ab18eee1f0..0580c1ba8a39b 100644 --- a/ctest/src/template.rs +++ b/ctest/src/template.rs @@ -7,9 +7,19 @@ use syn::spanned::Spanned; use crate::cdecl::Constness; use crate::ffi_items::FfiItems; -use crate::translator::{TranslationErrorKind, Translator}; +use crate::translator::{ + TranslationErrorKind, + Translator, +}; use crate::{ - BoxStr, Field, MapInput, Result, TestGenerator, TranslationError, VolatileItemKind, cdecl, + BoxStr, + Field, + MapInput, + Result, + TestGenerator, + TranslationError, + VolatileItemKind, + cdecl, }; /// Represents the Rust side of the generated testing suite. diff --git a/ctest/src/tests.rs b/ctest/src/tests.rs index 04f9ef96313b4..3ce0e75deb506 100644 --- a/ctest/src/tests.rs +++ b/ctest/src/tests.rs @@ -2,8 +2,16 @@ use syn::spanned::Spanned; use syn::visit::Visit; use crate::ffi_items::FfiItems; -use crate::translator::{TranslationErrorKind, Translator}; -use crate::{Result, TestGenerator, TranslationError, cdecl}; +use crate::translator::{ + TranslationErrorKind, + Translator, +}; +use crate::{ + Result, + TestGenerator, + TranslationError, + cdecl, +}; const ALL_ITEMS: &str = r#" use std::os::raw::c_void; diff --git a/ctest/src/translator.rs b/ctest/src/translator.rs index ac9599d1b06c7..b898e5b34d405 100644 --- a/ctest/src/translator.rs +++ b/ctest/src/translator.rs @@ -3,7 +3,10 @@ //! Simple to semi complex types are supported only. use std::fmt; -use std::ops::{Deref, DerefMut}; +use std::ops::{ + Deref, + DerefMut, +}; use proc_macro2::Span; use quote::ToTokens; @@ -12,7 +15,12 @@ use thiserror::Error; use crate::cdecl::Constness; use crate::ffi_items::FfiItems; -use crate::{BoxStr, MapInput, TestGenerator, cdecl}; +use crate::{ + BoxStr, + MapInput, + TestGenerator, + cdecl, +}; /// An error that occurs during translation, detailing cause and location. #[derive(Debug, Error)] diff --git a/ctest/tests/basic.rs b/ctest/tests/basic.rs index b1fce9cb1f116..6718456f32da9 100644 --- a/ctest/tests/basic.rs +++ b/ctest/tests/basic.rs @@ -1,7 +1,19 @@ -use std::path::{Path, PathBuf}; -use std::{env, fs}; - -use ctest::{__compile_test, __run_test, Result, TestGenerator, generate_test}; +use std::path::{ + Path, + PathBuf, +}; +use std::{ + env, + fs, +}; + +use ctest::{ + __compile_test, + __run_test, + Result, + TestGenerator, + generate_test, +}; use pretty_assertions::assert_eq; // Headers are found relevative to the include directory, all files are generated diff --git a/libc-test/build.rs b/libc-test/build.rs index 7e7d28c1d19a0..4716febb83f44 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -2,9 +2,20 @@ #![allow(clippy::match_like_matches_macro)] use std::fs::File; -use std::io::{BufRead, BufReader, BufWriter, Write}; -use std::path::{Path, PathBuf}; -use std::{env, io}; +use std::io::{ + BufRead, + BufReader, + BufWriter, + Write, +}; +use std::path::{ + Path, + PathBuf, +}; +use std::{ + env, + io, +}; fn do_cc() { let target = env::var("TARGET").unwrap(); diff --git a/libc-test/tests/cmsg.rs b/libc-test/tests/cmsg.rs index 18b3a58b968b7..b9573e1d040af 100644 --- a/libc-test/tests/cmsg.rs +++ b/libc-test/tests/cmsg.rs @@ -6,7 +6,14 @@ mod t { use std::mem; - use libc::{self, c_uchar, c_uint, c_void, cmsghdr, msghdr}; + use libc::{ + self, + c_uchar, + c_uint, + c_void, + cmsghdr, + msghdr, + }; extern "C" { pub fn cmsg_firsthdr(msgh: *const msghdr) -> *mut cmsghdr; diff --git a/libc-test/tests/errqueue.rs b/libc-test/tests/errqueue.rs index a7d1563c5cf39..c3d9c2e69a496 100644 --- a/libc-test/tests/errqueue.rs +++ b/libc-test/tests/errqueue.rs @@ -2,7 +2,11 @@ #[cfg(any(target_os = "linux", target_os = "android"))] mod t { - use libc::{self, sock_extended_err, sockaddr}; + use libc::{ + self, + sock_extended_err, + sockaddr, + }; extern "C" { pub fn so_ee_offender(ee: *const sock_extended_err) -> *mut sockaddr; diff --git a/libc-test/tests/makedev.rs b/libc-test/tests/makedev.rs index 1c08776d7260f..a5c1ec2ffc580 100644 --- a/libc-test/tests/makedev.rs +++ b/libc-test/tests/makedev.rs @@ -13,7 +13,11 @@ target_os = "cygwin", ))] -use libc::{self, c_uint, dev_t}; +use libc::{ + self, + c_uint, + dev_t, +}; cfg_if::cfg_if! { if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { diff --git a/libc-test/tests/style.rs b/libc-test/tests/style.rs index d5af8dddbf973..dcbe43bb59651 100644 --- a/libc-test/tests/style.rs +++ b/libc-test/tests/style.rs @@ -14,7 +14,10 @@ pub mod style_lib; use std::env; use std::path::Path; -use style_lib::{Result, StyleChecker}; +use style_lib::{ + Result, + StyleChecker, +}; /// Relative to `src/`. const SKIP_PREFIXES: &[&str] = &[ diff --git a/libc-test/tests/style_lib/mod.rs b/libc-test/tests/style_lib/mod.rs index cc953d32c3aed..04eec91df635d 100644 --- a/libc-test/tests/style_lib/mod.rs +++ b/libc-test/tests/style_lib/mod.rs @@ -28,13 +28,26 @@ use std::collections::HashMap; use std::fs; use std::ops::Deref; -use std::path::{Path, PathBuf}; - -use annotate_snippets::{Level, Renderer, Snippet}; +use std::path::{ + Path, + PathBuf, +}; + +use annotate_snippets::{ + Level, + Renderer, + Snippet, +}; use proc_macro2::Span; -use syn::parse::{Parse, ParseStream}; +use syn::parse::{ + Parse, + ParseStream, +}; use syn::spanned::Spanned; -use syn::visit::{self, Visit}; +use syn::visit::{ + self, + Visit, +}; use syn::Token; const ALLOWED_REPEATED_MACROS: &[&str] = &["s", "s_no_extra_traits", "s_paren"]; diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 8016e2bbfeca9..0000000000000 --- a/rustfmt.toml +++ /dev/null @@ -1,5 +0,0 @@ -# Note that there is a separate configuration for `src/` -edition = "2021" -error_on_line_overflow = true -group_imports = "StdExternalCrate" -imports_granularity = "Module" From 9da2ec928f25ef8fd412008ee6ca79a390446c8b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 20 Nov 2025 03:27:24 -0600 Subject: [PATCH 2/2] fmt: Replace style.sh with style.py We need a bit more flexibility in the formatting, which would be difficult to achieve with bash. Rewrite the style check to a python script that will be easier to extend. This is also faster than using inplace perl, an now prints a diff when semver files aren't sorted. --- .github/workflows/ci.yaml | 2 +- CONTRIBUTING.md | 2 +- ci/style.py | 165 ++++++++++++++++++++++++++++++++++++++ ci/style.sh | 90 --------------------- 4 files changed, 167 insertions(+), 92 deletions(-) create mode 100755 ci/style.py delete mode 100755 ci/style.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dd8ca94b1196b..2b9d46997ce56 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,7 +32,7 @@ jobs: - name: Setup Rust toolchain run: ./ci/install-rust.sh && rustup component add rustfmt - name: Check style - run: ./ci/style.sh + run: ./ci/style.py clippy: name: Clippy on ${{ matrix.os }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0cdfaeadf9059..f43cda673d37b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -93,7 +93,7 @@ We have two automated tests running on - `cd libc-test && cargo test` - Use the `skip_*()` functions in `build.rs` if you really need a workaround. 2. Style checker - - [`./ci/style.sh`](https://github.com/rust-lang/libc/blob/main/ci/style.sh) + - [`./ci/style.py`](https://github.com/rust-lang/libc/blob/main/ci/style.py) ## Breaking change policy diff --git a/ci/style.py b/ci/style.py new file mode 100755 index 0000000000000..18aa60d1bdb4c --- /dev/null +++ b/ci/style.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 + +import os +import re +import subprocess as sp +import sys + +from difflib import unified_diff +from glob import iglob +from pathlib import Path + + +FMT_DIRS = ["src", "ci"] +IGNORE_FILES = [ + # Too much special syntax that we don't want to format + "src/macros.rs" +] + + +def main(): + # if `CI` is set, do a check rather than overwriting + check_only = os.getenv("CI") is not None + run(["rustfmt", "-V"]) + + fmt_files = [] + for dir in FMT_DIRS: + fmt_files.extend(iglob(f"{dir}/**/*.rs", recursive=True)) + + for file in fmt_files: + if file in IGNORE_FILES: + continue + fmt_one(Path(file), check_only) + + # Run once from workspace root to get everything that wasn't handled as an + # individual file. + if check_only: + run(["cargo", "fmt", "--check"]) + else: + run(["cargo", "fmt"]) + + for file in iglob("libc-test/semver/*.txt"): + check_semver_file(Path(file)) + + # Style tests + run( + [ + "cargo", + "test", + "--manifest-path=libc-test/Cargo.toml", + "--test=style", + "--", + "--nocapture", + ] + ) + + try: + run(["shellcheck", "--version"]) + except sp.CalledProcessError: + eprint("ERROR: shellcheck not found") + exit(1) + + for file in iglob("**/*.sh", recursive=True): + run(["shellcheck", file]) + + +def fmt_one(fpath: Path, check_only: bool): + eprint(f"Formatting {fpath}") + text = fpath.read_text() + + # Rustfmt doesn't format the bodies of `{ ... }` macros, which is most of `libc`. To + # make things usable, we do some hacks to replace macros with some kind of + # alternative syntax that gets formatted about how we want, then reset the changes + # after formatting. + + # Turn all braced macro `foo! { /* ... */ }` invocations into + # `fn foo_fmt_tmp() { /* ... */ }`, since our macro bodies are usually valid in + # a function context. + text = re.sub(r"(?!macro_rules)\b(\w+)!\s*\{", r"fn \1_fmt_tmp() {", text) + + # Replace `if #[cfg(...)]` within `cfg_if` with `if cfg_tmp!([...])` which + # `rustfmt` will format. We put brackets within the parens so it is easy to + # match (trying to match parentheses would catch the first closing `)` which + # wouldn't be correct for something like `all(any(...), ...)`). + text = re.sub(r"if #\[cfg\((.*?)\)\]", r"if cfg_tmp!([\1])", text, flags=re.DOTALL) + + # The `c_enum!` macro allows anonymous enums without names, which isn't valid + # syntax. Replace it with a dummy name. + text = re.sub(r"enum #anon\b", r"enum _fmt_anon", text) + + # Invoke rustfmt passing via stdin/stdout so we don't need to write the file. Exits + # on failure. + cmd = ["rustfmt", "--config-path=.rustfmt.toml"] + if check_only: + res = check_output(cmd + ["--check"], input=text) + + # Unfortunately rustfmt on stdin always completes with 0 exit code even if + # there are errors, so we need to pick between writing the file to disk or + # relying on empty stdout to indicate success. + # . + if len(res) == 0: + return + eprint(f"ERROR: File {fpath} is not properly formatted") + print(res) + exit(1) + else: + text = check_output(cmd, input=text) + + # Restore all changes in the formatted text + text = re.sub(r"fn (\w+)_fmt_tmp\(\)", r"\1!", text) + text = re.sub(r"cfg_tmp!\(\[(.*?)\]\)", r"#[cfg(\1)]", text, flags=re.DOTALL) + text = re.sub(r"enum _fmt_anon", r"enum #anon", text) + + # And write the formatted file back + fpath.write_text(text) + + +def check_semver_file(fpath: Path): + if "TODO" in str(fpath): + eprint(f"Skipping semver file {fpath}") + return + + eprint(f"Checking semver file {fpath}") + + text = fpath.read_text() + lines = text.splitlines() + sort = sorted(lines) + if lines != sort: + eprint(f"ERROR: Unsorted semver file {fpath}") + eprint("\n".join(unified_diff(lines, sort, lineterm=""))) + exit(1) + + duplicates = [] + seen = set() + for line in lines: + if line in seen: + duplicates.append(line) + seen.add(line) + + if len(duplicates) > 0: + eprint(f"ERROR: Duplicates in semver file {fpath}") + eprint(duplicates) + exit(1) + + +def check_output(args: list[str], **kw) -> str: + xtrace(args) + return sp.check_output(args, encoding="utf8", text=True, **kw) + + +def run(args: list[str], **kw) -> sp.CompletedProcess: + xtrace(args) + return sp.run(args, check=True, text=True, **kw) + + +def xtrace(args: list[str]): + astr = " ".join(args) + eprint(f"+ {astr}") + + +def eprint(*args, **kw): + print(*args, file=sys.stderr, **kw) + + +if __name__ == "__main__": + main() diff --git a/ci/style.sh b/ci/style.sh deleted file mode 100755 index ef57a5efd0402..0000000000000 --- a/ci/style.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/sh - -set -eux - -[ -n "${CI:-}" ] && check="--check" - -cargo test --manifest-path libc-test/Cargo.toml --test style -- --nocapture - -command -v rustfmt -rustfmt -V - -# Save a list of all source files -tmpfile="file-list~" # trailing tilde for gitignore -find src ci -name '*.rs' > "$tmpfile" - -# Before formatting, replace all macro identifiers with a function signature. -# This allows `rustfmt` to format it. -while IFS= read -r file; do - if [ "$file" = "src/macros.rs" ]; then - # Too much special syntax in `macros.rs` that we don't want to format - continue - fi - - # Turn all braced macro `foo! { /* ... */ }` invocations into - # `fn foo_fmt_tmp() { /* ... */ }`. - perl -pi -e 's/(?!macro_rules)\b(\w+)!\s*\{/fn $1_fmt_tmp() {/g' "$file" - - # Replace `if #[cfg(...)]` within `cfg_if` with `if cfg_tmp!([...])` which - # `rustfmt` will format. We put brackets within the parens so it is easy to - # match (trying to match parentheses would catch the first closing `)` which - # wouldn't be correct for something like `all(any(...), ...)`). - perl -pi -0777 -e 's/if #\[cfg\((.*?)\)\]/if cfg_tmp!([$1])/gms' "$file" - - # The `c_enum!` macro allows anonymous enums without names, which - # isn't valid syntax. Replace it with a dummy name and an indicator - # comment on the preceding line (which is where rustfmt puts it. Also - # rust-lang/rustfmt#5464). - perl -pi -e 's/^(\s*)(.*)enum #anon\b/$1\/\* FMT-ANON-ENUM \*\/\n$1$2enum _fmt_anon/g' "$file" - - # Format the file. We need to invoke `rustfmt` directly since `cargo fmt` - # can't figure out the module tree with the hacks in place. - failed=false - rustfmt "$file" ${check:+"$check"} || failed=true - - # Restore all changes to the files. - perl -pi -e 's/fn (\w+)_fmt_tmp\(\)/$1!/g' "$file" - perl -pi -0777 -e 's/cfg_tmp!\(\[(.*?)\]\)/#[cfg($1)]/gms' "$file" - perl -pi -0777 -e 's/\/\* FMT-ANON-ENUM \*\/(?:\n\s*)?(.*?)enum _fmt_anon/$1enum #anon/gms' "$file" - - # Defer emitting the failure until after the files get reset - if [ "$failed" != "false" ]; then - echo "Formatting failed" - exit 1 - fi -done < "$tmpfile" - -rm "$tmpfile" - -# Run once from workspace root to get everything that wasn't handled as an -# individual file. -cargo fmt ${check:+"$check"} - -# Ensure that `sort` output is not locale-dependent -export LC_ALL=C - -for file in libc-test/semver/*.txt; do - case "$file" in - *TODO*) continue ;; - esac - - if ! sort -C "$file"; then - echo "Unsorted semver file $file" - exit 1 - fi - - duplicates=$(uniq -d "$file") - if [ -n "$duplicates" ]; then - echo "Semver file $file contains duplicates:" - echo "$duplicates" - - exit 1 - fi -done - -if shellcheck --version; then - find . -name '*.sh' -print0 | xargs -0 shellcheck -else - echo "shellcheck not found" - exit 1 -fi