gollvm: support reading from stdin (command line flag "-")
Add support for reading Go source code from stdin if the "-" command
line flag is specified. For compatibility with gccgo, support the
limited use case of "-x c -" provided that standard input is actually
empty (the Go tool invokes gccgo in some instances passing "-x c" when
detecting whether a given flag is supported).
Change-Id: I3a78eae62a90a79482d52856551e891e0301e541
Reviewed-on: https://go-review.googlesource.com/112365
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/driver-main/llvm-goc.cpp b/driver-main/llvm-goc.cpp
index 7fc6ef2..e788c0f 100644
--- a/driver-main/llvm-goc.cpp
+++ b/driver-main/llvm-goc.cpp
@@ -147,10 +147,17 @@
llvm::cl::ParseCommandLineOptions(nargs + 1, args.get());
}
- // Vett the -x option if present. At the moment only "-x go" is accepted
- // (assembler not allowed, but it could conceivably be added later).
+ // HACK: the go tool likes to invoke the C and Go compiler drivers
+ // at various points to detect whether a given command line flag is
+ // supported (ex: "gcc -x c - -someflag < /dev/null"), and tends to
+ // pass "-x c" even when the driver is gccgo. Gccgo is (mirabile
+ // dictu) a functional C compiler, but gollvm is not. For the time
+ // being we will "fake it" by allowing "-x ... -" but then requiring
+ // that standard input be empty (so as to support the Go tool, but
+ // not act as a general-purposes C compiler).
opt::Arg *xarg = args_.getLastArg(gollvm::options::OPT_x);
if (xarg != nullptr &&
+ ! llvm::StringRef(xarg->getValue()).equals("c") &&
! llvm::StringRef(xarg->getValue()).equals("go")) {
errs() << progname << ": invalid argument '"
<< xarg->getValue() << "' to '"
diff --git a/driver/Action.cpp b/driver/Action.cpp
index 2b9bb21..48e8176 100644
--- a/driver/Action.cpp
+++ b/driver/Action.cpp
@@ -22,7 +22,8 @@
const char *Action::getName() const
{
switch (type_) {
- case A_Input: return "inputfile";
+ case A_ReadStdin: return "readstdin";
+ case A_InputFile: return "inputfile";
case A_Compile: return "compile";
case A_Assemble: return "assemble";
case A_Link: return "link";
@@ -36,7 +37,11 @@
const char *Action::resultFileSuffix() const
{
switch (type_) {
- case A_Input: return "i";
+ case A_ReadStdin: {
+ const ReadStdinAction *rsia = this->castToReadStdinAction();
+ return rsia->suffix();
+ }
+ case A_InputFile: return "i";
case A_Compile: return "s";
case A_Assemble: return "o";
case A_Link: return "e";
diff --git a/driver/Action.h b/driver/Action.h
index 78f5327..cd92535 100644
--- a/driver/Action.h
+++ b/driver/Action.h
@@ -22,6 +22,7 @@
class Action;
class Artifact;
class InputAction;
+class ReadStdinAction;
// Action lists contain pointers to actions owned by a Compilation.
typedef llvm::SmallVector<Action*, 3> ActionList;
@@ -38,7 +39,8 @@
// Type of action
enum Type {
- A_Input,
+ A_InputFile,
+ A_ReadStdin,
A_Compile,
A_Assemble,
A_Link
@@ -55,6 +57,8 @@
inline InputAction *castToInputAction();
inline const InputAction *castToInputAction() const;
+ inline ReadStdinAction *castToReadStdinAction();
+ inline const ReadStdinAction *castToReadStdinAction() const;
const char *getName() const;
const char *resultFileSuffix() const;
@@ -72,18 +76,43 @@
class InputAction : public Action {
public:
explicit InputAction(Artifact *input)
- : Action(Action::A_Input),
+ : Action(Action::A_InputFile),
input_(input) { }
Artifact *input() const { return input_; }
private:
Artifact *input_;
};
-inline InputAction *Action::castToInputAction() {
- return type_ == A_Input ? static_cast<InputAction*>(this) : nullptr;
+// An input action that corresponds to the reading stdin.
+
+class ReadStdinAction : public Action {
+ public:
+ explicit ReadStdinAction(const char *suffix)
+ : Action(Action::A_ReadStdin),
+ suffix_(suffix) { }
+ const char *suffix() const { return suffix_; }
+ private:
+ const char *suffix_;
+};
+
+inline InputAction *Action::castToInputAction()
+{
+ return type_ == A_InputFile ? static_cast<InputAction*>(this) : nullptr;
}
-inline const InputAction *Action::castToInputAction() const {
- return type_ == A_Input ? static_cast<const InputAction*>(this) : nullptr;
+
+inline const InputAction *Action::castToInputAction() const
+{
+ return type_ == A_InputFile ? static_cast<const InputAction*>(this) : nullptr;
+}
+
+inline ReadStdinAction *Action::castToReadStdinAction()
+{
+ return type_ == A_ReadStdin ? static_cast<ReadStdinAction*>(this) : nullptr;
+}
+
+inline const ReadStdinAction *Action::castToReadStdinAction() const
+{
+ return type_ == A_ReadStdin ? static_cast<const ReadStdinAction*>(this) : nullptr;
}
} // end namespace driver
diff --git a/driver/CMakeLists.txt b/driver/CMakeLists.txt
index d1b13bd..5e5da98 100644
--- a/driver/CMakeLists.txt
+++ b/driver/CMakeLists.txt
@@ -30,6 +30,7 @@
GnuTools.cpp
GollvmOptions.cpp
LinuxToolChain.cpp
+ ReadStdin.cpp
Tool.cpp
ToolChain.cpp
DEPENDS
diff --git a/driver/Driver.cpp b/driver/Driver.cpp
index 9f92274..63d2e43 100644
--- a/driver/Driver.cpp
+++ b/driver/Driver.cpp
@@ -288,9 +288,23 @@
Compilation &compilation)
{
for (auto ifarg : ifargs) {
- InputAction *ia = new InputAction(compilation.newArgArtifact(ifarg));
- compilation.recordAction(ia);
- result.push_back(ia);
+ bool schedAction = false;
+ Action *act = nullptr;
+ if (!strcmp(ifarg->getValue(), "-")) {
+ opt::Arg *xarg = args_.getLastArg(gollvm::options::OPT_x);
+ assert(xarg);
+ const char *suf =
+ (llvm::StringRef(xarg->getValue()).equals("c") ? "c" :
+ (llvm::StringRef(xarg->getValue()).equals("go") ? "go" : "?"));
+ act = new ReadStdinAction(suf);
+ schedAction = true;
+ } else {
+ act = new InputAction(compilation.newArgArtifact(ifarg));
+ }
+ compilation.recordAction(act);
+ if (schedAction)
+ compilation.addAction(act);
+ result.push_back(act);
}
}
@@ -422,7 +436,7 @@
// It is an error if an internal-tool action is receiving a result
// from an external tool (in the current model all internal-tool actions
// have to take place before any external-tool actions).
- assert(it == nullptr);
+ assert(it == nullptr || input->castToReadStdinAction() != nullptr);
auto it = artmap_.find(input);
assert(it != artmap_.end());
result.push_back(it->second);
diff --git a/driver/ReadStdin.cpp b/driver/ReadStdin.cpp
new file mode 100644
index 0000000..7be44cb
--- /dev/null
+++ b/driver/ReadStdin.cpp
@@ -0,0 +1,78 @@
+//===-- ReadStdin.cpp -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Gollvm driver helper class "ReadStdin" methods.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReadStdin.h"
+
+#include "Compilation.h"
+#include "Driver.h"
+
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace gollvm {
+namespace driver {
+
+
+ReadStdin::ReadStdin(ToolChain &tc, bool mustBeEmpty)
+ : InternalTool("stdinreader", tc),
+ mustBeEmpty_(mustBeEmpty)
+{
+}
+
+ReadStdin::~ReadStdin()
+{
+}
+
+bool ReadStdin::performAction(Compilation &compilation,
+ const Action &jobAction,
+ const ArtifactList &inputArtifacts,
+ const Artifact &output)
+{
+ assert(inputArtifacts.empty());
+
+ // Read in standard input, the LLVM way...
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> stdinBufferOrErr =
+ llvm::MemoryBuffer::getSTDIN();
+ if (std::error_code errc = stdinBufferOrErr.getError()) {
+ llvm::errs() << compilation.driver().progname()
+ << ": error reading stdin: " << errc.message();
+ return false;
+ }
+ std::unique_ptr<llvm::MemoryBuffer> stdinBuf =
+ std::move(stdinBufferOrErr.get());
+
+ // Enforce the "must be empty" requirement if appropriate.
+ if (mustBeEmpty_ && stdinBuf->getBufferSize() != 0) {
+ llvm::errs() << compilation.driver().progname()
+ << ": unsupported language for -x, "
+ << "stdin be empty\n";
+ return false;
+ }
+
+ // Emit to the output artifact.
+ std::error_code errc;
+ llvm::raw_fd_ostream ostr(output.file(), errc,
+ llvm::sys::fs::OpenFlags::F_None);
+ if (errc) {
+ llvm::errs() << compilation.driver().progname()
+ << ": cannot open " << output.file() << " for writing: "
+ << errc.message();
+ return false;
+ }
+
+ ostr.write(stdinBuf->getBufferStart(), stdinBuf->getBufferSize());
+ return true;
+}
+
+} // end namespace driver
+} // end namespace gollvm
diff --git a/driver/ReadStdin.h b/driver/ReadStdin.h
new file mode 100644
index 0000000..c31cbe0
--- /dev/null
+++ b/driver/ReadStdin.h
@@ -0,0 +1,54 @@
+//===-- ReadStdin.h -------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the ReadStdin class (helper for driver functionality).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GOLLVM_DRIVER_READSTDIN_H
+#define GOLLVM_DRIVER_READSTDIN_H
+
+#include "Tool.h"
+
+namespace gollvm {
+namespace driver {
+
+class ToolChain;
+class Compilation;
+class Action;
+class Artifact;
+
+// This class encapsulates the reading of standard input during a compilation
+// that includes the pseudo-input flag "-". The driver handles this case
+// by creating a tool (ReadStdin) that consumes standard input and emits
+// a temporary file, which is then piped through the remainder of the compiler
+// as usual. See also the notes in the command parsing code relating to
+// handling of and "-x c".
+
+class ReadStdin : public InternalTool {
+ public:
+
+ // If mustBeEmpty is set, then issue an error if stdin has any contents.
+ ReadStdin(ToolChain &tc, bool mustBeEmpty);
+ ~ReadStdin();
+
+ // Perform action (reading stdin).
+ bool performAction(Compilation &compilation,
+ const Action &jobAction,
+ const ArtifactList &inputArtifacts,
+ const Artifact &output) override;
+
+ private:
+ bool mustBeEmpty_;
+};
+
+} // end namespace driver
+} // end namespace gollvm
+
+#endif // GOLLVM_DRIVER_READSTDIN_H
diff --git a/driver/ToolChain.cpp b/driver/ToolChain.cpp
index ba8e805..48ebdd8 100644
--- a/driver/ToolChain.cpp
+++ b/driver/ToolChain.cpp
@@ -17,6 +17,7 @@
#include "Driver.h"
#include "Tool.h"
#include "ToolChain.h"
+#include "ReadStdin.h"
namespace gollvm {
namespace driver {
@@ -53,6 +54,13 @@
return linker_.get();
}
+Tool *ToolChain::getStdinReader(bool mustBeEmpty)
+{
+ if (stdinReader_.get() == nullptr)
+ stdinReader_.reset(new ReadStdin(*this, mustBeEmpty));
+ return stdinReader_.get();
+}
+
Tool *ToolChain::getTool(Action *act)
{
assert(act != nullptr);
@@ -63,6 +71,10 @@
return getAssembler();
case Action::A_Link:
return getLinker();
+ case Action::A_ReadStdin: {
+ ReadStdinAction *rsia = act->castToReadStdinAction();
+ return getStdinReader(strcmp(rsia->suffix(), "go"));
+ }
default:
assert(false);
return nullptr;
diff --git a/driver/ToolChain.h b/driver/ToolChain.h
index ffd83e7..689796c 100644
--- a/driver/ToolChain.h
+++ b/driver/ToolChain.h
@@ -79,6 +79,7 @@
std::unique_ptr<Tool> compiler_;
std::unique_ptr<Tool> assembler_;
std::unique_ptr<Tool> linker_;
+ std::unique_ptr<Tool> stdinReader_;
// List of places to look for tools (ld, as, etc)
pathlist programPaths_;
@@ -86,6 +87,8 @@
// List of places to look for object files (ex: crt0.o)
pathlist filePaths_;
+ Tool *getStdinReader(bool mustBeEmpty);
+
ToolChain &thisToolChain() {
ToolChain *tc = const_cast<ToolChain*>(this);
return *tc;