#!/bin/bash # SPDX-License-Identifier: GPL-2.0-only # "scc" script. # Copyright (c) 2010-2013 Wind River Systems, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. version="0.8" BASEDIR=$(dirname $BASH_SOURCE) usage() { cat << EOF scc [--help] [--configs] [--force] [-i] [-w] [--cmds ] [-o outdir:] [-D=] [-I] [-v] infiles --help: This message --force: force overwrite output file if it already exists --cmds: list of supported commands. If not passed, all found commands are loaded, valid commands are: patch, kconf, branch, define --configs: Output the list of compiled configuration fragments (if available), -o is used to locate the configuration options -D: define to which will be available to sub scripts -I: include path will be searched for files -i: leave intermediate files on failure -w: only warn on missing files -v: verbose output -o: output directory and artifacts. artifacts follow the colon and ar a comma separated list of: meta: full meta-series (patches, config, branches, etc) cfg: configure fragment queue patch: patch queue infiles files to preprocess, and output into outfile or stdout EOF } if [ -z "$1" ]; then usage exit 1 fi outdir="." while [ $# -gt 0 ]; do case "$1" in --help) usage exit ;; -D*|--D*) if [ "$1" == "-D" ] || [ "$1" == "--D" ]; then x=$2; shift; else x=`echo $1 | sed s%^-*D%%` fi defines="$defines $x" ;; -I*|--I*) if [ "$1" == "-I" ] || [ "$1" == "--I" ]; then x=$2; shift; else x=`echo $1 | sed s%^-*I%%` fi if [ -n "$x" ] && [ -d "$x" ]; then include_paths="$include_paths -I$x" fi ;; --force|-f) force=t ;; --cmds) cmds=$2 shift ;; --configs) configs=t ;; -i) intermediate=t ;; -p|--patches) patches=t unified= ;; -c|--config) config=t unified= ;; -o) # the first field is the output directory outdir=$(echo $2 | cut -d: -f1) # the next field is the comma separated list of actions, but if it is # outdir again, we have no artifacts passed .. so you get them all artifacts=$(echo $2 | cut -d: -f2) if [ "${artifacts}" == "${outdir}" ]; then artifacts="meta,patch,cfg" fi shift ;; -v) verbose=t ;; *) break ;; esac shift done err() { echo "$@" 1>&2 } # args are the input files infiles=$@ shift # If --configs was passed, we dump the current queue and exit if [ -n "${configs}" ]; then if [ ! -f "${outdir}/config.queue" ]; then echo "[ERROR]: no configuration queue found in outdir (${outdir})" usage exit 1 else cat ${outdir}/config.queue | sed "s%^%${outdir}/%g" | sed 's%#.*$%%g' | tr '\n' ' ' exit 0 fi fi if [ -z "$infiles" ]; then err "ERROR: a input file must be supplied" exit 1 fi if [ -n "$outfile" ] && [ -f "$outfile" ]; then if [ -z "$force" ]; then err "ERROR: output file \"$outfile\" exists, and --force was not passed" exit 1 fi fi ## ## create variables for use in scripts ## if [ -n "$defines" ]; then vars=$(echo $defines | sed 's/,/ /g') for v in "$vars"; do # eval makes it available for this script eval $v # echo makes it available for other scripts dump_vars=$(echo "$dump_vars"; echo export $v) done fi header() { echo "#" echo "# scc v$version" echo "# processed: `date`" echo "#" echo "# This is a scc output file, do not edit" echo "#" } reloc_dir() { dir=$1 echo "# _reloc_dir $dir" } prefix() { prefix=$1 } # override function takes the name of an already defined function and saves it # as "_old" (or save_name if passed), and then declares a stub function of # the original function name to inhibit any future calls to that function. override_function() { orig=$1 save_name=$2 if [ -z "$save_name" ]; then save_name=old_$1 fi local ORIG_FUNC=$(declare -f $orig) if [ -n "${ORIG_FUNC}" ]; then local NEWNAME_FUNC="$save_name${ORIG_FUNC#$orig}" eval "$NEWNAME_FUNC" # create the override function o="$orig () { true; }" eval "${o}" fi } process_file() { local in=$1 local ret=0 local done="" local scc_output_file="$(mktemp)" # source the file, but clear the path, since we only want commands # that we declare as valid to run ( # find the path of valid, external commands. The implementation is available # in the command file "auto.cmd", which is always loaded basename=$(which basename) dirname=$(which dirname) mkdir=$(which mkdir) cp=$(which cp) sed=$(which sed) # load the commands. We load them all, and then check to see if we # should override a specific command to our "do nothing function" for c in ${all_cmds}; do if [ -n "${verbose}" ]; then echo "[INFO]: sourcing command: $c" fi source $c done for z in ${cmds_to_override}; do if [ -n "${verbose}" ]; then echo "[INFO]: disabling command: $z" fi override_function $(basename "$z" .cmd) done unset PATH ( set -e eval . $in $outfile_append > ${scc_output_file} 2>&1 ) ) if [ $? -ne 0 ]; then echo "[ERROR]: processing of file $in failed" # if the scc_output_file has content if [ ! -s "${scc_output_file}" ]; then cat ${scc_output_file} # look for common errors so we can point to the right input file # 1) /tmp/tmp.gfN6WsbDHN: line 403: cat: No such file or directory # "grep -oh" will only output what matches, which gets us "line 404: .." # cut gets us the second field, which is the line number line=$(cat ${scc_output_file} | grep -oh "line.*:" | cut -f2 -d' ' | sed 's/://g') if [ -n "$line" ]; then let start_line=$line-20 let end_line=$line+10 if [ $start_line -lt 0 ]; then start_line=0 fi echo "" echo "Context around the error is:" echo "" sed -n -e "$start_line,$end_line p" -e "$end_line q" $in | sed 's/^/ /' echo "" echo "See pre-processed file $in for more details" fi fi rm -f ${scc_output_file} exit 1 elif [ -n "${verbose}" ]; then cat ${scc_output_file} fi rm -f ${scc_output_file} return 0 } ## used in the feature scripts ## deprecated: use "define KERNEL_VERSION " set_kernel_version() { KERNEL_VERSION=$1 } # used in feature scripts # - encodes a generic git command git() { echo "# _git $@" } # used in feature scripts # arg1: the tag name tag() { echo "# _git tag $1" } # preprocess the input files into a single large meta-script if [ -n "$intermediate" ]; then intermediate_flags="-i" fi pre_processed_file="$outfile.pre" if [ -z "$outfile" ]; then pre_processed_file="$(mktemp)" fi # load commands all_cmds="" cmds=$(echo ${cmds} | sed 's/,/ /g') for cmd in ${BASEDIR}/scc-cmds/*.cmd; do valid= if [ -n "${cmds}" ]; then # check the cmd against the command line listed ones check_cmd=$(basename "$cmd" .cmd) echo $cmds | grep -q -w $check_cmd if [ $? -eq 0 ]; then # the command is in the valid list valid=t fi # we always load "auto.cmd" if [ "$check_cmd" == "auto" ]; then valid=t fi else valid=t fi if [ -n "${valid}" ]; then if [ -n "${verbose}" ]; then echo "[INFO]: loaded $cmd" >&2 fi cmds_to_source="${cmds_to_source} $cmd" else cmds_to_override="${cmds_to_override} $cmd" fi all_cmds="${all_cmds} $cmd" done # what artifacts need to be produced ? # # We can process the inputs into: # patch.queue # config.queue # meta-series (all commands) # meta is the entire meta series, so we set up a redirect echo ${artifacts} | grep -w -q meta if [ $? -eq 0 ]; then outfile="${outdir}/meta-series" outfile_append=">> $outfile" outfile_start="> $outfile" fi # And we optionally set up pointers to a patch, config and merge queues configqueue="/dev/null" patchqueue="${outdir}/unused.patch.queue" mergequeue="${outdir}/unused.merge.queue" echo ${artifacts} | grep -w -q cfg if [ $? -eq 0 ]; then configqueue="${outdir}/config.queue" rm -f "${configqueue}" fi echo ${artifacts} | grep -w -q patch if [ $? -eq 0 ]; then # remove the emp variant rm -f ${patchqueue} patchqueue="${outdir}/patch.queue" rm -f "${outdir}"/patch*.queue fi echo ${artifacts} | grep -w -q merge if [ $? -eq 0 ]; then # remove the emp variant rm -f ${mergequeue} mergequeue="${outdir}/merge.queue" rm -f "${outdir}"/merge.queue fi # preprocess the files. # This does things like make paths absolute, checks for errors, etc. spp --force $intermediate_flags -o $pre_processed_file $include_paths $infiles if [ $? -ne 0 ]; then err "ERROR: could not process input files: $infiles" err " See ${pre_processed_file} for details" exit 1 fi eval header $outfile_start # process the files and produce artifacts process_file ${pre_processed_file} ret=$? if [ $ret -ne 0 ]; then if [ -z "$intermediate" ]; then if [ -n "$outfile" ]; then rm -f $outfile fi rm -f ${pre_processed_file} fi else rm -f ${pre_processed_file} fi echo ${patchqueue} | grep -q unused.queue if [ $? -eq 0 ]; then rm -f ${patchqueue} fi echo ${mergequeue} | grep -q unused.queue if [ $? -eq 0 ]; then rm -f ${mergequeue} fi exit $ret