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

#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmXMLWriter.h"
#include "cmake.h"

#include "cmsys/SystemInformation.hxx"
#include <map>
#include <set>
#include <sstream>
#include <string.h>
#include <utility>

cmExtraCodeLiteGenerator::cmExtraCodeLiteGenerator()
: cmExternalMakefileProjectGenerator()
, ConfigName("NoConfig")
, CpuCount(2)
{}

cmExternalMakefileProjectGeneratorFactory*
cmExtraCodeLiteGenerator::GetFactory()
{
    static cmExternalMakefileProjectGeneratorSimpleFactory<
        cmExtraCodeLiteGenerator>
        factory("CodeLite", "Generates CodeLite project files.");

    if(factory.GetSupportedGlobalGenerators().empty())
    {
#if defined(_WIN32)
        factory.AddSupportedGlobalGenerator("MinGW Makefiles");
        factory.AddSupportedGlobalGenerator("NMake Makefiles");
#endif
        factory.AddSupportedGlobalGenerator("Ninja");
        factory.AddSupportedGlobalGenerator("Unix Makefiles");
    }

    return &factory;
}

void
cmExtraCodeLiteGenerator::Generate()
{
    // Hold root tree information for creating the workspace
    std::string workspaceProjectName;
    std::string workspaceOutputDir;
    std::string workspaceFileName;
    std::string workspaceSourcePath;

    const std::map<std::string, std::vector<cmLocalGenerator*>>& projectMap =
        this->GlobalGenerator->GetProjectMap();

    // loop projects and locate the root project.
    // and extract the information for creating the worspace
    // root makefile
    for(auto const& it : projectMap)
    {
        const cmMakefile* mf = it.second[0]->GetMakefile();
        this->ConfigName     = GetConfigurationName(mf);

        if(it.second[0]->GetCurrentBinaryDirectory() ==
           it.second[0]->GetBinaryDirectory())
        {
            workspaceOutputDir   = it.second[0]->GetCurrentBinaryDirectory();
            workspaceProjectName = it.second[0]->GetProjectName();
            workspaceSourcePath  = it.second[0]->GetSourceDirectory();
            workspaceFileName    = workspaceOutputDir + "/";
            workspaceFileName += workspaceProjectName + ".workspace";
            this->WorkspacePath = it.second[0]->GetCurrentBinaryDirectory();
            ;
            break;
        }
    }

    cmGeneratedFileStream fout(workspaceFileName);
    cmXMLWriter           xml(fout);

    xml.StartDocument("utf-8");
    xml.StartElement("CodeLite_Workspace");
    xml.Attribute("Name", workspaceProjectName);

    bool const targetsAreProjects =
        this->GlobalGenerator->GlobalSettingIsOn("CMAKE_CODELITE_USE_TARGETS");

    std::vector<std::string> ProjectNames;
    if(targetsAreProjects)
    {
        ProjectNames = CreateProjectsByTarget(&xml);
    } else
    {
        ProjectNames = CreateProjectsByProjectMaps(&xml);
    }

    xml.StartElement("BuildMatrix");
    xml.StartElement("WorkspaceConfiguration");
    xml.Attribute("Name", this->ConfigName);
    xml.Attribute("Selected", "yes");

    for(std::string const& it : ProjectNames)
    {
        xml.StartElement("Project");
        xml.Attribute("Name", it);
        xml.Attribute("ConfigName", this->ConfigName);
        xml.EndElement();
    }

    xml.EndElement();  // WorkspaceConfiguration
    xml.EndElement();  // BuildMatrix
    xml.EndElement();  // CodeLite_Workspace
}

