diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 718e52e8e..f183eb4c6 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -131,6 +131,8 @@ SET(SRCS cmComputeTargetDepends.cxx cmCustomCommand.cxx cmCustomCommand.h + cmCustomCommandGenerator.cxx + cmCustomCommandGenerator.h cmDefinitions.cxx cmDefinitions.h cmDepends.cxx @@ -156,6 +158,7 @@ SET(SRCS cmDocumentationFormatterText.cxx cmDocumentationFormatterUsage.cxx cmDocumentationSection.cxx + cmDocumentGeneratorExpressions.h cmDocumentVariables.cxx cmDynamicLoader.cxx cmDynamicLoader.h diff --git a/Source/cmAddTestCommand.h b/Source/cmAddTestCommand.h index 79fb48150..1cc86c493 100644 --- a/Source/cmAddTestCommand.h +++ b/Source/cmAddTestCommand.h @@ -13,6 +13,7 @@ #define cmAddTestCommand_h #include "cmCommand.h" +#include "cmDocumentGeneratorExpressions.h" /** \class cmAddTestCommand * \brief Add a test to the lists of tests to run. @@ -77,19 +78,7 @@ public: "\n" "Arguments after COMMAND may use \"generator expressions\" with the " "syntax \"$<...>\". " - "These expressions are evaluted during build system generation and " - "produce information specific to each generated build configuration. " - "Valid expressions are:\n" - " $ = configuration name\n" - " $ = main file (.exe, .so.1.2, .a)\n" - " $ = file used to link (.a, .lib, .so)\n" - " $ = file with soname (.so.3)\n" - "where \"tgt\" is the name of a target. " - "Target file expressions produce a full path, but _DIR and _NAME " - "versions can produce the directory and file name components:\n" - " $/$\n" - " $/$\n" - " $/$\n" + CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS "Example usage:\n" " add_test(NAME mytest\n" " COMMAND testDriver --config $\n" diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx index 5db88fa7b..bd860ee6a 100644 --- a/Source/cmCustomCommand.cxx +++ b/Source/cmCustomCommand.cxx @@ -11,6 +11,8 @@ ============================================================================*/ #include "cmCustomCommand.h" +#include "cmMakefile.h" + //---------------------------------------------------------------------------- cmCustomCommand::cmCustomCommand() { @@ -28,12 +30,14 @@ cmCustomCommand::cmCustomCommand(const cmCustomCommand& r): Comment(r.Comment), WorkingDirectory(r.WorkingDirectory), EscapeAllowMakeVars(r.EscapeAllowMakeVars), - EscapeOldStyle(r.EscapeOldStyle) + EscapeOldStyle(r.EscapeOldStyle), + Backtrace(new cmListFileBacktrace(*r.Backtrace)) { } //---------------------------------------------------------------------------- -cmCustomCommand::cmCustomCommand(const std::vector& outputs, +cmCustomCommand::cmCustomCommand(cmMakefile* mf, + const std::vector& outputs, const std::vector& depends, const cmCustomCommandLines& commandLines, const char* comment, @@ -45,10 +49,21 @@ cmCustomCommand::cmCustomCommand(const std::vector& outputs, Comment(comment?comment:""), WorkingDirectory(workingDirectory?workingDirectory:""), EscapeAllowMakeVars(false), - EscapeOldStyle(true) + EscapeOldStyle(true), + Backtrace(new cmListFileBacktrace) { this->EscapeOldStyle = true; this->EscapeAllowMakeVars = false; + if(mf) + { + mf->GetBacktrace(*this->Backtrace); + } +} + +//---------------------------------------------------------------------------- +cmCustomCommand::~cmCustomCommand() +{ + delete this->Backtrace; } //---------------------------------------------------------------------------- @@ -130,6 +145,12 @@ void cmCustomCommand::SetEscapeAllowMakeVars(bool b) this->EscapeAllowMakeVars = b; } +//---------------------------------------------------------------------------- +cmListFileBacktrace const& cmCustomCommand::GetBacktrace() const +{ + return *this->Backtrace; +} + //---------------------------------------------------------------------------- cmCustomCommand::ImplicitDependsList const& cmCustomCommand::GetImplicitDepends() const diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h index c9adddf05..dd92e34d7 100644 --- a/Source/cmCustomCommand.h +++ b/Source/cmCustomCommand.h @@ -13,6 +13,8 @@ #define cmCustomCommand_h #include "cmStandardIncludes.h" +class cmMakefile; +class cmListFileBacktrace; /** \class cmCustomCommand * \brief A class to encapsulate a custom command @@ -27,12 +29,15 @@ public: cmCustomCommand(const cmCustomCommand& r); /** Main constructor specifies all information for the command. */ - cmCustomCommand(const std::vector& outputs, + cmCustomCommand(cmMakefile* mf, + const std::vector& outputs, const std::vector& depends, const cmCustomCommandLines& commandLines, const char* comment, const char* workingDirectory); + ~cmCustomCommand(); + /** Get the output file produced by the command. */ const std::vector& GetOutputs() const; @@ -63,6 +68,9 @@ public: bool GetEscapeAllowMakeVars() const; void SetEscapeAllowMakeVars(bool b); + /** Backtrace of the command that created this custom command. */ + cmListFileBacktrace const& GetBacktrace() const; + typedef std::pair ImplicitDependsPair; class ImplicitDependsList: public std::vector {}; void SetImplicitDepends(ImplicitDependsList const&); @@ -78,6 +86,7 @@ private: std::string WorkingDirectory; bool EscapeAllowMakeVars; bool EscapeOldStyle; + cmListFileBacktrace* Backtrace; ImplicitDependsList ImplicitDepends; }; diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx new file mode 100644 index 000000000..2a3b55385 --- /dev/null +++ b/Source/cmCustomCommandGenerator.cxx @@ -0,0 +1,64 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2010 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCustomCommandGenerator.h" + +#include "cmMakefile.h" +#include "cmCustomCommand.h" +#include "cmLocalGenerator.h" + +//---------------------------------------------------------------------------- +cmCustomCommandGenerator::cmCustomCommandGenerator( + cmCustomCommand const& cc, const char* config, cmMakefile* mf): + CC(cc), Config(config), Makefile(mf), LG(mf->GetLocalGenerator()), + OldStyle(cc.GetEscapeOldStyle()), MakeVars(cc.GetEscapeAllowMakeVars()) +{ +} + +//---------------------------------------------------------------------------- +unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const +{ + return static_cast(this->CC.GetCommandLines().size()); +} + +//---------------------------------------------------------------------------- +std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const +{ + std::string const& argv0 = this->CC.GetCommandLines()[c][0]; + cmTarget* target = this->Makefile->FindTargetToUse(argv0.c_str()); + if(target && target->GetType() == cmTarget::EXECUTABLE && + (target->IsImported() || !this->Makefile->IsOn("CMAKE_CROSSCOMPILING"))) + { + return target->GetLocation(this->Config); + } + return argv0; +} + +//---------------------------------------------------------------------------- +void +cmCustomCommandGenerator +::AppendArguments(unsigned int c, std::string& cmd) const +{ + cmCustomCommandLine const& commandLine = this->CC.GetCommandLines()[c]; + for(unsigned int j=1;j < commandLine.size(); ++j) + { + std::string const& arg = commandLine[j]; + cmd += " "; + if(this->OldStyle) + { + cmd += this->LG->EscapeForShellOldStyle(arg.c_str()); + } + else + { + cmd += this->LG->EscapeForShell(arg.c_str(), this->MakeVars); + } + } +} diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h new file mode 100644 index 000000000..5417ec546 --- /dev/null +++ b/Source/cmCustomCommandGenerator.h @@ -0,0 +1,37 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2010 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmCustomCommandGenerator_h +#define cmCustomCommandGenerator_h + +#include "cmStandardIncludes.h" + +class cmCustomCommand; +class cmMakefile; +class cmLocalGenerator; + +class cmCustomCommandGenerator +{ + cmCustomCommand const& CC; + const char* Config; + cmMakefile* Makefile; + cmLocalGenerator* LG; + bool OldStyle; + bool MakeVars; +public: + cmCustomCommandGenerator(cmCustomCommand const& cc, const char* config, + cmMakefile* mf); + unsigned int GetNumberOfCommands() const; + std::string GetCommand(unsigned int c) const; + void AppendArguments(unsigned int c, std::string& cmd) const; +}; + +#endif diff --git a/Source/cmDocumentGeneratorExpressions.h b/Source/cmDocumentGeneratorExpressions.h new file mode 100644 index 000000000..535901397 --- /dev/null +++ b/Source/cmDocumentGeneratorExpressions.h @@ -0,0 +1,30 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2010 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmDocumentGeneratorExpressions_h +#define cmDocumentGeneratorExpressions_h + +#define CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS \ + "Generator expressions are evaluted during build system generation " \ + "to produce information specific to each build configuration. " \ + "Valid expressions are:\n" \ + " $ = configuration name\n" \ + " $ = main file (.exe, .so.1.2, .a)\n" \ + " $ = file used to link (.a, .lib, .so)\n" \ + " $ = file with soname (.so.3)\n" \ + "where \"tgt\" is the name of a target. " \ + "Target file expressions produce a full path, but _DIR and _NAME " \ + "versions can produce the directory and file name components:\n" \ + " $/$\n" \ + " $/$\n" \ + " $/$\n" + +#endif diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 15abd020a..f55850972 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1893,7 +1893,7 @@ cmTarget cmGlobalGenerator::CreateGlobalTarget( std::vector no_outputs; std::vector no_depends; // Store the custom command in the target. - cmCustomCommand cc(no_outputs, no_depends, *commandLines, 0, + cmCustomCommand cc(0, no_outputs, no_depends, *commandLines, 0, workingDirectory); target.GetPostBuildCommands().push_back(cc); target.SetProperty("EchoString", message); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 2f7bc44ab..e13acda61 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -18,6 +18,7 @@ #include "cmGeneratedFileStream.h" #include "cmComputeLinkInformation.h" #include "cmSourceFile.h" +#include "cmCustomCommandGenerator.h" #include @@ -1314,8 +1315,7 @@ void cmGlobalXCodeGenerator cmCustomCommand const& cc = *i; if(!cc.GetCommandLines().empty()) { - bool escapeOldStyle = cc.GetEscapeOldStyle(); - bool escapeAllowMakeVars = cc.GetEscapeAllowMakeVars(); + cmCustomCommandGenerator ccg(cc, configName, this->CurrentMakefile); makefileStream << "\n"; const std::vector& outputs = cc.GetOutputs(); if(!outputs.empty()) @@ -1334,11 +1334,13 @@ void cmGlobalXCodeGenerator cc.GetDepends().begin(); d != cc.GetDepends().end(); ++d) { - std::string dep = - this->CurrentLocalGenerator->GetRealDependency(d->c_str(), - configName); - makefileStream << "\\\n" << this - ->ConvertToRelativeForMake(dep.c_str()); + std::string dep; + if(this->CurrentLocalGenerator + ->GetRealDependency(d->c_str(), configName, dep)) + { + makefileStream << "\\\n" << + this->ConvertToRelativeForMake(dep.c_str()); + } } makefileStream << "\n"; @@ -1346,20 +1348,15 @@ void cmGlobalXCodeGenerator { std::string echo_cmd = "echo "; echo_cmd += (this->CurrentLocalGenerator-> - EscapeForShell(comment, escapeAllowMakeVars)); + EscapeForShell(comment, cc.GetEscapeAllowMakeVars())); makefileStream << "\t" << echo_cmd.c_str() << "\n"; } // Add each command line to the set of commands. - for(cmCustomCommandLines::const_iterator cl = - cc.GetCommandLines().begin(); - cl != cc.GetCommandLines().end(); ++cl) + for(unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) { // Build the command line in a single string. - const cmCustomCommandLine& commandLine = *cl; - std::string cmd2 = this->CurrentLocalGenerator - ->GetRealLocation(commandLine[0].c_str(), configName); - + std::string cmd2 = ccg.GetCommand(c); cmSystemTools::ReplaceString(cmd2, "/./", "/"); cmd2 = this->ConvertToRelativeForMake(cmd2.c_str()); std::string cmd; @@ -1370,21 +1367,7 @@ void cmGlobalXCodeGenerator cmd += " && "; } cmd += cmd2; - for(unsigned int j=1; j < commandLine.size(); ++j) - { - cmd += " "; - if(escapeOldStyle) - { - cmd += (this->CurrentLocalGenerator - ->EscapeForShellOldStyle(commandLine[j].c_str())); - } - else - { - cmd += (this->CurrentLocalGenerator-> - EscapeForShell(commandLine[j].c_str(), - escapeAllowMakeVars)); - } - } + ccg.AppendArguments(c, cmd); makefileStream << "\t" << cmd.c_str() << "\n"; } } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 5bffd5236..d3cbc1ff7 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1818,8 +1818,9 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, } //---------------------------------------------------------------------------- -std::string cmLocalGenerator::GetRealDependency(const char* inName, - const char* config) +bool cmLocalGenerator::GetRealDependency(const char* inName, + const char* config, + std::string& dep) { // Older CMake code may specify the dependency using the target // output file rather than the target name. Such code would have @@ -1855,7 +1856,8 @@ std::string cmLocalGenerator::GetRealDependency(const char* inName, // it is a full path to a depend that has the same name // as a target but is in a different location so do not use // the target as the depend - return inName; + dep = inName; + return true; } } switch (target->GetType()) @@ -1869,15 +1871,16 @@ std::string cmLocalGenerator::GetRealDependency(const char* inName, // Get the location of the target's output file and depend on it. if(const char* location = target->GetLocation(config)) { - return location; + dep = location; + return true; } } break; case cmTarget::UTILITY: case cmTarget::GLOBAL_TARGET: - // Depending on a utility target may not work but just trust - // the user to have given a valid name. - return inName; + // A utility target has no file on which to depend. This was listed + // only to get the target-level dependency. + return false; case cmTarget::INSTALL_FILES: case cmTarget::INSTALL_PROGRAMS: case cmTarget::INSTALL_DIRECTORY: @@ -1889,41 +1892,24 @@ std::string cmLocalGenerator::GetRealDependency(const char* inName, if(cmSystemTools::FileIsFullPath(inName)) { // This is a full path. Return it as given. - return inName; + dep = inName; + return true; } // Check for a source file in this directory that matches the // dependency. if(cmSourceFile* sf = this->Makefile->GetSource(inName)) { - name = sf->GetFullPath(); - return name; + dep = sf->GetFullPath(); + return true; } // Treat the name as relative to the source directory in which it // was given. - name = this->Makefile->GetCurrentDirectory(); - name += "/"; - name += inName; - return name; -} - -//---------------------------------------------------------------------------- -std::string cmLocalGenerator::GetRealLocation(const char* inName, - const char* config) -{ - std::string outName=inName; - // Look for a CMake target with the given name, which is an executable - // and which can be run - cmTarget* target = this->Makefile->FindTargetToUse(inName); - if ((target != 0) - && (target->GetType() == cmTarget::EXECUTABLE) - && ((this->Makefile->IsOn("CMAKE_CROSSCOMPILING") == false) - || (target->IsImported() == true))) - { - outName = target->GetLocation( config ); - } - return outName; + dep = this->Makefile->GetCurrentDirectory(); + dep += "/"; + dep += inName; + return true; } //---------------------------------------------------------------------------- diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 43bf1e73b..35aab99ec 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -158,18 +158,15 @@ public: /** Translate a dependency as given in CMake code to the name to appear in a generated build file. If the given name is that of + a utility target, returns false. If the given name is that of a CMake target it will be transformed to the real output location of that target for the given configuration. If the given name is the full path to a file it will be returned. Otherwise the name is treated as a relative path with respect to the source directory of this generator. This should only be used for dependencies of custom commands. */ - std::string GetRealDependency(const char* name, const char* config); - - /** Translate a command as given in CMake code to the location of the - executable if the command is the name of a CMake executable target. - If that's not the case, just return the original name. */ - std::string GetRealLocation(const char* inName, const char* config); + bool GetRealDependency(const char* name, const char* config, + std::string& dep); ///! for existing files convert to output path and short path if spaces std::string ConvertToOutputForExisting(const char* remote, diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index c5d8c0df5..ff48009ba 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -19,6 +19,7 @@ #include "cmake.h" #include "cmVersion.h" #include "cmFileTimeComparison.h" +#include "cmCustomCommandGenerator.h" // Include dependency scanners for supported languages. Only the // C/C++ scanner is needed for bootstrapping CMake. @@ -902,9 +903,12 @@ cmLocalUnixMakefileGenerator3 d != cc.GetDepends().end(); ++d) { // Lookup the real name of the dependency in case it is a CMake target. - std::string dep = this->GetRealDependency - (d->c_str(), this->ConfigurationName.c_str()); - depends.push_back(dep); + std::string dep; + if(this->GetRealDependency(d->c_str(), this->ConfigurationName.c_str(), + dep)) + { + depends.push_back(dep); + } } } @@ -958,18 +962,15 @@ cmLocalUnixMakefileGenerator3 { *content << dir; } - bool escapeOldStyle = cc.GetEscapeOldStyle(); - bool escapeAllowMakeVars = cc.GetEscapeAllowMakeVars(); + cmCustomCommandGenerator ccg(cc, this->ConfigurationName.c_str(), + this->Makefile); // Add each command line to the set of commands. std::vector commands1; - for(cmCustomCommandLines::const_iterator cl = cc.GetCommandLines().begin(); - cl != cc.GetCommandLines().end(); ++cl) + for(unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) { // Build the command line in a single string. - const cmCustomCommandLine& commandLine = *cl; - std::string cmd = GetRealLocation(commandLine[0].c_str(), - this->ConfigurationName.c_str()); + std::string cmd = ccg.GetCommand(c); if (cmd.size()) { // Use "call " before any invocations of .bat or .cmd files @@ -1022,19 +1023,8 @@ cmLocalUnixMakefileGenerator3 std::string launcher = this->MakeLauncher(cc, target, workingDir? NONE : START_OUTPUT); cmd = launcher + this->Convert(cmd.c_str(),NONE,SHELL); - for(unsigned int j=1; j < commandLine.size(); ++j) - { - cmd += " "; - if(escapeOldStyle) - { - cmd += this->EscapeForShellOldStyle(commandLine[j].c_str()); - } - else - { - cmd += this->EscapeForShell(commandLine[j].c_str(), - escapeAllowMakeVars); - } - } + + ccg.AppendArguments(c, cmd); if(content) { // Rule content does not include the launcher. diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx index eb4e4a4f2..7aabf4d99 100644 --- a/Source/cmLocalVisualStudio6Generator.cxx +++ b/Source/cmLocalVisualStudio6Generator.cxx @@ -65,13 +65,7 @@ public: { this->Code += "\\\n\t"; } - this->Code += - this->LG->ConstructScript(cc.GetCommandLines(), - cc.GetWorkingDirectory(), - this->Config, - cc.GetEscapeOldStyle(), - cc.GetEscapeAllowMakeVars(), - "\\\n\t"); + this->Code += this->LG->ConstructScript(cc, this->Config, "\\\n\t"); } private: cmLocalVisualStudio6Generator* LG; @@ -659,12 +653,7 @@ cmLocalVisualStudio6Generator { std::string config = this->GetConfigName(*i); std::string script = - this->ConstructScript(command.GetCommandLines(), - command.GetWorkingDirectory(), - config.c_str(), - command.GetEscapeOldStyle(), - command.GetEscapeAllowMakeVars(), - "\\\n\t"); + this->ConstructScript(command, config.c_str(), "\\\n\t"); if (i == this->Configurations.begin()) { @@ -686,10 +675,12 @@ cmLocalVisualStudio6Generator ++d) { // Lookup the real name of the dependency in case it is a CMake target. - std::string dep = this->GetRealDependency(d->c_str(), - config.c_str()); - fout << "\\\n\t" << - this->ConvertToOptionallyRelativeOutputPath(dep.c_str()); + std::string dep; + if(this->GetRealDependency(d->c_str(), config.c_str(), dep)) + { + fout << "\\\n\t" << + this->ConvertToOptionallyRelativeOutputPath(dep.c_str()); + } } fout << "\n"; @@ -847,7 +838,7 @@ cmLocalVisualStudio6Generator::MaybeCreateOutputDir(cmTarget& target, std::vector no_depends; cmCustomCommandLines commands; commands.push_back(command); - pcc.reset(new cmCustomCommand(no_output, no_depends, commands, 0, 0)); + pcc.reset(new cmCustomCommand(0, no_output, no_depends, commands, 0, 0)); pcc->SetEscapeOldStyle(false); pcc->SetEscapeAllowMakeVars(true); return pcc; diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 7fd7fd254..b22c429d5 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -546,12 +546,7 @@ public: { this->Stream << this->LG->EscapeForXML("\n"); } - std::string script = - this->LG->ConstructScript(cc.GetCommandLines(), - cc.GetWorkingDirectory(), - this->Config, - cc.GetEscapeOldStyle(), - cc.GetEscapeAllowMakeVars()); + std::string script = this->LG->ConstructScript(cc, this->Config); this->Stream << this->LG->EscapeForXML(script.c_str()); } private: @@ -1591,12 +1586,7 @@ WriteCustomRule(std::ostream& fout, << this->EscapeForXML(fc.CompileFlags.c_str()) << "\"/>\n"; } - std::string script = - this->ConstructScript(command.GetCommandLines(), - command.GetWorkingDirectory(), - i->c_str(), - command.GetEscapeOldStyle(), - command.GetEscapeAllowMakeVars()); + std::string script = this->ConstructScript(command, i->c_str()); fout << "\t\t\t\t\tGetRealDependency(d->c_str(), i->c_str()); - fout << this->ConvertToXMLOutputPath(dep.c_str()) - << ";"; + std::string dep; + if(this->GetRealDependency(d->c_str(), i->c_str(), dep)) + { + fout << this->ConvertToXMLOutputPath(dep.c_str()) + << ";"; + } } } fout << "\"\n"; diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx index ed0b07fe1..a044363c9 100644 --- a/Source/cmLocalVisualStudioGenerator.cxx +++ b/Source/cmLocalVisualStudioGenerator.cxx @@ -14,6 +14,7 @@ #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmSystemTools.h" +#include "cmCustomCommandGenerator.h" #include "windows.h" //---------------------------------------------------------------------------- @@ -52,7 +53,7 @@ cmLocalVisualStudioGenerator::MaybeCreateImplibDir(cmTarget& target, std::vector no_depends; cmCustomCommandLines commands; commands.push_back(command); - pcc.reset(new cmCustomCommand(no_output, no_depends, commands, 0, 0)); + pcc.reset(new cmCustomCommand(0, no_output, no_depends, commands, 0, 0)); pcc->SetEscapeOldStyle(false); pcc->SetEscapeAllowMakeVars(true); return pcc; @@ -151,13 +152,15 @@ void cmLocalVisualStudioGenerator::ComputeObjectNameRequirements //---------------------------------------------------------------------------- std::string cmLocalVisualStudioGenerator -::ConstructScript(const cmCustomCommandLines& commandLines, - const char* workingDirectory, +::ConstructScript(cmCustomCommand const& cc, const char* configName, - bool escapeOldStyle, - bool escapeAllowMakeVars, const char* newline_text) { + const cmCustomCommandLines& commandLines = cc.GetCommandLines(); + const char* workingDirectory = cc.GetWorkingDirectory(); + cmCustomCommandGenerator ccg(cc, configName, this->Makefile); + RelativeRoot relativeRoot = workingDirectory? NONE : START_OUTPUT; + // Avoid leading or trailing newlines. const char* newline = ""; @@ -196,40 +199,16 @@ cmLocalVisualStudioGenerator } } // Write each command on a single line. - for(cmCustomCommandLines::const_iterator cl = commandLines.begin(); - cl != commandLines.end(); ++cl) + for(unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) { // Start a new line. script += newline; newline = newline_text; - // Start with the command name. - const cmCustomCommandLine& commandLine = *cl; - std::string commandName = this->GetRealLocation(commandLine[0].c_str(), - configName); - if(!workingDirectory) - { - script += this->Convert(commandName.c_str(),START_OUTPUT,SHELL); - } - else - { - script += this->Convert(commandName.c_str(),NONE,SHELL); - } - - // Add the arguments. - for(unsigned int j=1;j < commandLine.size(); ++j) - { - script += " "; - if(escapeOldStyle) - { - script += this->EscapeForShellOldStyle(commandLine[j].c_str()); - } - else - { - script += this->EscapeForShell(commandLine[j].c_str(), - escapeAllowMakeVars); - } - } + // Add this command line. + std::string cmd = ccg.GetCommand(c); + script += this->Convert(cmd.c_str(), relativeRoot, SHELL); + ccg.AppendArguments(c, script); // After each custom command, check for an error result. // If there was an error, jump to the VCReportError label, diff --git a/Source/cmLocalVisualStudioGenerator.h b/Source/cmLocalVisualStudioGenerator.h index 6034b22c7..551b01cf9 100644 --- a/Source/cmLocalVisualStudioGenerator.h +++ b/Source/cmLocalVisualStudioGenerator.h @@ -18,6 +18,7 @@ class cmSourceFile; class cmSourceGroup; +class cmCustomCommand; /** \class cmLocalVisualStudioGenerator * \brief Base class for Visual Studio generators. @@ -31,11 +32,8 @@ public: cmLocalVisualStudioGenerator(); virtual ~cmLocalVisualStudioGenerator(); /** Construct a script from the given list of command lines. */ - std::string ConstructScript(const cmCustomCommandLines& commandLines, - const char* workingDirectory, + std::string ConstructScript(cmCustomCommand const& cc, const char* configName, - bool escapeOldStyle, - bool escapeAllowMakeVars, const char* newline = "\n"); protected: diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 56e0ed933..3bce01c34 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -827,7 +827,8 @@ cmMakefile::AddCustomCommandToTarget(const char* target, { // Add the command to the appropriate build step for the target. std::vector no_output; - cmCustomCommand cc(no_output, depends, commandLines, comment, workingDir); + cmCustomCommand cc(this, no_output, depends, + commandLines, comment, workingDir); cc.SetEscapeOldStyle(escapeOldStyle); cc.SetEscapeAllowMakeVars(true); switch(type) @@ -947,7 +948,7 @@ cmMakefile::AddCustomCommandToOutput(const std::vector& outputs, if(file) { cmCustomCommand* cc = - new cmCustomCommand(outputs, depends2, commandLines, + new cmCustomCommand(this, outputs, depends2, commandLines, comment, workingDir); cc->SetEscapeOldStyle(escapeOldStyle); cc->SetEscapeAllowMakeVars(true); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index ca61b1fc3..c82c11ec5 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -1357,8 +1357,8 @@ bool cmTargetTraceDependencies::IsUtility(std::string const& dep) util = cmSystemTools::GetFilenameWithoutLastExtension(util); } - // Check for a non-imported target with this name. - if(cmTarget* t = this->GlobalGenerator->FindTarget(0, util.c_str())) + // Check for a target with this name. + if(cmTarget* t = this->Makefile->FindTargetToUse(util.c_str())) { // If we find the target and the dep was given as a full path, // then make sure it was not a full path to something else, and @@ -1406,8 +1406,8 @@ cmTargetTraceDependencies cit != cc.GetCommandLines().end(); ++cit) { std::string const& command = *cit->begin(); - // Look for a non-imported target with this name. - if(cmTarget* t = this->GlobalGenerator->FindTarget(0, command.c_str())) + // Check for a target with this name. + if(cmTarget* t = this->Makefile->FindTargetToUse(command.c_str())) { if(t->GetType() == cmTarget::EXECUTABLE) { diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 524be8b48..eb8320c48 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -367,13 +367,7 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source, i != configs->end(); ++i) { std::string script = - cmVS10EscapeXML( - lg->ConstructScript(command.GetCommandLines(), - command.GetWorkingDirectory(), - i->c_str(), - command.GetEscapeOldStyle(), - command.GetEscapeAllowMakeVars()) - ); + cmVS10EscapeXML(lg->ConstructScript(command, i->c_str())); this->WritePlatformConfigTag("Message",i->c_str(), 3); (*this->BuildFileStream ) << cmVS10EscapeXML(comment) << "\n"; this->WritePlatformConfigTag("Command", i->c_str(), 3); @@ -386,10 +380,12 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source, d != command.GetDepends().end(); ++d) { - std::string dep = this->LocalGenerator-> - GetRealDependency(d->c_str(), i->c_str()); - this->ConvertToWindowsSlash(dep); - (*this->BuildFileStream ) << ";" << dep; + std::string dep; + if(this->LocalGenerator->GetRealDependency(d->c_str(), i->c_str(), dep)) + { + this->ConvertToWindowsSlash(dep); + (*this->BuildFileStream ) << ";" << dep; + } } (*this->BuildFileStream ) << ";%(AdditionalInputs)\n"; this->WritePlatformConfigTag("Outputs", i->c_str(), 3); @@ -1458,13 +1454,7 @@ void cmVisualStudio10TargetGenerator::WriteEvent( script += pre; pre = "\n"; script += - cmVS10EscapeXML( - lg->ConstructScript(command.GetCommandLines(), - command.GetWorkingDirectory(), - configName.c_str(), - command.GetEscapeOldStyle(), - command.GetEscapeAllowMakeVars()) - ); + cmVS10EscapeXML(lg->ConstructScript(command, configName.c_str())); } comment = cmVS10EscapeComment(comment); this->WriteString("",3); diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt index 76208d479..746c9a71b 100644 --- a/Tests/CustomCommand/CMakeLists.txt +++ b/Tests/CustomCommand/CMakeLists.txt @@ -130,6 +130,7 @@ ADD_CUSTOM_COMMAND( ################################################################ ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_BINARY_DIR}/foo.pre DEPENDS ${PROJECT_SOURCE_DIR}/foo.in + TDocument # Ensure doc1.h generates before this target COMMAND ${CMAKE_COMMAND} ARGS -E copy ${PROJECT_SOURCE_DIR}/foo.in ${PROJECT_BINARY_DIR}/foo.pre @@ -181,10 +182,6 @@ ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_BINARY_DIR}/generated.c TARGET_LINK_LIBRARIES(CustomCommand GeneratedHeader) -# must add a dependency on TDocument otherwise it might never build and -# the CustomCommand executable really needs doc1.h -ADD_DEPENDENCIES(CustomCommand TDocument) - ############################################################################## # Test for using just the target name as executable in the COMMAND # section. Has to be recognized and replaced by CMake with the output diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 082834383..e65e362ec 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -75,24 +75,64 @@ foreach(c DEBUG RELWITHDEBINFO) set_property(TARGET imp_testExe1b PROPERTY COMPILE_DEFINITIONS_${c} EXE_DBG) endforeach(c) +#----------------------------------------------------------------------------- # Create a custom target to generate a header for the libraries below. -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -add_custom_command( - OUTPUT testLib2.h - VERBATIM COMMAND - ${CMAKE_COMMAND} -E echo "extern int testLib2(void);" > testLib2.h - ) -add_custom_target(hdr_testLib2 DEPENDS testLib2.h) - # Drive the header generation through an indirect chain of imported # target dependencies. + +# testLib2tmp1.h +add_custom_command( + OUTPUT testLib2tmp1.h + VERBATIM COMMAND + ${CMAKE_COMMAND} -E echo "extern int testLib2(void);" > testLib2tmp1.h + ) + +# hdr_testLib2tmp1 needs testLib2tmp1.h +add_custom_target(hdr_testLib2tmp1 DEPENDS testLib2tmp1.h) + +# exp_testExe2 needs hdr_testLib2tmp1 +add_dependencies(exp_testExe2 hdr_testLib2tmp1) + +# testLib2tmp.h needs exp_testExe2 +add_custom_command( + OUTPUT testLib2tmp.h + VERBATIM COMMAND exp_testExe2 + COMMAND ${CMAKE_COMMAND} -E copy testLib2tmp1.h testLib2tmp.h + ) + +# hdr_testLib2tmp needs testLib2tmp.h +add_custom_target(hdr_testLib2tmp DEPENDS testLib2tmp.h) + +add_library(dep_testLib2tmp UNKNOWN IMPORTED) +set_property(TARGET dep_testLib2tmp PROPERTY + IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/testLib2tmp.h) + +# dep_testLib2tmp needs hdr_testLib2tmp +add_dependencies(dep_testLib2tmp hdr_testLib2tmp) + +# testLib2.h needs dep_testLib2tmp +add_custom_command( + OUTPUT testLib2.h + VERBATIM COMMAND ${CMAKE_COMMAND} -E copy testLib2tmp.h testLib2.h + DEPENDS dep_testLib2tmp + ) + +# hdr_testLib2 needs testLib2.h +add_custom_target(hdr_testLib2 DEPENDS testLib2.h) + add_library(dep_testLib2 UNKNOWN IMPORTED) + +# dep_testLib2 needs hdr_testLib2 add_dependencies(dep_testLib2 hdr_testLib2) + +# exp_testLib2 and bld_testLib2 both need dep_testLib2 add_dependencies(bld_testLib2 dep_testLib2) add_dependencies(exp_testLib2 dep_testLib2) +#----------------------------------------------------------------------------- # Create a library to be linked by another directory in this project # to test transitive linking to otherwise invisible imported targets. +include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_library(imp_lib1 STATIC imp_lib1.c) target_link_libraries(imp_lib1 exp_testLib2) add_library(imp_lib1b STATIC imp_lib1.c) diff --git a/Tests/PerConfig/CMakeLists.txt b/Tests/PerConfig/CMakeLists.txt new file mode 100644 index 000000000..2a55d3330 --- /dev/null +++ b/Tests/PerConfig/CMakeLists.txt @@ -0,0 +1,33 @@ +project(PerConfig C) + +# Targets with per-configuration names. +ADD_LIBRARY(pcStatic STATIC pcStatic.c) +SET_PROPERTY(TARGET pcStatic PROPERTY RELEASE_POSTFIX -opt) +SET_PROPERTY(TARGET pcStatic PROPERTY DEBUG_POSTFIX -dbg) +ADD_LIBRARY(pcShared SHARED pcShared.c) +SET_PROPERTY(TARGET pcShared PROPERTY RELEASE_POSTFIX -opt) +SET_PROPERTY(TARGET pcShared PROPERTY DEBUG_POSTFIX -dbg) +SET_PROPERTY(TARGET pcShared PROPERTY VERSION 1.2) +SET_PROPERTY(TARGET pcShared PROPERTY SOVERSION 3) +IF(UNIX AND NOT CYGWIN) + SET(soname_file -DpcShared_soname_file=$) +ENDIF() +ADD_EXECUTABLE(perconfig perconfig.c) +TARGET_LINK_LIBRARIES(perconfig pcStatic pcShared) +SET_PROPERTY(TARGET perconfig PROPERTY RELEASE_POSTFIX -opt) +SET_PROPERTY(TARGET perconfig PROPERTY DEBUG_POSTFIX -dbg) + +SET(PerConfig_COMMAND + ${CMAKE_COMMAND} + -Dconfiguration=$ + -Dperconfig_file_dir=$ + -Dperconfig_file_name=$ + -Dperconfig_file=$ + -DpcStatic_file=$ + -DpcStatic_linker_file=$ + -DpcShared_file=$ + -DpcShared_linker_file=$ + ${soname_file} + -P ${PerConfig_SOURCE_DIR}/perconfig.cmake + ) +SET(PerConfig_COMMAND "${PerConfig_COMMAND}" PARENT_SCOPE) diff --git a/Tests/Testing/pcShared.c b/Tests/PerConfig/pcShared.c similarity index 100% rename from Tests/Testing/pcShared.c rename to Tests/PerConfig/pcShared.c diff --git a/Tests/Testing/pcShared.h b/Tests/PerConfig/pcShared.h similarity index 100% rename from Tests/Testing/pcShared.h rename to Tests/PerConfig/pcShared.h diff --git a/Tests/Testing/pcStatic.c b/Tests/PerConfig/pcStatic.c similarity index 100% rename from Tests/Testing/pcStatic.c rename to Tests/PerConfig/pcStatic.c diff --git a/Tests/Testing/perconfig.c b/Tests/PerConfig/perconfig.c similarity index 100% rename from Tests/Testing/perconfig.c rename to Tests/PerConfig/perconfig.c diff --git a/Tests/Testing/driver.cmake b/Tests/PerConfig/perconfig.cmake similarity index 100% rename from Tests/Testing/driver.cmake rename to Tests/PerConfig/perconfig.cmake diff --git a/Tests/Testing/CMakeLists.txt b/Tests/Testing/CMakeLists.txt index 9e7a637ab..815b52b58 100644 --- a/Tests/Testing/CMakeLists.txt +++ b/Tests/Testing/CMakeLists.txt @@ -53,35 +53,7 @@ ADD_TEST(testing.1 ${Testing_BINARY_DIR}/bin/testing) # ADD_SUBDIRECTORY(Sub/Sub2) -# Per-config target name test. -ADD_LIBRARY(pcStatic STATIC pcStatic.c) -SET_PROPERTY(TARGET pcStatic PROPERTY RELEASE_POSTFIX -opt) -SET_PROPERTY(TARGET pcStatic PROPERTY DEBUG_POSTFIX -dbg) -ADD_LIBRARY(pcShared SHARED pcShared.c) -SET_PROPERTY(TARGET pcShared PROPERTY RELEASE_POSTFIX -opt) -SET_PROPERTY(TARGET pcShared PROPERTY DEBUG_POSTFIX -dbg) -SET_PROPERTY(TARGET pcShared PROPERTY VERSION 1.2) -SET_PROPERTY(TARGET pcShared PROPERTY SOVERSION 3) -IF(UNIX AND NOT CYGWIN) - SET(soname_file -DpcShared_soname_file=$) -ENDIF() -ADD_EXECUTABLE(perconfig perconfig.c) -TARGET_LINK_LIBRARIES(perconfig pcStatic pcShared) -SET_PROPERTY(TARGET perconfig PROPERTY RELEASE_POSTFIX -opt) -SET_PROPERTY(TARGET perconfig PROPERTY DEBUG_POSTFIX -dbg) +# Per-config target name and generator expressions. +ADD_SUBDIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../PerConfig PerConfig) ADD_TEST(NAME testing.perconfig COMMAND perconfig) - -# Test using a driver script with generator expressions. -ADD_TEST(NAME testing.driver - COMMAND ${CMAKE_COMMAND} - -Dconfiguration=$ - -Dperconfig_file_dir=$ - -Dperconfig_file_name=$ - -Dperconfig_file=$ - -DpcStatic_file=$ - -DpcStatic_linker_file=$ - -DpcShared_file=$ - -DpcShared_linker_file=$ - ${soname_file} - -P ${Testing_SOURCE_DIR}/driver.cmake - ) +ADD_TEST(NAME testing.driver COMMAND ${PerConfig_COMMAND}) diff --git a/bootstrap b/bootstrap index 0da868d8f..b4e19ef22 100755 --- a/bootstrap +++ b/bootstrap @@ -215,6 +215,7 @@ CMAKE_CXX_SOURCES="\ cmTarget \ cmTest \ cmCustomCommand \ + cmCustomCommandGenerator \ cmDocumentVariables \ cmCacheManager \ cmListFileCache \