[Buildroot] [PATCH] utils/brmake: filter output for parallel build

Yann E. MORIN yann.morin.1998 at free.fr
Fri Oct 17 15:08:58 UTC 2025


Vincent, Marcus, All,

On 2025-10-17 16:39 +0200, Vincent Stehlé spake thusly:
> On Fri, Oct 17, 2025 at 04:12:34PM +0200, Marcus Hoffmann wrote:
> > I have seen the following thing happen once during a CI run, where 2 of the
> > lines where truncated in the middle of the timestamp. I have looked through
> > a bunch of build logs now but haven't seen this again, so not sure if that's
> > an actual problem we need to care about
[--SNIP--]
> > And secondly, also in CI:
> > The "Done in Xmin Ys" text that is printed in the end is still always
> > displayed in the middle of a line during the end of the build output:
> > 
> > 2025-10-17T08:52:28 >>> owfs 3.2p4 Installing to staging directory
> > 2025-10-17T08:52:29 >>> owfs 3.2p4 Fixing libtool fDone in 7min 37s
> > iles
> > 2025-10-17T08:52:30 >>> owfs 3.2p4 Installing to target
> > 2025-10-17T08:52:38 >>> python-pydantic-core 2.33.2 Installing to target
> > 2025-10-17T08:52:52 >>> e2fsprogs 1.47.2 Building
> > [...]
[--SNIP--]
> What you described is a valid reason I think to add the --unbuffered argument to
> sed; I will respin a v2.

For what it's worth, I've locally extensively tweaked and almost
rewritten brmake over the years since I initially submitted the original
one, and which addresses all the issues I've seen mentionned in this
thread (but I only skimmed over0.

Basically, it inverses the filtering and the tee, tee-ing before
filtering. It also uses a bash coproc to beter catch-and-terminate the
process in case of Ctrl-C (but it is not 100% perfect).

The most important thing is that is forces -Otarget (unless -O was
already specified on the command line) to group per-target output and
avoid mixing output of multiple recipes.

It got quite a bit more complex, of course, but hey... :-]

With this, I can no longer observe any mangled or mixed-up line.

Feel free to properly submit it if you like it better. ;-)

Signed-off-by: Yann E. MORIN <yann.morin.1998 at free.fr>

Regards,
Yann E. MORIN.

-- 
.-----------------.--------------------.------------------.--------------------.
|  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
| +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
| +33 561 099 427 `------------.-------:  X  AGAINST      |  \e/  There is no  |
| http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
'------------------------------^-------^------------------^--------------------'
-------------- next part --------------
#!/bin/bash

trap_ctrl-c() {
    if [ -n "${MAKE_PID}" ]; then
        printf '\rInterrupted, waiting for jobs to finish...\n' >&6
        kill -INT ${MAKE_PID}
        wait ${MAKE_PID}
        exit ${?}
    fi
}

disp_time() {
    local start="${1}"
    local S h m s

    if [ "${BRMAKE_ABS_TIME}" ]; then
        printf '%(%T)T' -1
    else
        S=$((${EPOCHSECONDS:-$(date +%s)}-start))
        h=$((S/3600))
        m=$(((S%3600)/60))
        s=$((S%60))
        [ "${h}" -eq 0 ] || printf '%d:' "${h}"
        printf '%02d:%02d' "${m}" "${s}"
    fi
}

main() {
    local ret start dir_found dir O log_file outsync

    start=${BR_START:-${EPOCHSECONDS:-$(date +%s)}}

    # Crude parsing to find the directory with the .config
    dir="$(pwd)"
    dir_found=false
    outsync=-Otarget
    for i in "${@}"; do
        ${dir_found} && { dir="${i}"; dir_found=false; continue; }
        case "${i}" in
        (-C|--directory)    dir_found=true;;
        (-C*)               dir="${i#-C}";;
        (--directory=*)     dir="${i#--directory=}";;
        (O=*)               O="${i#O=}";;
        (-O|-O*)            outsync=;;
        esac
    done

    if [ -n "${O}" -a -e "${O}/.config" ]; then
        log_file="${O}/br.log"
    elif [ -z "${O}" -a -e "${dir}/.config" ]; then
        log_file="${dir}/br.log"
    else
        # No .config, don't log, and let Buildroot do its magic (i.e., whine)
        exec make "${@}"
    fi

    {   coproc LOGGER \
        {
            printf '\r%s' "$(disp_time "${start}")"
            IFS="${CR}"
            tee -a "${log_file}" \
            |sed -u -r -e '/^.{4}(>>> .+)/!d; s//\1/' \
            |while true; do
                if read -r -t 0.25 line; then
                    printf '\r%s %s%s%s\n' \
                        "$(disp_time "${start}")" \
                        "${BRMAKE_PREFIX:+${BRMAKE_PREFIX} }" \
                        "${prev}" \
                        "${line}"
                    disp_time "${start}"
                    prev=""
                elif [ ${?} -gt 128 ]; then
                    prev="${prev}${line}"
                    printf '\r%s' "$(disp_time "${start}")"
                else
                    break
                fi
            done
            exit 0
        } >&3
    } 3>&1

    trap trap_ctrl-c INT

    exec 5>&1 16>&1 6>&2 >&${LOGGER[1]} 2>&1 4>&1
    exec 16>&-
    ${MAKE:-make} ${outsync} "${@}" 2>&1 &
    MAKE_PID="${!}"
    wait ${MAKE_PID}
    ret="${?}"
    MAKE_PID=""
    exec >&5 2>&6 4>&-

    # bash seems to have issues closing an fd when it is an indexed-array
    # expansion, while it has no problem opening it (see above).
    eval exec "${LOGGER[1]}>&-"
    wait ${LOGGER_PID}

    printf '\r%s %s>>> ' "$(disp_time "${start}")" "${BRMAKE_PREFIX:+${BRMAKE_PREFIX} }"
    if [ "${BRMAKE_ABS_TIME}" ]; then
        printf 'Done in %s' "$(BRMAKE_ABS_TIME= disp_time "${start}")"
    else
        printf 'Finished at %(%T)T' -1
    fi
    if [ ${ret} -ne 0 ]; then
        printf ' (error %d)' ${ret}
    fi
    printf '\n'

    return ${ret}
}

CR="
"

main "${@}"


More information about the buildroot mailing list