Merge topic 'ninja_phony_targets'

539356f Ninja: Custom Command file depends don't need to exist before building
874e171 Ninja: GlobalNinjaGenerator WriteBuild and WritePhonyBuild non static
88d27ad Add a test to expose a bug with add_custom_command and ninja.
This commit is contained in:
Brad King 2013-07-01 09:15:09 -04:00 committed by CMake Topic Stage
commit 62428f5e72
7 changed files with 279 additions and 129 deletions

View File

@ -10,11 +10,12 @@
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpressionEvaluationFile.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmVersion.h"
#include <algorithm>
@ -140,8 +141,15 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
for(cmNinjaDeps::const_iterator i = explicitDeps.begin();
i != explicitDeps.end();
++i)
{
arguments << " " << EncodeIdent(EncodePath(*i), os);
//we need to track every dependency that comes in, since we are trying
//to find dependencies that are side effects of build commands
//
this->CombinedBuildExplicitDependencies.insert(*i);
}
// Write implicit dependencies.
if(!implicitDeps.empty())
{
@ -170,7 +178,10 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
build << "build";
for(cmNinjaDeps::const_iterator i = outputs.begin();
i != outputs.end(); ++i)
{
build << " " << EncodeIdent(EncodePath(*i), os);
this->CombinedBuildOutputs.insert(*i);
}
build << ":";
// Write the rule.
@ -208,14 +219,14 @@ void cmGlobalNinjaGenerator::WritePhonyBuild(std::ostream& os,
const cmNinjaDeps& orderOnlyDeps,
const cmNinjaVars& variables)
{
cmGlobalNinjaGenerator::WriteBuild(os,
comment,
"phony",
outputs,
explicitDeps,
implicitDeps,
orderOnlyDeps,
variables);
this->WriteBuild(os,
comment,
"phony",
outputs,
explicitDeps,
implicitDeps,
orderOnlyDeps,
variables);
}
void cmGlobalNinjaGenerator::AddCustomCommandRule()
@ -251,14 +262,14 @@ cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command,
vars["COMMAND"] = cmd;
vars["DESC"] = EncodeLiteral(description);
cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream,
comment,
"CUSTOM_COMMAND",
outputs,
deps,
cmNinjaDeps(),
orderOnlyDeps,
vars);
this->WriteBuild(*this->BuildFileStream,
comment,
"CUSTOM_COMMAND",
outputs,
deps,
cmNinjaDeps(),
orderOnlyDeps,
vars);
}
void
@ -293,14 +304,14 @@ cmGlobalNinjaGenerator::WriteMacOSXContentBuild(const std::string& input,
deps.push_back(input);
cmNinjaVars vars;
cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream,
"",
"COPY_OSX_CONTENT",
outputs,
deps,
cmNinjaDeps(),
cmNinjaDeps(),
cmNinjaVars());
this->WriteBuild(*this->BuildFileStream,
"",
"COPY_OSX_CONTENT",
outputs,
deps,
cmNinjaDeps(),
cmNinjaDeps(),
cmNinjaVars());
}
void cmGlobalNinjaGenerator::WriteRule(std::ostream& os,
@ -478,6 +489,7 @@ void cmGlobalNinjaGenerator::Generate()
this->WriteAssumedSourceDependencies();
this->WriteTargetAliases(*this->BuildFileStream);
this->WriteUnknownExplicitDependencies(*this->BuildFileStream);
this->WriteBuiltinTargets(*this->BuildFileStream);
if (cmSystemTools::GetErrorOccuredFlag()) {
@ -887,7 +899,7 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
cmGlobalNinjaGenerator::WriteDivider(os);
os << "# Target aliases.\n\n";
for (TargetAliasMap::iterator i = TargetAliases.begin();
for (TargetAliasMap::const_iterator i = TargetAliases.begin();
i != TargetAliases.end(); ++i) {
// Don't write ambiguous aliases.
if (!i->second)
@ -896,13 +908,124 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
cmNinjaDeps deps;
this->AppendTargetOutputs(i->second, deps);
cmGlobalNinjaGenerator::WritePhonyBuild(os,
"",
cmNinjaDeps(1, i->first),
deps);
this->WritePhonyBuild(os,
"",
cmNinjaDeps(1, i->first),
deps);
}
}
void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os)
{
//now write out the unknown explicit dependencies.
//union the configured files, evaluations files and the CombinedBuildOutputs,
//and then difference with CombinedExplicitDependencies to find the explicit
//dependencies that we have no rule for
cmGlobalNinjaGenerator::WriteDivider(os);
os << "# Unknown Build Time Dependencies.\n"
<< "# Tell Ninja that they may appear as side effects of build rules\n"
<< "# otherwise ordered by order-only dependencies.\n\n";
//get the list of files that cmake itself has generated as a
//product of configuration.
cmLocalNinjaGenerator *ng =
static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]);
std::set<std::string> knownDependencies;
for (std::vector<cmLocalGenerator *>::const_iterator i =
this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i)
{
//get the vector of files created by this makefile and convert them
//to ninja paths, which are all relative in respect to the build directory
const std::vector<std::string>& files =
(*i)->GetMakefile()->GetOutputFiles();
typedef std::vector<std::string>::const_iterator vect_it;
for(vect_it j = files.begin(); j != files.end(); ++j)
{
knownDependencies.insert(ng->ConvertToNinjaPath( j->c_str() ));
}
}
for(std::vector<cmGeneratorExpressionEvaluationFile*>::const_iterator
li = this->EvaluationFiles.begin();
li != this->EvaluationFiles.end();
++li)
{
//get all the files created by generator expressions and convert them
//to ninja paths
std::vector<std::string> files = (*li)->GetFiles();
typedef std::vector<std::string>::const_iterator vect_it;
for(vect_it j = files.begin(); j != files.end(); ++j)
{
knownDependencies.insert(ng->ConvertToNinjaPath( j->c_str() ));
}
}
//insert outputs from all WirteBuild commands
for(std::set<std::string>::iterator i = this->CombinedBuildOutputs.begin();
i != this->CombinedBuildOutputs.end(); ++i)
{
knownDependencies.insert(*i);
}
//after we have combined the data into knownDependencies we have no need
//to keep this data around
this->CombinedBuildOutputs.clear();
for(TargetAliasMap::const_iterator i= this->TargetAliases.begin();
i != this->TargetAliases.end();
++i)
{
knownDependencies.insert(i->first);
}
//remove all source files we know will exist.
typedef std::map<std::string, std::set<std::string> >::const_iterator map_it;
for(map_it i = this->AssumedSourceDependencies.begin();
i != this->AssumedSourceDependencies.end();
++i)
{
knownDependencies.insert(i->first);
}
//now we difference with CombinedBuildExplicitDependencies to find
//the list of items we know nothing about
std::vector<std::string> unkownExplicitDepends;
this->CombinedBuildExplicitDependencies.erase("all");
std::set_difference(this->CombinedBuildExplicitDependencies.begin(),
this->CombinedBuildExplicitDependencies.end(),
knownDependencies.begin(),
knownDependencies.end(),
std::back_inserter(unkownExplicitDepends));
std::string const rootBuildDirectory =
this->GetCMakeInstance()->GetHomeOutputDirectory();
for (std::vector<std::string>::const_iterator
i = unkownExplicitDepends.begin();
i != unkownExplicitDepends.end();
++i)
{
//verify the file is in the build directory
std::string const absDepPath = cmSystemTools::CollapseFullPath(
i->c_str(), rootBuildDirectory.c_str());
bool const inBuildDir = cmSystemTools::IsSubDirectory(absDepPath.c_str(),
rootBuildDirectory.c_str());
if(inBuildDir)
{
cmNinjaDeps deps(1,*i);
this->WritePhonyBuild(os,
"",
deps,
deps);
}
}
}
void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
{
// Write headers.
@ -920,10 +1043,10 @@ void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
cmNinjaDeps outputs;
outputs.push_back("all");
cmGlobalNinjaGenerator::WritePhonyBuild(os,
"The main all target.",
outputs,
this->AllDependencies);
this->WritePhonyBuild(os,
"The main all target.",
outputs,
this->AllDependencies);
cmGlobalNinjaGenerator::WriteDefault(os,
outputs,
@ -970,19 +1093,19 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
implicitDeps.end());
implicitDeps.push_back("CMakeCache.txt");
WriteBuild(os,
"Re-run CMake if any of its inputs changed.",
"RERUN_CMAKE",
/*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE),
/*explicitDeps=*/ cmNinjaDeps(),
implicitDeps,
/*orderOnlyDeps=*/ cmNinjaDeps(),
/*variables=*/ cmNinjaVars());
this->WriteBuild(os,
"Re-run CMake if any of its inputs changed.",
"RERUN_CMAKE",
/*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE),
/*explicitDeps=*/ cmNinjaDeps(),
implicitDeps,
/*orderOnlyDeps=*/ cmNinjaDeps(),
/*variables=*/ cmNinjaVars());
WritePhonyBuild(os,
"A missing CMake input file is not an error.",
implicitDeps,
cmNinjaDeps());
this->WritePhonyBuild(os,
"A missing CMake input file is not an error.",
implicitDeps,
cmNinjaDeps());
}
std::string cmGlobalNinjaGenerator::ninjaCmd() const

