diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -51,6 +51,7 @@ UnitTests bugpoint count + extract llc lli lli-child-target diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -145,7 +145,7 @@ # FIXME: Why do we have both `lli` and `%lli` that do slightly different things? tools.extend([ - 'dsymutil', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as', + 'dsymutil', 'extract', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as', 'llvm-addr2line', 'llvm-bcanalyzer', 'llvm-config', 'llvm-cov', 'llvm-cxxdump', 'llvm-cvtres', 'llvm-diff', 'llvm-dis', 'llvm-dwarfdump', 'llvm-exegesis', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs', diff --git a/llvm/tools/extract/CMakeLists.txt b/llvm/tools/extract/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/extract/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_llvm_tool(extract + extract.cpp + ) diff --git a/llvm/tools/extract/extract.cpp b/llvm/tools/extract/extract.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/extract/extract.cpp @@ -0,0 +1,106 @@ +//===- extract.cpp - Input splitting utility ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Split input into multipe parts separated by regex '^(.|//)--- ' and extract +// the specified part. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/WithColor.h" +#include + +using namespace llvm; + +const char tool_name[] = "extract"; + +static cl::OptionCategory cat("extract Options"); + +static cl::opt part( + cl::Positional, + cl::desc( + "Split input into multiple parts separated by regex '^(.|//)--- ' and " + "extract the specified part"), + cl::cat(cat)); + +static cl::opt input(cl::Positional, cl::desc("filename"), + cl::cat(cat)); + +static cl::opt output("o", cl::desc("Output filename"), + cl::value_desc("filename"), cl::init("-"), + cl::cat(cat)); + +LLVM_ATTRIBUTE_NORETURN static void error(Twine message) { + WithColor::error(errs(), tool_name) << message << '\n'; + exit(1); +} + +LLVM_ATTRIBUTE_NORETURN static void error(StringRef filename, Twine message) { + WithColor::error(errs(), tool_name) << filename << ": " << message << '\n'; + exit(1); +} + +static void handle(MemoryBuffer &inputBuf, StringRef input) { + const char *docBegin = nullptr, *docEnd = nullptr; + int numEmptyLines = 0; + StringRef separator; + for (line_iterator i(inputBuf, /*SkipBlanks=*/false, '\0'); !i.is_at_eof();) { + StringRef line = *i++; + size_t markerLen = line.startswith("//") ? 6 : 5; + if (!(line.size() > markerLen && + line.substr(markerLen - 4).startswith("--- "))) + continue; + separator = line.substr(0, markerLen); + StringRef cur = line.substr(markerLen); + if (cur == part) { + if (docBegin) { + error(input, "'" + separator + cur + "' occurs more than once"); + exit(1); + } + numEmptyLines = i.line_number() - 1; + if (i.is_at_eof()) + break; + docBegin = i->data(); + } else if (docBegin && !docEnd) { + docEnd = line.data(); + } + } + if (!docBegin) + error(input, "'" + separator + part + "' is not found"); + if (!docEnd) + docEnd = inputBuf.getBufferEnd(); + + Expected> outputBuf = + FileOutputBuffer::create(output, numEmptyLines + (docEnd - docBegin)); + if (!outputBuf) + error(input, toString(outputBuf.takeError())); + uint8_t *buf = (*outputBuf)->getBufferStart(); + std::fill_n(buf, numEmptyLines, '\n'); + std::copy(docBegin, docEnd, buf + numEmptyLines); + if (Error e = (*outputBuf)->commit()) + error(input, toString(std::move(e))); +} + +int main(int argc, const char **argv) { + cl::HideUnrelatedOptions({&cat}); + cl::ParseCommandLineOptions(argc, argv, "extract\n", nullptr, + /*EnvVar=*/nullptr, + /*LongOptionsUseDoubleDash=*/true); + + if (input.empty()) + error("input filename is not specified"); + ErrorOr> buffer_or_err = + MemoryBuffer::getFileOrSTDIN(input); + if (std::error_code ec = buffer_or_err.getError()) + error(input, ec.message()); + handle(**buffer_or_err, input); +}