aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-cas/llvm-cas.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-cas/llvm-cas.cpp')
-rw-r--r--llvm/tools/llvm-cas/llvm-cas.cpp405
1 files changed, 405 insertions, 0 deletions
diff --git a/llvm/tools/llvm-cas/llvm-cas.cpp b/llvm/tools/llvm-cas/llvm-cas.cpp
new file mode 100644
index 0000000..e72ee47
--- /dev/null
+++ b/llvm/tools/llvm-cas/llvm-cas.cpp
@@ -0,0 +1,405 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file A utility for operating on LLVM CAS.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/CAS/ActionCache.h"
+#include "llvm/CAS/BuiltinUnifiedCASDatabases.h"
+#include "llvm/CAS/ObjectStore.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::cas;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
+#include "Options.inc"
+#undef OPTION
+};
+
+#define OPTTABLE_STR_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
+
+using namespace llvm::opt;
+static constexpr opt::OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "Options.inc"
+#undef OPTION
+};
+
+class LLVMCASOptTable : public opt::GenericOptTable {
+public:
+ LLVMCASOptTable()
+ : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
+};
+
+enum class CommandKind {
+ Invalid,
+ Dump,
+ CatNodeData,
+ MakeBlob,
+ MakeNode,
+ ListObjectReferences,
+ Import,
+ PutCacheKey,
+ GetCacheResult,
+ Validate,
+ ValidateObject,
+ ValidateIfNeeded,
+ Prune,
+};
+
+struct CommandOptions {
+ CommandKind Command = CommandKind::Invalid;
+ std::vector<std::string> Inputs;
+ std::string CASPath;
+ std::string UpstreamCASPath;
+ std::string DataPath;
+ bool CheckHash;
+ bool AllowRecovery;
+ bool Force;
+ bool InProcess;
+
+ static CommandKind getCommandKind(opt::Arg &A) {
+ switch (A.getOption().getID()) {
+ case OPT_cas_dump:
+ return CommandKind::Dump;
+ case OPT_cat_node_data:
+ return CommandKind::CatNodeData;
+ case OPT_make_blob:
+ return CommandKind::MakeBlob;
+ case OPT_make_node:
+ return CommandKind::MakeNode;
+ case OPT_ls_node_refs:
+ return CommandKind::ListObjectReferences;
+ case OPT_import:
+ return CommandKind::Import;
+ case OPT_put_cache_key:
+ return CommandKind::PutCacheKey;
+ case OPT_get_cache_result:
+ return CommandKind::GetCacheResult;
+ case OPT_validate:
+ return CommandKind::Validate;
+ case OPT_validate_object:
+ return CommandKind::ValidateObject;
+ case OPT_validate_if_needed:
+ return CommandKind::ValidateIfNeeded;
+ case OPT_prune:
+ return CommandKind::Prune;
+ }
+ return CommandKind::Invalid;
+ }
+
+ // Command requires input.
+ static bool requiresInput(CommandKind Kind) {
+ return Kind != CommandKind::ValidateIfNeeded &&
+ Kind != CommandKind::Validate && Kind != CommandKind::MakeBlob &&
+ Kind != CommandKind::MakeNode && Kind != CommandKind::Dump &&
+ Kind != CommandKind::Prune;
+ }
+};
+} // namespace
+
+static int dump(ObjectStore &CAS);
+static int listObjectReferences(ObjectStore &CAS, const CASID &ID);
+static int catNodeData(ObjectStore &CAS, const CASID &ID);
+static int makeBlob(ObjectStore &CAS, StringRef DataPath);
+static int makeNode(ObjectStore &CAS, ArrayRef<std::string> References,
+ StringRef DataPath);
+static int import(ObjectStore &FromCAS, ObjectStore &ToCAS,
+ ArrayRef<std::string> Objects);
+static int putCacheKey(ObjectStore &CAS, ActionCache &AC,
+ ArrayRef<std::string> Objects);
+static int getCacheResult(ObjectStore &CAS, ActionCache &AC, const CASID &ID);
+static int validateObject(ObjectStore &CAS, const CASID &ID);
+static int validate(ObjectStore &CAS, ActionCache &AC, bool CheckHash);
+static int validateIfNeeded(StringRef Path, bool CheckHash, bool Force,
+ bool AllowRecovery, bool InProcess,
+ const char *Argv0);
+static int prune(cas::ObjectStore &CAS);
+
+static Expected<CommandOptions> parseOptions(int Argc, char **Argv) {
+ BumpPtrAllocator Alloc;
+ StringSaver Saver(Alloc);
+ SmallVector<const char *> ExpanedArgs;
+ if (!cl::expandResponseFiles(Argc, Argv, nullptr, Saver, ExpanedArgs))
+ return createStringError("cannot expand response file");
+
+ LLVMCASOptTable T;
+ unsigned MI, MC;
+ opt::InputArgList Args = T.ParseArgs(ExpanedArgs, MI, MC);
+
+ for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
+ llvm::errs() << "ignoring unknown option: " << Arg->getSpelling() << '\n';
+ }
+
+ if (Args.hasArg(OPT_help)) {
+ T.printHelp(
+ outs(),
+ (std::string(Argv[0]) + " [action] [options] <input files>").c_str(),
+ "llvm-cas tool that performs CAS actions.", false);
+ exit(0);
+ }
+
+ CommandOptions Opts;
+ for (auto *A : Args.filtered(OPT_grp_action))
+ Opts.Command = CommandOptions::getCommandKind(*A);
+
+ if (Opts.Command == CommandKind::Invalid)
+ return createStringError("no command action is specified");
+
+ for (auto *File : Args.filtered(OPT_INPUT))
+ Opts.Inputs.push_back(File->getValue());
+ Opts.CASPath = Args.getLastArgValue(OPT_cas_path);
+ Opts.UpstreamCASPath = Args.getLastArgValue(OPT_upstream_cas);
+ Opts.DataPath = Args.getLastArgValue(OPT_data);
+ Opts.CheckHash = Args.hasArg(OPT_check_hash);
+ Opts.AllowRecovery = Args.hasArg(OPT_allow_recovery);
+ Opts.Force = Args.hasArg(OPT_force);
+ Opts.InProcess = Args.hasArg(OPT_in_process);
+
+ // Validate options.
+ if (Opts.CASPath.empty())
+ return createStringError("missing --cas <path>");
+
+ if (Opts.Inputs.empty() && CommandOptions::requiresInput(Opts.Command))
+ return createStringError("missing <input> to operate on");
+
+ return Opts;
+}
+
+int main(int Argc, char **Argv) {
+ InitLLVM X(Argc, Argv);
+
+ ExitOnError ExitOnErr;
+ auto Opts = ExitOnErr(parseOptions(Argc, Argv));
+
+ if (Opts.Command == CommandKind::ValidateIfNeeded)
+ return validateIfNeeded(Opts.CASPath, Opts.CheckHash, Opts.Force,
+ Opts.AllowRecovery, Opts.InProcess, Argv[0]);
+
+ auto [CAS, AC] = ExitOnErr(createOnDiskUnifiedCASDatabases(Opts.CASPath));
+ assert(CAS);
+
+ if (Opts.Command == CommandKind::Dump)
+ return dump(*CAS);
+
+ if (Opts.Command == CommandKind::Validate)
+ return validate(*CAS, *AC, Opts.CheckHash);
+
+ if (Opts.Command == CommandKind::MakeBlob)
+ return makeBlob(*CAS, Opts.DataPath);
+
+ if (Opts.Command == CommandKind::MakeNode)
+ return makeNode(*CAS, Opts.Inputs, Opts.DataPath);
+
+ if (Opts.Command == CommandKind::Prune)
+ return prune(*CAS);
+
+ if (Opts.Command == CommandKind::Import) {
+ if (Opts.UpstreamCASPath.empty())
+ ExitOnErr(createStringError("missing '-upstream-cas'"));
+
+ auto [UpstreamCAS, _] =
+ ExitOnErr(createOnDiskUnifiedCASDatabases(Opts.UpstreamCASPath));
+ return import(*UpstreamCAS, *CAS, Opts.Inputs);
+ }
+
+ if (Opts.Command == CommandKind::PutCacheKey ||
+ Opts.Command == CommandKind::GetCacheResult) {
+ if (!AC)
+ ExitOnErr(createStringError("no action-cache available"));
+ }
+
+ if (Opts.Command == CommandKind::PutCacheKey)
+ return putCacheKey(*CAS, *AC, Opts.Inputs);
+
+ // Remaining commands need exactly one CAS object.
+ if (Opts.Inputs.size() > 1)
+ ExitOnErr(createStringError("too many <object>s, expected 1"));
+ CASID ID = ExitOnErr(CAS->parseID(Opts.Inputs.front()));
+
+ if (Opts.Command == CommandKind::GetCacheResult)
+ return getCacheResult(*CAS, *AC, ID);
+
+ if (Opts.Command == CommandKind::ListObjectReferences)
+ return listObjectReferences(*CAS, ID);
+
+ if (Opts.Command == CommandKind::CatNodeData)
+ return catNodeData(*CAS, ID);
+
+ assert(Opts.Command == CommandKind::ValidateObject);
+ return validateObject(*CAS, ID);
+}
+
+static Expected<std::unique_ptr<MemoryBuffer>> openBuffer(StringRef DataPath) {
+ if (DataPath.empty())
+ return createStringError("--data missing");
+ return errorOrToExpected(DataPath == "-"
+ ? llvm::MemoryBuffer::getSTDIN()
+ : llvm::MemoryBuffer::getFile(DataPath));
+}
+
+int dump(ObjectStore &CAS) {
+ ExitOnError ExitOnErr("llvm-cas: dump: ");
+ CAS.print(llvm::outs());
+ return 0;
+}
+
+int makeBlob(ObjectStore &CAS, StringRef DataPath) {
+ ExitOnError ExitOnErr("llvm-cas: make-blob: ");
+ std::unique_ptr<MemoryBuffer> Buffer = ExitOnErr(openBuffer(DataPath));
+
+ ObjectProxy Blob = ExitOnErr(CAS.createProxy({}, Buffer->getBuffer()));
+ llvm::outs() << Blob.getID() << "\n";
+ return 0;
+}
+
+int catNodeData(ObjectStore &CAS, const CASID &ID) {
+ ExitOnError ExitOnErr("llvm-cas: cat-node-data: ");
+ llvm::outs() << ExitOnErr(CAS.getProxy(ID)).getData();
+ return 0;
+}
+
+int listObjectReferences(ObjectStore &CAS, const CASID &ID) {
+ ExitOnError ExitOnErr("llvm-cas: ls-node-refs: ");
+
+ ObjectProxy Object = ExitOnErr(CAS.getProxy(ID));
+ ExitOnErr(Object.forEachReference([&](ObjectRef Ref) -> Error {
+ llvm::outs() << CAS.getID(Ref) << "\n";
+ return Error::success();
+ }));
+
+ return 0;
+}
+
+static int makeNode(ObjectStore &CAS, ArrayRef<std::string> Objects,
+ StringRef DataPath) {
+ std::unique_ptr<MemoryBuffer> Data =
+ ExitOnError("llvm-cas: make-node: data: ")(openBuffer(DataPath));
+
+ SmallVector<ObjectRef> IDs;
+ for (StringRef Object : Objects) {
+ ExitOnError ObjectErr("llvm-cas: make-node: ref: ");
+ std::optional<ObjectRef> ID =
+ CAS.getReference(ObjectErr(CAS.parseID(Object)));
+ if (!ID)
+ ObjectErr(createStringError("unknown object '" + Object + "'"));
+ IDs.push_back(*ID);
+ }
+
+ ExitOnError ExitOnErr("llvm-cas: make-node: ");
+ ObjectProxy Object = ExitOnErr(CAS.createProxy(IDs, Data->getBuffer()));
+ llvm::outs() << Object.getID() << "\n";
+ return 0;
+}
+
+static int import(ObjectStore &FromCAS, ObjectStore &ToCAS,
+ ArrayRef<std::string> Objects) {
+ ExitOnError ExitOnErr("llvm-cas: import: ");
+
+ for (StringRef Object : Objects) {
+ CASID ID = ExitOnErr(FromCAS.parseID(Object));
+ auto Ref = FromCAS.getReference(ID);
+ if (!Ref)
+ ExitOnErr(createStringError("input not found: " + ID.toString()));
+
+ auto Imported = ExitOnErr(ToCAS.importObject(FromCAS, *Ref));
+ llvm::outs() << ToCAS.getID(Imported).toString() << "\n";
+ }
+ return 0;
+}
+
+static int putCacheKey(ObjectStore &CAS, ActionCache &AC,
+ ArrayRef<std::string> Objects) {
+ ExitOnError ExitOnErr("llvm-cas: put-cache-key: ");
+
+ if (Objects.size() % 2 != 0)
+ ExitOnErr(createStringError("expected pairs of inputs"));
+ while (!Objects.empty()) {
+ CASID Key = ExitOnErr(CAS.parseID(Objects[0]));
+ CASID Result = ExitOnErr(CAS.parseID(Objects[1]));
+ Objects = Objects.drop_front(2);
+ ExitOnErr(AC.put(Key, Result));
+ }
+ return 0;
+}
+
+static int getCacheResult(ObjectStore &CAS, ActionCache &AC, const CASID &ID) {
+ ExitOnError ExitOnErr("llvm-cas: get-cache-result: ");
+
+ auto Result = ExitOnErr(AC.get(ID));
+ if (!Result) {
+ outs() << "result not found\n";
+ return 1;
+ }
+ outs() << *Result << "\n";
+ return 0;
+}
+
+int validateObject(ObjectStore &CAS, const CASID &ID) {
+ ExitOnError ExitOnErr("llvm-cas: validate-object: ");
+ ExitOnErr(CAS.validateObject(ID));
+ outs() << ID << ": validated successfully\n";
+ return 0;
+}
+
+int validate(ObjectStore &CAS, ActionCache &AC, bool CheckHash) {
+ ExitOnError ExitOnErr("llvm-cas: validate: ");
+ ExitOnErr(CAS.validate(CheckHash));
+ ExitOnErr(AC.validate());
+ outs() << "validated successfully\n";
+ return 0;
+}
+
+int validateIfNeeded(StringRef Path, bool CheckHash, bool Force,
+ bool AllowRecovery, bool InProcess, const char *Argv0) {
+ ExitOnError ExitOnErr("llvm-cas: validate-if-needed: ");
+ std::string ExecStorage;
+ std::optional<StringRef> Exec;
+ if (!InProcess) {
+ ExecStorage = sys::fs::getMainExecutable(Argv0, (void *)validateIfNeeded);
+ Exec = ExecStorage;
+ }
+ ValidationResult Result = ExitOnErr(validateOnDiskUnifiedCASDatabasesIfNeeded(
+ Path, CheckHash, AllowRecovery, Force, Exec));
+ switch (Result) {
+ case ValidationResult::Valid:
+ outs() << "validated successfully\n";
+ break;
+ case ValidationResult::Recovered:
+ outs() << "recovered from invalid data\n";
+ break;
+ case ValidationResult::Skipped:
+ outs() << "validation skipped\n";
+ break;
+ }
+ return 0;
+}
+
+static int prune(cas::ObjectStore &CAS) {
+ ExitOnError ExitOnErr("llvm-cas: prune: ");
+ ExitOnErr(CAS.pruneStorageData());
+ return 0;
+}