View File

@ -77,27 +77,27 @@ public:
* It also writes the variables bound to this build statement.
* @warning no escaping of any kind is done here.
*/
static void WriteBuild(std::ostream& os,
const std::string& comment,
const std::string& rule,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps,
const cmNinjaDeps& orderOnlyDeps,
const cmNinjaVars& variables,
const std::string& rspfile = std::string(),
int cmdLineLimit = -1);
void WriteBuild(std::ostream& os,
const std::string& comment,
const std::string& rule,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps,
const cmNinjaDeps& orderOnlyDeps,
const cmNinjaVars& variables,
const std::string& rspfile = std::string(),
int cmdLineLimit = -1);
/**
* Helper to write a build statement with the special 'phony' rule.
*/
static void WritePhonyBuild(std::ostream& os,
const std::string& comment,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps = cmNinjaDeps(),
const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps(),
const cmNinjaVars& variables = cmNinjaVars());
void WritePhonyBuild(std::ostream& os,
const std::string& comment,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps = cmNinjaDeps(),
const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps(),
const cmNinjaVars& variables = cmNinjaVars());
void WriteCustomCommandBuild(const std::string& command,
const std::string& description,
@ -321,6 +321,7 @@ private:
void WriteAssumedSourceDependencies();
void WriteTargetAliases(std::ostream& os);
void WriteUnknownExplicitDependencies(std::ostream& os);
void WriteBuiltinTargets(std::ostream& os);
void WriteTargetAll(std::ostream& os);
@ -358,6 +359,12 @@ private:
/// The set of custom command outputs we have seen.
std::set<std::string> CustomCommandOutputs;
//The combined explicit dependencies of all build commands that the global
//generator has issued. When combined with CombinedBuildOutputs it allows
//us to detect the set of explicit dependencies that have
std::set<std::string> CombinedBuildExplicitDependencies;
std::set<std::string> CombinedBuildOutputs;
/// The mapping from source file to assumed dependencies.
std::map<std::string, std::set<std::string> > AssumedSourceDependencies;

View File

@ -340,14 +340,15 @@ cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
this->AppendCustomCommandLines(cc, cmdLines);
if (cmdLines.empty()) {
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Phony custom command for " +
ninjaOutputs[0],
ninjaOutputs,
ninjaDeps,
cmNinjaDeps(),
orderOnlyDeps,
cmNinjaVars());
this->GetGlobalNinjaGenerator()->WritePhonyBuild(
this->GetBuildFileStream(),
"Phony custom command for " +
ninjaOutputs[0],
ninjaOutputs,
ninjaDeps,
cmNinjaDeps(),
orderOnlyDeps,
cmNinjaVars());
} else {
this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
this->BuildCommandLine(cmdLines),

View File

@ -598,32 +598,37 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
#endif
}
//Get the global generator as we are going to be call WriteBuild numerous
//times in the following section
cmGlobalNinjaGenerator* globalGenerator = this->GetGlobalGenerator();
const std::string rspfile = std::string
(cmake::GetCMakeFilesDirectoryPostSlash()) +
this->GetTarget()->GetName() + ".rsp";
// Write the build statement for this target.
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
comment.str(),
this->LanguageLinkerRule(),
outputs,
explicitDeps,
implicitDeps,
emptyDeps,
vars,
rspfile,
commandLineLengthLimit);
globalGenerator->WriteBuild(this->GetBuildFileStream(),
comment.str(),
this->LanguageLinkerRule(),
outputs,
explicitDeps,
implicitDeps,
emptyDeps,
vars,
rspfile,
commandLineLengthLimit);
if (targetOutput != targetOutputReal) {
if (targetType == cmTarget::EXECUTABLE) {
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
globalGenerator->WriteBuild(this->GetBuildFileStream(),
"Create executable symlink " + targetOutput,
"CMAKE_SYMLINK_EXECUTABLE",
cmNinjaDeps(1, targetOutput),
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
"CMAKE_SYMLINK_EXECUTABLE",
cmNinjaDeps(1, targetOutput),
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
} else {
cmNinjaDeps symlinks;
const std::string soName = this->GetTargetFilePath(this->TargetNameSO);
@ -635,30 +640,30 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
symlinks.push_back(soName);
}
symlinks.push_back(targetOutput);
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
"Create library symlink " + targetOutput,
"CMAKE_SYMLINK_LIBRARY",
symlinks,
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
globalGenerator->WriteBuild(this->GetBuildFileStream(),
"Create library symlink " + targetOutput,
"CMAKE_SYMLINK_LIBRARY",
symlinks,
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
}
}
if (!this->TargetNameImport.empty()) {
// Since using multiple outputs would mess up the $out variable, use an
// alias for the import library.
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Alias for import library.",
cmNinjaDeps(1, targetOutputImplib),
cmNinjaDeps(1, targetOutputReal));
globalGenerator->WritePhonyBuild(this->GetBuildFileStream(),
"Alias for import library.",
cmNinjaDeps(1, targetOutputImplib),
cmNinjaDeps(1, targetOutputReal));
}
// Add aliases for the file name and the target name.
this->GetGlobalGenerator()->AddTargetAlias(this->TargetNameOut,
globalGenerator->AddTargetAlias(this->TargetNameOut,
this->GetTarget());
this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
globalGenerator->AddTargetAlias(this->GetTargetName(),
this->GetTarget());
}
@ -669,11 +674,11 @@ void cmNinjaNormalTargetGenerator::WriteObjectLibStatement()
cmNinjaDeps outputs;
this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs);
cmNinjaDeps depends = this->GetObjects();
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Object library "
+ this->GetTargetName(),
outputs,
depends);
this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),
"Object library "
+ this->GetTargetName(),
outputs,
depends);
// Add aliases for the target name.
this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),

