text: /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
     file Copyright.txt or https://cmake.org/licensing for details.  */
  #include "cmUtilitySourceCommand.h"
  
  #include <cstring>
  
  #include "cmExecutionStatus.h"
  #include "cmMakefile.h"
  #include "cmProperty.h"
  #include "cmState.h"
  #include "cmStateTypes.h"
  #include "cmStringAlgorithms.h"
  #include "cmSystemTools.h"
  
  // cmUtilitySourceCommand
  bool cmUtilitySourceCommand(std::vector<std::string> const& args,
                              cmExecutionStatus& status)
  {
    if (args.size() < 3) {
      status.SetError("called with incorrect number of arguments");
      return false;
    }
  
    auto arg = args.begin();
  
    // The first argument is the cache entry name.
    std::string const& cacheEntry = *arg++;
    cmProp cacheValue = status.GetMakefile().GetDefinition(cacheEntry);
    // If it exists already and appears up to date then we are done.  If
    // the string contains "(IntDir)" but that is not the
    // CMAKE_CFG_INTDIR setting then the value is out of date.
    std::string const& intDir =
      status.GetMakefile().GetRequiredDefinition("CMAKE_CFG_INTDIR");
  
    bool haveCacheValue = false;
    if (status.GetMakefile().IsOn("CMAKE_CROSSCOMPILING")) {
      haveCacheValue = (cacheValue != nullptr);
      if (!haveCacheValue) {
        std::string msg = cmStrCat(
          "UTILITY_SOURCE is used in cross compiling mode for ", cacheEntry,
          ". If your intention is to run this executable, you need to "
          "preload the cache with the full path to a version of that "
          "program, which runs on this build machine.");
        cmSystemTools::Message(msg, "Warning");
      }
    } else {
      cmState* state = status.GetMakefile().GetState();
      haveCacheValue = (cacheValue &&
                        (strstr(cacheValue->c_str(), "(IntDir)") == nullptr ||
                         (intDir == "$(IntDir)")) &&
                        (state->GetCacheMajorVersion() != 0 &&
                         state->GetCacheMinorVersion() != 0));
    }
  
    if (haveCacheValue) {
      return true;
    }
  
    // The second argument is the utility's executable name, which will be
    // needed later.
    std::string const& utilityName = *arg++;
  
    // The third argument specifies the relative directory of the source
    // of the utility.
    std::string const& relativeSource = *arg++;
    std::string utilitySource = status.GetMakefile().GetCurrentSourceDirectory();
    utilitySource = utilitySource + "/" + relativeSource;
  
    // If the directory doesn't exist, the source has not been included.
    if (!cmSystemTools::FileExists(utilitySource)) {
      return true;
    }
  
    // Make sure all the files exist in the source directory.
    while (arg != args.end()) {
      std::string file = utilitySource + "/" + *arg++;
      if (!cmSystemTools::FileExists(file)) {
        return true;
      }
    }
  
    // The source exists.
    const std::string& cmakeCFGout =
      status.GetMakefile().GetRequiredDefinition("CMAKE_CFG_INTDIR");
    std::string utilityDirectory =
      status.GetMakefile().GetCurrentBinaryDirectory();
    std::string exePath;
    if (cmProp d =
          status.GetMakefile().GetDefinition("EXECUTABLE_OUTPUT_PATH")) {
      exePath = *d;
    }
    if (!exePath.empty()) {
      utilityDirectory = exePath;
    } else {
      utilityDirectory += "/" + relativeSource;
    }
  
    // Construct the cache entry for the executable's location.
    std::string utilityExecutable = utilityDirectory + "/" + cmakeCFGout + "/" +
      utilityName +
      *status.GetMakefile().GetDefinition("CMAKE_EXECUTABLE_SUFFIX");
  
    // make sure we remove any /./ in the name
    cmSystemTools::ReplaceString(utilityExecutable, "/./", "/");
  
    // Enter the value into the cache.
    status.GetMakefile().AddCacheDefinition(cacheEntry, utilityExecutable,
                                            "Path to an internal program.",
                                            cmStateEnums::FILEPATH);
    // add a value into the cache that maps from the
    // full path to the name of the project
    cmSystemTools::ConvertToUnixSlashes(utilityExecutable);
    status.GetMakefile().AddCacheDefinition(utilityExecutable, utilityName,
                                            "Executable to project name.",
                                            cmStateEnums::INTERNAL);
  
    return true;
  }
  