| #!/bin/bash |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| # This is a simple wrapper that runs the shell command specified by the arguments |
| # and generates a JUnitXML file if the command fails. It incorporates the output |
| # of the command into the JUnitXML file. |
| # |
| # This works best when it is invoking a single executable with arguments. There |
| # are some known limitations when invoked from the shell (as it would be if invoked |
| # by Make): |
| # 1. For a string of commands 'junitxml_command_wrapper.sh A && B && C', it only sees |
| # the first one (A). The command A runs in its own shell, so any state it sets for |
| # the shell is not seen in B && C. For example, if A = "cd directory", then B and |
| # C would not see the changed directory. |
| # 2. For output piping 'junitxml_command_wrapper.sh A | B", again it only sees the |
| # first one (A). It does leave the output unchanged (stdout remains stdout, stderr |
| # remains stderr), but if command B fails, it will not generate JUnitXML. |
| |
| COMMAND=("$@") |
| |
| # Run the command, piping output to temporary files. Note that this output can |
| # contain Unicode characters, because g++ (and likely others) can generate smart |
| # quotes. |
| STDOUT_TMP_FILE=$(mktemp) |
| STDERR_TMP_FILE=$(mktemp) |
| # The command's stdout and stderr need to remain separate, and we tee them to separate |
| # files. Some CMake build steps have a command like "command1 | command2" |
| # and command2 should not see stderr. That also means that this script must not produce |
| # its own output when the command runs successfully. |
| "${COMMAND[@]}" > >(tee "${STDOUT_TMP_FILE}") 2> >(tee "${STDERR_TMP_FILE}" >&2) |
| COMMAND_RET_CODE=${PIPESTATUS[0]} |
| if [[ ${COMMAND_RET_CODE} -ne 0 ]]; then |
| # Use a hash of the command to make sure multiple build failures generate distinct |
| # symptoms |
| # TODO: It would make sense to do some better parsing of the command to produce |
| # a better filename. |
| HASH=$(echo "${COMMAND[*]}" | md5sum | cut -d" " -f1) |
| "${IMPALA_HOME}"/bin/generate_junitxml.py --phase build --step "${HASH}" \ |
| --error "Build command failed: ${COMMAND[*]}" \ |
| --stdout "$(head -n 1000 ${STDOUT_TMP_FILE})" \ |
| --stderr "$(head -n 1000 ${STDERR_TMP_FILE})" |
| fi |
| rm "${STDOUT_TMP_FILE}" |
| rm "${STDERR_TMP_FILE}" |
| exit "${COMMAND_RET_CODE}" |