#71 speed up /usr/lib/rpm/redhat/brp-mangle-shebangs
Closed 3 years ago by ignatenkobrain. Opened 3 years ago by vda.
Unknown source master  into  master

file modified
+36 -20
@@ -70,13 +70,17 @@

  

  cd "$RPM_BUILD_ROOT"

  

- trim() {

-   printf '%s' "$*"

- }

- 

+ # Large packages such as kernel can have thousands of executable files.

+ # We take care to not fork/exec thousands of "file"s and "grep"s,

+ # but run just two of them.

+ # (Take care to exclude filenames which would mangle "file" output).

+ find -executable -type f ! -path '*:*' ! -path $'*\n*' \

+ | file -N --mime-type -f - \

+ | grep -P ".+(?=: text/)" \

+ | {

  fail=0

- while IFS= read -r -d $'\0' f; do

-   file -N --mime-type "$f" | grep -q -P ".+(?=: text/)" || continue

+ while IFS= read -r line; do

+   f=${line%%:*}

  

    # Remove the dot

    path="${f#.}"
@@ -88,24 +92,34 @@

      echo "$path" | grep -q -E -f "$exclude_files_from" && continue

    fi

  

-   ts=$(stat -c %y "$f")

  

-   read shebang_line < "$f" || :

-   orig_shebang=$(trim $(echo "$shebang_line" | grep -Po "#!\K.*" || echo))

-   shebang="$orig_shebang"

-   if [ -n "$exclude_shebangs" ]; then

-     echo "$shebang" | grep -q -E "$exclude_shebangs" && continue

-   fi

-   if [ -n "$exclude_shebangs_from" ]; then

-     echo "$shebang" | grep -q -E -f "$exclude_shebangs_from" && continue

+   read shebang_line < "$f"

+   orig_shebang="${shebang_line#\#!}"

+   if [ "$orig_shebang" = "$shebang_line" ]; then

+     echo >&2 "*** WARNING: $f is executable but has no shebang, removing executable bit"

+     ts=$(stat -c %y "$f")

+     chmod -x "$f"

+     touch -d "$ts" "$f"

+     continue

    fi

  

+   # Trim spaces

+   while shebang="${orig_shebang//  / }"; [ "$shebang" != "$orig_shebang" ]; do

+     orig_shebang="$shebang"

+   done

+   # Treat "#! /path/to " as "#!/path/to"

+   orig_shebang="${orig_shebang# }"

+ 

+   shebang="$orig_shebang"

+ 

    if [ -z "$shebang" ]; then

-     echo >&2 "*** WARNING: $f is executable but has empty or no shebang, removing executable bit"

+     echo >&2 "*** WARNING: $f is executable but has empty shebang, removing executable bit"

+     ts=$(stat -c %y "$f")

      chmod -x "$f"

      touch -d "$ts" "$f"

      continue

-   elif [ -n "${shebang##/*}" ]; then

+   fi

+   if [ -n "${shebang##/*}" ]; then

      echo >&2 "*** ERROR: $f has shebang which doesn't start with '/' ($shebang)"

      fail=1

      continue
@@ -132,11 +146,13 @@

      echo >&2 "*** ERROR: ambiguous python shebang in $path: #!$orig_shebang. Change it to python3 (or python2) explicitly."

      fail=1

    elif [ "#!$shebang" != "#!$orig_shebang" ]; then

-     sed -i -e "1c #!$shebang" "$f"

      echo "mangling shebang in $path from $orig_shebang to #!$shebang"

+     ts=$(stat -c %y "$f")

+     sed -i -e "1c #!$shebang" "$f"

+     touch -d "$ts" "$f"

    fi

  

-   touch -d "$ts" "$f"

- done < <(find -executable -type f -print0)

+ done

  

  exit $fail

+ }

file modified
+4 -1
@@ -6,7 +6,7 @@

  

  Summary: Red Hat specific rpm configuration files

  Name: redhat-rpm-config

- Version: 142

+ Version: 143

  Release: 1%{?dist}

  # No version specified.

  License: GPL+
@@ -207,6 +207,9 @@

  %{_rpmconfigdir}/macros.d/macros.kmp

  

  %changelog

+ * Thu Nov 21 2019 Denys Vlasenko <dvlasenk@redhat.com> - 143-1

+ - Speed up brp-mangle-shebangs.

+ 

  * Fri Nov 01 2019 Miro HronĨok <mhroncok@redhat.com> - 142-1

  - Fix the simple API of %%gpgverify.

  

See https://bugzilla.redhat.com/show_bug.cgi?id=1773216

For kernel builds, /usr/lib/rpm/redhat/brp-mangle-shebangs runs for 25 seconds.
It's mainly caused by this loop executing "file" and "grep" for every file in the tree:

fail=0
while IFS= read -r -d $'\0' f; do
file -N --mime-type "$f" | grep -q -P ".+(?=: text/)" || continue
...
done < <(find -executable -type f -print0)

exit $fail

The following replacement runs one "file" and one "grep" process to examine all files:

find -executable -type f ! -path ':' ! -path $'\n' \
| file -N --mime-type -f - \
| grep -P ".+(?=: text/)" \
| {
fail=0
while IFS= read -r line; do
f=${line%%:*}
...
done

exit $fail
}

The complicated "find" invocation is skipping any files with colons or newlines in their names.

Tested by me to work, and completes in 3 seconds.

Please bump version in spec and add changelog. LGTM otherwise.

1 new commit added

  • Update spec file
3 years ago

rebased onto 4f1703c53c1136c4162bd3c9cd63c77a73889d15

3 years ago

rebased onto 7b3ab97e9756b64c9d7610d4434a4efd73939392

3 years ago

rebased onto 03c9653

3 years ago

Merged manually, thanks for the contribution!

Pull-Request has been closed by ignatenkobrain

3 years ago