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