One of the special features of C TAP Harness is the environment that
it sets up for your test cases. If your test program is called under
the runtests driver, the environment variables SOURCE and BUILD will
- be set to the top of the source tree and the top of the build tree,
- respectively. You can use those environment variables to locate
- additional test data, programs and libraries built as part of your
- software build, and other supporting information needed by tests.
+ be set to the top of the test directory in the source tree and the top
+ of the build tree, respectively. You can use those environment
+ variables to locate additional test data, programs and libraries built
+ as part of your software build, and other supporting information
+ needed by tests.
+
+ The C and shell TAP libraries support a test_file_path() function,
+ which looks for a file under the build tree and then under the source
+ tree, using the BUILD and SOURCE environment variables, and return the
+ full path to the file. This can be used to locate supporting data
+ files.
Perl
of Test::More for all the details and lots of examples.
C TAP Harness can run Perl test scripts directly and interpret the
- results correctly, and similarly the Perl Test::Harness module can run
- TAP tests written in other languages using, for example, the TAP
- library that comes with C TAP Harness. However, the "prove" tool that
- comes with Perl and runs tests makes some Perl-specific assumptions
- that aren't always appropriate for packages that aren't written in
- Perl.
+ results correctly, and similarly the Perl Test::Harness module and
+ prove command can run TAP tests written in other languages using, for
+ example, the TAP library that comes with C TAP Harness. You can, if
+ you wish, use the library that comes with C TAP Harness but use prove
+ instead of runtests for running the test suite.
C
Here's a complete example test program that uses the C TAP library:
+ #include <stddef.h>
#include <tap/basic.h>
int
rest of its arguments as the program to run. That program is run with
standard error and standard output combined, and then its exit status
and output are tested against the provided values.
+
+ A utility function, strip_colon_error, is provided that runs the
+ command given as its arguments and strips text following a colon and a
+ space from the output (unless there is no whitespace on the line
+ before the colon and the space, normally indicating a prefix of the
+ program name). This function can be used to wrap commands that are
+ expected to fail with output that has a system- or locale-specific
+ error message appended, such as the output of strerror().
+
+License
+
+ This file is part of the documentation of C TAP Harness, which can be
+ found at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+
+ Copyright 2010 Russ Allbery <rra@stanford.edu>
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. This file is offered as-is,
+ without any warranty.
*
* Usage:
*
- * runtests <test-list>
+ * runtests [-b <build-dir>] [-s <source-dir>] <test-list>
+ * runtests -o [-b <build-dir>] [-s <source-dir>] <test>
*
- * Expects a list of executables located in the given file, one line per
- * executable. For each one, runs it as part of a test suite, reporting
- * results. Test output should start with a line containing the number of
- * tests (numbered from 1 to this number), optionally preceded by "1..",
- * although that line may be given anywhere in the output. Each additional
- * line should be in the following format:
+ * In the first case, expects a list of executables located in the given file,
+ * one line per executable. For each one, runs it as part of a test suite,
+ * reporting results. Test output should start with a line containing the
+ * number of tests (numbered from 1 to this number), optionally preceded by
+ * "1..", although that line may be given anywhere in the output. Each
+ * additional line should be in the following format:
*
* ok <number>
* not ok <number>
* This is a subset of TAP as documented in Test::Harness::TAP or
* TAP::Parser::Grammar, which comes with Perl.
*
+ * If the -o option is given, instead run a single test and display all of its
+ * output. This is intended for use with failing tests so that the person
+ * running the test suite can get more details about what failed.
+ *
+ * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP
+ * Harness will export those values in the environment so that tests can find
+ * the source and build directory and will look for tests under both
+ * directories. These paths can also be set with the -b and -s command-line
+ * options, which will override anything set at build time.
+ *
* Any bug reports, bug fixes, and improvements are very much welcome and
- * should be sent to the e-mail address below.
+ * should be sent to the e-mail address below. This program is part of C TAP
+ * Harness <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
- * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010
+ * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011
* Russ Allbery <rra@stanford.edu>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* DEALINGS IN THE SOFTWARE.
*/
+/* Required for fdopen(), getopt(), and putenv(). */
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+#endif
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <strings.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
unsigned long skipped; /* Count of skipped tests (passed). */
unsigned long allocated; /* The size of the results table. */
enum test_status *results; /* Table of results by test number. */
- int aborted; /* Whether the set as aborted. */
+ unsigned int aborted; /* Whether the set as aborted. */
int reported; /* Whether the results were reported. */
int status; /* The exit status of the test. */
- int all_skipped; /* Whether all tests were skipped. */
+ unsigned int all_skipped; /* Whether all tests were skipped. */
char *reason; /* Why all tests were skipped. */
};
};
/*
+ * Usage message. Should be used as a printf format with two arguments: the
+ * path to runtests, given twice.
+ */
+static const char usage_message[] = "\
+Usage: %s [-b <build-dir>] [-s <source-dir>] <test-list>\n\
+ %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\
+\n\
+Options:\n\
+ -b <build-dir> Set the build directory to <build-dir>\n\
+ -o Run a single test rather than a list of tests\n\
+ -s <source-dir> Set the source directory to <source-dir>\n\
+\n\
+runtests normally runs each test listed in a file whose path is given as\n\
+its command-line argument. With the -o option, it instead runs a single\n\
+test and shows its complete output.\n";
+
+/*
* Header used for test output. %s is replaced by the file name of the list
* of tests.
*/
* Get the count, check it for validity, and initialize the struct. If we
* have something of the form "1..0 # skip foo", the whole file was
* skipped; record that. If we do skip the whole file, zero out all of
- * our statistics, since they're no longer relevant.
+ * our statistics, since they're no longer relevant. strtol is called
+ * with a second argument to advance the line pointer past the count to
+ * make it simpler to detect the # skip case.
*/
n = strtol(line, (char **) &line, 10);
if (n == 0) {
char *end;
long number;
unsigned long i, current;
+ int outlen;
/* Before anything, check for a test abort. */
bail = strstr(line, "Bail out!");
if (bail[length - 1] == '\n')
length--;
test_backspace(ts);
- printf("ABORTED (%.*s)\n", (int)length, bail);
+ printf("ABORTED (%.*s)\n", (int) length, bail);
ts->reported = 1;
}
ts->aborted = 1;
case TEST_PASS: ts->passed++; break;
case TEST_FAIL: ts->failed++; break;
case TEST_SKIP: ts->skipped++; break;
- default: break;
+ case TEST_INVALID: break;
}
ts->current = current;
ts->results[current - 1] = status;
test_backspace(ts);
if (isatty(STDOUT_FILENO)) {
- ts->length = printf("%lu/%lu", current, ts->count);
+ outlen = printf("%lu/%lu", current, ts->count);
+ ts->length = (outlen >= 0) ? outlen : 0;
fflush(stdout);
}
}
/*
* Print out a range of test numbers, returning the number of characters it
- * took up. Add a comma and a space before the range if chars indicates that
+ * took up. Takes the first number, the last number, the number of characters
+ * already printed on the line, and the limit of number of characters the line
+ * can hold. Add a comma and a space before the range if chars indicates that
* something has already been printed on the line, and print ... instead if
* chars plus the space needed would go over the limit (use a limit of 0 to
- * disable this.
+ * disable this).
*/
static unsigned int
test_print_range(unsigned long first, unsigned long last, unsigned int chars,
unsigned int limit)
{
unsigned int needed = 0;
- unsigned int out = 0;
unsigned long n;
- if (chars > 0) {
- needed += 2;
- if (!limit || chars <= limit) out += printf(", ");
- }
for (n = first; n > 0; n /= 10)
needed++;
if (last > first) {
needed++;
needed++;
}
- if (limit && chars + needed > limit) {
- if (chars <= limit)
- out += printf("...");
+ if (chars > 0)
+ needed += 2;
+ if (limit > 0 && chars + needed > limit) {
+ needed = 0;
+ if (chars <= limit) {
+ if (chars > 0) {
+ printf(", ");
+ needed += 2;
+ }
+ printf("...");
+ needed += 3;
+ }
} else {
+ if (chars > 0)
+ printf(", ");
if (last > first)
- out += printf("%lu-", first);
- out += printf("%lu", last);
+ printf("%lu-", first);
+ printf("%lu", last);
}
- return out;
+ return needed;
}
last = i + 1;
else {
if (first != 0)
- chars += test_print_range(first, last, chars, 20);
+ chars += test_print_range(first, last, chars, 19);
first = i + 1;
last = i + 1;
}
}
}
if (first != 0)
- test_print_range(first, last, chars, 20);
+ test_print_range(first, last, chars, 19);
putchar('\n');
free(ts->file);
free(ts->path);
const char *build)
{
char *path;
- const char *bases[] = { ".", build, source, NULL };
+ const char *bases[4];
unsigned int i;
+ bases[0] = ".";
+ bases[1] = build;
+ bases[2] = source;
+ bases[3] = NULL;
+
for (i = 0; bases[i] != NULL; i++) {
path = xmalloc(strlen(bases[i]) + strlen(name) + 4);
sprintf(path, "%s/%s-t", bases[i], name);
const char *source = SOURCE;
const char *build = BUILD;
- while ((option = getopt(argc, argv, "b:os:")) != EOF) {
+ while ((option = getopt(argc, argv, "b:hos:")) != EOF) {
switch (option) {
case 'b':
build = optarg;
break;
+ case 'h':
+ printf(usage_message, argv[0], argv[0]);
+ exit(0);
+ break;
case 'o':
single = 1;
break;
exit(1);
}
}
- argc -= optind;
- argv += optind;
- if (argc != 1) {
- fprintf(stderr, "Usage: runtests <test-list>\n");
+ if (argc - optind != 1) {
+ fprintf(stderr, usage_message, argv[0], argv[0]);
exit(1);
}
+ argc -= optind;
+ argv += optind;
if (source != NULL) {
setting = xmalloc(strlen("SOURCE=") + strlen(source) + 1);
/*
* Some utility routines for writing tests.
*
- * Herein are a variety of utility routines for writing tests. All routines
- * of the form ok*() take a test number and some number of appropriate
- * arguments, check to be sure the results match the expected output using the
- * arguments, and print out something appropriate for that test number. Other
- * utility routines help in constructing more complex tests.
+ * Here are a variety of utility routines for writing tests compatible with
+ * the TAP protocol. All routines of the form ok() or is*() take a test
+ * number and some number of appropriate arguments, check to be sure the
+ * results match the expected output using the arguments, and print out
+ * something appropriate for that test number. Other utility routines help in
+ * constructing more complex tests, skipping tests, or setting up the TAP
+ * output format.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
* Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
- * Copyright 2006, 2007, 2008
- * Board of Trustees, Leland Stanford Jr. University
- * Copyright (c) 2004, 2005, 2006
- * by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
*
- * See LICENSE for licensing terms.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
*/
+/* Required for isnan() and isinf(). */
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+
#include <errno.h>
+#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
if (_planned == 0 && !_lazy)
return;
+ fflush(stderr);
if (_process != 0 && getpid() == _process) {
if (_lazy) {
printf("1..%lu\n", highest);
if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
strerror(errno));
+ fflush(stderr);
printf("1..%lu\n", count);
testnum = 1;
_planned = count;
void
skip_all(const char *format, ...)
{
+ fflush(stderr);
printf("1..0 # skip");
if (format != NULL) {
va_list args;
void
ok(int success, const char *format, ...)
{
+ fflush(stderr);
printf("%sok %lu", success ? "" : "not ", testnum++);
if (!success)
_failed++;
void
okv(int success, const char *format, va_list args)
{
+ fflush(stderr);
printf("%sok %lu", success ? "" : "not ", testnum++);
if (!success)
_failed++;
void
skip(const char *reason, ...)
{
+ fflush(stderr);
printf("ok %lu # skip", testnum++);
if (reason != NULL) {
va_list args;
{
unsigned long i;
+ fflush(stderr);
for (i = 0; i < count; i++) {
printf("%sok %lu", status ? "" : "not ", testnum++);
if (!status)
{
unsigned long i;
+ fflush(stderr);
for (i = 0; i < count; i++) {
printf("ok %lu # skip", testnum++);
if (reason != NULL) {
void
is_int(long wanted, long seen, const char *format, ...)
{
+ fflush(stderr);
if (wanted == seen)
printf("ok %lu", testnum++);
else {
wanted = "(null)";
if (seen == NULL)
seen = "(null)";
+ fflush(stderr);
if (strcmp(wanted, seen) == 0)
printf("ok %lu", testnum++);
else {
/*
* Takes an expected double and a seen double and assumes the test passes if
- * those two numbers match.
+ * those two numbers are within delta of each other.
*/
void
-is_double(double wanted, double seen, const char *format, ...)
+is_double(double wanted, double seen, double epsilon, const char *format, ...)
{
- if (wanted == seen)
+ fflush(stderr);
+ if ((isnan(wanted) && isnan(seen))
+ || (isinf(wanted) && isinf(seen) && wanted == seen)
+ || fabs(wanted - seen) <= epsilon)
printf("ok %lu", testnum++);
else {
printf("# wanted: %g\n# seen: %g\n", wanted, seen);
void
is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
{
+ fflush(stderr);
if (wanted == seen)
printf("ok %lu", testnum++);
else {
{
va_list args;
+ fflush(stderr);
fflush(stdout);
printf("Bail out! ");
va_start(args, format);
va_list args;
int oerrno = errno;
+ fflush(stderr);
fflush(stdout);
printf("Bail out! ");
va_start(args, format);
{
va_list args;
+ fflush(stderr);
fflush(stdout);
printf("# ");
va_start(args, format);
va_list args;
int oerrno = errno;
+ fflush(stderr);
fflush(stdout);
printf("# ");
va_start(args, format);
va_end(args);
printf(": %s\n", strerror(oerrno));
}
+
+
+/*
+ * Locate a test file. Given the partial path to a file, look under BUILD and
+ * then SOURCE for the file and return the full path to the file. Returns
+ * NULL if the file doesn't exist. A non-NULL return should be freed with
+ * test_file_path_free().
+ *
+ * This function uses sprintf because it attempts to be independent of all
+ * other portability layers. The use immediately after a memory allocation
+ * should be safe without using snprintf or strlcpy/strlcat.
+ */
+char *
+test_file_path(const char *file)
+{
+ char *base;
+ char *path = NULL;
+ size_t length;
+ const char *envs[] = { "BUILD", "SOURCE", NULL };
+ int i;
+
+ for (i = 0; envs[i] != NULL; i++) {
+ base = getenv(envs[i]);
+ if (base == NULL)
+ continue;
+ length = strlen(base) + 1 + strlen(file) + 1;
+ path = malloc(length);
+ if (path == NULL)
+ sysbail("cannot allocate memory");
+ sprintf(path, "%s/%s", base, file);
+ if (access(path, R_OK) == 0)
+ break;
+ free(path);
+ path = NULL;
+ }
+ return path;
+}
+
+
+/*
+ * Free a path returned from test_file_path(). This function exists primarily
+ * for Windows, where memory must be freed from the same library domain that
+ * it was allocated from.
+ */
+void
+test_file_path_free(char *path)
+{
+ if (path != NULL)
+ free(path);
+}
/*
* Basic utility routines for the TAP protocol.
*
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
* Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
- * Copyright 2006, 2007, 2008
- * Board of Trustees, Leland Stanford Jr. University
- * Copyright (c) 2004, 2005, 2006
- * by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
*
- * See LICENSE for licensing terms.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
*/
#ifndef TAP_BASIC_H
/* Check an expected value against a seen value. */
void is_int(long wanted, long seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
-void is_double(double wanted, double seen, const char *format, ...)
- __attribute__((__format__(printf, 3, 4)));
+void is_double(double wanted, double seen, double epsilon,
+ const char *format, ...)
+ __attribute__((__format__(printf, 4, 5)));
void is_string(const char *wanted, const char *seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
void sysdiag(const char *format, ...)
__attribute__((__nonnull__, __format__(printf, 1, 2)));
+/*
+ * Find a test file under BUILD or SOURCE, returning the full path. The
+ * returned path should be freed with test_file_path_free().
+ */
+char *test_file_path(const char *file)
+ __attribute__((__malloc__, __nonnull__));
+void test_file_path_free(char *path);
+
END_DECLS
#endif /* TAP_BASIC_H */
# Shell function library for test cases.
#
+# This file provides a TAP-compatible shell function library useful for
+# writing test cases. It is part of C TAP Harness, which can be found at
+# <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+#
# Written by Russ Allbery <rra@stanford.edu>
# Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
-# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University
+# Copyright 2006, 2007, 2008
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
#
-# See LICENSE for licensing terms.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
# Print out the number of test cases we expect to run.
plan () {
done
}
+# Portable variant of printf '%s\n' "$*". In the majority of cases, this
+# function is slower than printf, because the latter is often implemented
+# as a builtin command. The value of the variable IFS is ignored.
+puts () {
+ cat << EOH
+$@
+EOH
+}
+
# Run a program expected to succeed, and print ok if it does and produces the
# correct output. Takes the description, expected exit status, the expected
-# output, the command to run, and then any arguments for that command. Strip
-# a colon and everything after it off the output if the expected status is
-# non-zero, since this is probably a system-specific error message.
+# output, the command to run, and then any arguments for that command.
+# Standard output and standard error are combined when analyzing the output of
+# the command.
+#
+# If the command may contain system-specific error messages in its output,
+# add strip_colon_error before the command to post-process its output.
ok_program () {
local desc w_status w_output output status
desc="$1"
shift
output=`"$@" 2>&1`
status=$?
- if [ "$w_status" -ne 0 ] ; then
- output=`echo "$output" | sed 's/^\([^:]*\):.*/\1/'`
- fi
if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then
ok "$desc" true
else
fi
}
+# Strip a colon and everything after it off the output of a command, as long
+# as that colon comes after at least one whitespace character. (This is done
+# to avoid stripping the name of the program from the start of an error
+# message.) This is used to remove system-specific error messages (coming
+# from strerror, for example).
+strip_colon_error() {
+ local output status
+ output=`"$@" 2>&1`
+ status=$?
+ output=`puts "$output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'`
+ puts "$output"
+ return $status
+}
+
# Bail out with an error message.
bail () {
echo 'Bail out!' "$@"
diag () {
echo '#' "$@"
}
+
+# Search for the given file first in $BUILD and then in $SOURCE and echo the
+# path where the file was found, or the empty string if the file wasn't
+# found.
+test_file_path () {
+ if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then
+ puts "$BUILD/$1"
+ elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then
+ puts "$SOURCE/$1"
+ else
+ echo ''
+ fi
+}