void ExternalCommand::start(BuildSystemCommandInterface& bsci,
                            core::Task* task) {
  // Notify the client the command is preparing to run.
  bsci.getDelegate().commandPreparing(this);
    
  // Initialize the build state.
  shouldSkip = false;
  hasMissingInput = false;

  // Request all of the inputs.
  unsigned id = 0;
  for (auto it = inputs.begin(), ie = inputs.end(); it != ie; ++it, ++id) {
    bsci.taskNeedsInput(task, BuildKey::makeNode(*it), id);
  }
}
void ExternalCommand::provideValue(BuildSystemCommandInterface& bsci,
                                   core::Task*,
                                   uintptr_t inputID,
                                   const BuildValue& value) {
  // Process the input value to see if we should skip this command.

  // All direct inputs should be individual node values.
  assert(!value.hasMultipleOutputs());
  assert(value.isExistingInput() || value.isMissingInput() ||
         value.isMissingOutput() || value.isFailedInput() ||
         value.isVirtualInput());

  // Predicate for whether the input should cause the command to skip.
  auto shouldSkipForInput = [&] {
    // If the value is an existing or virtual input, we are always good.
    if (value.isExistingInput() || value.isVirtualInput())
      return false;

    // We explicitly allow running the command against a missing output, under
    // the expectation that responsibility for reporting this situation falls to
    // the command.
    //
    // FIXME: Eventually, it might be nice to harden the format so that we know
    // when an output was actually required versus optional.
    if (value.isMissingOutput())
      return false;

    // If the value is a missing input, but those are allowed, it is ok.
    if (allowMissingInputs && value.isMissingInput())
      return false;

    // For anything else, this is an error and the command should be skipped.
    return true;
  };

  // Check if we need to skip the command because of this input.
  if (shouldSkipForInput()) {
    shouldSkip = true;
    if (value.isMissingInput()) {
      hasMissingInput = true;

      // FIXME: Design the logging and status output APIs.
      bsci.getDelegate().error(
          "", {}, (Twine("missing input '") + inputs[inputID]->getName() +
                   "' and no rule to build it"));
    }
  } else {
    // If there is a missing input file (from a successful command), we always
    // need to run the command.
    if (value.isMissingOutput())
      canUpdateIfNewer = false;
  }
}
void ExternalCommand::start(BuildSystemCommandInterface& bsci,
                            core::Task* task) {
  // Initialize the build state.
  skipValue = llvm::None;
  hasMissingInput = false;

  // Request all of the inputs.
  unsigned id = 0;
  for (auto it = inputs.begin(), ie = inputs.end(); it != ie; ++it, ++id) {
    bsci.taskNeedsInput(task, BuildKey::makeNode(*it), id);
  }
}
BuildValue
ExternalCommand::computeCommandResult(BuildSystemCommandInterface& bsci) {
  // Capture the file information for each of the output nodes.
  //
  // FIXME: We need to delegate to the node here.
  SmallVector<FileInfo, 8> outputInfos;
  for (auto* node: outputs) {
    if (node->isCommandTimestamp()) {
      // FIXME: We currently have to shoehorn the timestamp into a fake file
      // info, but need to refactor the command result to just store the node
      // subvalues instead.
      FileInfo info{};
      info.size = bsci.getBuildEngine().getCurrentTimestamp();
      outputInfos.push_back(info);
    } else if (node->isVirtual()) {
      outputInfos.push_back(FileInfo{});
    } else {
      outputInfos.push_back(node->getFileInfo(
                                bsci.getDelegate().getFileSystem()));
    }
  }
  return BuildValue::makeSuccessfulCommand(outputInfos, getSignature());
}
BuildValue
ExternalCommand::computeCommandResult(BuildSystemCommandInterface& bsci) {
  // Capture the file information for each of the output nodes.
  //
  // FIXME: We need to delegate to the node here.
  SmallVector<FileInfo, 8> outputInfos;
  for (auto* node: outputs) {
    if (node->isVirtual()) {
      outputInfos.push_back(FileInfo{});
    } else {
      outputInfos.push_back(node->getFileInfo(
                                bsci.getDelegate().getFileSystem()));
    }
  }
  return BuildValue::makeSuccessfulCommand(outputInfos, getSignature());
}
void ExternalCommand::inputsAvailable(BuildSystemCommandInterface& bsci,
                                      core::Task* task) {
  // If the build should cancel, do nothing.
  if (bsci.getDelegate().isCancelled()) {
    bsci.taskIsComplete(task, BuildValue::makeSkippedCommand());
    return;
  }
    
  // If this command should be skipped, do nothing.
  if (shouldSkip) {
    // If this command had a failed input, treat it as having failed.
    if (hasMissingInput) {
      // FIXME: Design the logging and status output APIs.
      bsci.getDelegate().error(
          "", {}, (Twine("cannot build '") + outputs[0]->getName() +
                   "' due to missing input"));

      // Report the command failure.
      bsci.getDelegate().hadCommandFailure();
    }

    bsci.taskIsComplete(task, BuildValue::makeSkippedCommand());
    return;
  }
  assert(!hasMissingInput);

  // If it is legal to simply update the command, then see if we can do so.
  if (canUpdateIfNewer &&
      hasPriorResult && priorResultCommandSignature == getSignature()) {
    BuildValue result = computeCommandResult(bsci);
    if (canUpdateIfNewerWithResult(result)) {
      bsci.taskIsComplete(task, result);
      return;
    }
  }
    
  // Suppress static analyzer false positive on generalized lambda capture
  // (rdar://problem/22165130).
#ifndef __clang_analyzer__
  auto fn = [this, &bsci=bsci, task](QueueJobContext* context) {
    // Notify the client the actual command body is going to run.
    bsci.getDelegate().commandStarted(this);

    // Create the directories for the directories containing file outputs.
    //
    // FIXME: Implement a shared cache for this, to reduce the number of
    // syscalls required to make this happen.
    for (auto* node: outputs) {
      if (!node->isVirtual()) {
        // Attempt to create the directory; we ignore errors here under the
        // assumption the command will diagnose the situation if necessary.
        //
        // FIXME: Need to use the filesystem interfaces.
        auto parent = llvm::sys::path::parent_path(node->getName());
        if (!parent.empty()) {
          (void) llvm::sys::fs::create_directories(parent);
        }
      }
    }
    
    // Invoke the external command.
    auto result = executeExternalCommand(bsci, task, context);
    
    // Notify the client the command is complete.
    bsci.getDelegate().commandFinished(this);
    
    // Process the result.
    if (!result) {
      bsci.getDelegate().hadCommandFailure();

      // If the command failed, the result is failure.
      bsci.taskIsComplete(task, BuildValue::makeFailedCommand());
      return;
    }

    // Otherwise, complete with a successful result.
    bsci.taskIsComplete(task, computeCommandResult(bsci));
  };
  bsci.addJob({ this, std::move(fn) });
#endif
}
BuildValue ExternalCommand::execute(BuildSystemCommandInterface& bsci,
                                    core::Task* task,
                                    QueueJobContext* context) {
  // If this command should be skipped, do nothing.
  if (skipValue.hasValue()) {
    // If this command had a failed input, treat it as having failed.
    if (hasMissingInput) {
      // FIXME: Design the logging and status output APIs.
      bsci.getDelegate().error(
          "", {}, (Twine("cannot build '") + outputs[0]->getName() +
                   "' due to missing input"));

      // Report the command failure.
      bsci.getDelegate().hadCommandFailure();
    }

    return std::move(skipValue.getValue());
  }
  assert(!hasMissingInput);

  // If it is legal to simply update the command, then see if we can do so.
  if (canUpdateIfNewer &&
      hasPriorResult && priorResultCommandSignature == getSignature()) {
    BuildValue result = computeCommandResult(bsci);
    if (canUpdateIfNewerWithResult(result)) {
      return result;
    }
  }

  // Create the directories for the directories containing file outputs.
  //
  // FIXME: Implement a shared cache for this, to reduce the number of
  // syscalls required to make this happen.
  for (auto* node: outputs) {
    if (!node->isVirtual()) {
      // Attempt to create the directory; we ignore errors here under the
      // assumption the command will diagnose the situation if necessary.
      //
      // FIXME: Need to use the filesystem interfaces.
      auto parent = llvm::sys::path::parent_path(node->getName());
      if (!parent.empty()) {
        (void) bsci.getDelegate().getFileSystem().createDirectories(parent);
      }
    }
  }
    
  // Invoke the external command.
  bsci.getDelegate().commandStarted(this);
  auto result = executeExternalCommand(bsci, task, context);
  bsci.getDelegate().commandFinished(this, result);
    
  // Process the result.
  switch (result) {
  case CommandResult::Failed:
    return BuildValue::makeFailedCommand();
  case CommandResult::Cancelled:
    return BuildValue::makeCancelledCommand();
  case CommandResult::Succeeded:
    return computeCommandResult(bsci);
  case CommandResult::Skipped:
    // It is illegal to get skipped result at this point.
    break;
  }
  llvm::report_fatal_error("unknown result");
}
void ExternalCommand::provideValue(BuildSystemCommandInterface& bsci,
                                   core::Task*,
                                   uintptr_t inputID,
                                   const BuildValue& value) {
  // Process the input value to see if we should skip this command.

  // All direct inputs should be individual node values.
  assert(!value.hasMultipleOutputs());
  assert(value.isExistingInput() || value.isMissingInput() ||
         value.isMissingOutput() || value.isFailedInput() ||
         value.isVirtualInput()  || value.isSkippedCommand() ||
         value.isDirectoryTreeSignature() || value.isStaleFileRemoval());

  // If the input should cause this command to skip, how should it skip?
  auto getSkipValueForInput = [&]() -> llvm::Optional<BuildValue> {
    // If the value is an signature, existing, or virtual input, we are always
    // good.
    if (value.isDirectoryTreeSignature() | value.isExistingInput() ||
        value.isVirtualInput() || value.isStaleFileRemoval())
      return llvm::None;

    // We explicitly allow running the command against a missing output, under
    // the expectation that responsibility for reporting this situation falls to
    // the command.
    //
    // FIXME: Eventually, it might be nice to harden the format so that we know
    // when an output was actually required versus optional.
    if (value.isMissingOutput())
      return llvm::None;

    // If the value is a missing input, but those are allowed, it is ok.
    if (value.isMissingInput()) {
      if (allowMissingInputs)
        return llvm::None;
      else
        return BuildValue::makePropagatedFailureCommand();
    }

    // Propagate failure.
    if (value.isFailedInput())
      return BuildValue::makePropagatedFailureCommand();

    // A skipped dependency doesn't cause this command to skip.
    if (value.isSkippedCommand())
        return llvm::None;

    llvm_unreachable("unexpected input");
  };

  // Check if we need to skip the command because of this input.
  auto skipValueForInput = getSkipValueForInput();
  if (skipValueForInput.hasValue()) {
    skipValue = std::move(skipValueForInput);
    if (value.isMissingInput()) {
      hasMissingInput = true;

      // FIXME: Design the logging and status output APIs.
      bsci.getDelegate().error(
          "", {}, (Twine("missing input '") + inputs[inputID]->getName() +
                   "' and no rule to build it"));
    }
  } else {
    // If there is a missing input file (from a successful command), we always
    // need to run the command.
    if (value.isMissingOutput())
      canUpdateIfNewer = false;
  }
}