// Create projects where targets are the projects
std::vector<std::string>
cmExtraCodeLiteGenerator::CreateProjectsByTarget(cmXMLWriter* xml)
{
    std::vector<std::string> retval;
    // for each target in the workspace create a codelite project
    const std::vector<cmLocalGenerator*>& lgs =
        this->GlobalGenerator->GetLocalGenerators();
    for(cmLocalGenerator* lg : lgs)
    {
        for(cmGeneratorTarget* lt : lg->GetGeneratorTargets())
        {
            cmStateEnums::TargetType type = lt->GetType();
            std::string const& outputDir  = lg->GetCurrentBinaryDirectory();
            std::string        targetName = lt->GetName();
            std::string filename = outputDir + "/" + targetName + ".project";
            retval.push_back(targetName);
            // Make the project file relative to the workspace
            std::string relafilename =
                cmSystemTools::RelativePath(this->WorkspacePath, filename);
            std::string visualname = targetName;
            switch(type)
            {
                case cmStateEnums::SHARED_LIBRARY:
                case cmStateEnums::STATIC_LIBRARY:
                case cmStateEnums::MODULE_LIBRARY:
                    visualname = "lib" + visualname;
                    CM_FALLTHROUGH;
                case cmStateEnums::EXECUTABLE:
                    xml->StartElement("Project");
                    xml->Attribute("Name", visualname);
                    xml->Attribute("Path", relafilename);
                    xml->Attribute("Active", "No");
                    xml->EndElement();

                    CreateNewProjectFile(lt, filename);
                    break;
                default:
                    break;
            }
        }
    }
    return retval;
}

// The "older way of doing it.
std::vector<std::string>
cmExtraCodeLiteGenerator::CreateProjectsByProjectMaps(cmXMLWriter* xml)
{
    std::vector<std::string> retval;
    // for each sub project in the workspace create a codelite project
    for(auto const& it : this->GlobalGenerator->GetProjectMap())
    {
        std::string const& outputDir =
            it.second[0]->GetCurrentBinaryDirectory();
        std::string projectName = it.second[0]->GetProjectName();
        retval.push_back(projectName);
        std::string filename = outputDir + "/" + projectName + ".project";

        // Make the project file relative to the workspace
        filename = cmSystemTools::RelativePath(this->WorkspacePath, filename);

        // create a project file
        this->CreateProjectFile(it.second);
        xml->StartElement("Project");
        xml->Attribute("Name", projectName);
        xml->Attribute("Path", filename);
        xml->Attribute("Active", "No");
        xml->EndElement();
    }
    return retval;
}

/* create the project file */
void
cmExtraCodeLiteGenerator::CreateProjectFile(
    const std::vector<cmLocalGenerator*>& lgs)
{
    std::string const& outputDir   = lgs[0]->GetCurrentBinaryDirectory();
    std::string        projectName = lgs[0]->GetProjectName();
    std::string        filename    = outputDir + "/";

    filename += projectName + ".project";
    this->CreateNewProjectFile(lgs, filename);
}

