Merge topic 'ninja-cldeps'
eb410e8 Ninja: disable cldeps for bcc32, it's too old, and ninja would also not build 5ead31d Ninja: try work around for bcc32 bug 1333b57 Ninja: build server fixes 9081e3a remove warning about unused parameter f430bea Ninja: maybe this fixes the bcc32 build f2c1288 Ninja: msvc6 for-scoping 44b9bbc Ninja: build with old msvc versions 57156a5 Ninja: build server fixes f1abdce Ninja: some bytes of the rc files couldn't be piped correctly 2de963d Ninja: don't remove space between command and parameters 50b6f33 Ninja: build cmcldeps with mingw c05653e Ninja: try to make GetProcessId visible ab245ff Ninja: but cl supports /nologo ... bf58e9a Ninja: no /nologo option in old rc.exe 2fb07fc Ninja: Eclipse and KDevelop fixes for ninja 518c065 Ninja: don't pollute build dir with preprocessed rc files ...
This commit is contained in:
commit
565744bd3d
@ -48,3 +48,6 @@ SET(CMAKE_C_HAS_ISYSROOT "@CMAKE_C_HAS_ISYSROOT@")
|
|||||||
|
|
||||||
SET(CMAKE_C_IMPLICIT_LINK_LIBRARIES "@CMAKE_C_IMPLICIT_LINK_LIBRARIES@")
|
SET(CMAKE_C_IMPLICIT_LINK_LIBRARIES "@CMAKE_C_IMPLICIT_LINK_LIBRARIES@")
|
||||||
SET(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "@CMAKE_C_IMPLICIT_LINK_DIRECTORIES@")
|
SET(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "@CMAKE_C_IMPLICIT_LINK_DIRECTORIES@")
|
||||||
|
|
||||||
|
@SET_CMAKE_CMCLDEPS_EXECUTABLE@
|
||||||
|
@SET_CMAKE_CL_SHOWINCLUDE_PREFIX@
|
||||||
|
@ -49,3 +49,6 @@ SET(CMAKE_CXX_HAS_ISYSROOT "@CMAKE_CXX_HAS_ISYSROOT@")
|
|||||||
|
|
||||||
SET(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "@CMAKE_CXX_IMPLICIT_LINK_LIBRARIES@")
|
SET(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "@CMAKE_CXX_IMPLICIT_LINK_LIBRARIES@")
|
||||||
SET(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES@")
|
SET(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES@")
|
||||||
|
|
||||||
|
@SET_CMAKE_CMCLDEPS_EXECUTABLE@
|
||||||
|
@SET_CMAKE_CL_SHOWINCLUDE_PREFIX@
|
||||||
|
37
Modules/CMakeClDeps.cmake
Normal file
37
Modules/CMakeClDeps.cmake
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2012 Kitware, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
# see accompanying file Copyright.txt for details.
|
||||||
|
#
|
||||||
|
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the License for more information.
|
||||||
|
#=============================================================================
|
||||||
|
# (To distribute this file outside of CMake, substitute the full
|
||||||
|
# License text for the above reference.)
|
||||||
|
|
||||||
|
#
|
||||||
|
# When using Ninja cl.exe is wrapped by cmcldeps to extract the included
|
||||||
|
# headers for dependency tracking.
|
||||||
|
#
|
||||||
|
# cmcldeps path is set, and cmcldeps needs to know the localized string
|
||||||
|
# in front of each include path, so it can remove it.
|
||||||
|
#
|
||||||
|
|
||||||
|
IF(MSVC_C_ARCHITECTURE_ID AND CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_C_COMPILER AND CMAKE_COMMAND)
|
||||||
|
STRING(REPLACE "cmake.exe" "cmcldeps.exe" CMAKE_CMCLDEPS_EXECUTABLE ${CMAKE_COMMAND})
|
||||||
|
SET(showdir ${CMAKE_BINARY_DIR}/CMakeFiles/ShowIncludes)
|
||||||
|
FILE(WRITE ${showdir}/foo.h "\n")
|
||||||
|
FILE(WRITE ${showdir}/main.c "#include \"foo.h\" \nint main(){}\n")
|
||||||
|
EXECUTE_PROCESS(COMMAND ${CMAKE_C_COMPILER} /nologo /showIncludes ${showdir}/main.c
|
||||||
|
WORKING_DIRECTORY ${showdir} OUTPUT_VARIABLE showOut)
|
||||||
|
STRING(REPLACE main.c "" showOut1 ${showOut})
|
||||||
|
STRING(REPLACE "/" "\\" header1 ${showdir}/foo.h)
|
||||||
|
STRING(TOLOWER ${header1} header2)
|
||||||
|
STRING(REPLACE ${header2} "" showOut2 ${showOut1})
|
||||||
|
STRING(REPLACE "\n" "" showOut3 ${showOut2})
|
||||||
|
SET(SET_CMAKE_CMCLDEPS_EXECUTABLE "SET(CMAKE_CMCLDEPS_EXECUTABLE \"${CMAKE_CMCLDEPS_EXECUTABLE}\")")
|
||||||
|
SET(SET_CMAKE_CL_SHOWINCLUDE_PREFIX "SET(CMAKE_CL_SHOWINCLUDE_PREFIX \"${showOut3}\")")
|
||||||
|
ENDIF()
|
@ -165,9 +165,7 @@ ENDIF (CMAKE_CROSSCOMPILING
|
|||||||
AND "${CMAKE_C_COMPILER_ID}" MATCHES "GNU"
|
AND "${CMAKE_C_COMPILER_ID}" MATCHES "GNU"
|
||||||
AND NOT _CMAKE_TOOLCHAIN_PREFIX)
|
AND NOT _CMAKE_TOOLCHAIN_PREFIX)
|
||||||
|
|
||||||
|
INCLUDE(${CMAKE_ROOT}/Modules/CMakeClDeps.cmake)
|
||||||
|
|
||||||
|
|
||||||
INCLUDE(CMakeFindBinUtils)
|
INCLUDE(CMakeFindBinUtils)
|
||||||
IF(MSVC_C_ARCHITECTURE_ID)
|
IF(MSVC_C_ARCHITECTURE_ID)
|
||||||
SET(SET_MSVC_C_ARCHITECTURE_ID
|
SET(SET_MSVC_C_ARCHITECTURE_ID
|
||||||
|
@ -173,6 +173,7 @@ ENDIF (CMAKE_CROSSCOMPILING
|
|||||||
AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"
|
AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"
|
||||||
AND NOT _CMAKE_TOOLCHAIN_PREFIX)
|
AND NOT _CMAKE_TOOLCHAIN_PREFIX)
|
||||||
|
|
||||||
|
INCLUDE(${CMAKE_ROOT}/Modules/CMakeClDeps.cmake)
|
||||||
INCLUDE(CMakeFindBinUtils)
|
INCLUDE(CMakeFindBinUtils)
|
||||||
IF(MSVC_CXX_ARCHITECTURE_ID)
|
IF(MSVC_CXX_ARCHITECTURE_ID)
|
||||||
SET(SET_MSVC_CXX_ARCHITECTURE_ID
|
SET(SET_MSVC_CXX_ARCHITECTURE_ID
|
||||||
|
@ -383,6 +383,11 @@ IF(CMAKE_ENABLE_NINJA)
|
|||||||
cmNinjaUtilityTargetGenerator.h
|
cmNinjaUtilityTargetGenerator.h
|
||||||
)
|
)
|
||||||
ADD_DEFINITIONS(-DCMAKE_USE_NINJA)
|
ADD_DEFINITIONS(-DCMAKE_USE_NINJA)
|
||||||
|
IF(WIN32 AND NOT CYGWIN AND NOT BORLAND)
|
||||||
|
SET_SOURCE_FILES_PROPERTIES(cmcldeps.cxx PROPERTIES COMPILE_DEFINITIONS _WIN32_WINNT=0x0501)
|
||||||
|
ADD_EXECUTABLE(cmcldeps cmcldeps.cxx)
|
||||||
|
INSTALL_TARGETS(/bin cmcldeps)
|
||||||
|
ENDIF()
|
||||||
ELSE()
|
ELSE()
|
||||||
MESSAGE(STATUS "Ninja generator disabled, enable it with -DCMAKE_ENABLE_NINJA=ON")
|
MESSAGE(STATUS "Ninja generator disabled, enable it with -DCMAKE_ENABLE_NINJA=ON")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
@ -33,6 +33,9 @@ cmExtraEclipseCDT4Generator
|
|||||||
this->SupportedGlobalGenerators.push_back("NMake Makefiles");
|
this->SupportedGlobalGenerators.push_back("NMake Makefiles");
|
||||||
this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
|
this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
|
||||||
// this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
|
// this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
|
||||||
|
#endif
|
||||||
|
#ifdef CMAKE_USE_NINJA
|
||||||
|
this->SupportedGlobalGenerators.push_back("Ninja");
|
||||||
#endif
|
#endif
|
||||||
this->SupportedGlobalGenerators.push_back("Unix Makefiles");
|
this->SupportedGlobalGenerators.push_back("Unix Makefiles");
|
||||||
|
|
||||||
@ -1070,9 +1073,8 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
//insert rules for compiling, preprocessing and assembling individual files
|
//insert rules for compiling, preprocessing and assembling individual files
|
||||||
cmLocalUnixMakefileGenerator3* lumg=(cmLocalUnixMakefileGenerator3*)*it;
|
|
||||||
std::vector<std::string> objectFileTargets;
|
std::vector<std::string> objectFileTargets;
|
||||||
lumg->GetIndividualFileTargets(objectFileTargets);
|
(*it)->GetIndividualFileTargets(objectFileTargets);
|
||||||
for(std::vector<std::string>::const_iterator fit=objectFileTargets.begin();
|
for(std::vector<std::string>::const_iterator fit=objectFileTargets.begin();
|
||||||
fit != objectFileTargets.end();
|
fit != objectFileTargets.end();
|
||||||
++fit)
|
++fit)
|
||||||
|
@ -44,6 +44,9 @@ cmGlobalKdevelopGenerator::cmGlobalKdevelopGenerator()
|
|||||||
:cmExternalMakefileProjectGenerator()
|
:cmExternalMakefileProjectGenerator()
|
||||||
{
|
{
|
||||||
this->SupportedGlobalGenerators.push_back("Unix Makefiles");
|
this->SupportedGlobalGenerators.push_back("Unix Makefiles");
|
||||||
|
#ifdef CMAKE_USE_NINJA
|
||||||
|
this->SupportedGlobalGenerators.push_back("Ninja");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmGlobalKdevelopGenerator::Generate()
|
void cmGlobalKdevelopGenerator::Generate()
|
||||||
|
@ -43,12 +43,13 @@ void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
|
|||||||
std::string replace = comment;
|
std::string replace = comment;
|
||||||
std::string::size_type lpos = 0;
|
std::string::size_type lpos = 0;
|
||||||
std::string::size_type rpos;
|
std::string::size_type rpos;
|
||||||
|
os << "\n#############################################\n";
|
||||||
while((rpos = replace.find('\n', lpos)) != std::string::npos)
|
while((rpos = replace.find('\n', lpos)) != std::string::npos)
|
||||||
{
|
{
|
||||||
os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
|
os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
|
||||||
lpos = rpos + 1;
|
lpos = rpos + 1;
|
||||||
}
|
}
|
||||||
os << "# " << replace.substr(lpos) << "\n";
|
os << "# " << replace.substr(lpos) << "\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsIdentChar(char c)
|
static bool IsIdentChar(char c)
|
||||||
@ -176,7 +177,8 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
|
|||||||
|
|
||||||
// check if a response file rule should be used
|
// check if a response file rule should be used
|
||||||
const std::string args = arguments.str();
|
const std::string args = arguments.str();
|
||||||
if (cmdLineLimit > 0 && args.size() > (size_t)cmdLineLimit)
|
if (cmdLineLimit > 0 &&
|
||||||
|
(args.size() + + builds.str().size()) > (size_t)cmdLineLimit)
|
||||||
builds << "_RSPFILE";
|
builds << "_RSPFILE";
|
||||||
|
|
||||||
os << builds.str() << args;
|
os << builds.str() << args;
|
||||||
@ -318,6 +320,8 @@ void cmGlobalNinjaGenerator::WriteRule(std::ostream& os,
|
|||||||
cmGlobalNinjaGenerator::Indent(os, 1);
|
cmGlobalNinjaGenerator::Indent(os, 1);
|
||||||
os << "generator = 1\n";
|
os << "generator = 1\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
|
void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
|
||||||
@ -380,6 +384,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator()
|
|||||||
this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
|
this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Virtual public methods.
|
// Virtual public methods.
|
||||||
|
|
||||||
@ -458,9 +463,8 @@ void cmGlobalNinjaGenerator
|
|||||||
else if(*l == "RC")
|
else if(*l == "RC")
|
||||||
{
|
{
|
||||||
// check if mingw is used
|
// check if mingw is used
|
||||||
const char* cc = mf->GetDefinition("CMAKE_C_COMPILER");
|
if(mf->IsOn("CMAKE_COMPILER_IS_MINGW"))
|
||||||
if(cc && std::string(cc).find("gcc.exe") != std::string::npos)
|
{
|
||||||
{
|
|
||||||
UsingMinGW = true;
|
UsingMinGW = true;
|
||||||
std::string rc = cmSystemTools::FindProgram("windres");
|
std::string rc = cmSystemTools::FindProgram("windres");
|
||||||
if(rc.empty())
|
if(rc.empty())
|
||||||
@ -470,7 +474,7 @@ void cmGlobalNinjaGenerator
|
|||||||
}
|
}
|
||||||
this->cmGlobalGenerator::EnableLanguage(language, mf, optional);
|
this->cmGlobalGenerator::EnableLanguage(language, mf, optional);
|
||||||
this->ResolveLanguageCompiler(*l, mf, optional);
|
this->ResolveLanguageCompiler(*l, mf, optional);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmGlobalNinjaGenerator::UsingMinGW = false;
|
bool cmGlobalNinjaGenerator::UsingMinGW = false;
|
||||||
@ -538,7 +542,9 @@ void cmGlobalNinjaGenerator::AddRule(const std::string& name,
|
|||||||
{
|
{
|
||||||
// Do not add the same rule twice.
|
// Do not add the same rule twice.
|
||||||
if (this->HasRule(name))
|
if (this->HasRule(name))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->Rules.insert(name);
|
this->Rules.insert(name);
|
||||||
cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream,
|
cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream,
|
||||||
@ -818,7 +824,7 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
|
|||||||
// Insert the alias into the map. If the alias was already present in the
|
// Insert the alias into the map. If the alias was already present in the
|
||||||
// map and referred to another target, mark it as ambiguous.
|
// map and referred to another target, mark it as ambiguous.
|
||||||
std::pair<TargetAliasMap::iterator, bool> newAlias =
|
std::pair<TargetAliasMap::iterator, bool> newAlias =
|
||||||
TargetAliases.insert(make_pair(alias, target));
|
TargetAliases.insert(std::make_pair(alias, target));
|
||||||
if (newAlias.second && newAlias.first->second != target)
|
if (newAlias.second && newAlias.first->second != target)
|
||||||
newAlias.first->second = 0;
|
newAlias.first->second = 0;
|
||||||
}
|
}
|
||||||
@ -922,11 +928,22 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
|
|||||||
cmNinjaDeps());
|
cmNinjaDeps());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string cmGlobalNinjaGenerator::ninjaCmd() const
|
||||||
|
{
|
||||||
|
cmLocalGenerator* lgen = this->LocalGenerators[0];
|
||||||
|
if (lgen) {
|
||||||
|
return lgen->ConvertToOutputFormat(
|
||||||
|
lgen->GetMakefile()->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"),
|
||||||
|
cmLocalGenerator::SHELL);
|
||||||
|
}
|
||||||
|
return "ninja";
|
||||||
|
}
|
||||||
|
|
||||||
void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
|
void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
|
||||||
{
|
{
|
||||||
WriteRule(*this->RulesFileStream,
|
WriteRule(*this->RulesFileStream,
|
||||||
"CLEAN",
|
"CLEAN",
|
||||||
"ninja -t clean",
|
(ninjaCmd() + " -t clean").c_str(),
|
||||||
"Cleaning all built files...",
|
"Cleaning all built files...",
|
||||||
"Rule for cleaning all built files.",
|
"Rule for cleaning all built files.",
|
||||||
/*depfile=*/ "",
|
/*depfile=*/ "",
|
||||||
@ -947,7 +964,7 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
|
|||||||
{
|
{
|
||||||
WriteRule(*this->RulesFileStream,
|
WriteRule(*this->RulesFileStream,
|
||||||
"HELP",
|
"HELP",
|
||||||
"ninja -t targets",
|
(ninjaCmd() + " -t tagets").c_str(),
|
||||||
"All primary targets available:",
|
"All primary targets available:",
|
||||||
"Rule for printing all primary targets available.",
|
"Rule for printing all primary targets available.",
|
||||||
/*depfile=*/ "",
|
/*depfile=*/ "",
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
# include "cmGlobalGenerator.h"
|
# include "cmGlobalGenerator.h"
|
||||||
# include "cmNinjaTypes.h"
|
# include "cmNinjaTypes.h"
|
||||||
|
|
||||||
|
//#define NINJA_GEN_VERBOSE_FILES
|
||||||
|
|
||||||
class cmLocalGenerator;
|
class cmLocalGenerator;
|
||||||
class cmGeneratedFileStream;
|
class cmGeneratedFileStream;
|
||||||
class cmGeneratorTarget;
|
class cmGeneratorTarget;
|
||||||
@ -145,6 +147,9 @@ public:
|
|||||||
const cmNinjaDeps& targets,
|
const cmNinjaDeps& targets,
|
||||||
const std::string& comment = "");
|
const std::string& comment = "");
|
||||||
|
|
||||||
|
|
||||||
|
static bool IsMinGW() { return UsingMinGW; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Default constructor.
|
/// Default constructor.
|
||||||
cmGlobalNinjaGenerator();
|
cmGlobalNinjaGenerator();
|
||||||
@ -312,6 +317,8 @@ private:
|
|||||||
ASD.insert(deps.begin(), deps.end());
|
ASD.insert(deps.begin(), deps.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ninjaCmd() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// The file containing the build statement. (the relation ship of the
|
/// The file containing the build statement. (the relation ship of the
|
||||||
/// compilation DAG).
|
/// compilation DAG).
|
||||||
@ -346,6 +353,7 @@ private:
|
|||||||
static cmLocalGenerator* LocalGenerator;
|
static cmLocalGenerator* LocalGenerator;
|
||||||
|
|
||||||
static bool UsingMinGW;
|
static bool UsingMinGW;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ! cmGlobalNinjaGenerator_h
|
#endif // ! cmGlobalNinjaGenerator_h
|
||||||
|
@ -872,6 +872,13 @@ cmLocalGenerator::ExpandRuleVariable(std::string const& variable,
|
|||||||
return replaceValues.TargetPDB;
|
return replaceValues.TargetPDB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(replaceValues.DependencyFile )
|
||||||
|
{
|
||||||
|
if(variable == "DEP_FILE")
|
||||||
|
{
|
||||||
|
return replaceValues.DependencyFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(replaceValues.Target)
|
if(replaceValues.Target)
|
||||||
{
|
{
|
||||||
|
@ -205,6 +205,10 @@ public:
|
|||||||
/** Compute the language used to compile the given source file. */
|
/** Compute the language used to compile the given source file. */
|
||||||
const char* GetSourceFileLanguage(const cmSourceFile& source);
|
const char* GetSourceFileLanguage(const cmSourceFile& source);
|
||||||
|
|
||||||
|
// Fill the vector with the target names for the object files,
|
||||||
|
// preprocessed files and assembly files.
|
||||||
|
virtual void GetIndividualFileTargets(std::vector<std::string>&) {}
|
||||||
|
|
||||||
// Create a struct to hold the varibles passed into
|
// Create a struct to hold the varibles passed into
|
||||||
// ExpandRuleVariables
|
// ExpandRuleVariables
|
||||||
struct RuleVariables
|
struct RuleVariables
|
||||||
@ -236,6 +240,7 @@ public:
|
|||||||
const char* LanguageCompileFlags;
|
const char* LanguageCompileFlags;
|
||||||
const char* Defines;
|
const char* Defines;
|
||||||
const char* RuleLauncher;
|
const char* RuleLauncher;
|
||||||
|
const char* DependencyFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Set whether to treat conversions to SHELL as a link script shell. */
|
/** Set whether to treat conversions to SHELL as a link script shell. */
|
||||||
|
@ -46,7 +46,9 @@ void cmLocalNinjaGenerator::Generate()
|
|||||||
this->SetConfigName();
|
this->SetConfigName();
|
||||||
|
|
||||||
this->WriteProcessedMakefile(this->GetBuildFileStream());
|
this->WriteProcessedMakefile(this->GetBuildFileStream());
|
||||||
|
#ifdef NINJA_GEN_VERBOSE_FILES
|
||||||
this->WriteProcessedMakefile(this->GetRulesFileStream());
|
this->WriteProcessedMakefile(this->GetRulesFileStream());
|
||||||
|
#endif
|
||||||
|
|
||||||
this->WriteBuildFileTop();
|
this->WriteBuildFileTop();
|
||||||
|
|
||||||
|
@ -55,21 +55,6 @@ cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
cmNinjaNormalTargetGenerator
|
|
||||||
::EnsureDirectoryExists(const std::string& dir)
|
|
||||||
{
|
|
||||||
cmSystemTools::MakeDirectory(dir.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmNinjaNormalTargetGenerator
|
|
||||||
::EnsureParentDirectoryExists(const std::string& path)
|
|
||||||
{
|
|
||||||
EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void cmNinjaNormalTargetGenerator::Generate()
|
void cmNinjaNormalTargetGenerator::Generate()
|
||||||
{
|
{
|
||||||
if (!this->TargetLinkLanguage) {
|
if (!this->TargetLinkLanguage) {
|
||||||
@ -96,13 +81,11 @@ void cmNinjaNormalTargetGenerator::Generate()
|
|||||||
#endif
|
#endif
|
||||||
this->WriteLinkStatement();
|
this->WriteLinkStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->GetBuildFileStream() << "\n";
|
|
||||||
this->GetRulesFileStream() << "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
|
void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
|
||||||
{
|
{
|
||||||
|
#ifdef NINJA_GEN_VERBOSE_FILES
|
||||||
cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
|
cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
|
||||||
this->GetRulesFileStream()
|
this->GetRulesFileStream()
|
||||||
<< "# Rules for each languages for "
|
<< "# Rules for each languages for "
|
||||||
@ -110,6 +93,7 @@ void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
|
|||||||
<< " target "
|
<< " target "
|
||||||
<< this->GetTargetName()
|
<< this->GetTargetName()
|
||||||
<< "\n\n";
|
<< "\n\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
std::set<cmStdString> languages;
|
std::set<cmStdString> languages;
|
||||||
this->GetTarget()->GetLanguages(languages);
|
this->GetTarget()->GetLanguages(languages);
|
||||||
@ -180,16 +164,8 @@ cmNinjaNormalTargetGenerator
|
|||||||
responseFlag += rspfile;
|
responseFlag += rspfile;
|
||||||
vars.Objects = responseFlag.c_str();
|
vars.Objects = responseFlag.c_str();
|
||||||
}
|
}
|
||||||
std::string objdir =
|
|
||||||
this->GetLocalGenerator()->GetHomeRelativeOutputPath();
|
vars.ObjectDir = "$OBJECT_DIR";
|
||||||
objdir += objdir.empty() ? "" : "/";
|
|
||||||
objdir += cmake::GetCMakeFilesDirectoryPostSlash();
|
|
||||||
objdir += this->GetTargetName();
|
|
||||||
objdir += ".dir";
|
|
||||||
objdir = this->GetLocalGenerator()->Convert(objdir.c_str(),
|
|
||||||
cmLocalGenerator::START_OUTPUT,
|
|
||||||
cmLocalGenerator::SHELL);
|
|
||||||
vars.ObjectDir = objdir.c_str();
|
|
||||||
vars.Target = "$out";
|
vars.Target = "$out";
|
||||||
vars.SONameFlag = "$SONAME_FLAG";
|
vars.SONameFlag = "$SONAME_FLAG";
|
||||||
vars.TargetSOName = "$SONAME";
|
vars.TargetSOName = "$SONAME";
|
||||||
@ -402,13 +378,18 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
|||||||
// Compute architecture specific link flags. Yes, these go into a different
|
// Compute architecture specific link flags. Yes, these go into a different
|
||||||
// variable for executables, probably due to a mistake made when duplicating
|
// variable for executables, probably due to a mistake made when duplicating
|
||||||
// code between the Makefile executable and library generators.
|
// code between the Makefile executable and library generators.
|
||||||
this->GetLocalGenerator()
|
std::string flags = (targetType == cmTarget::EXECUTABLE
|
||||||
->AddArchitectureFlags(targetType == cmTarget::EXECUTABLE
|
|
||||||
? vars["FLAGS"]
|
? vars["FLAGS"]
|
||||||
: vars["ARCH_FLAGS"],
|
: vars["ARCH_FLAGS"]);
|
||||||
|
this->GetLocalGenerator()->AddArchitectureFlags(flags,
|
||||||
this->GetTarget(),
|
this->GetTarget(),
|
||||||
this->TargetLinkLanguage,
|
this->TargetLinkLanguage,
|
||||||
this->GetConfigName());
|
this->GetConfigName());
|
||||||
|
if (targetType == cmTarget::EXECUTABLE) {
|
||||||
|
vars["FLAGS"] = flags;
|
||||||
|
} else {
|
||||||
|
vars["ARCH_FLAGS"] = flags;
|
||||||
|
}
|
||||||
if (this->GetTarget()->HasSOName(this->GetConfigName())) {
|
if (this->GetTarget()->HasSOName(this->GetConfigName())) {
|
||||||
vars["SONAME_FLAG"] =
|
vars["SONAME_FLAG"] =
|
||||||
this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage);
|
this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage);
|
||||||
@ -434,10 +415,24 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
|||||||
EnsureParentDirectoryExists(path);
|
EnsureParentDirectoryExists(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
path = this->GetLocalGenerator()->ConvertToOutputFormat(
|
// TODO move to GetTargetPDB
|
||||||
this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL);
|
cmMakefile* mf = this->GetMakefile();
|
||||||
vars["TARGET_PDB"] = path;
|
if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
|
||||||
EnsureParentDirectoryExists(path);
|
mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID"))
|
||||||
|
{
|
||||||
|
path = this->GetTargetPDB();
|
||||||
|
vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
|
||||||
|
ConvertToNinjaPath(path.c_str()).c_str(),
|
||||||
|
cmLocalGenerator::SHELL);
|
||||||
|
EnsureParentDirectoryExists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mf->IsOn("CMAKE_COMPILER_IS_MINGW"))
|
||||||
|
{
|
||||||
|
path = GetTarget()->GetSupportDirectory();
|
||||||
|
vars["OBJECT_DIR"] = ConvertToNinjaPath(path.c_str());
|
||||||
|
EnsureDirectoryExists(path);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<cmCustomCommand> *cmdLists[3] = {
|
std::vector<cmCustomCommand> *cmdLists[3] = {
|
||||||
&this->GetTarget()->GetPreBuildCommands(),
|
&this->GetTarget()->GetPreBuildCommands(),
|
||||||
@ -483,12 +478,13 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
|||||||
symlinkVars["POST_BUILD"] = postBuildCmdLine;
|
symlinkVars["POST_BUILD"] = postBuildCmdLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cmdLineLimit = -1;
|
int cmdLineLimit;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
cmdLineLimit = 8100;
|
cmdLineLimit = 8000;
|
||||||
#else
|
#else
|
||||||
// TODO
|
cmdLineLimit = -1; // TODO
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Write the build statement for this target.
|
// Write the build statement for this target.
|
||||||
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
|
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
|
||||||
comment.str(),
|
comment.str(),
|
||||||
|
@ -35,9 +35,6 @@ private:
|
|||||||
void WriteObjectLibStatement();
|
void WriteObjectLibStatement();
|
||||||
std::vector<std::string> ComputeLinkCmd();
|
std::vector<std::string> ComputeLinkCmd();
|
||||||
|
|
||||||
void EnsureDirectoryExists(const std::string& dir);
|
|
||||||
void EnsureParentDirectoryExists(const std::string& path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Target name info.
|
// Target name info.
|
||||||
std::string TargetNameOut;
|
std::string TargetNameOut;
|
||||||
|
@ -151,6 +151,8 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
|
|||||||
language.c_str());
|
language.c_str());
|
||||||
std::string includeFlags =
|
std::string includeFlags =
|
||||||
this->LocalGenerator->GetIncludeFlags(includes, language.c_str(), false);
|
this->LocalGenerator->GetIncludeFlags(includes, language.c_str(), false);
|
||||||
|
if(cmGlobalNinjaGenerator::IsMinGW())
|
||||||
|
cmSystemTools::ReplaceString(includeFlags, "\\", "/");
|
||||||
this->LocalGenerator->AppendFlags(flags, includeFlags.c_str());
|
this->LocalGenerator->AppendFlags(flags, includeFlags.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +300,7 @@ std::string cmNinjaTargetGenerator::GetTargetPDB() const
|
|||||||
targetFullPathPDB += this->Target->GetPDBName(this->GetConfigName());
|
targetFullPathPDB += this->Target->GetPDBName(this->GetConfigName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConvertToNinjaPath(targetFullPathPDB.c_str());
|
return targetFullPathPDB.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -306,10 +308,11 @@ void
|
|||||||
cmNinjaTargetGenerator
|
cmNinjaTargetGenerator
|
||||||
::WriteLanguageRules(const std::string& language)
|
::WriteLanguageRules(const std::string& language)
|
||||||
{
|
{
|
||||||
|
#ifdef NINJA_GEN_VERBOSE_FILES
|
||||||
this->GetRulesFileStream()
|
this->GetRulesFileStream()
|
||||||
<< "# Rules for language " << language << "\n\n";
|
<< "# Rules for language " << language << "\n\n";
|
||||||
|
#endif
|
||||||
this->WriteCompileRule(language);
|
this->WriteCompileRule(language);
|
||||||
this->GetRulesFileStream() << "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -327,20 +330,45 @@ cmNinjaTargetGenerator
|
|||||||
vars.Defines = "$DEFINES";
|
vars.Defines = "$DEFINES";
|
||||||
vars.TargetPDB = "$TARGET_PDB";
|
vars.TargetPDB = "$TARGET_PDB";
|
||||||
|
|
||||||
|
|
||||||
|
cmMakefile* mf = this->GetMakefile();
|
||||||
|
|
||||||
|
bool useClDeps = false;
|
||||||
|
std::string clDepsBinary;
|
||||||
|
std::string clShowPrefix;
|
||||||
|
if (lang == "C" || lang == "CXX" || lang == "RC")
|
||||||
|
{
|
||||||
|
const char* depsPtr = mf->GetDefinition("CMAKE_CMCLDEPS_EXECUTABLE");
|
||||||
|
const char* showPtr = mf->GetDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX");
|
||||||
|
if (depsPtr && showPtr)
|
||||||
|
{
|
||||||
|
// don't wrap for try_compile,
|
||||||
|
// TODO but why doesn't it work with cmcldeps?
|
||||||
|
const std::string projectName = mf->GetProjectName() ?
|
||||||
|
mf->GetProjectName() : "";
|
||||||
|
if (projectName != "CMAKE_TRY_COMPILE")
|
||||||
|
{
|
||||||
|
useClDeps = true;
|
||||||
|
std::string qu = "\"";
|
||||||
|
clDepsBinary = qu + depsPtr + qu;
|
||||||
|
clShowPrefix = qu + showPtr + qu;
|
||||||
|
vars.DependencyFile = "$DEP_FILE";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string depfile;
|
std::string depfile;
|
||||||
std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language;
|
std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language;
|
||||||
const char *depfileFlags =
|
const char *depfileFlags = mf->GetDefinition(depfileFlagsName.c_str());
|
||||||
this->GetMakefile()->GetDefinition(depfileFlagsName.c_str());
|
if (depfileFlags || useClDeps) {
|
||||||
if (depfileFlags) {
|
std::string depFlagsStr = depfileFlags ? depfileFlags : "";
|
||||||
std::string depfileFlagsStr = depfileFlags;
|
depfile = "$DEP_FILE";
|
||||||
depfile = "$out.d";
|
cmSystemTools::ReplaceString(depFlagsStr, "<DEPFILE>", "\"$DEP_FILE\"");
|
||||||
cmSystemTools::ReplaceString(depfileFlagsStr, "<DEPFILE>",
|
cmSystemTools::ReplaceString(depFlagsStr, "<OBJECT>", "$out");
|
||||||
depfile.c_str());
|
cmSystemTools::ReplaceString(depFlagsStr, "<CMAKE_C_COMPILER>",
|
||||||
cmSystemTools::ReplaceString(depfileFlagsStr, "<OBJECT>",
|
mf->GetDefinition("CMAKE_C_COMPILER"));
|
||||||
"$out");
|
flags += " " + depFlagsStr;
|
||||||
cmSystemTools::ReplaceString(depfileFlagsStr, "<CMAKE_C_COMPILER>",
|
|
||||||
this->GetMakefile()->GetDefinition("CMAKE_C_COMPILER"));
|
|
||||||
flags += " " + depfileFlagsStr;
|
|
||||||
}
|
}
|
||||||
vars.Flags = flags.c_str();
|
vars.Flags = flags.c_str();
|
||||||
|
|
||||||
@ -349,8 +377,7 @@ cmNinjaTargetGenerator
|
|||||||
std::string compileCmdVar = "CMAKE_";
|
std::string compileCmdVar = "CMAKE_";
|
||||||
compileCmdVar += language;
|
compileCmdVar += language;
|
||||||
compileCmdVar += "_COMPILE_OBJECT";
|
compileCmdVar += "_COMPILE_OBJECT";
|
||||||
std::string compileCmd =
|
std::string compileCmd = mf->GetRequiredDefinition(compileCmdVar.c_str());
|
||||||
this->GetMakefile()->GetRequiredDefinition(compileCmdVar.c_str());
|
|
||||||
std::vector<std::string> compileCmds;
|
std::vector<std::string> compileCmds;
|
||||||
cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
|
cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
|
||||||
|
|
||||||
@ -361,6 +388,14 @@ cmNinjaTargetGenerator
|
|||||||
std::string cmdLine =
|
std::string cmdLine =
|
||||||
this->GetLocalGenerator()->BuildCommandLine(compileCmds);
|
this->GetLocalGenerator()->BuildCommandLine(compileCmds);
|
||||||
|
|
||||||
|
if(useClDeps)
|
||||||
|
{
|
||||||
|
std::string cl = mf->GetDefinition("CMAKE_C_COMPILER");
|
||||||
|
cl = "\"" + cl + "\" ";
|
||||||
|
cmdLine = clDepsBinary + " " + lang + " $in \"$DEP_FILE\" $out "
|
||||||
|
+ clShowPrefix + " " + cl + cmdLine;
|
||||||
|
}
|
||||||
|
|
||||||
// Write the rule for compiling file of the given language.
|
// Write the rule for compiling file of the given language.
|
||||||
cmOStringStream comment;
|
cmOStringStream comment;
|
||||||
comment << "Rule for compiling " << language << " files.";
|
comment << "Rule for compiling " << language << " files.";
|
||||||
@ -481,8 +516,18 @@ cmNinjaTargetGenerator
|
|||||||
cmNinjaVars vars;
|
cmNinjaVars vars;
|
||||||
vars["FLAGS"] = this->ComputeFlagsForObject(source, language);
|
vars["FLAGS"] = this->ComputeFlagsForObject(source, language);
|
||||||
vars["DEFINES"] = this->ComputeDefines(source, language);
|
vars["DEFINES"] = this->ComputeDefines(source, language);
|
||||||
vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
|
vars["DEP_FILE"] = objectFileName + ".d";;
|
||||||
this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL);
|
EnsureParentDirectoryExists(objectFileName);
|
||||||
|
|
||||||
|
// TODO move to GetTargetPDB
|
||||||
|
cmMakefile* mf = this->GetMakefile();
|
||||||
|
if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
|
||||||
|
mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID"))
|
||||||
|
{
|
||||||
|
vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
|
||||||
|
ConvertToNinjaPath(GetTargetPDB().c_str()).c_str(),
|
||||||
|
cmLocalGenerator::SHELL);
|
||||||
|
}
|
||||||
|
|
||||||
if(this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS"))
|
if(this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS"))
|
||||||
{
|
{
|
||||||
@ -563,3 +608,17 @@ cmNinjaTargetGenerator
|
|||||||
this->ModuleDefinitionFile.c_str()));
|
this->ModuleDefinitionFile.c_str()));
|
||||||
this->LocalGenerator->AppendFlags(flags, flag.c_str());
|
this->LocalGenerator->AppendFlags(flags, flag.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmNinjaTargetGenerator
|
||||||
|
::EnsureDirectoryExists(const std::string& dir)
|
||||||
|
{
|
||||||
|
cmSystemTools::MakeDirectory(dir.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmNinjaTargetGenerator
|
||||||
|
::EnsureParentDirectoryExists(const std::string& path)
|
||||||
|
{
|
||||||
|
EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path.c_str()));
|
||||||
|
}
|
||||||
|
@ -111,6 +111,9 @@ protected:
|
|||||||
// Helper to add flag for windows .def file.
|
// Helper to add flag for windows .def file.
|
||||||
void AddModuleDefinitionFlag(std::string& flags);
|
void AddModuleDefinitionFlag(std::string& flags);
|
||||||
|
|
||||||
|
void EnsureDirectoryExists(const std::string& dir);
|
||||||
|
void EnsureParentDirectoryExists(const std::string& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cmTarget* Target;
|
cmTarget* Target;
|
||||||
cmGeneratorTarget* GeneratorTarget;
|
cmGeneratorTarget* GeneratorTarget;
|
||||||
|
736
Source/cmcldeps.cxx
Normal file
736
Source/cmcldeps.cxx
Normal file
@ -0,0 +1,736 @@
|
|||||||
|
/*
|
||||||
|
ninja's subprocess.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef NINJA_SUBPROCESS_H_
|
||||||
|
#define NINJA_SUBPROCESS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
|
typedef unsigned __int64 cmULONG_PTR;
|
||||||
|
#else
|
||||||
|
typedef unsigned long cmULONG_PTR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#include "exit_status.h"
|
||||||
|
enum ExitStatus {
|
||||||
|
ExitSuccess,
|
||||||
|
ExitFailure,
|
||||||
|
ExitInterrupted
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Subprocess wraps a single async subprocess. It is entirely
|
||||||
|
/// passive: it expects the caller to notify it when its fds are ready
|
||||||
|
/// for reading, as well as call Finish() to reap the child once done()
|
||||||
|
/// is true.
|
||||||
|
struct Subprocess {
|
||||||
|
~Subprocess();
|
||||||
|
|
||||||
|
/// Returns ExitSuccess on successful process exit, ExitInterrupted if
|
||||||
|
/// the process was interrupted, ExitFailure if it otherwise failed.
|
||||||
|
ExitStatus Finish();
|
||||||
|
|
||||||
|
bool Done() const;
|
||||||
|
|
||||||
|
const std::string& GetOutput() const;
|
||||||
|
|
||||||
|
int ExitCode() const { return exit_code_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Subprocess();
|
||||||
|
bool Start(struct SubprocessSet* set, const std::string& command,
|
||||||
|
const std::string& dir);
|
||||||
|
void OnPipeReady();
|
||||||
|
|
||||||
|
std::string buf_;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/// Set up pipe_ as the parent-side pipe of the subprocess; return the
|
||||||
|
/// other end of the pipe, usable in the child process.
|
||||||
|
HANDLE SetupPipe(HANDLE ioport);
|
||||||
|
|
||||||
|
PROCESS_INFORMATION child_;
|
||||||
|
HANDLE pipe_;
|
||||||
|
OVERLAPPED overlapped_;
|
||||||
|
char overlapped_buf_[4 << 10];
|
||||||
|
bool is_reading_;
|
||||||
|
int exit_code_;
|
||||||
|
#else
|
||||||
|
int fd_;
|
||||||
|
pid_t pid_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
friend struct SubprocessSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
|
||||||
|
/// DoWork() waits for any state change in subprocesses; finished_
|
||||||
|
/// is a queue of subprocesses as they finish.
|
||||||
|
struct SubprocessSet {
|
||||||
|
SubprocessSet();
|
||||||
|
~SubprocessSet();
|
||||||
|
|
||||||
|
Subprocess* Add(const std::string& command, const std::string& dir);
|
||||||
|
bool DoWork();
|
||||||
|
Subprocess* NextFinished();
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
std::vector<Subprocess*> running_;
|
||||||
|
std::queue<Subprocess*> finished_;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType);
|
||||||
|
static HANDLE ioport_;
|
||||||
|
#else
|
||||||
|
static void SetInterruptedFlag(int signum);
|
||||||
|
static bool interrupted_;
|
||||||
|
|
||||||
|
struct sigaction old_act_;
|
||||||
|
sigset_t old_mask_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NINJA_SUBPROCESS_H_
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
ninja's util functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static void Fatal(const char* msg, ...) {
|
||||||
|
va_list ap;
|
||||||
|
fprintf(stderr, "ninja: FATAL: ");
|
||||||
|
va_start(ap, msg);
|
||||||
|
vfprintf(stderr, msg, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
#ifdef _WIN32
|
||||||
|
// On Windows, some tools may inject extra threads.
|
||||||
|
// exit() may block on locks held by those threads, so forcibly exit.
|
||||||
|
fflush(stderr);
|
||||||
|
fflush(stdout);
|
||||||
|
ExitProcess(1);
|
||||||
|
#else
|
||||||
|
exit(1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string GetLastErrorString() {
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
|
||||||
|
char* msg_buf;
|
||||||
|
FormatMessageA(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL,
|
||||||
|
err,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(char*)&msg_buf,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
std::string msg = msg_buf;
|
||||||
|
LocalFree(msg_buf);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define snprintf _snprintf
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
ninja's subprocess-win32.cc
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//#include "subprocess.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
//#include "util.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void Win32Fatal(const char* function) {
|
||||||
|
Fatal("%s: %s", function, GetLastErrorString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
Subprocess::Subprocess() : overlapped_(), is_reading_(false),
|
||||||
|
exit_code_(1) {
|
||||||
|
child_.hProcess = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Subprocess::~Subprocess() {
|
||||||
|
if (pipe_) {
|
||||||
|
if (!CloseHandle(pipe_))
|
||||||
|
Win32Fatal("CloseHandle");
|
||||||
|
}
|
||||||
|
// Reap child if forgotten.
|
||||||
|
if (child_.hProcess)
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE Subprocess::SetupPipe(HANDLE ioport) {
|
||||||
|
char pipe_name[100];
|
||||||
|
snprintf(pipe_name, sizeof(pipe_name),
|
||||||
|
"\\\\.\\pipe\\ninja_pid%u_sp%p", GetCurrentProcessId(), this);
|
||||||
|
|
||||||
|
pipe_ = ::CreateNamedPipeA(pipe_name,
|
||||||
|
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
||||||
|
PIPE_TYPE_BYTE,
|
||||||
|
PIPE_UNLIMITED_INSTANCES,
|
||||||
|
0, 0, INFINITE, NULL);
|
||||||
|
if (pipe_ == INVALID_HANDLE_VALUE)
|
||||||
|
Win32Fatal("CreateNamedPipe");
|
||||||
|
|
||||||
|
if (!CreateIoCompletionPort(pipe_, ioport, (cmULONG_PTR)this, 0))
|
||||||
|
Win32Fatal("CreateIoCompletionPort");
|
||||||
|
|
||||||
|
memset(&overlapped_, 0, sizeof(overlapped_));
|
||||||
|
if (!ConnectNamedPipe(pipe_, &overlapped_) &&
|
||||||
|
GetLastError() != ERROR_IO_PENDING) {
|
||||||
|
Win32Fatal("ConnectNamedPipe");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the write end of the pipe as a handle inheritable across processes.
|
||||||
|
HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0,
|
||||||
|
NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
HANDLE output_write_child;
|
||||||
|
if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
|
||||||
|
GetCurrentProcess(), &output_write_child,
|
||||||
|
0, TRUE, DUPLICATE_SAME_ACCESS)) {
|
||||||
|
Win32Fatal("DuplicateHandle");
|
||||||
|
}
|
||||||
|
CloseHandle(output_write_handle);
|
||||||
|
|
||||||
|
return output_write_child;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Subprocess::Start(SubprocessSet* set, const std::string& command,
|
||||||
|
const std::string& dir) {
|
||||||
|
HANDLE child_pipe = SetupPipe(set->ioport_);
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES security_attributes;
|
||||||
|
memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES));
|
||||||
|
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
security_attributes.bInheritHandle = TRUE;
|
||||||
|
// Must be inheritable so subprocesses can dup to children.
|
||||||
|
HANDLE nul = CreateFile("NUL", GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
&security_attributes, OPEN_EXISTING, 0, NULL);
|
||||||
|
if (nul == INVALID_HANDLE_VALUE)
|
||||||
|
Fatal("couldn't open nul");
|
||||||
|
|
||||||
|
STARTUPINFOA startup_info;
|
||||||
|
memset(&startup_info, 0, sizeof(startup_info));
|
||||||
|
startup_info.cb = sizeof(STARTUPINFO);
|
||||||
|
startup_info.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
startup_info.hStdInput = nul;
|
||||||
|
startup_info.hStdOutput = child_pipe;
|
||||||
|
startup_info.hStdError = child_pipe;
|
||||||
|
|
||||||
|
PROCESS_INFORMATION process_info;
|
||||||
|
memset(&process_info, 0, sizeof(process_info));
|
||||||
|
|
||||||
|
// Do not prepend 'cmd /c' on Windows, this breaks command
|
||||||
|
// lines greater than 8,191 chars.
|
||||||
|
if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
|
||||||
|
/* inherit handles */ TRUE, CREATE_NEW_PROCESS_GROUP,
|
||||||
|
NULL, (dir.empty() ? NULL : dir.c_str()),
|
||||||
|
&startup_info, &process_info)) {
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error == ERROR_FILE_NOT_FOUND) {
|
||||||
|
// file (program) not found error is treated
|
||||||
|
// as a normal build action failure
|
||||||
|
if (child_pipe)
|
||||||
|
CloseHandle(child_pipe);
|
||||||
|
CloseHandle(pipe_);
|
||||||
|
CloseHandle(nul);
|
||||||
|
pipe_ = NULL;
|
||||||
|
// child_ is already NULL;
|
||||||
|
buf_ =
|
||||||
|
"CreateProcess failed: The system cannot find the file specified.\n";
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close pipe channel only used by the child.
|
||||||
|
if (child_pipe)
|
||||||
|
CloseHandle(child_pipe);
|
||||||
|
CloseHandle(nul);
|
||||||
|
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
|
child_ = process_info;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Subprocess::OnPipeReady() {
|
||||||
|
DWORD bytes;
|
||||||
|
if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
|
||||||
|
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||||
|
CloseHandle(pipe_);
|
||||||
|
pipe_ = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Win32Fatal("GetOverlappedResult");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_reading_ && bytes)
|
||||||
|
buf_.append(overlapped_buf_, bytes);
|
||||||
|
|
||||||
|
memset(&overlapped_, 0, sizeof(overlapped_));
|
||||||
|
is_reading_ = true;
|
||||||
|
if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),
|
||||||
|
&bytes, &overlapped_)) {
|
||||||
|
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||||
|
CloseHandle(pipe_);
|
||||||
|
pipe_ = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (GetLastError() != ERROR_IO_PENDING)
|
||||||
|
Win32Fatal("ReadFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if we read any bytes in the readfile call, we'll enter this
|
||||||
|
// function again later and get them at that point.
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitStatus Subprocess::Finish() {
|
||||||
|
if (!child_.hProcess)
|
||||||
|
return ExitFailure;
|
||||||
|
|
||||||
|
// TODO: add error handling for all of these.
|
||||||
|
WaitForSingleObject(child_.hProcess, INFINITE);
|
||||||
|
|
||||||
|
DWORD exit_code = 0;
|
||||||
|
GetExitCodeProcess(child_.hProcess, &exit_code);
|
||||||
|
|
||||||
|
CloseHandle(child_.hProcess);
|
||||||
|
child_.hProcess = NULL;
|
||||||
|
exit_code_ = exit_code;
|
||||||
|
return exit_code == 0 ? ExitSuccess :
|
||||||
|
exit_code == CONTROL_C_EXIT ? ExitInterrupted :
|
||||||
|
ExitFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Subprocess::Done() const {
|
||||||
|
return pipe_ == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Subprocess::GetOutput() const {
|
||||||
|
return buf_;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE SubprocessSet::ioport_;
|
||||||
|
|
||||||
|
SubprocessSet::SubprocessSet() {
|
||||||
|
ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
|
||||||
|
if (!ioport_)
|
||||||
|
Win32Fatal("CreateIoCompletionPort");
|
||||||
|
if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
|
||||||
|
Win32Fatal("SetConsoleCtrlHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
SubprocessSet::~SubprocessSet() {
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
|
||||||
|
CloseHandle(ioport_);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {
|
||||||
|
if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
|
||||||
|
if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))
|
||||||
|
Win32Fatal("PostQueuedCompletionStatus");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Subprocess *SubprocessSet::Add(const std::string& command,
|
||||||
|
const std::string& dir) {
|
||||||
|
Subprocess *subprocess = new Subprocess;
|
||||||
|
if (!subprocess->Start(this, command, dir)) {
|
||||||
|
delete subprocess;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (subprocess->child_.hProcess)
|
||||||
|
running_.push_back(subprocess);
|
||||||
|
else
|
||||||
|
finished_.push(subprocess);
|
||||||
|
return subprocess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SubprocessSet::DoWork() {
|
||||||
|
DWORD bytes_read;
|
||||||
|
Subprocess* subproc;
|
||||||
|
OVERLAPPED* overlapped;
|
||||||
|
|
||||||
|
if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (cmULONG_PTR*)&subproc,
|
||||||
|
&overlapped, INFINITE)) {
|
||||||
|
if (GetLastError() != ERROR_BROKEN_PIPE)
|
||||||
|
Win32Fatal("GetQueuedCompletionStatus");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subproc) // A NULL subproc indicates that we were interrupted and is
|
||||||
|
// delivered by NotifyInterrupted above.
|
||||||
|
return true;
|
||||||
|
|
||||||
|
subproc->OnPipeReady();
|
||||||
|
|
||||||
|
if (subproc->Done()) {
|
||||||
|
std::vector<Subprocess*>::iterator end =
|
||||||
|
std::remove(running_.begin(), running_.end(), subproc);
|
||||||
|
if (running_.end() != end) {
|
||||||
|
finished_.push(subproc);
|
||||||
|
running_.resize(end - running_.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Subprocess* SubprocessSet::NextFinished() {
|
||||||
|
if (finished_.empty())
|
||||||
|
return NULL;
|
||||||
|
Subprocess* subproc = finished_.front();
|
||||||
|
finished_.pop();
|
||||||
|
return subproc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SubprocessSet::Clear() {
|
||||||
|
std::vector<Subprocess*>::iterator it = running_.begin();
|
||||||
|
for (; it != running_.end(); ++it) {
|
||||||
|
if ((*it)->child_.hProcess) {
|
||||||
|
if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
|
||||||
|
(*it)->child_.dwProcessId))
|
||||||
|
Win32Fatal("GenerateConsoleCtrlEvent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = running_.begin();
|
||||||
|
for (; it != running_.end(); ++it)
|
||||||
|
delete *it;
|
||||||
|
running_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
// Wrapper around cl that adds /showIncludes to command line, and uses that to
|
||||||
|
// generate .d files that match the style from gcc -MD.
|
||||||
|
//
|
||||||
|
// /showIncludes is equivalent to -MD, not -MMD, that is, system headers are
|
||||||
|
// included.
|
||||||
|
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <sstream>
|
||||||
|
//#include "subprocess.h"
|
||||||
|
//#include "util.h"
|
||||||
|
|
||||||
|
// We don't want any wildcard expansion.
|
||||||
|
// See http://msdn.microsoft.com/en-us/library/zay8tzh6(v=vs.85).aspx
|
||||||
|
void _setargv() {}
|
||||||
|
|
||||||
|
static void usage(const char* msg) {
|
||||||
|
Fatal("%s\n\nusage:\n "
|
||||||
|
"cmcldeps "
|
||||||
|
"<language C, CXX or RC> "
|
||||||
|
"<source file path> "
|
||||||
|
"<output path for *.d file> "
|
||||||
|
"<output path for *.obj file> "
|
||||||
|
"<prefix of /showIncludes> "
|
||||||
|
"<path to cl.exe> "
|
||||||
|
"<path to tool (cl or rc)> "
|
||||||
|
"<rest of command ...>\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string trimLeadingSpace(const std::string& cmdline) {
|
||||||
|
int i = 0;
|
||||||
|
for (; cmdline[i] == ' '; ++i)
|
||||||
|
;
|
||||||
|
return cmdline.substr(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void doEscape(std::string& str, const std::string& search,
|
||||||
|
const std::string& repl) {
|
||||||
|
std::string::size_type pos = 0;
|
||||||
|
while ((pos = str.find(search, pos)) != std::string::npos) {
|
||||||
|
str.replace(pos, search.size(), repl);
|
||||||
|
pos += repl.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strips one argument from the cmdline and returns it. "surrounding quotes"
|
||||||
|
// are removed from the argument if there were any.
|
||||||
|
static std::string getArg(std::string& cmdline) {
|
||||||
|
std::string ret;
|
||||||
|
bool in_quoted = false;
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
|
cmdline = trimLeadingSpace(cmdline);
|
||||||
|
|
||||||
|
for (;; ++i) {
|
||||||
|
if (i >= cmdline.size())
|
||||||
|
usage("Couldn't parse arguments.");
|
||||||
|
if (!in_quoted && cmdline[i] == ' ')
|
||||||
|
break; // "a b" "x y"
|
||||||
|
if (cmdline[i] == '"')
|
||||||
|
in_quoted = !in_quoted;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cmdline.substr(0, i);
|
||||||
|
if (ret[0] == '"' && ret[i - 1] == '"')
|
||||||
|
ret = ret.substr(1, ret.size() - 2);
|
||||||
|
cmdline = cmdline.substr(i);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parseCommandLine(LPTSTR wincmdline,
|
||||||
|
std::string& lang,
|
||||||
|
std::string& srcfile,
|
||||||
|
std::string& dfile,
|
||||||
|
std::string& objfile,
|
||||||
|
std::string& prefix,
|
||||||
|
std::string& clpath,
|
||||||
|
std::string& binpath,
|
||||||
|
std::string& rest) {
|
||||||
|
std::string cmdline(wincmdline);
|
||||||
|
/* self */ getArg(cmdline);
|
||||||
|
lang = getArg(cmdline);
|
||||||
|
srcfile = getArg(cmdline);
|
||||||
|
dfile = getArg(cmdline);
|
||||||
|
objfile = getArg(cmdline);
|
||||||
|
prefix = getArg(cmdline);
|
||||||
|
clpath = getArg(cmdline);
|
||||||
|
binpath = getArg(cmdline);
|
||||||
|
rest = trimLeadingSpace(cmdline);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void outputDepFile(const std::string& dfile, const std::string& objfile,
|
||||||
|
std::vector<std::string>& incs) {
|
||||||
|
|
||||||
|
if (dfile.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// strip duplicates
|
||||||
|
std::sort(incs.begin(), incs.end());
|
||||||
|
incs.erase(std::unique(incs.begin(), incs.end()), incs.end());
|
||||||
|
|
||||||
|
FILE* out = fopen(dfile.c_str(), "wb");
|
||||||
|
|
||||||
|
// FIXME should this be fatal or not? delete obj? delete d?
|
||||||
|
if (!out)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string tmp = objfile;
|
||||||
|
doEscape(tmp, " ", "\\ ");
|
||||||
|
fprintf(out, "%s: \\\n", tmp.c_str());
|
||||||
|
|
||||||
|
std::vector<std::string>::iterator it = incs.begin();
|
||||||
|
for (; it != incs.end(); ++it) {
|
||||||
|
tmp = *it;
|
||||||
|
doEscape(tmp, "\\", "/");
|
||||||
|
doEscape(tmp, " ", "\\ ");
|
||||||
|
fprintf(out, "%s \\\n", tmp.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, "\n");
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool startsWith(const std::string& str, const std::string& what) {
|
||||||
|
return str.compare(0, what.size(), what) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const std::string& str, const std::string& what) {
|
||||||
|
return str.find(what) != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string replace(const std::string& str, const std::string& what,
|
||||||
|
const std::string& replacement) {
|
||||||
|
size_t pos = str.find(what);
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return str;
|
||||||
|
std::string replaced = str;
|
||||||
|
return replaced.replace(pos, what.size(), replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int process( const std::string& srcfilename,
|
||||||
|
const std::string& dfile,
|
||||||
|
const std::string& objfile,
|
||||||
|
const std::string& prefix,
|
||||||
|
const std::string& cmd,
|
||||||
|
const std::string& dir = "",
|
||||||
|
bool quiet = false) {
|
||||||
|
|
||||||
|
SubprocessSet subprocs;
|
||||||
|
Subprocess* subproc = subprocs.Add(cmd, dir);
|
||||||
|
|
||||||
|
if(!subproc)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
while ((subproc = subprocs.NextFinished()) == NULL) {
|
||||||
|
subprocs.DoWork();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = subproc->Finish() == ExitSuccess;
|
||||||
|
int exit_code = subproc->ExitCode();
|
||||||
|
|
||||||
|
std::string output = subproc->GetOutput();
|
||||||
|
delete subproc;
|
||||||
|
|
||||||
|
// process the include directives and output everything else
|
||||||
|
std::stringstream ss(output);
|
||||||
|
std::string line;
|
||||||
|
std::vector<std::string> includes;
|
||||||
|
bool isFirstLine = true; // cl prints always first the source filename
|
||||||
|
while (std::getline(ss, line)) {
|
||||||
|
if (startsWith(line, prefix)) {
|
||||||
|
std::string inc = trimLeadingSpace(line.substr(prefix.size()).c_str());
|
||||||
|
if (inc[inc.size() - 1] == '\r') // blech, stupid \r\n
|
||||||
|
inc = inc.substr(0, inc.size() - 1);
|
||||||
|
includes.push_back(inc);
|
||||||
|
} else {
|
||||||
|
if (!isFirstLine || !startsWith(line, srcfilename)) {
|
||||||
|
if (!quiet) {
|
||||||
|
fprintf(stdout, "%s\n", line.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isFirstLine = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't update .d until/unless we succeed compilation
|
||||||
|
outputDepFile(dfile, objfile, includes);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// Use the Win32 api instead of argc/argv so we can avoid interpreting the
|
||||||
|
// rest of command line after the .d and .obj. Custom parsing seemed
|
||||||
|
// preferable to the ugliness you get into in trying to re-escape quotes for
|
||||||
|
// subprocesses, so by avoiding argc/argv, the subprocess is called with
|
||||||
|
// the same command line verbatim.
|
||||||
|
|
||||||
|
std::string lang, srcfile, dfile, objfile, prefix, cl, binpath, rest;
|
||||||
|
parseCommandLine(GetCommandLine(), lang, srcfile, dfile, objfile,
|
||||||
|
prefix, cl, binpath, rest);
|
||||||
|
|
||||||
|
// needed to suppress filename output of msvc tools
|
||||||
|
std::string srcfilename;
|
||||||
|
std::string::size_type pos = srcfile.rfind("\\");
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
srcfilename = srcfile.substr(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string nol = " /nologo ";
|
||||||
|
std::string show = " /showIncludes ";
|
||||||
|
if (lang == "C" || lang == "CXX") {
|
||||||
|
return process(srcfilename, dfile, objfile, prefix,
|
||||||
|
binpath + nol + show + rest);
|
||||||
|
} else if (lang == "RC") {
|
||||||
|
// "misuse" cl.exe to get headers from .rc files
|
||||||
|
|
||||||
|
std::string clrest = rest;
|
||||||
|
// rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj
|
||||||
|
clrest = replace(clrest, "/fo", "/out:");
|
||||||
|
clrest = replace(clrest, objfile, objfile + ".dep.obj ");
|
||||||
|
// rc: src\x\x.rc -> cl: /Tc src\x\x.rc
|
||||||
|
clrest = replace(clrest, srcfile, "/Tc " + srcfile);
|
||||||
|
|
||||||
|
cl = "\"" + cl + "\" /P /DRC_INVOKED ";
|
||||||
|
|
||||||
|
// call cl in object dir so the .i is generated there
|
||||||
|
std::string objdir;
|
||||||
|
std::string::size_type pos = objfile.rfind("\\");
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
objdir = objfile.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract dependencies with cl.exe
|
||||||
|
process(srcfilename, dfile, objfile,
|
||||||
|
prefix, cl + nol + show + clrest, objdir, true);
|
||||||
|
|
||||||
|
// compile rc file with rc.exe
|
||||||
|
return process(srcfilename, "" , objfile, prefix, binpath + " " + rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
usage("Invalid language specified.");
|
||||||
|
return 1;
|
||||||
|
}
|
@ -28,6 +28,10 @@ function(help_xcode_depends)
|
|||||||
endif(HELP_XCODE)
|
endif(HELP_XCODE)
|
||||||
endfunction(help_xcode_depends)
|
endfunction(help_xcode_depends)
|
||||||
|
|
||||||
|
if("${CMAKE_GENERATOR}" MATCHES "Ninja")
|
||||||
|
set(HELP_NINJA 1) # TODO Why is this needed?
|
||||||
|
endif()
|
||||||
|
|
||||||
# The Intel compiler causes the MSVC linker to crash during
|
# The Intel compiler causes the MSVC linker to crash during
|
||||||
# incremental linking, so avoid the /INCREMENTAL:YES flag.
|
# incremental linking, so avoid the /INCREMENTAL:YES flag.
|
||||||
if(WIN32 AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel")
|
if(WIN32 AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel")
|
||||||
@ -154,7 +158,7 @@ try_compile(RESULT
|
|||||||
OUTPUT_VARIABLE OUTPUT)
|
OUTPUT_VARIABLE OUTPUT)
|
||||||
|
|
||||||
# Xcode is in serious need of help here
|
# Xcode is in serious need of help here
|
||||||
if(HELP_XCODE)
|
if(HELP_XCODE OR HELP_NINJA)
|
||||||
try_compile(RESULT
|
try_compile(RESULT
|
||||||
${BuildDepends_BINARY_DIR}/Project
|
${BuildDepends_BINARY_DIR}/Project
|
||||||
${BuildDepends_SOURCE_DIR}/Project
|
${BuildDepends_SOURCE_DIR}/Project
|
||||||
@ -165,7 +169,7 @@ if(HELP_XCODE)
|
|||||||
${BuildDepends_SOURCE_DIR}/Project
|
${BuildDepends_SOURCE_DIR}/Project
|
||||||
testRebuild
|
testRebuild
|
||||||
OUTPUT_VARIABLE OUTPUT)
|
OUTPUT_VARIABLE OUTPUT)
|
||||||
endif(HELP_XCODE)
|
endif()
|
||||||
|
|
||||||
message("Output from second build:\n${OUTPUT}")
|
message("Output from second build:\n${OUTPUT}")
|
||||||
if(NOT RESULT)
|
if(NOT RESULT)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user