text: //===-- BuildSystemCommand.cpp --------------------------------------------===//
  //
  // This source file is part of the Swift.org open source project
  //
  // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
  // Licensed under Apache License v2.0 with Runtime Library Exception
  //
  // See http://swift.org/LICENSE.txt for license information
  // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  //
  //===----------------------------------------------------------------------===//
  
  #include "llbuild/Commands/Commands.h"
  
  #include "llbuild/Basic/FileSystem.h"
  #include "llbuild/Basic/LLVM.h"
  #include "llbuild/Basic/PlatformUtility.h"
  #include "llbuild/BuildSystem/BuildDescription.h"
  #include "llbuild/BuildSystem/BuildFile.h"
  #include "llbuild/BuildSystem/BuildSystem.h"
  #include "llbuild/BuildSystem/BuildSystemFrontend.h"
  #include "llbuild/BuildSystem/BuildValue.h"
  
  #include "llvm/ADT/SmallString.h"
  #include "llvm/ADT/STLExtras.h"
  #include "llvm/ADT/StringMap.h"
  #include "llvm/Support/SourceMgr.h"
  #include "llvm/Support/raw_ostream.h"
  
  #include "CommandUtil.h"
  
  #include <cerrno>
  #include <thread>
  
  #include <signal.h>
  #if defined(_WIN32)
  #include <windows.h>
  #else
  #include <unistd.h>
  #endif
  
  using namespace llbuild;
  using namespace llbuild::commands;
  using namespace llbuild::core;
  using namespace llbuild::buildsystem;
  
  namespace {
  
  /*  Parse Command */
  
  class ParseBuildFileDelegate : public BuildFileDelegate {
    bool showOutput;
    StringRef bufferBeingParsed;
    std::unique_ptr<basic::FileSystem> fileSystem;
    llvm::StringMap<bool> internedStrings;
    
  public:
    ParseBuildFileDelegate(bool showOutput)
        : showOutput(showOutput),
          fileSystem(basic::createLocalFileSystem()) {}
    ~ParseBuildFileDelegate() {}
  
    virtual bool shouldShowOutput() { return showOutput; }
  
    virtual StringRef getInternedString(StringRef value) override {
      auto entry = internedStrings.insert(std::make_pair(value, true));
      return entry.first->getKey();
    }
    
    virtual basic::FileSystem& getFileSystem() override { return *fileSystem; }
    
    virtual void setFileContentsBeingParsed(StringRef buffer) override;
    
    virtual void error(StringRef filename,
                       const BuildFileToken& at,
                       const Twine& message) override;
  
    virtual bool configureClient(const ConfigureContext&, StringRef name,
                                 uint32_t version,
                                 const property_list_type& properties) override;
  
    virtual std::unique_ptr<Tool> lookupTool(StringRef name) override;
  
    virtual void loadedTarget(StringRef name,
                              const Target& target) override;
  
    virtual void loadedDefaultTarget(StringRef target) override;
    
    virtual std::unique_ptr<Node> lookupNode(StringRef name,
                                             bool isImplicit) override;
  
    virtual void loadedCommand(StringRef name,
                               const Command& command) override;
  };
  
  class ParseDummyNode : public Node {
    ParseBuildFileDelegate& delegate;
    
  public:
    ParseDummyNode(ParseBuildFileDelegate& delegate, StringRef name)
        : Node(name), delegate(delegate) {}
    
    virtual bool configureAttribute(const ConfigureContext&, StringRef name,
                                    StringRef value) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': '%s'\n", name.str().c_str(), value.str().c_str());
      }
      return true;
    }
    virtual bool configureAttribute(const ConfigureContext&, StringRef name,
                                    ArrayRef<StringRef> values) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': [", name.str().c_str());
        bool first = true;
        for (const auto& value: values) {
          printf("%s'%s'", first ? "" : ", ", value.str().c_str());
          first = false;
        }
        printf("]\n");
      }
      return true;
    }
    virtual bool configureAttribute(
        const ConfigureContext&, StringRef name,
        ArrayRef<std::pair<StringRef, StringRef>> values) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': {\n", name.str().c_str());
        for (const auto& value: values) {
          printf("  --   '%s': '%s'\n", value.first.str().c_str(),
                 value.second.str().c_str());
        }
        printf("  -- }\n");
      }
      return true;
    }
  };
  
  class ParseDummyCommand : public Command {
    ParseBuildFileDelegate& delegate;
    
  public:
    ParseDummyCommand(ParseBuildFileDelegate& delegate, StringRef name)
        : Command(name), delegate(delegate) {}
  
    virtual void getShortDescription(SmallVectorImpl<char> &result) const override {
      llvm::raw_svector_ostream(result) << "<dummy command>";
    }
  
    virtual void getVerboseDescription(SmallVectorImpl<char> &result) const override {
      llvm::raw_svector_ostream(result) << "<dummy command>";
    }
  
    virtual void configureDescription(const ConfigureContext&,
                                      StringRef description) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- 'description': '%s'", description.str().c_str());
      }
    }
    
    virtual void configureInputs(const ConfigureContext&,
                                 const std::vector<Node*>& inputs) override {
      if (delegate.shouldShowOutput()) {
        bool first = true;
        printf("  -- 'inputs': [");
        for (const auto& node: inputs) {
          printf("%s'%s'", first ? "" : ", ", node->getName().str().c_str());
          first = false;
        }
        printf("]\n");
      }
    }
  
    virtual void configureOutputs(const ConfigureContext&,
                                  const std::vector<Node*>& outputs) override {
      if (delegate.shouldShowOutput()) {
        bool first = true;
        printf("  -- 'outputs': [");
        for (const auto& node: outputs) {
          printf("%s'%s'", first ? "" : ", ", node->getName().str().c_str());
          first = false;
        }
        printf("]\n");
      }
    }
  
    virtual bool configureAttribute(const ConfigureContext&, StringRef name,
                                    StringRef value) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': '%s'\n", name.str().c_str(), value.str().c_str());
      }
      return true;
    }
    virtual bool configureAttribute(const ConfigureContext&, StringRef name,
                                    ArrayRef<StringRef> values) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': [", name.str().c_str());
        bool first = true;
        for (const auto& value: values) {
          printf("%s'%s'", first ? "" : ", ", value.str().c_str());
          first = false;
        }
        printf("]\n");
      }
      return true;
    }
    virtual bool configureAttribute(
        const ConfigureContext&, StringRef name,
        ArrayRef<std::pair<StringRef, StringRef>> values) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': {\n", name.str().c_str());
        for (const auto& value: values) {
          printf("  --   '%s': '%s'\n", value.first.str().c_str(),
                 value.second.str().c_str());
        }
        printf("  -- }\n");
      }
      return true;
    }
  
    virtual BuildValue getResultForOutput(Node* node,
                                          const BuildValue& value) override {
      return BuildValue::makeMissingInput();
    }
    virtual bool isResultValid(BuildSystem&, const BuildValue&) override {
      return false;
    }
    virtual void start(BuildSystemCommandInterface&, Task*) override {}
    virtual void providePriorValue(BuildSystemCommandInterface&, Task*,
                                   const BuildValue&) override {}
    virtual void provideValue(BuildSystemCommandInterface&, Task*,
                                   uintptr_t inputID,
                                   const BuildValue&) override {}
    virtual void execute(BuildSystemCommandInterface&, Task*,
                         basic::QueueJobContext*, ResultFn resultFn) override {
      resultFn(BuildValue::makeFailedCommand());
    }
  };
  
  class ParseDummyTool : public Tool {
    ParseBuildFileDelegate& delegate;
    
  public:
    ParseDummyTool(ParseBuildFileDelegate& delegate, StringRef name)
        : Tool(name), delegate(delegate) {}
    
    virtual bool configureAttribute(const ConfigureContext&, StringRef name,
                                    StringRef value) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': '%s'\n", name.str().c_str(), value.str().c_str());
      }
      return true;
    }
    virtual bool configureAttribute(const ConfigureContext&, StringRef name,
                                    ArrayRef<StringRef> values) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': [", name.str().c_str());
        bool first = true;
        for (const auto& value: values) {
          printf("%s'%s'", first ? "" : ", ", value.str().c_str());
          first = false;
        }
        printf("]\n");
      }
      return true;
    }
    virtual bool configureAttribute(
        const ConfigureContext&, StringRef name,
        ArrayRef<std::pair<StringRef, StringRef>> values) override {
      if (delegate.shouldShowOutput()) {
        printf("  -- '%s': {\n", name.str().c_str());
        for (const auto& value: values) {
          printf("  --   '%s': '%s'", value.first.str().c_str(),
                 value.second.str().c_str());
        }
        printf("  -- }\n");
      }
      return true;
    }
  
    virtual std::unique_ptr<Command> createCommand(
        StringRef name) override {
      if (delegate.shouldShowOutput()) {
        printf("command('%s')\n", name.str().c_str());
        printf("  -- 'tool': '%s')\n", getName().str().c_str());
      }
  
      return llvm::make_unique<ParseDummyCommand>(delegate, name);
    }
  };
  
  void ParseBuildFileDelegate::setFileContentsBeingParsed(StringRef buffer) {
    bufferBeingParsed = buffer;
  }
  
  void ParseBuildFileDelegate::error(StringRef filename,
                                     const BuildFileToken& at,
                                     const Twine& message) {
    if (at.start) {
      util::emitError(filename, message.str(), at.start, at.length,
                      bufferBeingParsed);
    } else {
      fprintf(stderr, "%s: error: %s\n", filename.str().c_str(),
              message.str().c_str());
    }
  }
  
  bool
  ParseBuildFileDelegate::configureClient(const ConfigureContext&,
                                          StringRef name,
                                          uint32_t version,
                                          const property_list_type& properties) {
    if (showOutput) {
      // Dump the client information.
      printf("client ('%s', version: %u)\n", name.str().c_str(), version);
      for (const auto& property: properties) {
        printf("  -- '%s': '%s'\n", property.first.c_str(),
               property.second.c_str());
      }
    }
  
    return true;
  }
  
  std::unique_ptr<Tool>
  ParseBuildFileDelegate::lookupTool(StringRef name) {
    if (showOutput) {
      printf("tool('%s')\n", name.str().c_str());
    }
  
    return llvm::make_unique<ParseDummyTool>(*this, name);
  }
  
  void ParseBuildFileDelegate::loadedTarget(StringRef name,
                                            const Target& target) {
    if (showOutput) {
      printf("target('%s')\n", target.getName().str().c_str());
  
      // Print the nodes in the target.
      bool first = true;
      printf(" -- nodes: [");
      for (const auto& node: target.getNodes()) {
        printf("%s'%s'", first ? "" : ", ", node->getName().str().c_str());
        first = false;
      }
      printf("]\n");
    }
  }
    
  void ParseBuildFileDelegate::loadedDefaultTarget(StringRef target) {
    if (showOutput) {
      printf("default_target('%s')\n", target.str().c_str());
    }
  }
  
  std::unique_ptr<Node>
  ParseBuildFileDelegate::lookupNode(StringRef name,
                                     bool isImplicit) {
    if (!isImplicit) {
      if (showOutput) {
        printf("node('%s')\n", name.str().c_str());
      }
    }
  
    return llvm::make_unique<ParseDummyNode>(*this, name);
  }
  
  void ParseBuildFileDelegate::loadedCommand(StringRef name,
                                          const Command& command) {
    if (showOutput) {
      printf("  -- -- loaded command('%s')\n", command.getName().str().c_str());
    }
  }
  
  static void parseUsage(int exitCode) {
    int optionWidth = 20;
    fprintf(stderr, "Usage: %s buildsystem parse [options] <path>\n",
            getProgramName());
    fprintf(stderr, "\nOptions:\n");
    fprintf(stderr, "  %-*s %s\n", optionWidth, "--help",
            "show this help message and exit");
    fprintf(stderr, "  %-*s %s\n", optionWidth, "--no-output",
            "don't display parser output");
    ::exit(exitCode);
  }
  
  static int executeParseCommand(std::vector<std::string> args) {
  #if defined(_WIN32)
    // TODO: Not yet implemented
    abort();
  #else
    bool showOutput = true;
    
    while (!args.empty() && args[0][0] == '-') {
      const std::string option = args[0];
      args.erase(args.begin());
  
      if (option == "--")
        break;
  
      if (option == "--help") {
        parseUsage(0);
      } else if (option == "--no-output") {
        showOutput = false;
      } else {
        fprintf(stderr, "error: %s: invalid option: '%s'\n\n",
                getProgramName(), option.c_str());
        parseUsage(1);
      }
    }
  
    if (args.size() != 1) {
      fprintf(stderr, "error: %s: invalid number of arguments\n",
              getProgramName());
      parseUsage(1);
    }
  
    std::string filename = args[0].c_str();
  
    // Load the BuildFile.
    fprintf(stderr, "note: parsing '%s'\n", filename.c_str());
    ParseBuildFileDelegate delegate(showOutput);
    BuildFile buildFile(filename, delegate);
    buildFile.load();
  
  #endif
    return 0;
  }
  
  
  /* Build Command */
  
  class BasicBuildSystemFrontendDelegate : public BuildSystemFrontendDelegate {
  #if defined(_WIN32)
    // TODO: Not yet implemented
  #else
    std::unique_ptr<basic::FileSystem> fileSystem;
  
    /// The previous SIGINT handler.
    struct sigaction previousSigintHandler;
  
    /// Low-level flag for when a SIGINT has been received.
    static std::atomic<bool> wasInterrupted;
  
    /// Pipe used to allow detection of signals.
    static int signalWatchingPipe[2];
  
    static void sigintHandler(int) {
      // Set the atomic interrupt flag.
      BasicBuildSystemFrontendDelegate::wasInterrupted = true;
  
      // Write to wake up the signal monitoring thread.
      char byte{};
      basic::sys::write(signalWatchingPipe[1], &byte, 1);
    }
  
    /// Check if an interrupt has occurred.
    void checkForInterrupt() {
      // Save and clear the interrupt flag, atomically.
      bool wasInterrupted = BasicBuildSystemFrontendDelegate::wasInterrupted.exchange(false);
  
      // Process the interrupt flag, if present.
      if (wasInterrupted) {
        // Otherwise, cancel the build.
        printf("cancelling build.\n");
        cancel();
      }
    }
  
    /// Thread function to wait for indications that signals have arrived and to
    /// process them.
    void signalWaitThread() {
      // Wait for signal arrival indications.
      while (true) {
        char byte;
        int res = basic::sys::read(signalWatchingPipe[0], &byte, 1);
  
        // If nothing was read, the pipe has been closed and we should shut down.
        if (res == 0)
          break;
  
        // Otherwise, check if we were awoke because of an interrupt.
        checkForInterrupt();
      }
  
      // Shut down the pipe.
      basic::sys::close(signalWatchingPipe[0]);
      signalWatchingPipe[0] = -1;
    }
    
  public:
    BasicBuildSystemFrontendDelegate(llvm::SourceMgr& sourceMgr,
                                     const BuildSystemInvocation& invocation)
        : BuildSystemFrontendDelegate(sourceMgr, invocation,
                                      "basic", /*version=*/0),
          fileSystem(basic::createLocalFileSystem()) {
      // Register an interrupt handler.
      struct sigaction action{};
      action.sa_handler = &BasicBuildSystemFrontendDelegate::sigintHandler;
      sigaction(SIGINT, &action, &previousSigintHandler);
  
      // Create a pipe and thread to watch for signals.
      assert(BasicBuildSystemFrontendDelegate::signalWatchingPipe[0] == -1 &&
             BasicBuildSystemFrontendDelegate::signalWatchingPipe[1] == -1);
      if (basic::sys::pipe(BasicBuildSystemFrontendDelegate::signalWatchingPipe) < 0) {
        perror("pipe");
      }
      std::thread handlerThread(
          &BasicBuildSystemFrontendDelegate::signalWaitThread, this);
      handlerThread.detach();
    }
  
    ~BasicBuildSystemFrontendDelegate() {
      // Restore any previous SIGINT handler.
      sigaction(SIGINT, &previousSigintHandler, NULL);
  
      // Close the signal watching pipe.
      basic::sys::close(BasicBuildSystemFrontendDelegate::signalWatchingPipe[1]);
      signalWatchingPipe[1] = -1;
    }
  
    virtual void hadCommandFailure() override {
      // Call the base implementation.
      BuildSystemFrontendDelegate::hadCommandFailure();
  
      // Cancel the build, by default.
      cancel();
    }
  
    virtual std::unique_ptr<Tool> lookupTool(StringRef name) override {
      // We do not support any non-built-in tools.
      return nullptr;
    }
  
    virtual void cycleDetected(const std::vector<Rule*>& cycle) override {
      auto message = BuildSystemInvocation::formatDetectedCycle(cycle);
      error(message);
    }
  #endif
  };
  
  #if defined(_WIN32)
  // TODO: Not yet implemented
  #else
  std::atomic<bool> BasicBuildSystemFrontendDelegate::wasInterrupted{false};
  int BasicBuildSystemFrontendDelegate::signalWatchingPipe[2]{-1, -1};
  #endif
  
  static void buildUsage(int exitCode) {
    int optionWidth = 25;
    fprintf(stderr, "Usage: %s buildsystem build [options] [<target>]\n",
            getProgramName());
    fprintf(stderr, "\nOptions:\n");
    BuildSystemInvocation::getUsage(optionWidth, llvm::errs());
    ::exit(exitCode);
  }
  
  static int executeBuildCommand(std::vector<std::string> args) {
  #if defined(_WIN32)
    // TODO: Not yet implemented
    abort();
  #else
    // The source manager to use for diagnostics.
    llvm::SourceMgr sourceMgr;
  
    // Create the invocation.
    BuildSystemInvocation invocation{};
  
    // Initialize defaults.
    invocation.dbPath = "build.db";
    invocation.buildFilePath = "build.llbuild";
    invocation.parse(args, sourceMgr);
  
    // Handle invocation actions.
    if (invocation.showUsage) {
      buildUsage(0);
    } else if (invocation.hadErrors) {
      buildUsage(1);
    }
    
    if (invocation.positionalArgs.size() > 1) {
      fprintf(stderr, "error: %s: invalid number of arguments\n",
              getProgramName());
      buildUsage(1);
    }
  
    // Select the target to build.
    std::string targetToBuild =
      invocation.positionalArgs.empty() ? "" : invocation.positionalArgs[0];
  
    // Create the frontend object.
    BasicBuildSystemFrontendDelegate delegate(sourceMgr, invocation);
    BuildSystemFrontend frontend(delegate, invocation,
                                 basic::createLocalFileSystem());
    if (!frontend.build(targetToBuild)) {
      // If there were failed commands, report the count and return an error.
      if (delegate.getNumFailedCommands()) {
        delegate.error("build had " + Twine(delegate.getNumFailedCommands()) +
                       " command failures");
      }
  
      return 1;
    }
  
  #endif
    return 0;
  }
  
  }
  
  #pragma mark - Build System Top-Level Command
  
  static void usage(int exitCode) {
    fprintf(stderr, "Usage: %s buildsystem [--help] <command> [<args>]\n",
            getProgramName());
    fprintf(stderr, "\n");
    fprintf(stderr, "Available commands:\n");
    fprintf(stderr, "  parse         -- Parse a build file\n");
    fprintf(stderr, "  build         -- Build using a build file\n");
    fprintf(stderr, "\n");
    exit(exitCode);
  }
  
  int commands::executeBuildSystemCommand(const std::vector<std::string> &args) {
    // Expect the first argument to be the name of another subtool to delegate to.
    if (args.empty() || args[0] == "--help")
      usage(0);
  
    if (args[0] == "parse") {
      return executeParseCommand({args.begin()+1, args.end()});
    } else if (args[0] == "build") {
      return executeBuildCommand({args.begin()+1, args.end()});
    } else {
      fprintf(stderr, "error: %s: unknown command '%s'\n", getProgramName(),
              args[0].c_str());
      usage(1);
      return 1;
    }
  }
  