std::string
cmExtraCodeLiteGenerator::CollectSourceFiles(
    const cmMakefile* makefile, const cmGeneratorTarget* gt,
    std::map<std::string, cmSourceFile*>& cFiles,
    std::set<std::string>&                otherFiles)
{
    std::string projectType;
    switch(gt->GetType())
    {
        case cmStateEnums::EXECUTABLE:
        {
            projectType = "Executable";
        }
        break;
        case cmStateEnums::STATIC_LIBRARY:
        {
            projectType = "Static Library";
        }
        break;
        case cmStateEnums::SHARED_LIBRARY:
        {
            projectType = "Dynamic Library";
        }
        break;
        case cmStateEnums::MODULE_LIBRARY:
        {
            projectType = "Dynamic Library";
        }
        break;
        default:  // intended fallthrough
            break;
    }

    switch(gt->GetType())
    {
        case cmStateEnums::EXECUTABLE:
        case cmStateEnums::STATIC_LIBRARY:
        case cmStateEnums::SHARED_LIBRARY:
        case cmStateEnums::MODULE_LIBRARY:
        {
            std::vector<cmSourceFile*> sources;
            gt->GetSourceFiles(sources,
                               makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
            for(cmSourceFile* s : sources)
            {
                // check whether it is a source or a include file
                // then put it accordingly into one of the two containers
                switch(cmSystemTools::GetFileFormat(s->GetExtension().c_str()))
                {
                    case cmSystemTools::C_FILE_FORMAT:
                    case cmSystemTools::CXX_FILE_FORMAT:
                    case cmSystemTools::CUDA_FILE_FORMAT:
                    case cmSystemTools::FORTRAN_FILE_FORMAT:
                    {
                        cFiles[s->GetFullPath()] = s;
                    }
                    break;
                    default:
                    {
                        otherFiles.insert(s->GetFullPath());
                    }
                }
            }
        }
        default:  // intended fallthrough
            break;
    }
    return projectType;
}

void
cmExtraCodeLiteGenerator::CreateNewProjectFile(
    const std::vector<cmLocalGenerator*>& lgs, const std::string& filename)
{
    const cmMakefile*     mf = lgs[0]->GetMakefile();
    cmGeneratedFileStream fout(filename);
    if(!fout)
    {
        return;
    }
    cmXMLWriter xml(fout);

    ////////////////////////////////////
    xml.StartDocument("utf-8");
    xml.StartElement("CodeLite_Project");
    xml.Attribute("Name", lgs[0]->GetProjectName());
    xml.Attribute("InternalType", "");

    std::string projectType;

    // Collect all used source files in the project
    // Sort them into two containers, one for C/C++ implementation files
    // which may have an accompanying header, one for all other files
    std::map<std::string, cmSourceFile*> cFiles;
    std::set<std::string>                otherFiles;

    for(cmLocalGenerator* lg : lgs)
    {
        cmMakefile*                            makefile = lg->GetMakefile();
        const std::vector<cmGeneratorTarget*>& targets =
            lg->GetGeneratorTargets();
        for(cmGeneratorTarget* target : targets)
        {
            projectType =
                CollectSourceFiles(makefile, target, cFiles, otherFiles);
        }
    }

    // Get the project path ( we need it later to convert files to
    // their relative path)
    std::string projectPath = cmSystemTools::GetFilenamePath(filename);

    CreateProjectSourceEntries(cFiles, otherFiles, &xml, projectPath, mf,
                               projectType, "");

    xml.EndElement();  // CodeLite_Project
}

void
cmExtraCodeLiteGenerator::FindMatchingHeaderfiles(
    std::map<std::string, cmSourceFile*>& cFiles,
    std::set<std::string>&                otherFiles)
{
    const std::vector<std::string>& headerExts =
        this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();

    // The following loop tries to add header files matching to implementation
    // files to the project. It does that by iterating over all source files,
    // replacing the file name extension with ".h" and checks whether such a
    // file exists. If it does, it is inserted into the map of files.
    // A very similar version of that code exists also in the CodeBlocks
    // project generator.
    for(auto const& sit : cFiles)
    {
        std::string headerBasename = cmSystemTools::GetFilenamePath(sit.first);
        headerBasename += "/";
        headerBasename += cmSystemTools::GetFilenameWithoutExtension(sit.first);

        // check if there's a matching header around
        for(std::string const& ext : headerExts)
        {
            std::string hname = headerBasename;
            hname += ".";
            hname += ext;
            // if it's already in the set, don't check if it exists on disk
            std::set<std::string>::const_iterator headerIt =
                otherFiles.find(hname);
            if(headerIt != otherFiles.end())
            {
                break;
            }

            if(cmSystemTools::FileExists(hname))
            {
                otherFiles.insert(hname);
                break;
            }
        }
    }
}

void
cmExtraCodeLiteGenerator::CreateFoldersAndFiles(std::set<std::string>& cFiles,
                                                cmXMLWriter&           xml,
                                                const std::string& projectPath)
{
    std::vector<std::string> tmp_path;
    std::vector<std::string> components;
    size_t                   numOfEndEl = 0;

    for(std::string const& cFile : cFiles)
    {
        std::string frelapath = cmSystemTools::RelativePath(projectPath, cFile);
        cmsys::SystemTools::SplitPath(frelapath, components, false);
        components.pop_back();  // erase last member -> it is file, not folder
        components.erase(components.begin());  // erase "root"

        size_t sizeOfSkip = 0;

        for(size_t i = 0; i < components.size(); ++i)
        {
            // skip relative path
            if(components[i] == ".." || components[i] == ".")
            {
                sizeOfSkip++;
                continue;
            }

            // same folder
            if(tmp_path.size() > i - sizeOfSkip &&
               tmp_path[i - sizeOfSkip] == components[i])
            {
                continue;
            }

            // delete "old" subfolders
            if(tmp_path.size() > i - sizeOfSkip)
            {
                numOfEndEl = tmp_path.size() - i + sizeOfSkip;
                tmp_path.erase(tmp_path.end() - numOfEndEl, tmp_path.end());
                for(; numOfEndEl--;)
                {
                    xml.EndElement();
                }
            }

            // add folder
            xml.StartElement("VirtualDirectory");
            xml.Attribute("Name", components[i]);
            tmp_path.push_back(components[i]);
        }

        // delete "old" subfolders
        numOfEndEl = tmp_path.size() - components.size() + sizeOfSkip;
        if(numOfEndEl)
        {
            tmp_path.erase(tmp_path.end() - numOfEndEl, tmp_path.end());
            for(; numOfEndEl--;)
            {
                xml.EndElement();
            }
        }

        // add file
        xml.StartElement("File");
        xml.Attribute("Name", frelapath);
        xml.EndElement();
    }

    // end of folders
    numOfEndEl = tmp_path.size();
    for(; numOfEndEl--;)
    {
        xml.EndElement();
    }
}

void
cmExtraCodeLiteGenerator::CreateFoldersAndFiles(
    std::map<std::string, cmSourceFile*>& cFiles, cmXMLWriter& xml,
    const std::string& projectPath)
{
    std::set<std::string> s;
    for(auto const& it : cFiles)
    {
        s.insert(it.first);
    }
    this->CreateFoldersAndFiles(s, xml, projectPath);
}

void
cmExtraCodeLiteGenerator::CreateProjectSourceEntries(
    std::map<std::string, cmSourceFile*>& cFiles,
    std::set<std::string>& otherFiles, cmXMLWriter* _xml,
    const std::string& projectPath, const cmMakefile* mf,
    const std::string& projectType, const std::string& targetName)
{
    cmXMLWriter& xml(*_xml);
    FindMatchingHeaderfiles(cFiles, otherFiles);
    // Create 2 virtual folders: src and include
    // and place all the implementation files into the src
    // folder, the rest goes to the include folder
    xml.StartElement("VirtualDirectory");
    xml.Attribute("Name", "src");

    // insert all source files in the codelite project
    // first the C/C++ implementation files, then all others
    this->CreateFoldersAndFiles(cFiles, xml, projectPath);
    xml.EndElement();  // VirtualDirectory

    xml.StartElement("VirtualDirectory");
    xml.Attribute("Name", "include");
    this->CreateFoldersAndFiles(otherFiles, xml, projectPath);
    xml.EndElement();  // VirtualDirectory

    // Get the number of CPUs. We use this information for the make -jN
    // command
    cmsys::SystemInformation info;
    info.RunCPUCheck();

    this->CpuCount =
        info.GetNumberOfLogicalCPU() * info.GetNumberOfPhysicalCPU();

    std::string codeliteCompilerName = this->GetCodeLiteCompilerName(mf);

    xml.StartElement("Settings");
    xml.Attribute("Type", projectType);

    xml.StartElement("Configuration");
    xml.Attribute("Name", this->ConfigName);
    xml.Attribute("CompilerType", this->GetCodeLiteCompilerName(mf));
    xml.Attribute("DebuggerType", "GNU gdb debugger");
    xml.Attribute("Type", projectType);
    xml.Attribute("BuildCmpWithGlobalSettings", "append");
    xml.Attribute("BuildLnkWithGlobalSettings", "append");
    xml.Attribute("BuildResWithGlobalSettings", "append");

    xml.StartElement("Compiler");
    xml.Attribute("Options", "-g");
    xml.Attribute("Required", "yes");
    xml.Attribute("PreCompiledHeader", "");
    xml.StartElement("IncludePath");
    xml.Attribute("Value", ".");
    xml.EndElement();  // IncludePath
    xml.EndElement();  // Compiler

    xml.StartElement("Linker");
    xml.Attribute("Options", "");
    xml.Attribute("Required", "yes");
    xml.EndElement();  // Linker

    xml.StartElement("ResourceCompiler");
    xml.Attribute("Options", "");
    xml.Attribute("Required", "no");
    xml.EndElement();  // ResourceCompiler

    xml.StartElement("General");
    std::string outputPath =
        mf->GetSafeDefinition("CMAKE_RUNTIME_OUTPUT_DIRECTORY");
    if(outputPath.empty())
    {
        outputPath = mf->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH");
    }
    std::string relapath;
    if(!outputPath.empty())
    {
        relapath = cmSystemTools::RelativePath(projectPath, outputPath);
        xml.Attribute("OutputFile", relapath + "/$(ProjectName)");
    } else
    {
        xml.Attribute("OutputFile", "$(IntermediateDirectory)/$(ProjectName)");
    }
    xml.Attribute("IntermediateDirectory", "./");
    xml.Attribute("Command", "./$(ProjectName)");
    xml.Attribute("CommandArguments", "");
    if(!outputPath.empty())
    {
        xml.Attribute("WorkingDirectory", relapath);
    } else
    {
        xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)");
    }
    xml.Attribute("PauseExecWhenProcTerminates", "yes");
    xml.EndElement();  // General

    xml.StartElement("Debugger");
    xml.Attribute("IsRemote", "no");
    xml.Attribute("RemoteHostName", "");
    xml.Attribute("RemoteHostPort", "");
    xml.Attribute("DebuggerPath", "");
    xml.Element("PostConnectCommands");
    xml.Element("StartupCommands");
    xml.EndElement();  // Debugger

    xml.Element("PreBuild");
    xml.Element("PostBuild");

    xml.StartElement("CustomBuild");
    xml.Attribute("Enabled", "yes");
    xml.Element("RebuildCommand", GetRebuildCommand(mf, targetName));
    xml.Element("CleanCommand", GetCleanCommand(mf, targetName));
    xml.Element("BuildCommand", GetBuildCommand(mf, targetName));
    xml.Element("SingleFileCommand", GetSingleFileBuildCommand(mf));
    xml.Element("PreprocessFileCommand");
    xml.Element("WorkingDirectory", "$(WorkspacePath)");
    xml.EndElement();  // CustomBuild

    xml.StartElement("AdditionalRules");
    xml.Element("CustomPostBuild");
    xml.Element("CustomPreBuild");
    xml.EndElement();  // AdditionalRules

    xml.EndElement();  // Configuration
    xml.StartElement("GlobalSettings");

    xml.StartElement("Compiler");
    xml.Attribute("Options", "");
    xml.StartElement("IncludePath");
    xml.Attribute("Value", ".");
    xml.EndElement();  // IncludePath
    xml.EndElement();  // Compiler

    xml.StartElement("Linker");
    xml.Attribute("Options", "");
    xml.StartElement("LibraryPath");
    xml.Attribute("Value", ".");
    xml.EndElement();  // LibraryPath
    xml.EndElement();  // Linker

    xml.StartElement("ResourceCompiler");
    xml.Attribute("Options", "");
    xml.EndElement();  // ResourceCompiler

    xml.EndElement();  // GlobalSettings
    xml.EndElement();  // Settings
}

