/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#include "cmMakefileUtilityTargetGenerator.h"

#include <ostream>
#include <string>
#include <utility>
#include <vector>

#include <cm/memory>

#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalUnixMakefileGenerator3.h"
#include "cmLocalUnixMakefileGenerator3.h"
#include "cmMakefile.h"
#include "cmOSXBundleGenerator.h"
#include "cmSystemTools.h"

cmMakefileUtilityTargetGenerator::cmMakefileUtilityTargetGenerator(
  cmGeneratorTarget* target)
  : cmMakefileTargetGenerator(target)
{
  CustomCommandDriver = OnUtility;
  OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
  OSXBundleGenerator->SetMacContentFolders(&MacContentFolders);
}

cmMakefileUtilityTargetGenerator::~cmMakefileUtilityTargetGenerator() =
  default;

void cmMakefileUtilityTargetGenerator::WriteRuleFiles()
{
  CreateRuleFile();

  *BuildFileStream << "# Utility rule file for " << GeneratorTarget->GetName()
                   << ".\n\n";

  if (!NoRuleMessages) {
    const char* root =
      (Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT") ? "$(CMAKE_BINARY_DIR)/"
                                                      : "");
    // Include the progress variables for the target.
    *BuildFileStream << "# Include the progress variables for this target.\n"
                     << GlobalGenerator->IncludeDirective << " " << root
                     << cmSystemTools::ConvertToOutputPath(
                          LocalGenerator->MaybeConvertToRelativePath(
                            LocalGenerator->GetBinaryDirectory(),
                            ProgressFileNameFull))
                     << "\n\n";
  }

  // write the custom commands for this target
  WriteTargetBuildRules();

  // Collect the commands and dependencies.
  std::vector<std::string> commands;
  std::vector<std::string> depends;

  // Utility targets store their rules in pre- and post-build commands.
  LocalGenerator->AppendCustomDepends(depends,
                                      GeneratorTarget->GetPreBuildCommands());

  LocalGenerator->AppendCustomDepends(depends,
                                      GeneratorTarget->GetPostBuildCommands());

  LocalGenerator->AppendCustomCommands(
    commands, GeneratorTarget->GetPreBuildCommands(), GeneratorTarget,
    LocalGenerator->GetBinaryDirectory());

  // Depend on all custom command outputs for sources
  DriveCustomCommands(depends);

  LocalGenerator->AppendCustomCommands(
    commands, GeneratorTarget->GetPostBuildCommands(), GeneratorTarget,
    LocalGenerator->GetBinaryDirectory());

  // Add dependencies on targets that must be built first.
  AppendTargetDepends(depends);

  // Add a dependency on the rule file itself.
  LocalGenerator->AppendRuleDepend(depends, BuildFileNameFull.c_str());

  // If the rule is empty add the special empty rule dependency needed
  // by some make tools.
  if (depends.empty() && commands.empty()) {
    std::string hack = GlobalGenerator->GetEmptyRuleHackDepends();
    if (!hack.empty()) {
      depends.push_back(std::move(hack));
    }
  }

  // Write the rule.
  LocalGenerator->WriteMakeRule(*BuildFileStream, nullptr,
                                GeneratorTarget->GetName(), depends, commands,
                                true);

  // Write the main driver rule to build everything in this target.
  WriteTargetDriverRule(GeneratorTarget->GetName(), false);

  // Write clean target
  WriteTargetCleanRules();

  // Write the dependency generation rule.  This must be done last so
  // that multiple output pair information is available.
  WriteTargetDependRules();

  // close the streams
  CloseFileStreams();
}
