|
| 1 | +#!/bin/bash |
| 2 | +# The compiler-warning gate: compile part of src/ with the gate's warning flags |
| 3 | +# and judge what the compiler says about the files this repository owns. |
| 4 | +# Handbook: handbook/build/warnings/README.md |
| 5 | +# |
| 6 | +# Usage: |
| 7 | +# scripts/warnings.sh [glue] # compile the glue, fail on a glue warning |
| 8 | +# scripts/warnings.sh vendored # compile everything else, report and exit 0 |
| 9 | +# |
| 10 | +# A warning belongs to the file it points at, not to the translation unit that |
| 11 | +# raised it: a glue file is not answerable for what an igraph header says. |
| 12 | +# So every warning is attributed to a scope by its path, |
| 13 | +# only the glue's fail the gate, and the others are counted. |
| 14 | +# |
| 15 | +# The flags reach this one compile through a makefile of its own, |
| 16 | +# never src/Makevars (CRAN rejects -Wno-* there) |
| 17 | +# and never ~/.R/Makevars (every later build in the job would read it, |
| 18 | +# R CMD check among them, and turn these warnings into its own). |
| 19 | +# The user Makevars is included, so ccache's wrappers still apply. |
| 20 | +# |
| 21 | +# Linux and macOS only: it needs the src/Makevars that ./configure writes. |
| 22 | + |
| 23 | +set -euo pipefail |
| 24 | + |
| 25 | +cd "$(dirname "$0")/.." |
| 26 | +root="$(pwd)" |
| 27 | + |
| 28 | +scope="${1:-glue}" |
| 29 | +case "${scope}" in |
| 30 | + glue | vendored) ;; |
| 31 | + *) |
| 32 | + echo "Usage: scripts/warnings.sh [glue|vendored]" >&2 |
| 33 | + exit 2 |
| 34 | + ;; |
| 35 | +esac |
| 36 | + |
| 37 | +# The reasons for each flag are the handbook page's. |
| 38 | +WARNINGS="-Wall -Wextra -pedantic -Wno-unused-parameter -Wno-cast-function-type" |
| 39 | + |
| 40 | +tmp="$(mktemp -d)" |
| 41 | +trap 'rm -rf "${tmp}"' EXIT |
| 42 | + |
| 43 | +user_makevars="${R_MAKEVARS_USER:-${HOME}/.R/Makevars}" |
| 44 | +{ |
| 45 | + [ -f "${user_makevars}" ] && echo "include ${user_makevars}" |
| 46 | + # What R CMD INSTALL puts on the command line for the LinkingTo packages. |
| 47 | + Rscript -e 'l <- tools:::.get_requires_from_package_db(read.dcf("DESCRIPTION")[1, ], "LinkingTo") |
| 48 | + cat("CLINK_CPPFLAGS =", paste0("-I\"", vapply(l, function(p) system.file("include", package = p), ""), "\""), "\n")' |
| 49 | + for v in CFLAGS CXXFLAGS CXX11FLAGS CXX14FLAGS CXX17FLAGS CXX20FLAGS FFLAGS; do |
| 50 | + echo "${v} += ${WARNINGS}" |
| 51 | + done |
| 52 | + echo 'print-%: ; @echo "objects: $($*)"' |
| 53 | +} > "${tmp}/warnings.mk" |
| 54 | + |
| 55 | +[ -f src/Makevars ] || ./configure |
| 56 | + |
| 57 | +r_make() { |
| 58 | + (cd src && R CMD make -f Makevars -f "$(R RHOME)/etc/Makeconf" -f "${tmp}/warnings.mk" "$@") |
| 59 | +} |
| 60 | + |
| 61 | +# The object lists as src/Makevars has them, one per line. |
| 62 | +# Make may say other things on the way (src/deps.mk refreshes the .dd files), |
| 63 | +# and everything here is later passed to `rm`, so only *.o words are kept. |
| 64 | +objects_of() { |
| 65 | + r_make -s "${@/#/print-}" | sed -n 's/^objects: //p' | tr ' ' '\n' | grep -E '^[A-Za-z0-9_./-]+\.o$' | sort -u |
| 66 | +} |
| 67 | + |
| 68 | +objects_of GLUE_C_SOURCES GLUE_CPP_SOURCES > "${tmp}/glue.txt" |
| 69 | +if [ "${scope}" = glue ]; then |
| 70 | + objects="$(cat "${tmp}/glue.txt")" |
| 71 | +else |
| 72 | + objects="$(objects_of OBJECTS | grep -vxF -f "${tmp}/glue.txt")" |
| 73 | +fi |
| 74 | +if [ -z "${objects}" ]; then |
| 75 | + echo "No ${scope} objects in src/Makevars; nothing was judged." |
| 76 | + exit 1 |
| 77 | +fi |
| 78 | + |
| 79 | +# Removed first, so that every object is compiled and so speaks; |
| 80 | +# ccache replays a cached compile's warnings, so a warm cache hides nothing. |
| 81 | +# shellcheck disable=SC2086 |
| 82 | +(cd src && rm -f ${objects}) |
| 83 | +# shellcheck disable=SC2086 |
| 84 | +r_make -j"$(nproc)" ${objects} > "${tmp}/build.log" 2>&1 || { |
| 85 | + cat "${tmp}/build.log" |
| 86 | + echo "The compile failed; see above." |
| 87 | + exit 1 |
| 88 | +} |
| 89 | + |
| 90 | +# One line per distinct warning, paths relative to src/ where they are ours. |
| 91 | +# R CMD check itself exempts ISO C's function pointer conversions |
| 92 | +# (tools:::.check_packages), and the igraph API stores R closures behind |
| 93 | +# igraph_function_pointer_t, so the glue cannot avoid them; they are dropped. |
| 94 | +grep -E '^[^ ].*:[0-9]+:[0-9]+: warning:' "${tmp}/build.log" | |
| 95 | + sed -e "s|^${root}/src/||" -e "s|^${root}/||" -e 's|^src/||' | |
| 96 | + grep -vE 'warning: *ISO C forbids.*function pointer' | |
| 97 | + sort -u > "${tmp}/warnings.txt" || true |
| 98 | + |
| 99 | +# vendored: src/vendor/, including the lexers whose #line points back there; |
| 100 | +# external: anything outside the checkout (R's headers, cpp11's, the system's); |
| 101 | +# glue: the rest of src/. |
| 102 | +awk -F: ' |
| 103 | + { s = ($1 ~ /(^|\/)vendor\//) ? "vendored" : ($1 ~ /^\//) ? "external" : "glue"; print s "\t" $0 } |
| 104 | +' "${tmp}/warnings.txt" > "${tmp}/scoped.txt" |
| 105 | + |
| 106 | +fortran="$(grep -c '^Warning:' "${tmp}/build.log" || true)" |
| 107 | + |
| 108 | +echo "Compiled $(echo "${objects}" | wc -w) objects with ${WARNINGS}." |
| 109 | +for s in glue vendored external; do |
| 110 | + echo " $(grep -c "^${s} " "${tmp}/scoped.txt" || true) warnings in ${s} files" |
| 111 | +done |
| 112 | +[ "${scope}" = vendored ] && echo " ${fortran} Fortran warnings (vendored ARPACK)" |
| 113 | + |
| 114 | +if [ "${scope}" = vendored ]; then |
| 115 | + cut -f2- "${tmp}/scoped.txt" |
| 116 | + exit 0 |
| 117 | +fi |
| 118 | + |
| 119 | +if grep -q '^glue ' "${tmp}/scoped.txt"; then |
| 120 | + echo |
| 121 | + echo "The glue must compile without warnings (handbook/build/warnings/README.md):" |
| 122 | + grep '^glue ' "${tmp}/scoped.txt" | cut -f2- |
| 123 | + echo |
| 124 | + echo "Context from the compiler:" |
| 125 | + cat "${tmp}/build.log" |
| 126 | + exit 1 |
| 127 | +fi |
| 128 | +echo "The glue compiles without warnings." |
0 commit comments