void
cmExtraCodeLiteGenerator::CreateNewProjectFile(const cmGeneratorTarget* gt,
                                               const std::string& filename)
{
    const cmMakefile*     mf = gt->Makefile;
    cmGeneratedFileStream fout(filename);
    if(!fout)
    {
        return;
    }
    cmXMLWriter xml(fout);

    ////////////////////////////////////
    xml.StartDocument("utf-8");
    xml.StartElement("CodeLite_Project");
    std::string targetName = gt->GetName();
    std::string visualname = targetName;
    switch(gt->GetType())
    {
        case cmStateEnums::STATIC_LIBRARY:
        case cmStateEnums::SHARED_LIBRARY:
        case cmStateEnums::MODULE_LIBRARY:
            visualname = "lib" + targetName;
        default:  // intended fallthrough
            break;
    }
    xml.Attribute("Name", visualname);
    xml.Attribute("InternalType", "");

    // Collect all used source files in the project
    // Sort them into two containers, one for C/C++ implementation files
    // which may have an accompanying header, one for all other files
    std::string projectType;

    std::map<std::string, cmSourceFile*> cFiles;
    std::set<std::string>                otherFiles;

    projectType = CollectSourceFiles(mf, gt, cFiles, otherFiles);

    // Get the project path ( we need it later to convert files to
    // their relative path)
    std::string projectPath = cmSystemTools::GetFilenamePath(filename);

    CreateProjectSourceEntries(cFiles, otherFiles, &xml, projectPath, mf,
                               projectType, targetName);

    xml.EndElement();  // CodeLite_Project
}

