#include "third_party/fmt/core.h"
#include "third_party/minitrace.h"
+#include "third_party/nonstd/optional.hpp"
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
#define STRINGIFY(x) #x
#define TO_STRING(x) STRINGIFY(x)
+using nonstd::nullopt;
+using nonstd::optional;
using nonstd::string_view;
static const char VERSION_TEXT[] = MYNAME
args_free(prefix);
}
-static void failed(enum stats stat = STATS_NONE) ATTR_NORETURN;
+// If `exit_code` is set, just exit with that code directly, otherwise execute
+// the real compiler and exit with its exit code. Also updates statistics
+// counter `stat` if it's not STATS_NONE.
+static void failed(enum stats stat = STATS_NONE,
+ optional<int> exit_code = nullopt) ATTR_NORETURN;
-// Something went badly wrong - just execute the real compiler.
static void
-failed(enum stats stat)
+failed(enum stats stat, optional<int> exit_code)
{
- throw Failure(stat);
+ throw Failure(stat, exit_code);
}
static const char*
if (status != 0) {
cc_log("Compiler gave exit status %d", status);
- stats_update(ctx, STATS_STATUS);
int fd = open(tmp_stderr, O_RDONLY | O_BINARY);
if (fd != -1) {
close(fd);
tmp_unlink(tmp_stderr);
- x_exit(status);
+ failed(STATS_STATUS, status);
}
tmp_unlink(tmp_stderr);
- failed();
+ failed(STATS_STATUS);
}
if (ctx.config.depend_mode()) {
fmt::print("({}) {} = {}\n", origin, key, value);
}
-static void cache_compilation(int argc, char* argv[]) ATTR_NORETURN;
+static int cache_compilation(int argc, char* argv[]);
static void do_cache_compilation(Context& ctx, char* argv[]) ATTR_NORETURN;
// The entry point when invoked to cache a compilation.
-static void
+static int
cache_compilation(int argc, char* argv[])
{
#ifndef _WIN32
stats_update(ctx, e.stat());
}
+ if (e.exit_code()) {
+ return *e.exit_code();
+ }
+ // Else: Fall back to running the real compiler.
+
assert(ctx.orig_args);
args_strip(ctx.orig_args, "--ccache-");
if (ctx.actual_cwd.empty()) {
cc_log("Unable to determine current working directory: %s",
strerror(errno));
- throw Failure(STATS_ERROR);
+ failed(STATS_ERROR);
}
MTR_BEGIN("main", "clean_up_internal_tempdir");
}
}
- cache_compilation(argc, argv);
+ return cache_compilation(argc, argv);
} catch (const ErrorBase& e) {
fmt::print(stderr, "ccache: error: {}\n", e.what());
return EXIT_FAILURE;
#include "stats.hpp"
+#include "third_party/nonstd/optional.hpp"
+
#include <stdexcept>
// Don't throw or catch ErrorBase directly, use a subclass.
using ErrorBase::ErrorBase;
};
-// Throw a Failure to make ccache fall back to running the real compiler. Also
-// updates statistics counter `stat` if it's not STATS_NONE.
+// Throw a Failure if ccache did not succeed in getting or putting a result in
+// the cache. If `exit_code` is set, just exit with that code directly,
+// otherwise execute the real compiler and exit with its exit code. Also updates
+// statistics counter `stat` if it's not STATS_NONE.
class Failure : public std::exception
{
public:
- Failure(enum stats stat = STATS_NONE);
+ Failure(enum stats stat, nonstd::optional<int> exit_code);
+ nonstd::optional<int> exit_code() const;
enum stats stat() const;
private:
enum stats m_stat;
+ nonstd::optional<int> m_exit_code;
};
-inline Failure::Failure(enum stats stat) : m_stat(stat)
+inline Failure::Failure(enum stats stat, nonstd::optional<int> exit_code)
+ : m_stat(stat), m_exit_code(exit_code)
+{
+}
+
+inline nonstd::optional<int>
+Failure::exit_code() const
{
+ return m_exit_code;
}
inline enum stats