summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2006-04-11 11:06:19 -0400
committerBrad King <brad.king@kitware.com>2006-04-11 11:06:19 -0400
commitd5719f22c1dca3e100f1e3b5dfaa4fe7d26796a0 (patch)
tree714a99bed97290f96ff8f846fa6864cebbdc0a28
parentb613cf0be806cc1d37d2b590f1a5ba7898236ae8 (diff)
downloadcmake-d5719f22c1dca3e100f1e3b5dfaa4fe7d26796a0.tar.gz
ENH: Added support for multiple outputs generated by a single custom command. For Visual Studio generators the native tool provides support. For Xcode and Makefile generators a simple trick is used. The first output is considered primary and has the build rule attached. Other outputs simply depend on the first output with no build rule. During cmake_check_build_system CMake detects when a secondary output is missing and removes the primary output to make sure all outputs are regenerated. This approach always builds the custom command at the right time and only once even during parallel builds.
-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;
}