std::string
cmExtraCodeLiteGenerator::GetCodeLiteCompilerName(const cmMakefile* mf) const
{
    // figure out which language to use
    // for now care only for C and C++
    std::string compilerIdVar = "CMAKE_CXX_COMPILER_ID";
    if(!this->GlobalGenerator->GetLanguageEnabled("CXX"))
    {
        compilerIdVar = "CMAKE_C_COMPILER_ID";
    }

    std::string const& compilerId = mf->GetSafeDefinition(compilerIdVar);
    std::string        compiler   = "gnu g++";  // default to g++

    // Since we need the compiler for parsing purposes only
    // it does not matter if we use clang or clang++, same as
    // "gnu gcc" vs "gnu g++"
    if(compilerId == "MSVC")
    {
        compiler = "VC++";
    } else if(compilerId == "Clang")
    {
        compiler = "clang++";
    } else if(compilerId == "GNU")
    {
        compiler = "gnu g++";
    }
    return compiler;
}

std::string
cmExtraCodeLiteGenerator::GetConfigurationName(const cmMakefile* mf) const
{
    std::string confName = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
    // Trim the configuration name from whitespaces (left and right)
    confName.erase(0, confName.find_first_not_of(" \t\r\v\n"));
    confName.erase(confName.find_last_not_of(" \t\r\v\n") + 1);
    if(confName.empty())
    {
        confName = "NoConfig";
    }
    return confName;
}

