xref: /OK3568_Linux_fs/buildroot/utils/test-pkg (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#!/usr/bin/env bash
2*4882a593Smuzhiyunset -e
3*4882a593Smuzhiyun
4*4882a593SmuzhiyunTOOLCHAINS_CSV='support/config-fragments/autobuild/toolchain-configs.csv'
5*4882a593SmuzhiyunTEMP_CONF=""
6*4882a593Smuzhiyun
7*4882a593Smuzhiyundo_clean() {
8*4882a593Smuzhiyun    if [ ! -z "${TEMP_CONF}" ]; then
9*4882a593Smuzhiyun        rm -f "${TEMP_CONF}"
10*4882a593Smuzhiyun    fi
11*4882a593Smuzhiyun}
12*4882a593Smuzhiyun
13*4882a593Smuzhiyunmain() {
14*4882a593Smuzhiyun    local o O opts
15*4882a593Smuzhiyun    local cfg dir pkg random toolchains_csv toolchain all number mode prepare_only
16*4882a593Smuzhiyun    local ret nb nb_skip nb_fail nb_legal nb_tc build_dir keep
17*4882a593Smuzhiyun    local -a toolchains
18*4882a593Smuzhiyun    local pkg_br_name
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun    o='hakc:d:n:p:r:t:'
21*4882a593Smuzhiyun    O='help,all,keep,prepare-only,config-snippet:,build-dir:,number:,package:,random:,toolchains-csv:'
22*4882a593Smuzhiyun    opts="$(getopt -n "${my_name}" -o "${o}" -l "${O}" -- "${@}")"
23*4882a593Smuzhiyun    eval set -- "${opts}"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun    random=0
26*4882a593Smuzhiyun    all=0
27*4882a593Smuzhiyun    keep=0
28*4882a593Smuzhiyun    number=0
29*4882a593Smuzhiyun    mode=0
30*4882a593Smuzhiyun    prepare_only=0
31*4882a593Smuzhiyun    toolchains_csv="${TOOLCHAINS_CSV}"
32*4882a593Smuzhiyun    while [ ${#} -gt 0 ]; do
33*4882a593Smuzhiyun        case "${1}" in
34*4882a593Smuzhiyun        (-h|--help)
35*4882a593Smuzhiyun            help; exit 0
36*4882a593Smuzhiyun            ;;
37*4882a593Smuzhiyun        (-a|--all)
38*4882a593Smuzhiyun            all=1; shift 1
39*4882a593Smuzhiyun            ;;
40*4882a593Smuzhiyun        (-k|--keep)
41*4882a593Smuzhiyun            keep=1; shift 1
42*4882a593Smuzhiyun            ;;
43*4882a593Smuzhiyun        (--prepare-only)
44*4882a593Smuzhiyun            prepare_only=1; shift 1
45*4882a593Smuzhiyun            ;;
46*4882a593Smuzhiyun        (-c|--config-snippet)
47*4882a593Smuzhiyun            cfg="${2}"; shift 2
48*4882a593Smuzhiyun            ;;
49*4882a593Smuzhiyun        (-d|--build-dir)
50*4882a593Smuzhiyun            dir="${2}"; shift 2
51*4882a593Smuzhiyun            ;;
52*4882a593Smuzhiyun        (-n|--number)
53*4882a593Smuzhiyun            number="${2}"; shift 2
54*4882a593Smuzhiyun            ;;
55*4882a593Smuzhiyun        (-p|--package)
56*4882a593Smuzhiyun            pkg="${2}"; shift 2
57*4882a593Smuzhiyun            ;;
58*4882a593Smuzhiyun        (-r|--random)
59*4882a593Smuzhiyun            random="${2}"; shift 2
60*4882a593Smuzhiyun            ;;
61*4882a593Smuzhiyun        (-t|--toolchains-csv)
62*4882a593Smuzhiyun            toolchains_csv="${2}"; shift 2
63*4882a593Smuzhiyun            ;;
64*4882a593Smuzhiyun        (--)
65*4882a593Smuzhiyun            shift; break
66*4882a593Smuzhiyun            ;;
67*4882a593Smuzhiyun        esac
68*4882a593Smuzhiyun    done
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun    trap do_clean INT TERM HUP EXIT
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun    if [ -z "${cfg}" ]; then
73*4882a593Smuzhiyun        pkg_br_name="${pkg//-/_}"
74*4882a593Smuzhiyun        pkg_br_name="BR2_PACKAGE_${pkg_br_name^^}"
75*4882a593Smuzhiyun        TEMP_CONF=$(mktemp /tmp/test-${pkg}-config.XXXXXX)
76*4882a593Smuzhiyun        echo "${pkg_br_name}=y" > ${TEMP_CONF}
77*4882a593Smuzhiyun        cfg="${TEMP_CONF}"
78*4882a593Smuzhiyun    fi
79*4882a593Smuzhiyun    if [ ! -e "${cfg}" ]; then
80*4882a593Smuzhiyun        printf "error: %s: no such file\n" "${cfg}" >&2; exit 1
81*4882a593Smuzhiyun    fi
82*4882a593Smuzhiyun    if [ -z "${dir}" ]; then
83*4882a593Smuzhiyun        dir="${HOME}/br-test-pkg"
84*4882a593Smuzhiyun    fi
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun    if [ ${random} -gt 0 ]; then
87*4882a593Smuzhiyun        mode=$((mode+1))
88*4882a593Smuzhiyun    fi
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun    if [ ${number} -gt 0 ]; then
91*4882a593Smuzhiyun        mode=$((mode+1))
92*4882a593Smuzhiyun    fi
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun    if [ ${all} -eq 1 ]; then
95*4882a593Smuzhiyun        mode=$((mode+1))
96*4882a593Smuzhiyun    fi
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun    # Default mode is to test the N first toolchains, which have been
99*4882a593Smuzhiyun    # chosen to be a good selection of toolchains.
100*4882a593Smuzhiyun    if [ ${mode} -eq 0 ] ; then
101*4882a593Smuzhiyun        number=6
102*4882a593Smuzhiyun    elif [ ${mode} -gt 1 ] ; then
103*4882a593Smuzhiyun        printf "error: --all, --number and --random are mutually exclusive\n" >&2; exit 1
104*4882a593Smuzhiyun    fi
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun    # Extract the URLs of the toolchains; drop internal toolchains
107*4882a593Smuzhiyun    # E.g.: http://server/path/to/name.config,arch,libc
108*4882a593Smuzhiyun    #  -->  http://server/path/to/name.config
109*4882a593Smuzhiyun    toolchains=($(sed -r -e 's/,.*//; /internal/d; /^#/d; /^$/d;' "${toolchains_csv}" \
110*4882a593Smuzhiyun                  |if [ ${random} -gt 0 ]; then \
111*4882a593Smuzhiyun                      sort -R |head -n ${random}
112*4882a593Smuzhiyun                  elif [ ${number} -gt 0 ]; then \
113*4882a593Smuzhiyun                      head -n ${number}
114*4882a593Smuzhiyun                  else
115*4882a593Smuzhiyun                      sort
116*4882a593Smuzhiyun                  fi
117*4882a593Smuzhiyun                 )
118*4882a593Smuzhiyun               )
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun    nb_tc="${#toolchains[@]}"
121*4882a593Smuzhiyun    if [ ${nb_tc} -eq 0 ]; then
122*4882a593Smuzhiyun        printf "error: no toolchain found (networking issue?)\n" >&2; exit 1
123*4882a593Smuzhiyun    fi
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun    nb=0
126*4882a593Smuzhiyun    nb_skip=0
127*4882a593Smuzhiyun    nb_fail=0
128*4882a593Smuzhiyun    nb_legal=0
129*4882a593Smuzhiyun    for toolchainconfig in "${toolchains[@]}"; do
130*4882a593Smuzhiyun        : $((nb++))
131*4882a593Smuzhiyun        toolchain="$(basename "${toolchainconfig}" .config)"
132*4882a593Smuzhiyun        build_dir="${dir}/${toolchain}"
133*4882a593Smuzhiyun        printf "%40s [%*d/%d]: " "${toolchain}" ${#nb_tc} ${nb} ${nb_tc}
134*4882a593Smuzhiyun        build_one "${build_dir}" "${toolchainconfig}" "${cfg}" "${pkg}" "${prepare_only}" && ret=0 || ret=${?}
135*4882a593Smuzhiyun        case ${ret} in
136*4882a593Smuzhiyun        (0) printf "OK\n";;
137*4882a593Smuzhiyun        (1) : $((nb_skip++)); printf "SKIPPED\n";;
138*4882a593Smuzhiyun        (2) : $((nb_fail++)); printf "FAILED\n";;
139*4882a593Smuzhiyun        (3) : $((nb_legal++)); printf "FAILED\n";;
140*4882a593Smuzhiyun        esac
141*4882a593Smuzhiyun    done
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun    printf "%d builds, %d skipped, %d build failed, %d legal-info failed\n" \
144*4882a593Smuzhiyun        ${nb} ${nb_skip} ${nb_fail} ${nb_legal}
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun    return $((nb_fail + nb_legal))
147*4882a593Smuzhiyun}
148*4882a593Smuzhiyun
149*4882a593Smuzhiyunbuild_one() {
150*4882a593Smuzhiyun    local dir="${1}"
151*4882a593Smuzhiyun    local toolchainconfig="${2}"
152*4882a593Smuzhiyun    local cfg="${3}"
153*4882a593Smuzhiyun    local pkg="${4}"
154*4882a593Smuzhiyun    local prepare_only="${5}"
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun    mkdir -p "${dir}"
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun    CONFIG_= support/kconfig/merge_config.sh -O "${dir}" \
159*4882a593Smuzhiyun        "${toolchainconfig}" "support/config-fragments/minimal.config" "${cfg}" \
160*4882a593Smuzhiyun        >> "${dir}/logfile" 2>&1
161*4882a593Smuzhiyun    # We want all the options from the snippet to be present as-is (set
162*4882a593Smuzhiyun    # or not set) in the actual .config; if one of them is not, it means
163*4882a593Smuzhiyun    # some dependency from the toolchain or arch is not available, in
164*4882a593Smuzhiyun    # which case this config is untestable and we skip it.
165*4882a593Smuzhiyun    # We don't care about the locale to sort in, as long as both sort are
166*4882a593Smuzhiyun    # done in the same locale.
167*4882a593Smuzhiyun    comm -23 <(sort "${cfg}") <(sort "${dir}/.config") >"${dir}/missing.config"
168*4882a593Smuzhiyun    if [ -s "${dir}/missing.config" ]; then
169*4882a593Smuzhiyun        if [ ${keep} -ne 1 ]; then
170*4882a593Smuzhiyun            # Invalid configuration, drop it
171*4882a593Smuzhiyun            rm -f "${dir}/.config"
172*4882a593Smuzhiyun        fi
173*4882a593Smuzhiyun        return 1
174*4882a593Smuzhiyun    fi
175*4882a593Smuzhiyun    # Remove file, it's empty anyway.
176*4882a593Smuzhiyun    rm -f "${dir}/missing.config"
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun    # Defer building the job to the caller (e.g. a gitlab pipeline)
179*4882a593Smuzhiyun    if [ ${prepare_only} -eq 1 ]; then
180*4882a593Smuzhiyun        return 0
181*4882a593Smuzhiyun    fi
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun    if [ -n "${pkg}" ]; then
184*4882a593Smuzhiyun        if ! make O="${dir}" "${pkg}-dirclean" >> "${dir}/logfile" 2>&1; then
185*4882a593Smuzhiyun            return 2
186*4882a593Smuzhiyun        fi
187*4882a593Smuzhiyun    fi
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun    # shellcheck disable=SC2086
190*4882a593Smuzhiyun    if ! BR_FORCE_CHECK_DEPENDENCIES=YES make O="${dir}" ${pkg} >> "${dir}/logfile" 2>&1; then
191*4882a593Smuzhiyun        return 2
192*4882a593Smuzhiyun    fi
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun    # legal-info done systematically, because some packages have different
195*4882a593Smuzhiyun    # sources depending on the configuration (e.g. lua-5.2 vs. lua-5.3)
196*4882a593Smuzhiyun    if ! make O="${dir}" legal-info >> "${dir}/logfile" 2>&1; then
197*4882a593Smuzhiyun        return 3
198*4882a593Smuzhiyun    fi
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun    # If we get here, the build was successful. Clean up the build/host
201*4882a593Smuzhiyun    # directories to save disk space, unless 'keep' was set.
202*4882a593Smuzhiyun    if [ ${keep} -ne 1 ]; then
203*4882a593Smuzhiyun        make O="${dir}" clean >> "${dir}/logfile" 2>&1
204*4882a593Smuzhiyun    fi
205*4882a593Smuzhiyun}
206*4882a593Smuzhiyun
207*4882a593Smuzhiyunhelp() {
208*4882a593Smuzhiyun    cat <<_EOF_
209*4882a593Smuzhiyuntest-pkg: test-build a package against various toolchains and architectures
210*4882a593Smuzhiyun
211*4882a593SmuzhiyunThe supplied config snippet is appended to each toolchain config, the
212*4882a593Smuzhiyunresulting configuration is checked to ensure it still contains all options
213*4882a593Smuzhiyunspecified in the snippet; if any is missing, the build is skipped, on the
214*4882a593Smuzhiyunassumption that the package under test requires a toolchain or architecture
215*4882a593Smuzhiyunfeature that is missing.
216*4882a593Smuzhiyun
217*4882a593SmuzhiyunIn case failures are noticed, you can fix the package and just re-run the
218*4882a593Smuzhiyunsame command again; it will re-run the test where it failed. If you did
219*4882a593Smuzhiyunspecify a package (with -p), the package build dir will be removed first.
220*4882a593Smuzhiyun
221*4882a593SmuzhiyunThe list of toolchains is retrieved from ${TOOLCHAINS_CSV}.
222*4882a593SmuzhiyunOnly the external toolchains are tried, because building a Buildroot toolchain
223*4882a593Smuzhiyunwould take too long. An alternative toolchains CSV file can be specified with
224*4882a593Smuzhiyunthe -t option. This file should have lines consisting of the path to the
225*4882a593Smuzhiyuntoolchain config fragment and the required host architecture, separated by a
226*4882a593Smuzhiyuncomma. The config fragments should contain only the toolchain and architecture
227*4882a593Smuzhiyunsettings.
228*4882a593Smuzhiyun
229*4882a593SmuzhiyunBy default, a useful subset of toolchains is tested. If needed, all
230*4882a593Smuzhiyuntoolchains can be tested (-a), an arbitrary number of toolchains (-n
231*4882a593Smuzhiyunin order, -r for random).
232*4882a593Smuzhiyun
233*4882a593SmuzhiyunOptions:
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun    -h, --help
236*4882a593Smuzhiyun        Print this help.
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun    -c CFG, --config-snippet CFG
239*4882a593Smuzhiyun        Use the CFG file as the source for the config snippet. This file
240*4882a593Smuzhiyun        should contain all the config options required to build a package.
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun    -d DIR, --build-dir DIR
243*4882a593Smuzhiyun        Do the builds in directory DIR, one sub-dir per toolchain.
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun    -p PKG, --package PKG
246*4882a593Smuzhiyun        Test-build the package PKG, by running 'make PKG'; if not specified,
247*4882a593Smuzhiyun        just runs 'make'.
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun    -a, --all
250*4882a593Smuzhiyun        Test all toolchains, instead of the default subset defined by
251*4882a593Smuzhiyun        Buildroot developers.
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun    -n N, --number N
254*4882a593Smuzhiyun        Test N toolchains, in the order defined in the toolchain CSV
255*4882a593Smuzhiyun        file.
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun    -r N, --random N
258*4882a593Smuzhiyun        Limit the tests to the N randomly selected toolchains.
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun    -t CSVFILE, --toolchains-csv CSVFILE
261*4882a593Smuzhiyun        CSV file containing the paths to config fragments of toolchains to
262*4882a593Smuzhiyun        try. If not specified, the toolchains in ${TOOLCHAINS_CSV} will be
263*4882a593Smuzhiyun        used.
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun    -k, --keep
266*4882a593Smuzhiyun        Keep the build directories even if the build succeeds.
267*4882a593Smuzhiyun        Note: the logfile and configuration is always retained, even without
268*4882a593Smuzhiyun        this option.
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun    --prepare-only
271*4882a593Smuzhiyun        Only prepare the .config files, but do not build them. Output the
272*4882a593Smuzhiyun        list of build directories to stdout, and the status on stderr.
273*4882a593Smuzhiyun
274*4882a593SmuzhiyunExample:
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun    Testing libcec would require a config snippet that contains:
277*4882a593Smuzhiyun        BR2_PACKAGE_LIBCEC=y
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun    Testing libcurl with openSSL support would require a snippet such as:
280*4882a593Smuzhiyun        BR2_PACKAGE_OPENSSL=y
281*4882a593Smuzhiyun        BR2_PACKAGE_LIBCURL=y
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun_EOF_
284*4882a593Smuzhiyun}
285*4882a593Smuzhiyun
286*4882a593Smuzhiyunmy_name="${0##*/}"
287*4882a593Smuzhiyunmain "${@}"
288