View File

@ -612,24 +612,24 @@ cmNinjaTargetGenerator
sourceFileName);
}
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
comment,
rule,
outputs,
explicitDeps,
implicitDeps,
orderOnlyDeps,
vars);
this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(),
comment,
rule,
outputs,
explicitDeps,
implicitDeps,
orderOnlyDeps,
vars);
if(const char* objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
std::vector<std::string> outputList;
cmSystemTools::ExpandListArgument(objectOutputs, outputList);
std::transform(outputList.begin(), outputList.end(), outputList.begin(),
MapToNinjaPath());
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Additional output files.",
outputList,
outputs);
this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),
"Additional output files.",
outputList,
outputs);
}
}

View File

@ -61,11 +61,11 @@ void cmNinjaUtilityTargetGenerator::Generate()
this->GetLocalGenerator()->AppendTargetDepends(this->GetTarget(), deps);
if (commands.empty()) {
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Utility command for "
+ this->GetTargetName(),
outputs,
deps);
this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),
"Utility command for "
+ this->GetTargetName(),
outputs,
deps);
} else {
std::string command =
this->GetLocalGenerator()->BuildCommandLine(commands);
@ -105,10 +105,11 @@ void cmNinjaUtilityTargetGenerator::Generate()
cmNinjaDeps(1, utilCommandName),
deps);
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"",
outputs,
cmNinjaDeps(1, utilCommandName));
this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),
"",
outputs,
cmNinjaDeps(1, utilCommandName)
);
}
this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),

View File

@ -123,6 +123,19 @@ add_custom_command(
COMMENT "Running TDocument post-build commands"
)
# Setup a custom target that will fail if the POST_BUILD custom command
# isn't run before it.
add_custom_command(
OUTPUT doc3post.txt
DEPENDS ${PROJECT_BINARY_DIR}/doc2post.txt
COMMAND ${CMAKE_COMMAND} -E echo " Copying doc2pre.txt to doc3post.txt."
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/doc2post.txt
${PROJECT_BINARY_DIR}/doc3post.txt
COMMENT "Running TDocument post-build dependent custom command"
)
add_custom_target(doc3Post ALL DEPENDS doc3post.txt)
add_dependencies(doc3Post TDocument)
################################################################
#
# Test using a multistep generated file