std::string
cmExtraCodeLiteGenerator::GetBuildCommand(const cmMakefile*  mf,
                                          const std::string& targetName) const
{
    std::string        generator = mf->GetSafeDefinition("CMAKE_GENERATOR");
    std::string        make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
    std::string        buildCommand = make;  // Default
    std::ostringstream ss;
    if(generator == "NMake Makefiles" || generator == "Ninja")
    {
        ss << make;
    } else if(generator == "MinGW Makefiles" || generator == "Unix Makefiles")
    {
        ss << make << " -f$(ProjectPath)/Makefile -j " << this->CpuCount;
    }
    if(!targetName.empty())
    {
        ss << " " << targetName;
    }
    buildCommand = ss.str();
    return buildCommand;
}

std::string
cmExtraCodeLiteGenerator::GetCleanCommand(const cmMakefile*  mf,
                                          const std::string& targetName) const
{
    std::string        generator = mf->GetSafeDefinition("CMAKE_GENERATOR");
    std::ostringstream ss;
    std::string        buildcommand = GetBuildCommand(mf, "");
    if(!targetName.empty() && generator == "Ninja")
    {
        ss << buildcommand << " -t clean " << targetName;
    } else
    {
        ss << buildcommand << " clean";
    }
    return ss.str();
}

std::string
cmExtraCodeLiteGenerator::GetRebuildCommand(const cmMakefile*  mf,
                                            const std::string& targetName) const
{
    return GetCleanCommand(mf, targetName) + " && " +
           GetBuildCommand(mf, targetName);
}

std::string
cmExtraCodeLiteGenerator::GetSingleFileBuildCommand(const cmMakefile* mf) const
{
    std::string buildCommand;
    std::string make      = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
    std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR");
    if(generator == "Unix Makefiles" || generator == "MinGW Makefiles")
    {
        std::ostringstream ss;
#if defined(_WIN32)
        ss << make
           << " -f$(ProjectPath)/Makefile -B $(CurrentFileFullName).obj";
#else
        ss << make << " -f$(ProjectPath)/Makefile -B $(CurrentFileFullName).o";
#endif
        buildCommand = ss.str();
    }
    return buildCommand;
}
