summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Source/cmAddCustomCommandCommand.cxx70
-rw-r--r--Source/cmAddCustomCommandCommand.h4
-rw-r--r--Source/cmCustomCommand.cxx10
-rw-r--r--Source/cmCustomCommand.h6
-rw-r--r--Source/cmGlobalGenerator.cxx12
-rw-r--r--Source/cmGlobalGenerator.h3
-rw-r--r--Source/cmGlobalUnixMakefileGenerator3.cxx67
-rw-r--r--Source/cmGlobalUnixMakefileGenerator3.h16
-rw-r--r--Source/cmGlobalVisualStudio8Generator.cxx4
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx56
-rw-r--r--Source/cmLocalGenerator.cxx31
-rw-r--r--Source/cmLocalGenerator.h6
-rw-r--r--Source/cmLocalVisualStudio6Generator.cxx71
-rw-r--r--Source/cmLocalVisualStudio6Generator.h2
-rw-r--r--Source/cmLocalVisualStudio7Generator.cxx42
-rw-r--r--Source/cmLocalVisualStudio7Generator.h2
-rw-r--r--Source/cmMakefile.cxx75
-rw-r--r--Source/cmMakefile.h6
-rw-r--r--Source/cmMakefileTargetGenerator.cxx58
-rw-r--r--Source/cmake.cxx4
-rw-r--r--Tests/CustomCommand/CMakeLists.txt5
-rw-r--r--Tests/CustomCommand/wrapper.cxx11
22 files changed, 423 insertions, 138 deletions
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index a9f948ca9e..4f6df9e846 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -32,9 +32,9 @@ bool cmAddCustomCommandCommand::InitialPass(
return false;
}
- std::string source, target, comment, output, main_dependency,
+ std::string source, target, comment, main_dependency,
working;
- std::vector<std::string> depends, outputs;
+ std::vector<std::string> depends, outputs, output;
// Accumulate one command line at a time.
cmCustomCommandLine currentLine;
@@ -155,7 +155,7 @@ bool cmAddCustomCommandCommand::InitialPass(
source = copy;
break;
case doing_output:
- output = filename;
+ output.push_back(filename);
break;
case doing_main_dependency:
main_dependency = copy;
@@ -204,34 +204,9 @@ bool cmAddCustomCommandCommand::InitialPass(
return false;
}
- if ( !this->Makefile->CanIWriteThisFile(output.c_str()) )
+ // Make sure the output names and locations are safe.
+ if(!this->CheckOutputs(output) || !this->CheckOutputs(outputs))
{
- std::string e = "attempted to have a file: " + output +
- " in a source directory as an output of custom command.";
- this->SetError(e.c_str());
- cmSystemTools::SetFatalErrorOccured();
- return false;
- }
- std::vector<std::string>::iterator oit;
- for ( oit = outputs.begin(); oit != outputs.end(); ++ oit )
- {
- if ( !this->Makefile->CanIWriteThisFile(oit->c_str()) )
- {
- std::string e = "attempted to have a file: " + *oit +
- " in a source directory as an output of custom command.";
- this->SetError(e.c_str());
- cmSystemTools::SetFatalErrorOccured();
- return false;
- }
- }
-
- std::string::size_type pos = output.find_first_of("#<>");
- if(pos != output.npos)
- {
- cmOStringStream msg;
- msg << "called with OUTPUT containing a \"" << output[pos]
- << "\". This character is not allowed.";
- this->SetError(msg.str().c_str());
return false;
}
@@ -247,7 +222,7 @@ bool cmAddCustomCommandCommand::InitialPass(
else if(target.empty())
{
// Target is empty, use the output.
- this->Makefile->AddCustomCommandToOutput(output.c_str(), depends,
+ this->Makefile->AddCustomCommandToOutput(output, depends,
main_dependency.c_str(),
commandLines, comment.c_str(),
working.c_str());
@@ -261,3 +236,36 @@ bool cmAddCustomCommandCommand::InitialPass(
}
return true;
}
+
+//----------------------------------------------------------------------------
+bool
+cmAddCustomCommandCommand
+::CheckOutputs(const std::vector<std::string>& outputs)
+{
+ for(std::vector<std::string>::const_iterator o = outputs.begin();
+ o != outputs.end(); ++o)
+ {
+ // Make sure the file will not be generated into the source
+ // directory during an out of source build.
+ if(!this->Makefile->CanIWriteThisFile(o->c_str()))
+ {
+ std::string e = "attempted to have a file \"" + *o +
+ "\" in a source directory as an output of custom command.";
+ this->SetError(e.c_str());
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+
+ // Make sure the output file name has no invalid characters.
+ std::string::size_type pos = o->find_first_of("#<>");
+ if(pos != o->npos)
+ {
+ cmOStringStream msg;
+ msg << "called with OUTPUT containing a \"" << (*o)[pos]
+ << "\". This character is not allowed.";
+ this->SetError(msg.str().c_str());
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/Source/cmAddCustomCommandCommand.h b/Source/cmAddCustomCommandCommand.h
index 96f85f6512..7a40c477e1 100644
--- a/Source/cmAddCustomCommandCommand.h
+++ b/Source/cmAddCustomCommandCommand.h
@@ -66,7 +66,7 @@ public:
"There are two main signatures for ADD_CUSTOM_COMMAND "
"The first signature is for adding a custom command "
"to produce an output.\n"
- " ADD_CUSTOM_COMMAND(OUTPUT result\n"
+ " ADD_CUSTOM_COMMAND(OUTPUT output1 [output2 ...]\n"
" COMMAND command1 [ARGS] [args1...]\n"
" [COMMAND command2 [ARGS] [args2...] ...]\n"
" [MAIN_DEPENDENCY depend]\n"
@@ -106,6 +106,8 @@ public:
}
cmTypeMacro(cmAddCustomCommandCommand, cmCommand);
+protected:
+ bool CheckOutputs(const std::vector<std::string>& outputs);
};
diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx
index b66568cdad..162f10f30a 100644
--- a/Source/cmCustomCommand.cxx
+++ b/Source/cmCustomCommand.cxx
@@ -24,7 +24,7 @@ cmCustomCommand::cmCustomCommand()
//----------------------------------------------------------------------------
cmCustomCommand::cmCustomCommand(const cmCustomCommand& r):
- Output(r.Output),
+ Outputs(r.Outputs),
Depends(r.Depends),
CommandLines(r.CommandLines),
Comment(r.Comment),
@@ -34,12 +34,12 @@ cmCustomCommand::cmCustomCommand(const cmCustomCommand& r):
}
//----------------------------------------------------------------------------
-cmCustomCommand::cmCustomCommand(const char* output,
+cmCustomCommand::cmCustomCommand(const std::vector<std::string>& outputs,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines,
const char* comment,
const char* workingDirectory):
- Output(output?output:""),
+ Outputs(outputs),
Depends(depends),
CommandLines(commandLines),
Comment(comment?comment:""),
@@ -49,9 +49,9 @@ cmCustomCommand::cmCustomCommand(const char* output,
}
//----------------------------------------------------------------------------
-const char* cmCustomCommand::GetOutput() const
+const std::vector<std::string>& cmCustomCommand::GetOutputs() const
{
- return this->Output.c_str();
+ return this->Outputs;
}
//----------------------------------------------------------------------------
diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h
index a33f1234dd..4fc300ceea 100644
--- a/Source/cmCustomCommand.h
+++ b/Source/cmCustomCommand.h
@@ -32,14 +32,14 @@ public:
cmCustomCommand(const cmCustomCommand& r);
/** Main constructor specifies all information for the command. */
- cmCustomCommand(const char* output,
+ cmCustomCommand(const std::vector<std::string>& outputs,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines,
const char* comment,
const char* workingDirectory);
/** Get the output file produced by the command. */
- const char* GetOutput() const;
+ const std::vector<std::string>& GetOutputs() const;
/** Get the working directory. */
const char* GetWorkingDirectory() const;
@@ -58,7 +58,7 @@ public:
bool IsUsed() { return this->Used;};
private:
- std::string Output;
+ std::vector<std::string> Outputs;
std::vector<std::string> Depends;
cmCustomCommandLines CommandLines;
std::string Comment;
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 9e9bbe4225..57f2eb0ff4 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1428,9 +1428,10 @@ cmTarget cmGlobalGenerator::CreateGlobalTarget(
target.SetType(cmTarget::GLOBAL_TARGET, name);
target.SetInAll(false);
- std::vector<std::string> fileDepends;
+ std::vector<std::string> no_outputs;
+ std::vector<std::string> no_depends;
// Store the custom command in the target.
- cmCustomCommand cc(0, fileDepends, *commandLines, 0, 0);
+ cmCustomCommand cc(no_outputs, no_depends, *commandLines, 0, 0);
target.GetPostBuildCommands().push_back(cc);
target.SetProperty("EchoString", message);
if ( depends_on_all )
@@ -1453,3 +1454,10 @@ void cmGlobalGenerator::AppendDirectoryForConfig(const char*, const char*,
// this method to append the subdirectory for the given build
// configuration.
}
+
+//----------------------------------------------------------------------------
+void cmGlobalGenerator::CheckMultipleOutputs(cmMakefile*, bool)
+{
+ // Only certain generators need this check. They define this
+ // method.
+}
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index bd21de0c6a..d8e35ae383 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -175,6 +175,9 @@ public:
configuration. This is valid during generation only. */
cmTargetManifest const& GetTargetManifest() { return this->TargetManifest; }
+ /** Support for multiple custom command outputs. */
+ virtual void CheckMultipleOutputs(cmMakefile* mf, bool verbose);
+
virtual const char* GetAllTargetName() { return "ALL_BUILD"; }
virtual const char* GetInstallTargetName() { return "INSTALL"; }
virtual const char* GetPreinstallTargetName() { return 0; }
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index 048ff5090f..4b17ded8e6 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -99,6 +99,15 @@ void cmGlobalUnixMakefileGenerator3::GetDocumentation(cmDocumentationEntry& entr
}
//----------------------------------------------------------------------------
+void
+cmGlobalUnixMakefileGenerator3
+::AddMultipleOutputPair(const char* depender, const char* dependee)
+{
+ MultipleOutputPairsType::value_type p(depender, dependee);
+ this->MultipleOutputPairs.insert(p);
+}
+
+//----------------------------------------------------------------------------
void cmGlobalUnixMakefileGenerator3::Generate()
{
// first do superclass method
@@ -298,8 +307,64 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile()
cmakefileStream << " )\n\n";
this->WriteMainCMakefileLanguageRules(cmakefileStream, this->LocalGenerators);
+
+ if(!this->MultipleOutputPairs.empty())
+ {
+ cmakefileStream
+ << "\n"
+ << "SET(CMAKE_MULTIPLE_OUTPUT_PAIRS\n";
+ for(MultipleOutputPairsType::const_iterator pi =
+ this->MultipleOutputPairs.begin();
+ pi != this->MultipleOutputPairs.end(); ++pi)
+ {
+ cmakefileStream << " \"" << pi->first << "\" \""
+ << pi->second << "\"\n";
+ }
+ cmakefileStream << " )\n\n";
+ }
}
-
+
+//----------------------------------------------------------------------------
+void cmGlobalUnixMakefileGenerator3::CheckMultipleOutputs(cmMakefile* mf,
+ bool verbose)
+{
+ // Get the string listing the multiple output pairs.
+ const char* pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS");
+ if(!pairs_string)
+ {
+ return;
+ }
+
+ // Convert the string to a list and preserve empty entries.
+ std::vector<std::string> pairs;
+ cmSystemTools::ExpandListArgument(pairs_string, pairs, true);
+ for(std::vector<std::string>::const_iterator i = pairs.begin();
+ i != pairs.end(); ++i)
+ {
+ const std::string& depender = *i;
+ if(++i != pairs.end())
+ {
+ const std::string& dependee = *i;
+
+ // If the depender is missing then delete the dependee to make
+ // sure both will be regenerated.
+ if(cmSystemTools::FileExists(dependee.c_str()) &&
+ !cmSystemTools::FileExists(depender.c_str()))
+ {
+ if(verbose)
+ {
+ cmOStringStream msg;
+ msg << "Deleting primary custom command output \"" << dependee
+ << "\" because another output \""
+ << depender << "\" does not exist." << std::endl;
+ cmSystemTools::Stdout(msg.str().c_str());
+ }
+ cmSystemTools::RemoveFile(dependee.c_str());
+ }
+ }
+ }
+}
+
void cmGlobalUnixMakefileGenerator3
::WriteMainCMakefileLanguageRules(cmGeneratedFileStream& cmakefileStream,
std::vector<cmLocalGenerator *> &lGenerators)
diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h
index 60ca2da2d0..3f8dbc23af 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.h
+++ b/Source/cmGlobalUnixMakefileGenerator3.h
@@ -97,6 +97,19 @@ public:
void WriteConvenienceRules(std::ostream& ruleFileStream,
std::set<cmStdString> &emitted);
+ /** In order to support parallel builds for custom commands with
+ multiple outputs the outputs are given a serial order, and only
+ the first output actually has the build rule. Other outputs
+ just depend on the first one. The check-build-system step must
+ remove a dependee if the depender is missing to make sure both
+ are regenerated properly. This method is used by the local
+ makefile generators to register such pairs. */
+ void AddMultipleOutputPair(const char* depender, const char* dependee);
+
+ /** Support for multiple custom command outputs. Called during
+ check-build-system step. */
+ virtual void CheckMultipleOutputs(cmMakefile* mf, bool verbose);
+
protected:
void WriteMainMakefile2();
void WriteMainCMakefile();
@@ -137,6 +150,9 @@ protected:
// added later. If non-empty this variable holds a fake dependency
// that can be added.
std::string EmptyRuleHackDepends;
+
+ typedef std::map<cmStdString, cmStdString> MultipleOutputPairsType;
+ MultipleOutputPairsType MultipleOutputPairs;
};
#endif
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index 969999d895..051f7180fd 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -135,12 +135,12 @@ void cmGlobalVisualStudio8Generator::Generate()
// file as the main dependency because it would get
// overwritten by the AddVCProjBuildRule of the ALL_BUILD
// target.
- const char* no_comment = 0;
const char* no_main_dependency = 0;
const char* no_working_directory = 0;
mf->AddCustomCommandToOutput(
CMAKE_CHECK_BUILD_SYSTEM_TARGET ".vcproj.cmake", listFiles,
- no_main_dependency, commandLines, no_comment, no_working_directory, true);
+ no_main_dependency, commandLines, "Checking Build System",
+ no_working_directory, true);
if(cmSourceFile* file = mf->GetSource(CMAKE_CHECK_BUILD_SYSTEM_TARGET ".vcproj.cmake.rule"))
{
tgt->GetSourceFiles().push_back(file);
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index ffef3210fc..d86bf2d49d 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -887,6 +887,8 @@ cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase,
this->CreateString(makecmd.c_str()));
return;
}
+
+ std::map<cmStdString, cmStdString> multipleOutputPairs;
std::string dir = this->CurrentMakefile->GetCurrentOutputDirectory();
dir += "/CMakeScripts";
@@ -916,10 +918,15 @@ cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase,
cmCustomCommand const& cc = *i;
if(!cc.GetCommandLines().empty())
{
- if(cc.GetOutput()[0])
+ const std::vector<std::string>& outputs = cc.GetOutputs();
+ if(!outputs.empty())
{
- makefileStream << "\\\n\t" << this->
- ConvertToRelativeForMake(cc.GetOutput());
+ for(std::vector<std::string>::const_iterator o = outputs.begin();
+ o != outputs.end(); ++o)
+ {
+ makefileStream
+ << "\\\n\t" << this->ConvertToRelativeForMake(o->c_str());
+ }
}
else
{
@@ -940,13 +947,29 @@ cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase,
makefileStream << "\n#" << "Custom command rule: " <<
cc.GetComment() << "\n";
- if(cc.GetOutput()[0])
+ const std::vector<std::string>& outputs = cc.GetOutputs();
+ if(!outputs.empty())
{
- makefileStream << this
- ->ConvertToRelativeForMake(cc.GetOutput()) << ": ";
+ // There is at least one output. If there is more than one treat the
+ // first as the primary output and make the rest depend on it.
+ std::vector<std::string>::const_iterator o = outputs.begin();
+ std::string primary_output =
+ this->ConvertToRelativeForMake(o->c_str());
+ for(++o; o != outputs.end(); ++o)
+ {
+ std::string current_output =
+ this->ConvertToRelativeForMake(o->c_str());
+ makefileStream << current_output << ": "
+ << primary_output << "\n";
+ multipleOutputPairs[current_output] = primary_output;
+ }
+
+ // Start the rule for the primary output.
+ makefileStream << primary_output << ": ";
}
else
{
+ // There are no outputs. Use the generated force rule name.
makefileStream << tname[&cc] << ": ";
}
for(std::vector<std::string>::const_iterator d = cc.GetDepends().begin();
@@ -1001,12 +1024,33 @@ cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase,
}
}
}
+
+ // Add a rule to deal with multiple outputs of custom commands.
+ if(!multipleOutputPairs.empty())
+ {
+ makefileStream <<
+ "\n"
+ "cmake_check_multiple_outputs:\n";
+ for(std::map<cmStdString, cmStdString>::const_iterator o =
+ multipleOutputPairs.begin(); o != multipleOutputPairs.end(); ++o)
+ {
+ makefileStream << "\t@if [ ! -f "
+ << o->first << " ]; then rm -f "
+ << o->second << "; fi\n";
+ }
+ }
+
std::string cdir = this->CurrentMakefile->GetCurrentOutputDirectory();
cdir = this->ConvertToRelativeForXCode(cdir.c_str());
std::string makecmd = "make -C ";
makecmd += cdir;
makecmd += " -f ";
makecmd += this->ConvertToRelativeForMake(makefile.c_str());
+ if(!multipleOutputPairs.empty())
+ {
+ makecmd += " cmake_check_multiple_outputs";
+ }
+ makecmd += " all";
cmSystemTools::ReplaceString(makecmd, "\\ ", "\\\\ ");
buildphase->AddAttribute("shellScript", this->CreateString(makecmd.c_str()));
}
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 14205b42e0..bd4b9b005f 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -1670,6 +1670,37 @@ cmLocalGenerator::ConstructScript(const cmCustomCommandLines& commandLines,
//----------------------------------------------------------------------------
std::string
+cmLocalGenerator::ConstructComment(const cmCustomCommand& cc,
+ const char* default_comment)
+{
+ // Check for a comment provided with the command.
+ if(cc.GetComment() && *cc.GetComment())
+ {
+ return cc.GetComment();
+ }
+
+ // Construct a reasonable default comment if possible.
+ if(!cc.GetOutputs().empty())
+ {
+ std::string comment;
+ comment = "Generating ";
+ const char* sep = "";
+ for(std::vector<std::string>::const_iterator o = cc.GetOutputs().begin();
+ o != cc.GetOutputs().end(); ++o)
+ {
+ comment += sep;
+ comment += this->Convert(o->c_str(), cmLocalGenerator::START_OUTPUT);
+ sep = ", ";
+ }
+ return comment;
+ }
+
+ // Otherwise use the provided default.
+ return default_comment;
+}
+
+//----------------------------------------------------------------------------
+std::string
cmLocalGenerator::ConvertToOptionallyRelativeOutputPath(const char* remote)
{
return this->Convert(remote, START_OUTPUT, SHELL, true);
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index a7e07403dd..41c2c44597 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -24,7 +24,7 @@ class cmGlobalGenerator;
class cmTarget;
class cmTargetManifest;
class cmSourceFile;
-
+class cmCustomCommand;
/** \class cmLocalGenerator
* \brief Create required build files for a directory.
@@ -211,6 +211,10 @@ protected:
const char* workingDirectory,
const char* newline = "\n");
+ /** Construct a comment for a custom command. */
+ std::string ConstructComment(const cmCustomCommand& cc,
+ const char* default_comment = "");
+
/** Fill out these strings for the given target. Libraries to link,
* flags, and linkflags. */
void GetTargetFlags(std::string& linkLibs,
diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx
index ec5d7ea3d0..b6fd4f8ea9 100644
--- a/Source/cmLocalVisualStudio6Generator.cxx
+++ b/Source/cmLocalVisualStudio6Generator.cxx
@@ -215,6 +215,8 @@ void cmLocalVisualStudio6Generator::AddDSPBuildRule(cmTarget& tgt)
std::string makefileIn = this->Makefile->GetStartDirectory();
makefileIn += "/";
makefileIn += "CMakeLists.txt";
+ std::string comment = "Building Custom Rule ";
+ comment += makefileIn;
std::string args;
args = "-H";
args +=
@@ -246,10 +248,11 @@ void cmLocalVisualStudio6Generator::AddDSPBuildRule(cmTarget& tgt)
cmCustomCommandLines commandLines;
commandLines.push_back(commandLine);
- const char* no_comment = 0;
const char* no_working_directory = 0;
- this->Makefile->AddCustomCommandToOutput(dspname.c_str(), listFiles, makefileIn.c_str(),
- commandLines, no_comment, no_working_directory, true);
+ this->Makefile->AddCustomCommandToOutput(dspname.c_str(), listFiles,
+ makefileIn.c_str(), commandLines,
+ comment.c_str(),
+ no_working_directory, true);
if(cmSourceFile* file = this->Makefile->GetSource(makefileIn.c_str()))
{
tgt.GetSourceFiles().push_back(file);
@@ -443,12 +446,17 @@ void cmLocalVisualStudio6Generator::WriteGroup(const cmSourceGroup *sg, cmTarget
this->ConstructScript(command->GetCommandLines(),
command->GetWorkingDirectory(),
"\\\n\t");
- const char* comment = command->GetComment();
+ std::string comment =
+ this->ConstructComment(*command,
+ "Building Custom Rule $(InputPath)");
+ if(comment == "<hack>")
+ {
+ comment = "";
+ }
const char* flags = compileFlags.size() ? compileFlags.c_str(): 0;
- this->WriteCustomRule(fout, source.c_str(), script.c_str(),
- (*comment?comment:"Custom Rule"),
- command->GetDepends(),
- command->GetOutput(), flags);
+ this->WriteCustomRule(fout, source.c_str(), script.c_str(),
+ comment.c_str(), command->GetDepends(),
+ command->GetOutputs(), flags);
}
else if(compileFlags.size())
{
@@ -501,6 +509,7 @@ cmLocalVisualStudio6Generator
strlen(target.GetName()) + 30)];
sprintf(output,"%s/%s_force_%i", this->Makefile->GetStartOutputDirectory(),
target.GetName(), count);
+ std::string comment = this->ConstructComment(origCommand, "<hack>");
// Add the rule with the given dependencies and commands.
const char* no_main_dependency = 0;
@@ -508,7 +517,7 @@ cmLocalVisualStudio6Generator
depends,
no_main_dependency,
origCommand.GetCommandLines(),
- origCommand.GetComment(),
+ comment.c_str(),
origCommand.GetWorkingDirectory());
// Replace the dependencies with the output of this rule so that the
@@ -524,15 +533,17 @@ cmLocalVisualStudio6Generator
delete [] output;
}
-void cmLocalVisualStudio6Generator::WriteCustomRule(std::ostream& fout,
- const char* source,
- const char* command,
- const char* comment,
- const std::vector<std::string>& depends,
- const char *output,
- const char* flags
- )
+void
+cmLocalVisualStudio6Generator
+::WriteCustomRule(std::ostream& fout,
+ const char* source,
+ const char* command,
+ const char* comment,
+ const std::vector<std::string>& depends,
+ const std::vector<std::string>& outputs,
+ const char* flags)
{
+ // Write the rule for each configuration.
std::vector<std::string>::iterator i;
for(i = this->Configurations.begin(); i != this->Configurations.end(); ++i)
{
@@ -561,18 +572,28 @@ void cmLocalVisualStudio6Generator::WriteCustomRule(std::ostream& fout,
fout << "\n";
fout << "# PROP Ignore_Default_Tool 1\n";
- fout << "# Begin Custom Build - Building " << comment
- << " $(InputPath)\n\n";
- if(output == 0)
+ fout << "# Begin Custom Build -";
+ if(comment && *comment)
+ {
+ fout << " " << comment;
+ }
+ fout << "\n\n";
+ if(outputs.empty())
{
fout << source << "_force : \"$(SOURCE)\" \"$(INTDIR)\" \"$(OUTDIR)\"\n\t";
fout << command << "\n\n";
}
-
- // Write a rule for every output generated by this command.
- fout << this->ConvertToOptionallyRelativeOutputPath(output)
- << " : \"$(SOURCE)\" \"$(INTDIR)\" \"$(OUTDIR)\"\n\t";
- fout << command << "\n\n";
+ else
+ {
+ for(std::vector<std::string>::const_iterator o = outputs.begin();
+ o != outputs.end(); ++o)
+ {
+ // Write a rule for every output generated by this command.
+ fout << this->ConvertToOptionallyRelativeOutputPath(o->c_str())
+ << " : \"$(SOURCE)\" \"$(INTDIR)\" \"$(OUTDIR)\"\n\t";
+ fout << command << "\n\n";
+ }
+ }
fout << "# End Custom Build\n\n";
}
diff --git a/Source/cmLocalVisualStudio6Generator.h b/Source/cmLocalVisualStudio6Generator.h
index a1450b3e08..cfa49acb4b 100644
--- a/Source/cmLocalVisualStudio6Generator.h
+++ b/Source/cmLocalVisualStudio6Generator.h
@@ -85,7 +85,7 @@ private:
const char* command,
const char* comment,
const std::vector<std::string>& depends,
- const char* output,
+ const std::vector<std::string>& outputs,
const char* flags);
void AddUtilityCommandHack(cmTarget& target, int count,
std::vector<std::string>& depends,
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 55b80efeaf..3c9942f08e 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -170,6 +170,8 @@ void cmLocalVisualStudio7Generator::AddVCProjBuildRule(cmTarget& tgt)
std::string makefileIn = this->Makefile->GetStartDirectory();
makefileIn += "/";
makefileIn += "CMakeLists.txt";
+ std::string comment = "Building Custom Rule ";
+ comment += makefileIn;
std::string args;
args = "-H";
args +=
@@ -202,9 +204,10 @@ void cmLocalVisualStudio7Generator::AddVCProjBuildRule(cmTarget& tgt)
cmCustomCommandLines commandLines;
commandLines.push_back(commandLine);
const char* no_working_directory = 0;
- const char* no_comment = 0;
- this->Makefile->AddCustomCommandToOutput(dspname.c_str(), listFiles, makefileIn.c_str(),
- commandLines, no_comment, no_working_directory, true);
+ this->Makefile->AddCustomCommandToOutput(dspname.c_str(), listFiles,
+ makefileIn.c_str(), commandLines,
+ comment.c_str(),
+ no_working_directory, true);
if(cmSourceFile* file = this->Makefile->GetSource(makefileIn.c_str()))
{
tgt.GetSourceFiles().push_back(file);
@@ -1024,12 +1027,11 @@ void cmLocalVisualStudio7Generator::WriteGroup(const cmSourceGroup *sg, cmTarget
// Construct the entire set of commands in one string.
std::string script = this->ConstructScript(command->GetCommandLines(),
command->GetWorkingDirectory());
- const char* comment = command->GetComment();
+ std::string comment = this->ConstructComment(*command);
const char* flags = compileFlags.size() ? compileFlags.c_str(): 0;
this->WriteCustomRule(fout, source.c_str(), script.c_str(),
- (*comment?comment:"Custom Rule"),
- command->GetDepends(),
- command->GetOutput(), flags);
+ comment.c_str(), command->GetDepends(),
+ command->GetOutputs(), flags);
}
else if(compileFlags.size() || additionalDeps.length())
{
@@ -1093,9 +1095,10 @@ WriteCustomRule(std::ostream& fout,
const char* command,
const char* comment,
const std::vector<std::string>& depends,
- const char *output,
+ const std::vector<std::string>& outputs,
const char* compileFlags)
{
+ // Write the rule for each configuration.
std::vector<std::string>::iterator i;
std::vector<std::string> *configs =
static_cast<cmGlobalVisualStudio7Generator *>(this->GlobalGenerator)->GetConfigurations();
@@ -1112,11 +1115,8 @@ WriteCustomRule(std::ostream& fout,
}
fout << "\t\t\t\t\t<Tool\n"
<< "\t\t\t\t\tName=\"VCCustomBuildTool\"\n"
- << "\t\t\t\t\tDescription=\"Building " << comment;
- fout << " " << output;
- fout << "\"\n"
- << "\t\t\t\t\tCommandLine=\""
- << this->EscapeForXML(command) << "\"\n"
+ << "\t\t\t\t\tDescription=\"" << this->EscapeForXML(comment) << "\"\n"
+ << "\t\t\t\t\tCommandLine=\"" << this->EscapeForXML(command) << "\"\n"
<< "\t\t\t\t\tAdditionalDependencies=\"";
// Write out the dependencies for the rule.
std::string temp;
@@ -1130,13 +1130,21 @@ WriteCustomRule(std::ostream& fout,
}
fout << "\"\n";
fout << "\t\t\t\t\tOutputs=\"";
- if(output == 0)
+ if(outputs.empty())
{
fout << source << "_force";
}
-
- // Write a rule for the output generated by this command.
- fout << this->ConvertToXMLOutputPathSingle(output);
+ else
+ {
+ // Write a rule for the output generated by this command.
+ const char* sep = "";
+ for(std::vector<std::string>::const_iterator o = outputs.begin();
+ o != outputs.end(); ++o)
+ {
+ fout << sep << this->ConvertToXMLOutputPathSingle(o->c_str());
+ sep = ";";
+ }
+ }
fout << "\"/>\n";
fout << "\t\t\t\t</FileConfiguration>\n";
}
diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h
index 387762bb55..d6cfe5aa4d 100644
--- a/Source/cmLocalVisualStudio7Generator.h
+++ b/Source/cmLocalVisualStudio7Generator.h
@@ -108,7 +108,7 @@ private:
const char* command,
const char* comment,
const std::vector<std::string>& depends,
- const char* output,
+ const std::vector<std::string>& outputs,
const char* extraFlags);
void WriteGroup(const cmSourceGroup *sg, cmTarget target, std::ostream &fout,
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 7e434cf5b3..3d3b2d18ea 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -499,7 +499,7 @@ cmMakefile::AddCustomCommandToTarget(const char* target,
if(ti != this->Targets.end())
{
// Add the command to the appropriate build step for the target.
- const char* no_output = 0;
+ std::vector<std::string> no_output;
cmCustomCommand cc(no_output, depends, commandLines, comment, workingDir);
switch(type)
{
@@ -530,7 +530,7 @@ cmMakefile::AddCustomCommandToTarget(const char* target,
//----------------------------------------------------------------------------
void
-cmMakefile::AddCustomCommandToOutput(const char* output,
+cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs,
const std::vector<std::string>& depends,
const char* main_dependency,
const cmCustomCommandLines& commandLines,
@@ -538,6 +538,13 @@ cmMakefile::AddCustomCommandToOutput(const char* output,
const char* workingDir,
bool replace)
{
+ // Make sure there is at least one output.
+ if(outputs.empty())
+ {
+ cmSystemTools::Error("Attempt to add a custom rule with no output!");
+ return;
+ }
+
// Choose a source file on which to store the custom command.
cmSourceFile* file = 0;
if(main_dependency && main_dependency[0])
@@ -572,8 +579,8 @@ cmMakefile::AddCustomCommandToOutput(const char* output,
// Generate a rule file if the main dependency is not available.
if(!file)
{
- // Construct a rule file associated with the output produced.
- std::string outName = output;
+ // Construct a rule file associated with the first output produced.
+ std::string outName = outputs[0];
outName += ".rule";
// Check if the rule file already exists.
@@ -584,7 +591,8 @@ cmMakefile::AddCustomCommandToOutput(const char* output,
if(commandLines != file->GetCustomCommand()->GetCommandLines())
{
cmSystemTools::Error("Attempt to add a custom rule to output \"",
- output, "\" which already has a custom rule.");
+ outName.c_str(),
+ "\" which already has a custom rule.");
}
return;
}
@@ -593,10 +601,14 @@ cmMakefile::AddCustomCommandToOutput(const char* output,
file = this->GetOrCreateSource(outName.c_str(), true);
}
- // Always create the output and mark it generated.
- if(cmSourceFile* out = this->GetOrCreateSource(output, true))
+ // Always create the output sources and mark them generated.
+ for(std::vector<std::string>::const_iterator o = outputs.begin();
+ o != outputs.end(); ++o)
{
- out->SetProperty("GENERATED", "1");
+ if(cmSourceFile* out = this->GetOrCreateSource(o->c_str(), true))
+ {
+ out->SetProperty("GENERATED", "1");
+ }
}
// Construct a complete list of dependencies.
@@ -610,13 +622,31 @@ cmMakefile::AddCustomCommandToOutput(const char* output,
if(file)
{
cmCustomCommand* cc =
- new cmCustomCommand(output, depends2, commandLines, comment, workingDir);
+ new cmCustomCommand(outputs, depends2, commandLines,
+ comment, workingDir);
file->SetCustomCommand(cc);
}
}
//----------------------------------------------------------------------------
void
+cmMakefile::AddCustomCommandToOutput(const char* output,
+ const std::vector<std::string>& depends,
+ const char* main_dependency,
+ const cmCustomCommandLines& commandLines,
+ const char* comment,
+ const char* workingDir,
+ bool replace)
+{
+ std::vector<std::string> outputs;
+ outputs.push_back(output);
+ this->AddCustomCommandToOutput(outputs, depends, main_dependency,
+ commandLines, comment, workingDir,
+ replace);
+}
+
+//----------------------------------------------------------------------------
+void
cmMakefile::AddCustomCommandOldStyle(const char* target,
const std::vector<std::string>& outputs,
const std::vector<std::string>& depends,
@@ -735,7 +765,12 @@ void cmMakefile::AddUtilityCommand(const char* utilityName, bool all,
target.SetInAll(all);
target.SetMakefile(this);
// Store the custom command in the target.
- cmCustomCommand cc(output, depends, commandLines, 0, workingDirectory);
+ std::vector<std::string> outputs;
+ if(output)
+ {
+ outputs.push_back(output);
+ }
+ cmCustomCommand cc(outputs, depends, commandLines, 0, workingDirectory);
target.GetPostBuildCommands().push_back(cc);
// Add the target to the set of targets.
@@ -1149,14 +1184,20 @@ cmSourceFile *cmMakefile::GetSourceFileWithOutput(const char *cname)
if ((*i)->GetCustomCommand())
{
// is the output of the custom command match the source files name
- out = (*i)->GetCustomCommand()->GetOutput();
- std::string::size_type pos = out.rfind(name);
- // If the output matches exactly
- if (pos != out.npos &&
- pos == out.size() - name.size() &&
- (pos ==0 || out[pos-1] == '/'))
+ const std::vector<std::string>& outputs =
+ (*i)->GetCustomCommand()->GetOutputs();
+ for(std::vector<std::string>::const_iterator o = outputs.begin();
+ o != outputs.end(); ++o)
{
- return *i;
+ out = *o;
+ std::string::size_type pos = out.rfind(name);
+ // If the output matches exactly
+ if (pos != out.npos &&
+ pos == out.size() - name.size() &&
+ (pos ==0 || out[pos-1] == '/'))
+ {
+ return *i;
+ }
}
}
}
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 189bce6f7c..e59e90e733 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -143,6 +143,12 @@ public:
const cmCustomCommandLines& commandLines,
cmTarget::CustomCommandType type,
const char* comment, const char* workingDir);
+ void AddCustomCommandToOutput(const std::vector<std::string>& outputs,
+ const std::vector<std::string>& depends,
+ const char* main_dependency,
+ const cmCustomCommandLines& commandLines,
+ const char* comment, const char* workingDir,
+ bool replace = false);
void AddCustomCommandToOutput(const char* output,
const std::vector<std::string>& depends,
const char* main_dependency,
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 1f7c17fb17..f2d3dc01fb 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -18,6 +18,7 @@
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
+#include "cmGlobalUnixMakefileGenerator3.h"
#include "cmLocalUnixMakefileGenerator3.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
@@ -608,10 +609,15 @@ void cmMakefileTargetGenerator::WriteCustomCommands()
this->GenerateCustomRuleFile(*cc);
if (clean)
{
- this->CleanFiles.push_back
- (this->Convert(cc->GetOutput(),
- cmLocalGenerator::START_OUTPUT,
- cmLocalGenerator::UNCHANGED));
+ const std::vector<std::string>& outputs = cc->GetOutputs();
+ for(std::vector<std::string>::const_iterator o = outputs.begin();
+ o != outputs.end(); ++o)
+ {
+ this->CleanFiles.push_back
+ (this->Convert(o->c_str(),
+ cmLocalGenerator::START_OUTPUT,
+ cmLocalGenerator::UNCHANGED));
+ }
}
}
}
@@ -621,17 +627,15 @@ void cmMakefileTargetGenerator::WriteCustomCommands()
void cmMakefileTargetGenerator
::GenerateCustomRuleFile(const cmCustomCommand& cc)
{
- // Convert the output name to a relative path if possible.
- std::string output = this->Convert(cc.GetOutput(),
- cmLocalGenerator::START_OUTPUT);
-
// Collect the commands.
std::vector<std::string> commands;
- std::string preEcho = "Generating ";
- preEcho += output;
- this->LocalGenerator
- ->AppendEcho(commands, preEcho.c_str(),
- cmLocalUnixMakefileGenerator3::EchoGenerate);
+ std::string comment = this->LocalGenerator->ConstructComment(cc);
+ if(!comment.empty())
+ {
+ this->LocalGenerator
+ ->AppendEcho(commands, comment.c_str(),
+ cmLocalUnixMakefileGenerator3::EchoGenerate);
+ }
this->LocalGenerator->AppendCustomCommand(commands, cc);
// Collect the dependencies.
@@ -639,14 +643,30 @@ void cmMakefileTargetGenerator
this->LocalGenerator->AppendCustomDepend(depends, cc);
// Write the rule.
- const char* comment = 0;
- if(cc.GetComment() && *cc.GetComment())
+ const std::vector<std::string>& outputs = cc.GetOutputs();
+ std::vector<std::string>::const_iterator o = outputs.begin();
+ this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
+ o->c_str(), depends, commands,
+ false);
+
+ // If the rule has multiple outputs, add a rule for the extra
+ // outputs to just depend on the first output with no command. Also
+ // register the extra outputs as paired with the first output so
+ // that the check-build-system step will remove the primary output
+ // if any extra outputs are missing, forcing the rule to regenerate
+ // all outputs.
+ depends.clear();
+ depends.push_back(*o);
+ commands.clear();
+ cmGlobalUnixMakefileGenerator3* gg =
+ static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
+ for(++o; o != outputs.end(); ++o)
{
- comment = cc.GetComment();
+ this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
+ o->c_str(), depends, commands,
+ false);
+ gg->AddMultipleOutputPair(o->c_str(), depends[0].c_str());
}
- this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment,
- cc.GetOutput(), depends, commands,
- false);
}
//----------------------------------------------------------------------------
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 1a0cfdca93..341f21068a 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1921,9 +1921,13 @@ int cmake::CheckBuildSystem()
cmGlobalGenerator *ggd = this->CreateGlobalGenerator(genName);
if (ggd)
{
+ // Check the dependencies in case source files were removed.
std::auto_ptr<cmLocalGenerator> lgd(ggd->CreateLocalGenerator());
lgd->SetGlobalGenerator(ggd);
lgd->CheckDependencies(mf, verbose, this->ClearBuildSystem);
+
+ // Check for multiple output pairs.
+ ggd->CheckMultipleOutputs(mf, verbose);
}
// No need to rerun.
diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt
index f62be0eaf5..97c4d812aa 100644
--- a/Tests/CustomCommand/CMakeLists.txt
+++ b/Tests/CustomCommand/CMakeLists.txt
@@ -48,11 +48,11 @@ ADD_EXECUTABLE(wrapper wrapper.cxx)
# is instantiated for the output, with GENERATED 1
# at the end of the day this becomes a what in VS ?
ADD_CUSTOM_COMMAND(
- OUTPUT ${PROJECT_BINARY_DIR}/wrapped.c
+ OUTPUT ${PROJECT_BINARY_DIR}/wrapped.c ${PROJECT_BINARY_DIR}/wrapped_help.c
DEPENDS wrapper
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/wrapped.h
COMMAND ${EXECUTABLE_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/wrapper
- ARGS ${PROJECT_BINARY_DIR}/wrapped.c ${PROJECT_SOURCE_DIR}/wrapped.h
+ ${PROJECT_BINARY_DIR}/wrapped.c ${PROJECT_BINARY_DIR}/wrapped_help.c
)
################################################################
@@ -145,6 +145,7 @@ ADD_EXECUTABLE(CustomCommand
${PROJECT_BINARY_DIR}/foo.h
${PROJECT_BINARY_DIR}/foo.c
${PROJECT_BINARY_DIR}/wrapped.c
+ ${PROJECT_BINARY_DIR}/wrapped_help.c
${PROJECT_BINARY_DIR}/generated.c
)
diff --git a/Tests/CustomCommand/wrapper.cxx b/Tests/CustomCommand/wrapper.cxx
index 3a1149da5f..f65eb9b506 100644
--- a/Tests/CustomCommand/wrapper.cxx
+++ b/Tests/CustomCommand/wrapper.cxx
@@ -2,14 +2,17 @@
int main(int argc, char *argv[])
{
- if ( argc < 2 )
+ if ( argc < 3 )
{
- fprintf(stderr, "Usage: %s <file>\n", argv[0]);
+ fprintf(stderr, "Usage: %s <file1> <file2>\n", argv[0]);
return 1;
}
FILE *fp = fopen(argv[1],"w");
-
- fprintf(fp,"int wrapped() { return 5; }\n");
+ fprintf(fp,"extern int wrapped_help(void);\n");
+ fprintf(fp,"int wrapped(void) { return wrapped_help(); }\n");
+ fclose(fp);
+ fp = fopen(argv[2],"w");
+ fprintf(fp,"int wrapped_help(void) { return 5; }\n");
fclose(fp);
return 0;
}