diff options
Diffstat (limited to 'Source')
70 files changed, 2543 insertions, 683 deletions
diff --git a/Source/CPack/cmCPackGenericGenerator.cxx b/Source/CPack/cmCPackGenericGenerator.cxx index 846226c..f4e51b3 100644 --- a/Source/CPack/cmCPackGenericGenerator.cxx +++ b/Source/CPack/cmCPackGenericGenerator.cxx @@ -43,6 +43,21 @@ cmCPackGenericGenerator::~cmCPackGenericGenerator() } //---------------------------------------------------------------------- +void cmCPackGenericGeneratorProgress(const char *msg, float prog, void* ptr) +{ + cmCPackGenericGenerator* self = static_cast<cmCPackGenericGenerator*>(ptr); + self->DisplayVerboseOutput(msg, prog); +} + +//---------------------------------------------------------------------- +void cmCPackGenericGenerator::DisplayVerboseOutput(const char* msg, + float progress) +{ + (void)progress; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "" << msg << std::endl); +} + +//---------------------------------------------------------------------- int cmCPackGenericGenerator::PrepareNames() { this->SetOption("CPACK_GENERATOR", this->Name.c_str()); @@ -152,6 +167,7 @@ int cmCPackGenericGenerator::InstallProject() ignoreFilesRegex.push_back(it->c_str()); } } + this->CleanTemporaryDirectory(); const char* tempInstallDirectory = this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); int res = 1; @@ -174,6 +190,9 @@ int cmCPackGenericGenerator::InstallProject() destDir += tempInstallDirectory; cmSystemTools::PutEnv(destDir.c_str()); } + + // If the CPackConfig file sets CPACK_INSTALL_COMMANDS then run them + // as listed const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS"); if ( installCommands && *installCommands ) { @@ -207,6 +226,10 @@ int cmCPackGenericGenerator::InstallProject() } } } + + // If the CPackConfig file sets CPACK_INSTALLED_DIRECTORIES + // then glob it and copy it to CPACK_TEMPORARY_DIRECTORY + // This is used in Source packageing const char* installDirectories = this->GetOption("CPACK_INSTALLED_DIRECTORIES"); if ( installDirectories && *installDirectories ) @@ -281,6 +304,9 @@ int cmCPackGenericGenerator::InstallProject() } } } + + // If the project is a CMAKE project then run pre-install + // and then read the cmake_install script to run it const char* cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS"); const char* cmakeGenerator @@ -346,7 +372,7 @@ int cmCPackGenericGenerator::InstallProject() = globalGenerator->GenerateBuildCommand(cmakeMakeProgram, installProjectName.c_str(), 0, globalGenerator->GetPreinstallTargetName(), - buildConfig, false); + buildConfig, false, false); cmCPackLogger(cmCPackLog::LOG_DEBUG, "- Install command: " << buildCommand << std::endl); cmCPackLogger(cmCPackLog::LOG_OUTPUT, @@ -381,6 +407,7 @@ int cmCPackGenericGenerator::InstallProject() cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Install project: " << installProjectName << std::endl); cmake cm; + cm.SetProgressCallback(cmCPackGenericGeneratorProgress, this); cmGlobalGenerator gg; gg.SetCMakeInstance(&cm); std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator()); @@ -414,6 +441,8 @@ int cmCPackGenericGenerator::InstallProject() } } } + + // ????? const char* binaryDirectories = this->GetOption("CPACK_BINARY_DIR"); if ( binaryDirectories && !cmakeProjects ) { @@ -846,3 +875,25 @@ bool cmCPackGenericGenerator::ConfigureFile(const char* inName, return this->MakefileMap->ConfigureFile(inName, outName, false, true, false) == 1; } + +//---------------------------------------------------------------------- +int cmCPackGenericGenerator::CleanTemporaryDirectory() +{ + const char* tempInstallDirectory + = this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); + if(cmsys::SystemTools::FileExists(tempInstallDirectory)) + { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Clean temporary : " + << tempInstallDirectory << std::endl); + if(!cmsys::SystemTools::RemoveADirectory(tempInstallDirectory)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem removing temporary directory: " << + tempInstallDirectory + << std::endl); + return 0; + } + } + return 1; +} diff --git a/Source/CPack/cmCPackGenericGenerator.h b/Source/CPack/cmCPackGenericGenerator.h index 6867613..87c486d 100644 --- a/Source/CPack/cmCPackGenericGenerator.h +++ b/Source/CPack/cmCPackGenericGenerator.h @@ -87,10 +87,13 @@ public: //! Set the logger void SetLogger(cmCPackLog* log) { this->Logger = log; } + //! Display verbose information via logger + void DisplayVerboseOutput(const char* msg, float progress); + protected: int PrepareNames(); int InstallProject(); - + int CleanTemporaryDirectory(); virtual const char* GetOutputExtension() { return "cpack"; } virtual const char* GetOutputPostfix() { return 0; } virtual int CompressFiles(const char* outFileName, const char* toplevel, diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index b7a27d9..6948bc6 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -196,7 +196,7 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) this->SourceDir.c_str(), this->BinaryDir.c_str(), this->BuildProject.c_str(), tarIt->c_str(), &output, this->BuildMakeProgram.c_str(), - this->CTest->GetConfigType().c_str(),!this->BuildNoClean); + this->CTest->GetConfigType().c_str(),!this->BuildNoClean, false); out << output; // if the build failed then return diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 7838118..f6a196d 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -93,7 +93,7 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() std::string buildCommand = this->GlobalGenerator->GenerateBuildCommand(cmakeMakeProgram, cmakeProjectName, - cmakeBuildAdditionalFlags, 0, cmakeBuildConfiguration, true); + cmakeBuildAdditionalFlags, 0, cmakeBuildConfiguration, true, false); this->CTest->SetCTestConfiguration("MakeCommand", buildCommand.c_str()); } else diff --git a/Source/CursesDialog/form/form.priv.h b/Source/CursesDialog/form/form.priv.h index 886121c..3691f2f 100644 --- a/Source/CursesDialog/form/form.priv.h +++ b/Source/CursesDialog/form/form.priv.h @@ -33,6 +33,12 @@ #include "mf_common.h" #include "form.h" +/* get around odd bug on aCC and itanium */ +#if defined(__hpux) && defined(__ia64) +#define getmaxx __getmaxx +#define getmaxy __getmaxy +#endif + /* form status values */ #define _OVLMODE (0x04) /* Form is in overlay mode */ #define _WINDOW_MODIFIED (0x10) /* Current field window has been modified */ @@ -41,7 +47,7 @@ /* field status values */ #define _CHANGED (0x01) /* Field has been changed */ #define _NEWTOP (0x02) /* Vertical scrolling occured */ -#define _NEWPAGE (0x04) /* field begins new page of form */ +#define _NEWPAGE (0x04) /* field begins new page of form */ #define _MAY_GROW (0x08) /* dynamic field may still grow */ /* fieldtype status values */ @@ -91,20 +97,20 @@ typedef struct typearg { #define FIRST_ACTIVE_MAGIC (-291056) #define ALL_FORM_OPTS ( \ - O_NL_OVERLOAD |\ - O_BS_OVERLOAD ) + O_NL_OVERLOAD |\ + O_BS_OVERLOAD ) #define ALL_FIELD_OPTS ( \ - O_VISIBLE |\ - O_ACTIVE |\ - O_PUBLIC |\ - O_EDIT |\ - O_WRAP |\ - O_BLANK |\ - O_AUTOSKIP|\ - O_NULLOK |\ - O_PASSOK |\ - O_STATIC ) + O_VISIBLE |\ + O_ACTIVE |\ + O_PUBLIC |\ + O_EDIT |\ + O_WRAP |\ + O_BLANK |\ + O_AUTOSKIP|\ + O_NULLOK |\ + O_PASSOK |\ + O_STATIC ) #define C_BLANK ' ' diff --git a/Source/MFCDialog/CMakeSetupDialog.cpp b/Source/MFCDialog/CMakeSetupDialog.cpp index 534f713..0a605f2 100644 --- a/Source/MFCDialog/CMakeSetupDialog.cpp +++ b/Source/MFCDialog/CMakeSetupDialog.cpp @@ -83,7 +83,7 @@ void MFCMessageCallback(const char* m, const char* title, bool& nomore, void*) // CMakeSetupDialog dialog void updateProgress(const char *msg, float prog, void *cd) { - char tmp[1024]; + char* tmp = new char[strlen(msg) + 40]; if (prog >= 0) { sprintf(tmp,"%s %i%%",msg,(int)(100*prog)); @@ -120,6 +120,7 @@ void updateProgress(const char *msg, float prog, void *cd) break; } } + delete [] tmp; } // Convert to Win32 path (slashes). This calls the system tools one and then diff --git a/Source/cmAddCustomCommandCommand.h b/Source/cmAddCustomCommandCommand.h index 7a40c47..bacce8f 100644 --- a/Source/cmAddCustomCommandCommand.h +++ b/Source/cmAddCustomCommandCommand.h @@ -74,7 +74,9 @@ public: " [WORKING_DIRECTORY dir]\n" " [COMMENT comment])\n" "This defines a new command that can be executed during the build " - "process. Note that MAIN_DEPENDENCY is completely optional and is " + "process. The outputs named should be listed as source files in the " + "target for which they are to be generated. " + "Note that MAIN_DEPENDENCY is completely optional and is " "used as a suggestion to visual studio about where to hang the " "custom command. In makefile terms this creates a new target in the " "following form:\n" @@ -100,9 +102,9 @@ public: " POST_BUILD - run after the target has been built\n" "Note that the PRE_BUILD option is only supported on Visual " "Studio 7 or later. For all other generators PRE_BUILD " - "will be treated as PRE_LINK." - "If WORKING_DIRECTORY is specified the command a cd \"dir\" is " - "done prior to running the command."; + "will be treated as PRE_LINK. " + "If WORKING_DIRECTORY is specified the command will be executed " + "in the directory given."; } cmTypeMacro(cmAddCustomCommandCommand, cmCommand); diff --git a/Source/cmAddCustomTargetCommand.h b/Source/cmAddCustomTargetCommand.h index 7bc8c5d..4a448b3 100644 --- a/Source/cmAddCustomTargetCommand.h +++ b/Source/cmAddCustomTargetCommand.h @@ -70,13 +70,16 @@ public: "Adds a target with the given name that executes the given commands " "every time the target is built. If the ALL option is specified " "it indicates that this target should be added to the default build " - "target so that it will be run every time. " - "The command and arguments are optional. If not specified, " - "it will create an empty target. The ADD_DEPENDENCIES command can be " - "used in conjunction with this command to drive custom target " - "generation. The command cannot be called ALL. " + "target so that it will be run every time " + "(the command cannot be called ALL). " + "The command and arguments are optional and if not specified an " + "empty target will be created. " "If WORKING_DIRECTORY is set, then the command will be run in that " - "directory."; + "directory. " + "Dependencies listed with the DEPENDS argument may reference files " + "and outputs of custom commands created with ADD_CUSTOM_COMMAND. " + "Dependencies on other targets may be added using the " + "ADD_DEPENDENCIES command."; } cmTypeMacro(cmAddCustomTargetCommand, cmCommand); diff --git a/Source/cmAddSubDirectoryCommand.cxx b/Source/cmAddSubDirectoryCommand.cxx index 5e13150..6591b44 100644 --- a/Source/cmAddSubDirectoryCommand.cxx +++ b/Source/cmAddSubDirectoryCommand.cxx @@ -54,14 +54,12 @@ bool cmAddSubDirectoryCommand::InitialPass } // check for relative arguments - bool relativeSource = true; std::string binPath = binArg; std::string srcPath = std::string(this->Makefile->GetCurrentDirectory()) + "/" + srcArg; // if the path does not exist then the arg was relative if (!cmSystemTools::FileIsDirectory(srcPath.c_str())) { - relativeSource = false; srcPath = srcArg; if (!cmSystemTools::FileIsDirectory(srcPath.c_str())) { @@ -74,6 +72,7 @@ bool cmAddSubDirectoryCommand::InitialPass // at this point srcPath has the full path to the source directory // now we need to compute the binPath if it was not provided + srcPath = cmSystemTools::CollapseFullPath(srcPath.c_str()); // if the argument was provided then use it if (binArg.size()) @@ -87,21 +86,13 @@ bool cmAddSubDirectoryCommand::InitialPass // otherwise compute the binPath from the srcPath else { - // if the srcArg was relative then we just do the same for the binPath - if (relativeSource) - { - binPath = std::string(this->Makefile->GetCurrentOutputDirectory()) + - "/" + srcArg; - } - // otherwise we try to remove the CurrentDirectory from the srcPath and + // we try to remove the CurrentDirectory from the srcPath and // replace it with the CurrentOutputDirectory. This may not really work // because the source dir they provided may not be "in" the source // tree. This is an error if this happens. - else - { // try replacing the home dir with the home output dir binPath = srcPath; - if (!cmSystemTools::FindLastString(binPath.c_str(), + if(!cmSystemTools::FindLastString(binPath.c_str(), this->Makefile->GetHomeDirectory())) { this->SetError("A full source directory was specified that is not " @@ -114,7 +105,6 @@ bool cmAddSubDirectoryCommand::InitialPass cmSystemTools::ReplaceString(binPath, this->Makefile->GetHomeDirectory(), this->Makefile->GetHomeOutputDirectory()); - } } // now we have all the arguments diff --git a/Source/cmBuildCommand.cxx b/Source/cmBuildCommand.cxx index 13443b0..405b463 100644 --- a/Source/cmBuildCommand.cxx +++ b/Source/cmBuildCommand.cxx @@ -34,7 +34,7 @@ bool cmBuildCommand::InitialPass(std::vector<std::string> const& args) std::string makecommand = this->Makefile->GetLocalGenerator() ->GetGlobalGenerator()->GenerateBuildCommand (makeprogram.c_str(), this->Makefile->GetProjectName(), 0, - 0, "Release", true); + 0, "Release", true, false); if(cacheValue) { diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index a0a231f..5eecb1d 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx @@ -19,6 +19,7 @@ #include "cmSystemTools.h" #include "cmCacheManager.h" #include "cmMakefile.h" +#include "cmake.h" #include <cmsys/Directory.hxx> #include <cmsys/Glob.hxx> @@ -163,7 +164,8 @@ bool cmCacheManager::ParseEntry(const char* entry, void cmCacheManager::CleanCMakeFiles(const char* path) { std::string glob = path; - glob += "/CMakeFiles/*.cmake"; + glob += cmake::GetCMakeFilesDirectory(); + glob += "/*.cmake"; cmsys::Glob globIt; globIt.FindFiles(glob); std::vector<std::string> files = globIt.GetFiles(); @@ -601,7 +603,7 @@ bool cmCacheManager::SaveCache(const char* path) cacheFile.c_str()); cmSystemTools::RemoveFile(tempFile.c_str()); std::string checkCacheFile = path; - checkCacheFile += "/CMakeFiles"; + checkCacheFile += cmake::GetCMakeFilesDirectory(); cmSystemTools::MakeDirectory(checkCacheFile.c_str()); checkCacheFile += "/cmake.check_cache"; std::ofstream checkCache(checkCacheFile.c_str()); @@ -626,7 +628,7 @@ bool cmCacheManager::DeleteCache(const char* path) // now remove the files in the CMakeFiles directory // this cleans up language cache files cmsys::Directory dir; - cmakeFiles += "/CMakeFiles"; + cmakeFiles += cmake::GetCMakeFilesDirectory(); dir.Load(cmakeFiles.c_str()); for (unsigned long fileNum = 0; fileNum < dir.GetNumberOfFiles(); diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx index f6d577c..37c177b 100644 --- a/Source/cmDepends.cxx +++ b/Source/cmDepends.cxx @@ -22,7 +22,11 @@ #include <string.h> //---------------------------------------------------------------------------- -cmDepends::cmDepends(): Verbose(false), FileComparison(0), +cmDepends::cmDepends(): + CompileDirectory(), + LocalGenerator(0), + Verbose(false), + FileComparison(0), MaxPath(cmSystemTools::GetMaximumFilePathLength()), Dependee(new char[MaxPath]), Depender(new char[MaxPath]) diff --git a/Source/cmDepends.h b/Source/cmDepends.h index d6269af..b7a11de 100644 --- a/Source/cmDepends.h +++ b/Source/cmDepends.h @@ -20,6 +20,7 @@ #include "cmStandardIncludes.h" class cmFileTimeComparison; +class cmLocalGenerator; /** \class cmDepends * \brief Dependency scanner superclass. @@ -38,11 +39,11 @@ public: /** at what level will the compile be done from */ void SetCompileDirectory(const char *dir) {this->CompileDirectory = dir;}; - /** Set the full path to the top of the build tree. This is - the base path from which dependencies are referenced as - relative paths. */ - void SetHomeOutputDirectory(const char *dir) { - this->HomeOutputDirectory = dir;}; + /** Set the local generator for the directory in which we are + scanning dependencies. This is not a full local generator; it + has been setup to do relative path conversions for the current + directory. */ + void SetLocalGenerator(cmLocalGenerator* lg) { this->LocalGenerator = lg; } /** should this be verbose in its output */ void SetVerbose(bool verb) { this->Verbose = verb; } @@ -79,8 +80,8 @@ protected: // The directory in which the build rule for the target file is executed. std::string CompileDirectory; - // The full path to the top of the build tree. - std::string HomeOutputDirectory; + // The local generator. + cmLocalGenerator* LocalGenerator; // Flag for verbose output. bool Verbose; diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index ad1d021..7806ab3 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx @@ -16,8 +16,9 @@ =========================================================================*/ #include "cmDependsC.h" -#include "cmSystemTools.h" #include "cmFileTimeComparison.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" #include <ctype.h> // isspace @@ -194,13 +195,18 @@ bool cmDependsC::WriteDependencies(const char *src, const char *obj, first = false; } - // Write the dependencies to the output stream. + // Write the dependencies to the output stream. Makefile rules + // written by the original local generator for this directory + // convert the dependencies to paths relative to the home output + // directory. We must do the same here. internalDepends << obj << std::endl; for(std::set<cmStdString>::iterator i=dependencies.begin(); i != dependencies.end(); ++i) { - makeDepends << obj << ": " - << cmSystemTools::ConvertToOutputPath(i->c_str()).c_str() + makeDepends << obj << ": " << + this->LocalGenerator->Convert(i->c_str(), + cmLocalGenerator::HOME_OUTPUT, + cmLocalGenerator::MAKEFILE) << std::endl; internalDepends << " " << i->c_str() << std::endl; } @@ -370,8 +376,9 @@ bool cmDependsC::FileExistsOrIsGenerated(const std::string& fname, // Note that CMAKE_GENERATED_FILES is written with a conversion // relative to the home output directory. std::string rname = - cmSystemTools::RelativePath(this->HomeOutputDirectory.c_str(), - fname.c_str()); + this->LocalGenerator->Convert(fname.c_str(), + cmLocalGenerator::HOME_OUTPUT, + cmLocalGenerator::UNCHANGED); if(this->FileIsGenerated(rname, scanned, dependencies)) { return true; diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 27c5e58..bbf4265 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -233,6 +233,11 @@ bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args, } g.SetRelative(i->c_str()); ++i; + if(i == args.end()) + { + this->SetError("GLOB requires a glob expression after the directory"); + return false; + } } if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) ) { @@ -775,15 +780,10 @@ bool cmFileCommand::HandleInstallCommand( std::string libname = toFile; std::string soname = toFile; std::string soname_nopath = fromName; - soname += "."; - soname += lib_soversion; - soname_nopath += "."; - soname_nopath += lib_soversion; - - fromName += "."; - fromName += lib_version; - toFile += "."; - toFile += lib_version; + this->ComputeVersionedName(soname, lib_soversion); + this->ComputeVersionedName(soname_nopath, lib_soversion); + this->ComputeVersionedName(fromName, lib_version); + this->ComputeVersionedName(toFile, lib_version); cmSystemTools::RemoveFile(soname.c_str()); cmSystemTools::RemoveFile(libname.c_str()); @@ -946,6 +946,26 @@ bool cmFileCommand::HandleInstallCommand( } //---------------------------------------------------------------------------- +void cmFileCommand::ComputeVersionedName(std::string& name, + const char* version) +{ +#if defined(__APPLE__) + std::string ext; + kwsys_stl::string::size_type dot_pos = name.rfind("."); + if(dot_pos != name.npos) + { + ext = name.substr(dot_pos, name.npos); + name = name.substr(0, dot_pos); + } +#endif + name += "."; + name += version; +#if defined(__APPLE__) + name += ext; +#endif +} + +//---------------------------------------------------------------------------- bool cmFileCommand::HandleRelativePathCommand( std::vector<std::string> const& args) { diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index 249cbb6..610fb7d 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -125,6 +125,7 @@ protected: bool HandleRelativePathCommand(std::vector<std::string> const& args); bool HandleCMakePathCommand(std::vector<std::string> const& args, bool nativePath); + void ComputeVersionedName(std::string& name, const char* version); }; diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index fe53b12..b7f388b 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -26,13 +26,17 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) return false; } - // at end of for each execute recorded commands - if (cmSystemTools::LowerCase(lff.Name) == "endforeach") + if (!cmSystemTools::Strucmp(lff.Name.c_str(),"foreach")) { - std::vector<std::string> expandedArguments; - mf.ExpandArguments(lff.Arguments, expandedArguments); - if(!expandedArguments.empty() && (expandedArguments[0] == this->Args[0])) + // record the number of nested foreach commands + this->Depth++; + } + else if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endforeach")) + { + // if this is the endofreach for this statement + if (!this->Depth) { + // at end of for each execute recorded commands // store the old value std::string oldDef; if (mf.GetDefinition(this->Args[0].c_str())) @@ -60,6 +64,11 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) mf.RemoveFunctionBlocker(lff); return true; } + else + { + // close out a nested foreach + this->Depth--; + } } // record the command @@ -72,11 +81,13 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) bool cmForEachFunctionBlocker:: ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) { - if(cmSystemTools::LowerCase(lff.Name) == "endforeach") + if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endforeach")) { std::vector<std::string> expandedArguments; mf.ExpandArguments(lff.Arguments, expandedArguments); - if(!expandedArguments.empty() && (expandedArguments[0] == this->Args[0])) + if ((!expandedArguments.empty() && + (expandedArguments[0] == this->Args[0])) + || mf.IsOn("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS")) { return true; } diff --git a/Source/cmForEachCommand.h b/Source/cmForEachCommand.h index 5a30359..04843c4 100644 --- a/Source/cmForEachCommand.h +++ b/Source/cmForEachCommand.h @@ -29,7 +29,7 @@ class cmForEachFunctionBlocker : public cmFunctionBlocker { public: - cmForEachFunctionBlocker() {this->Executing = false;} + cmForEachFunctionBlocker() {this->Executing = false; Depth = 0;} virtual ~cmForEachFunctionBlocker() {} virtual bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf); @@ -39,6 +39,8 @@ public: std::vector<std::string> Args; std::vector<cmListFileFunction> Functions; bool Executing; +private: + int Depth; }; /** \class cmForEachCommand diff --git a/Source/cmGlobalBorlandMakefileGenerator.cxx b/Source/cmGlobalBorlandMakefileGenerator.cxx index 5018a8c..1ce506d 100644 --- a/Source/cmGlobalBorlandMakefileGenerator.cxx +++ b/Source/cmGlobalBorlandMakefileGenerator.cxx @@ -25,6 +25,7 @@ cmGlobalBorlandMakefileGenerator::cmGlobalBorlandMakefileGenerator() this->FindMakeProgramFile = "CMakeBorlandFindMake.cmake"; this->ForceUnixPaths = false; this->ToolSupportsColor = true; + this->UseLinkScript = false; } diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index ca1c126..42e404f 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -36,6 +36,12 @@ cmGlobalGenerator::cmGlobalGenerator() // By default do not try to support color. this->ToolSupportsColor = false; + + // By default do not use link scripts. + this->UseLinkScript = false; + + // Relative paths are not configured in the constructor. + this->RelativePathsConfigured = false; } cmGlobalGenerator::~cmGlobalGenerator() @@ -175,7 +181,7 @@ cmGlobalGenerator::EnableLanguage(std::vector<std::string>const& languages, } mf->AddDefinition("RUN_CONFIGURE", true); std::string rootBin = mf->GetHomeOutputDirectory(); - rootBin += "/CMakeFiles"; + rootBin += cmake::GetCMakeFilesDirectory(); // If the configuration files path has been set, // then we are in a try compile and need to copy the enable language @@ -589,8 +595,6 @@ void cmGlobalGenerator::Configure() } this->LocalGenerators.clear(); - // Setup relative path generation. - this->ConfigureRelativePaths(); this->TotalTargets.clear(); // start with this directory @@ -772,13 +776,13 @@ int cmGlobalGenerator::TryCompile(const char *srcdir, const char *bindir, const char* config = mf->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); return this->Build(srcdir,bindir,projectName, newTarget.c_str(), - output,makeCommand.c_str(),config,false); + output,makeCommand.c_str(),config,false,true); } std::string cmGlobalGenerator ::GenerateBuildCommand(const char* makeProgram, const char *projectName, const char* additionalOptions, const char *targetName, - const char* config, bool ignoreErrors) + const char* config, bool ignoreErrors, bool) { // Project name and config are not used yet. (void)projectName; @@ -816,7 +820,7 @@ int cmGlobalGenerator::Build( std::string *output, const char *makeCommandCSTR, const char *config, - bool clean) + bool clean, bool fast) { *output += "\nTesting TryCompileWithoutMakefile\n"; @@ -836,7 +840,7 @@ int cmGlobalGenerator::Build( { std::string cleanCommand = this->GenerateBuildCommand(makeCommandCSTR, projectName, - 0, "clean", config, false); + 0, "clean", config, false, fast); if (!cmSystemTools::RunSingleCommand(cleanCommand.c_str(), output, &retVal, 0, false, timeout)) { @@ -856,7 +860,7 @@ int cmGlobalGenerator::Build( // now build std::string makeCommand = this->GenerateBuildCommand(makeCommandCSTR, projectName, - 0, target, config, false); + 0, target, config, false, fast); if (!cmSystemTools::RunSingleCommand(makeCommand.c_str(), output, &retVal, 0, false, timeout)) @@ -932,7 +936,7 @@ cmLocalGenerator *cmGlobalGenerator::CreateLocalGenerator() void cmGlobalGenerator::EnableLanguagesFromGenerator(cmGlobalGenerator *gen ) { std::string cfp = gen->GetCMakeInstance()->GetHomeOutputDirectory(); - cfp += "/CMakeFiles"; + cfp += cmake::GetCMakeFilesDirectory(); this->SetConfiguredFilesPath(cfp.c_str()); const char* make = gen->GetCMakeInstance()->GetCacheDefinition("CMAKE_MAKE_PROGRAM"); @@ -1097,6 +1101,13 @@ std::string cmGlobalGenerator return in_remote; } + // Make sure relative path conversion is configured. + if(!this->RelativePathsConfigured) + { + this->ConfigureRelativePaths(); + this->RelativePathsConfigured = true; + } + std::string original = in_remote; // Skip conversion if the path and local are not both in the source or both @@ -1219,6 +1230,12 @@ inline std::string removeQuotes(const std::string& s) return s; } +void cmGlobalGenerator::SetCMakeInstance(cmake* cm) +{ + // Store a pointer to the cmake object instance. + this->CMakeInstance = cm; +} + void cmGlobalGenerator::SetupTests() { std::string ctest = this->LocalGenerators[0]->GetMakefile()-> diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 1134c2a..07ee8d8 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -100,16 +100,16 @@ public: const char *projectName, const char *targetName, std::string *output, const char *makeProgram, const char *config, - bool clean); + bool clean, bool fast); virtual std::string GenerateBuildCommand (const char* makeProgram, const char *projectName, const char* additionalOptions, const char *targetName, - const char* config, bool ignoreErrors); + const char* config, bool ignoreErrors, bool fast); ///! Set the CMake instance - void SetCMakeInstance(cmake *cm) { this->CMakeInstance = cm; }; + void SetCMakeInstance(cmake *cm); ///! Get the CMake instance cmake *GetCMakeInstance() { return this->CMakeInstance; }; @@ -152,6 +152,9 @@ public: std::string ConvertToRelativePath(const std::vector<std::string>& local, const char* remote); + /** Get whether the generator should use a script for link commands. */ + bool GetUseLinkScript() { return this->UseLinkScript; } + /* * Determine what program to use for building the project. */ @@ -197,6 +200,7 @@ protected: bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen); void ConfigureRelativePaths(); + bool RelativePathsConfigured; void SetupTests(); void CreateDefaultGlobalTargets(cmTargets* targets); @@ -204,6 +208,7 @@ protected: const cmCustomCommandLines* commandLines, std::vector<std::string> depends, bool depends_on_all = false); + bool UseLinkScript; bool ForceUnixPaths; bool ToolSupportsColor; cmStdString FindMakeProgramFile; diff --git a/Source/cmGlobalKdevelopGenerator.cxx b/Source/cmGlobalKdevelopGenerator.cxx index 244e031..ca90216 100644 --- a/Source/cmGlobalKdevelopGenerator.cxx +++ b/Source/cmGlobalKdevelopGenerator.cxx @@ -134,7 +134,9 @@ bool cmGlobalKdevelopGenerator tmp=*lt; cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); // make sure the file is part of this source tree - if ((tmp[0]!='/') && (strstr(tmp.c_str(), "CMakeFiles/")==0)) + if ((tmp[0]!='/') && + (strstr(tmp.c_str(), + cmake::GetCMakeFilesDirectoryPostSlash())==0)) { files.insert(tmp); tmp=cmSystemTools::GetFilenameName(tmp); @@ -159,7 +161,9 @@ bool cmGlobalKdevelopGenerator { tmp=(*si)->GetFullPath(); cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); - if ((tmp[0]!='/') && (strstr(tmp.c_str(), "CMakeFiles/")==0)) + if ((tmp[0]!='/') && + (strstr(tmp.c_str(), + cmake::GetCMakeFilesDirectoryPostSlash())==0)) { files.insert(tmp); } @@ -169,7 +173,9 @@ bool cmGlobalKdevelopGenerator { tmp=*lt; cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); - if ((tmp[0]!='/') && (strstr(tmp.c_str(), "CMakeFiles/")==0)) + if ((tmp[0]!='/') && + (strstr(tmp.c_str(), + cmake::GetCMakeFilesDirectoryPostSlash())==0)) { files.insert(tmp.c_str()); } diff --git a/Source/cmGlobalMSYSMakefileGenerator.cxx b/Source/cmGlobalMSYSMakefileGenerator.cxx index ca1a292..53ea649 100644 --- a/Source/cmGlobalMSYSMakefileGenerator.cxx +++ b/Source/cmGlobalMSYSMakefileGenerator.cxx @@ -24,6 +24,7 @@ cmGlobalMSYSMakefileGenerator::cmGlobalMSYSMakefileGenerator() this->FindMakeProgramFile = "CMakeMSYSFindMake.cmake"; this->ForceUnixPaths = true; this->ToolSupportsColor = true; + this->UseLinkScript = false; } std::string diff --git a/Source/cmGlobalMinGWMakefileGenerator.cxx b/Source/cmGlobalMinGWMakefileGenerator.cxx index 7e9ad82..f66134c 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.cxx +++ b/Source/cmGlobalMinGWMakefileGenerator.cxx @@ -23,6 +23,7 @@ cmGlobalMinGWMakefileGenerator::cmGlobalMinGWMakefileGenerator() this->FindMakeProgramFile = "CMakeMinGWFindMake.cmake"; this->ForceUnixPaths = true; this->ToolSupportsColor = true; + this->UseLinkScript = true; } void cmGlobalMinGWMakefileGenerator diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx index bfb09db..35a2a86 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.cxx +++ b/Source/cmGlobalNMakeMakefileGenerator.cxx @@ -23,6 +23,7 @@ cmGlobalNMakeMakefileGenerator::cmGlobalNMakeMakefileGenerator() this->FindMakeProgramFile = "CMakeNMakeFindMake.cmake"; this->ForceUnixPaths = false; this->ToolSupportsColor = true; + this->UseLinkScript = false; } void cmGlobalNMakeMakefileGenerator diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index 7c506ea..df34fa3 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -20,6 +20,8 @@ #include "cmMakefile.h" #include "cmake.h" #include "cmGeneratedFileStream.h" +#include "cmSourceFile.h" +#include "cmTarget.h" cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3() { @@ -27,6 +29,13 @@ cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3() this->ForceUnixPaths = true; this->FindMakeProgramFile = "CMakeUnixFindMake.cmake"; this->ToolSupportsColor = true; + this->NumberOfSourceFiles = 0; + this->NumberOfSourceFilesWritten = 0; +#ifdef _WIN32 + this->UseLinkScript = false; +#else + this->UseLinkScript = true; +#endif } void cmGlobalUnixMakefileGenerator3 @@ -110,8 +119,83 @@ cmGlobalUnixMakefileGenerator3 } //---------------------------------------------------------------------------- +int cmGlobalUnixMakefileGenerator3::ShouldAddProgressRule() +{ + // add progress to 100 source files + if (this->NumberOfSourceFiles && + (((this->NumberOfSourceFilesWritten + 1)*100)/this->NumberOfSourceFiles) + -(this->NumberOfSourceFilesWritten*100)/this->NumberOfSourceFiles) + { + this->NumberOfSourceFilesWritten++; + return (this->NumberOfSourceFilesWritten*100)/this->NumberOfSourceFiles; + } + this->NumberOfSourceFilesWritten++; + return 0; +} + +int cmGlobalUnixMakefileGenerator3:: +GetNumberOfCompilableSourceFilesForTarget(cmTarget &tgt) +{ + std::map<cmStdString, int >::iterator tgtI = + this->TargetSourceFileCount.find(tgt.GetName()); + if (tgtI != this->TargetSourceFileCount.end()) + { + return tgtI->second; + } + + int result = 0; + + if((tgt.GetType() == cmTarget::EXECUTABLE) || + (tgt.GetType() == cmTarget::STATIC_LIBRARY) || + (tgt.GetType() == cmTarget::SHARED_LIBRARY) || + (tgt.GetType() == cmTarget::MODULE_LIBRARY) ) + { + std::vector<cmSourceFile*>& sources = tgt.GetSourceFiles(); + for(std::vector<cmSourceFile*>::iterator source = sources.begin(); + source != sources.end(); ++source) + { + if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY") && + !(*source)->GetCustomCommand()) + { + if(!this->IgnoreFile((*source)->GetSourceExtension().c_str())) + { + const char* lang = + static_cast<cmLocalUnixMakefileGenerator3 *> + (tgt.GetMakefile()->GetLocalGenerator()) + ->GetSourceFileLanguage(**source); + if(lang) + { + result++; + } + } + } + } + } + this->TargetSourceFileCount[tgt.GetName()] = result; + return result; +} + + +//---------------------------------------------------------------------------- void cmGlobalUnixMakefileGenerator3::Generate() { + // initialize progress + this->NumberOfSourceFiles = 0; + unsigned int i; + for (i = 0; i < this->LocalGenerators.size(); ++i) + { + // for all of out targets + for (cmTargets::iterator l = + this->LocalGenerators[i]->GetMakefile()->GetTargets().begin(); + l != this->LocalGenerators[i]->GetMakefile()->GetTargets().end(); + l++) + { + this->NumberOfSourceFiles += + this->GetNumberOfCompilableSourceFilesForTarget(l->second); + } + } + this->NumberOfSourceFilesWritten = 0; + // first do superclass method this->cmGlobalGenerator::Generate(); @@ -127,7 +211,8 @@ void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2() // see if the build system must be regenerated. std::string makefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); - makefileName += "/CMakeFiles/Makefile2"; + makefileName += cmake::GetCMakeFilesDirectory(); + makefileName += "/Makefile2"; cmGeneratedFileStream makefileStream(makefileName.c_str()); if(!makefileStream) { @@ -214,7 +299,8 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile() // see if the build system must be regenerated. std::string cmakefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); - cmakefileName += "/CMakeFiles/Makefile.cmake"; + cmakefileName += cmake::GetCMakeFilesDirectory(); + cmakefileName += "/Makefile.cmake"; cmGeneratedFileStream cmakefileStream(cmakefileName.c_str()); if(!cmakefileStream) { @@ -281,7 +367,8 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile() // Build the path to the cache check file. std::string check = this->GetCMakeInstance()->GetHomeOutputDirectory(); - check += "/CMakeFiles/cmake.check_cache"; + check += cmake::GetCMakeFilesDirectory(); + check += "/cmake.check_cache"; // Set the corresponding makefile in the cmake file. cmakefileStream @@ -301,7 +388,8 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile() lg = static_cast<cmLocalUnixMakefileGenerator3 *>(this->LocalGenerators[i]); tmpStr = lg->GetMakefile()->GetStartOutputDirectory(); - tmpStr += "/CMakeFiles/CMakeDirectoryInformation.cmake"; + tmpStr += cmake::GetCMakeFilesDirectory(); + tmpStr += "/CMakeDirectoryInformation.cmake"; cmakefileStream << " \"" << lg->Convert(tmpStr.c_str(),cmLocalGenerator::HOME_OUTPUT).c_str() << "\"\n"; @@ -518,7 +606,7 @@ cmGlobalUnixMakefileGenerator3 std::string cmGlobalUnixMakefileGenerator3 ::GenerateBuildCommand(const char* makeProgram, const char *projectName, const char* additionalOptions, const char *targetName, - const char* config, bool ignoreErrors) + const char* config, bool ignoreErrors, bool fast) { // Project name and config are not used yet. (void)projectName; @@ -565,7 +653,10 @@ std::string cmGlobalUnixMakefileGenerator3 lg->SetupPathConversions(); makeCommand += " \""; std::string tname = targetName; + if(fast) + { tname += "/fast"; + } tname = lg->Convert(tname.c_str(),cmLocalGenerator::HOME_OUTPUT, cmLocalGenerator::MAKEFILE); tname = lg->ConvertToMakeTarget(tname.c_str()); @@ -601,18 +692,20 @@ cmGlobalUnixMakefileGenerator3 cmTargets& targets = lg->GetMakefile()->GetTargets(); for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t) { - if((t->second.GetType() == cmTarget::EXECUTABLE) || - (t->second.GetType() == cmTarget::STATIC_LIBRARY) || - (t->second.GetType() == cmTarget::SHARED_LIBRARY) || - (t->second.GetType() == cmTarget::MODULE_LIBRARY) || - (t->second.GetType() == cmTarget::UTILITY)) - { // Don't emit the same rule twice (e.g. two targets with the same // simple name) if(t->second.GetName() && strlen(t->second.GetName()) && emitted.insert(t->second.GetName()).second) { + // Handle user targets here. Global targets are handled in + // the local generator on a per-directory basis. + if((t->second.GetType() == cmTarget::EXECUTABLE) || + (t->second.GetType() == cmTarget::STATIC_LIBRARY) || + (t->second.GetType() == cmTarget::SHARED_LIBRARY) || + (t->second.GetType() == cmTarget::MODULE_LIBRARY) || + (t->second.GetType() == cmTarget::UTILITY)) + { // Add a rule to build the target by name. lg->WriteDivider(ruleFileStream); ruleFileStream @@ -621,8 +714,10 @@ cmGlobalUnixMakefileGenerator3 // Write the rule. commands.clear(); + std::string tmp = cmake::GetCMakeFilesDirectoryPostSlash(); + tmp += "Makefile2"; commands.push_back(lg->GetRecursiveMakeCall - ("CMakeFiles/Makefile2",t->second.GetName())); + (tmp.c_str(),t->second.GetName())); depends.clear(); depends.push_back("cmake_check_build_system"); lg->WriteMakeRule(ruleFileStream, @@ -647,17 +742,6 @@ cmGlobalUnixMakefileGenerator3 localName.c_str(), depends, commands, true); } } - else - { - // Add a fast rule to build the target - depends.clear(); - commands.clear(); - std::string localName = t->second.GetName(); - depends.push_back(localName); - localName += "/fast"; - lg->WriteMakeRule(ruleFileStream, "fast build rule for target.", - localName.c_str(), depends, commands, true); - } } } } @@ -675,6 +759,7 @@ cmGlobalUnixMakefileGenerator3 std::string localName; std::string makeTargetName; + // write the directory level rules for this local gen this->WriteDirectoryRules2(ruleFileStream,lg); @@ -731,6 +816,30 @@ cmGlobalUnixMakefileGenerator3 // Write the rule. localName += "/all"; depends.clear(); + + std::string progressDir = + lg->GetMakefile()->GetHomeOutputDirectory(); + progressDir += cmake::GetCMakeFilesDirectory(); + { + cmOStringStream progCmd; + progCmd << "$(CMAKE_COMMAND) -E cmake_progress_report "; + // all target counts + progCmd << lg->Convert(progressDir.c_str(), + cmLocalGenerator::FULL, + cmLocalGenerator::SHELL); + progCmd << " "; + std::vector<int> &progFiles = lg->ProgressFiles[t->first]; + for (std::vector<int>::iterator i = progFiles.begin(); + i != progFiles.end(); ++i) + { + progCmd << " " << *i; + } + commands.push_back(progCmd.str()); + } + progressDir = "Building target "; + progressDir += t->first; + lg->AppendEcho(commands,progressDir.c_str()); + this->AppendGlobalTargetDepends(depends,t->second); lg->WriteMakeRule(ruleFileStream, "All Build rule for target.", localName.c_str(), depends, commands, true); @@ -747,8 +856,35 @@ cmGlobalUnixMakefileGenerator3 // Write the rule. commands.clear(); + progressDir = lg->GetMakefile()->GetHomeOutputDirectory(); + progressDir += cmake::GetCMakeFilesDirectory(); + + { + // TODO: Convert the total progress count to a make variable. + cmOStringStream progCmd; + progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start "; + // # in target + progCmd << lg->Convert(progressDir.c_str(), + cmLocalGenerator::FULL, + cmLocalGenerator::SHELL); + // + progCmd << " " + << this->GetTargetTotalNumberOfProgressFiles(t->second); + commands.push_back(progCmd.str()); + } + std::string tmp = cmake::GetCMakeFilesDirectoryPostSlash(); + tmp += "Makefile2"; commands.push_back(lg->GetRecursiveMakeCall - ("CMakeFiles/Makefile2",localName.c_str())); + (tmp.c_str(),localName.c_str())); + { + cmOStringStream progCmd; + progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start "; // # 0 + progCmd << lg->Convert(progressDir.c_str(), + cmLocalGenerator::FULL, + cmLocalGenerator::SHELL); + progCmd << " 0"; + commands.push_back(progCmd.str()); + } depends.clear(); depends.push_back("cmake_check_build_system"); localName = lg->GetRelativeTargetDirectory(t->second); @@ -773,7 +909,6 @@ cmGlobalUnixMakefileGenerator3 commands.clear(); commands.push_back(lg->GetRecursiveMakeCall (makefileName.c_str(), localName.c_str())); - this->AppendGlobalTargetDepends(depends,t->second); lg->WriteMakeRule(ruleFileStream, "Pre-install relink rule for target.", localName.c_str(), depends, commands, true); @@ -803,6 +938,101 @@ cmGlobalUnixMakefileGenerator3 } } +//---------------------------------------------------------------------------- +int cmGlobalUnixMakefileGenerator3 +::GetTargetTotalNumberOfProgressFiles(cmTarget& target) +{ + cmLocalUnixMakefileGenerator3 *lg = + static_cast<cmLocalUnixMakefileGenerator3 *> + (target.GetMakefile()->GetLocalGenerator()); + int result = static_cast<int>(lg->ProgressFiles[target.GetName()].size()); + std::vector<cmTarget *>& depends = this->GetTargetDepends(target); + + std::vector<cmTarget *>::iterator i; + for (i = depends.begin(); i != depends.end(); ++i) + { + result += this->GetTargetTotalNumberOfProgressFiles(**i); + } + + return result; +} + + +//---------------------------------------------------------------------------- +std::vector<cmTarget *>& cmGlobalUnixMakefileGenerator3 +::GetTargetDepends(cmTarget& target) +{ + // if the depends are already in the map then return + std::map<cmStdString, std::vector<cmTarget *> >::iterator tgtI = + this->TargetDependencies.find(target.GetName()); + if (tgtI != this->TargetDependencies.end()) + { + return tgtI->second; + } + + // A target should not depend on itself. + std::set<cmStdString> emitted; + emitted.insert(target.GetName()); + + // the vector of results + std::vector<cmTarget *>& result = + this->TargetDependencies[target.GetName()]; + + // Loop over all library dependencies but not for static libs + if (target.GetType() != cmTarget::STATIC_LIBRARY) + { + const cmTarget::LinkLibraryVectorType& tlibs = target.GetLinkLibraries(); + for(cmTarget::LinkLibraryVectorType::const_iterator lib = tlibs.begin(); + lib != tlibs.end(); ++lib) + { + // Don't emit the same library twice for this target. + if(emitted.insert(lib->first).second) + { + cmTarget *target2 = + target.GetMakefile()->FindTarget(lib->first.c_str()); + + // search each local generator until a match is found + if (!target2) + { + target2 = this->FindTarget(0,lib->first.c_str()); + } + + // if a match was found then ... + if (target2) + { + // Add this dependency. + result.push_back(target2); + } + } + } + } + + // Loop over all utility dependencies. + const std::set<cmStdString>& tutils = target.GetUtilities(); + for(std::set<cmStdString>::const_iterator util = tutils.begin(); + util != tutils.end(); ++util) + { + // Don't emit the same utility twice for this target. + if(emitted.insert(*util).second) + { + cmTarget *target2 = target.GetMakefile()->FindTarget(util->c_str()); + + // search each local generator until a match is found + if (!target2) + { + target2 = this->FindTarget(0,util->c_str()); + } + + // if a match was found then ... + if (target2) + { + // Add this dependency. + result.push_back(target2); + } + } + } + return result; +} //---------------------------------------------------------------------------- void diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index 1b7733d..47557b9 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -121,7 +121,16 @@ public: (const char* makeProgram, const char *projectName, const char* additionalOptions, const char *targetName, - const char* config, bool ignoreErrors); + const char* config, bool ignoreErrors, bool fast); + + // returns true if a progress rule should be added + int ShouldAddProgressRule(); + int GetNumberOfCompilableSourceFilesForTarget(cmTarget &tgt); + int GetTargetTotalNumberOfProgressFiles(cmTarget& target); + int GetNumberOfSourceFiles() { return this->NumberOfSourceFiles; }; + + // what targets does the specified target depend on + std::vector<cmTarget *>& GetTargetDepends(cmTarget& target); protected: void WriteMainMakefile2(); @@ -173,6 +182,12 @@ protected: typedef std::map<cmStdString, cmStdString> MultipleOutputPairsType; MultipleOutputPairsType MultipleOutputPairs; + + int NumberOfSourceFiles; + int NumberOfSourceFilesWritten; + + std::map<cmStdString, std::vector<cmTarget *> > TargetDependencies; + std::map<cmStdString, int > TargetSourceFileCount; }; #endif diff --git a/Source/cmGlobalVisualStudio6Generator.cxx b/Source/cmGlobalVisualStudio6Generator.cxx index f076efb..d2644d0 100644 --- a/Source/cmGlobalVisualStudio6Generator.cxx +++ b/Source/cmGlobalVisualStudio6Generator.cxx @@ -73,7 +73,8 @@ std::string cmGlobalVisualStudio6Generator const char* additionalOptions, const char *targetName, const char* config, - bool ignoreErrors) + bool ignoreErrors, + bool) { // Ingoring errors is not implemented in visual studio 6 (void) ignoreErrors; diff --git a/Source/cmGlobalVisualStudio6Generator.h b/Source/cmGlobalVisualStudio6Generator.h index 07456a2..792e7b1 100644 --- a/Source/cmGlobalVisualStudio6Generator.h +++ b/Source/cmGlobalVisualStudio6Generator.h @@ -60,7 +60,8 @@ public: const char* additionalOptions, const char *targetName, const char* config, - bool ignoreErrors); + bool ignoreErrors, + bool fast); /** * Generate the all required files for building this project/tree. This diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 791daea..7bc0654 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -71,7 +71,7 @@ std::string cmGlobalVisualStudio7Generator ::GenerateBuildCommand(const char* makeProgram, const char *projectName, const char* additionalOptions, const char *targetName, - const char* config, bool ignoreErrors) + const char* config, bool ignoreErrors, bool) { // Ingoring errors is not implemented in visual studio 6 (void) ignoreErrors; diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 5b14449..9ff97da 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -60,7 +60,8 @@ public: const char* additionalOptions, const char *targetName, const char* config, - bool ignoreErrors); + bool ignoreErrors, + bool fast); /** * Generate the all required files for building this project/tree. This diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index d48723f..c7c50cd 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -133,7 +133,8 @@ std::string cmGlobalXCodeGenerator const char* additionalOptions, const char *targetName, const char* config, - bool ignoreErrors) + bool ignoreErrors, + bool) { // Config is not used yet (void) ignoreErrors; @@ -365,7 +366,8 @@ void cmGlobalXCodeGenerator::CreateReRunCMakeFile(cmLocalGenerator* root) (this->CurrentReRunCMakeMakefile.c_str()); makefileStream.SetCopyIfDifferent(true); makefileStream << "# Generated by CMake, DO NOT EDIT\n"; - makefileStream << "CMakeFiles/cmake.check_cache: "; + makefileStream << cmake::GetCMakeFilesDirectoryPostSlash(); + makefileStream << "cmake.check_cache: "; for(std::vector<std::string>::const_iterator i = lfiles.begin(); i != lfiles.end(); ++i) { @@ -946,8 +948,9 @@ cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase, } else { - char c = '1' + count++; - tname[&cc] = std::string(target.GetName()) + c; + cmOStringStream str; + str << "_buildpart_" << count++ ; + tname[&cc] = std::string(target.GetName()) + str.str(); makefileStream << "\\\n\t" << tname[&cc]; } } diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 326bfd0..991a223 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -63,7 +63,8 @@ public: const char* additionalOptions, const char *targetName, const char* config, - bool ignoreErrors); + bool ignoreErrors, + bool fast); /** * Generate the all required files for building this project/tree. This diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 4e7c777..5ee5d90 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -22,23 +22,19 @@ bool cmIfFunctionBlocker:: IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) { - const char* name = lff.Name.c_str(); - const std::vector<cmListFileArgument>& args = lff.Arguments; // always let if statements through - if (cmSystemTools::LowerCase(lff.Name) == "if") + if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if")) { return false; } // watch for our ELSE or ENDIF - if (cmSystemTools::LowerCase(lff.Name) == "else" || - cmSystemTools::LowerCase(lff.Name) == "endif") - { - if (args == this->Args) + if (!cmSystemTools::Strucmp(lff.Name.c_str(),"else") || + !cmSystemTools::Strucmp(lff.Name.c_str(),"endif")) { // if it was an else statement then we should change state // and block this Else Command - if (cmSystemTools::LowerCase(lff.Name) == "else") + if (!cmSystemTools::Strucmp(lff.Name.c_str(),"else")) { this->IsBlocking = !this->IsBlocking; return true; @@ -48,39 +44,22 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) mf.RemoveFunctionBlocker(lff); return true; } - else if(args.empty()) - { - std::string err = "Empty arguments for "; - err += name; - err += ". Did you mean "; - err += name; - err += "( "; - for(std::vector<cmListFileArgument>::const_iterator a = - this->Args.begin(); - a != this->Args.end();++a) - { - err += (a->Quoted?"\"":""); - err += a->Value; - err += (a->Quoted?"\"":""); - err += " "; - } - err += ")?"; - cmSystemTools::Error(err.c_str()); - } - } + return this->IsBlocking; } bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, - cmMakefile&) + cmMakefile& mf) { - if (cmSystemTools::LowerCase(lff.Name) == "endif") + if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif")) { - if (lff.Arguments == this->Args) + if (mf.IsOn("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS") + || lff.Arguments == this->Args) { return true; } } + return false; } diff --git a/Source/cmIfCommand.h b/Source/cmIfCommand.h index a8a7b81..e49c21f 100644 --- a/Source/cmIfCommand.h +++ b/Source/cmIfCommand.h @@ -123,12 +123,14 @@ public: " IF(variable1 OR variable2)\n" "True if either variable would be considered true individually.\n" " IF(COMMAND command-name)\n" - "True if the given name is a file or directory.\n" + "True if the given name is a command that can be invoked.\n" " IF(EXISTS file-name)\n" " IF(EXISTS directory-name)\n" - "True if the given name is a directory.\n" + "True if the named file or directory exists. " + "Behavior is well-defined only for full paths.\n" " IF(IS_DIRECTORY directory-name)\n" - "True if the named file or directory exists.\n" + "True if the given name is a directory. " + "Behavior is well-defined only for full paths.\n" " IF(variable MATCHES regex)\n" " IF(string MATCHES regex)\n" "True if the given string or variable's value matches the given " diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index b7be4cb..860ecad 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -20,6 +20,7 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmTarget.h" +#include "cmake.h" //---------------------------------------------------------------------------- cmInstallTargetGenerator @@ -48,7 +49,8 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os) if(this->Target->NeedRelinkBeforeInstall()) { fromDir = this->Target->GetMakefile()->GetStartOutputDirectory(); - fromDir += "/CMakeFiles/CMakeRelink.dir/"; + fromDir += cmake::GetCMakeFilesDirectory(); + fromDir += "/CMakeRelink.dir/"; } else { @@ -361,7 +363,7 @@ void cmInstallTargetGenerator { os << "\n -change \"" << i->first << "\" \"" << i->second << "\""; } - os << "\n \"" << destination << "/" + os << "\n \"$ENV{DESTDIR}" << destination << "/" << this->GetScriptReference(this->Target, "REMAPPED", true) << "\")\n"; os << "ENDIF(" << component_test << ")\n"; } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 8021765..b9675f9 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -52,7 +52,7 @@ void cmLocalGenerator::Configure() { // make sure the CMakeFiles dir is there std::string filesDir = this->Makefile->GetStartOutputDirectory(); - filesDir += "/CMakeFiles"; + filesDir += cmake::GetCMakeFilesDirectory(); cmSystemTools::MakeDirectory(filesDir.c_str()); // find & read the list file @@ -1418,6 +1418,9 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout, outputRuntime && tgt.HaveInstallTreeRPATH() && linking_for_install; bool use_build_rpath = outputRuntime && tgt.HaveBuildTreeRPATH() && !linking_for_install; + bool use_link_rpath = + outputRuntime && linking_for_install && + tgt.GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH"); // Construct the RPATH. std::vector<std::string> runtimeDirs; @@ -1454,13 +1457,29 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout, && libDir->find("${") == std::string::npos) { linkLibs += libPathFlag; + linkLibs += fullLibPath; + linkLibs += " "; + + // Put this directory in the rpath if using build-tree rpath + // support or if using the link path as an rpath. if(use_build_rpath) { - runtimeDirs.push_back( fullLibPath ); + runtimeDirs.push_back(fullLibPath); + } + else if(use_link_rpath) + { + // Do not add any path inside the source or build tree. + const char* topSourceDir = this->Makefile->GetHomeDirectory(); + const char* topBinaryDir = this->Makefile->GetHomeOutputDirectory(); + if(!cmSystemTools::ComparePath(libDir->c_str(), topSourceDir) && + !cmSystemTools::ComparePath(libDir->c_str(), topBinaryDir) && + !cmSystemTools::IsSubDirectory(libDir->c_str(), topSourceDir) && + !cmSystemTools::IsSubDirectory(libDir->c_str(), topBinaryDir)) + { + runtimeDirs.push_back(fullLibPath); + } } } - linkLibs += fullLibPath; - linkLibs += " "; } } diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index ccc9bef..6e4c60e 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -160,8 +160,7 @@ public: bool /* clear */) {}; /** Called from command-line hook to scan dependencies. */ - virtual bool ScanDependencies(std::vector<std::string> const& /* args */) - {return true;}; + virtual bool ScanDependencies(const char* /* tgtInfo */) { return true; } /** Compute the list of link libraries and directories for the given target and configuration. */ diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 95bf9aa..8f72567 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -48,6 +48,7 @@ cmLocalUnixMakefileGenerator3::cmLocalUnixMakefileGenerator3() this->DefineWindowsNULL = false; this->UnixCD = true; this->ForceVerboseMakefiles=false; + this->ColorMakefile = false; } //---------------------------------------------------------------------------- @@ -75,6 +76,10 @@ void cmLocalUnixMakefileGenerator3::Generate() // Setup our configuration variables for this directory. this->ConfigureOutputPaths(); + // Record whether color makefiles are enabled to avoid checking many + // times later. + this->ColorMakefile = this->Makefile->IsOn("CMAKE_COLOR_MAKEFILE"); + // Generate the rule files for each target. cmTargets& targets = this->Makefile->GetTargets(); std::string empty; @@ -275,8 +280,10 @@ void cmLocalUnixMakefileGenerator3 depends.clear(); // Build the target for this pass. + std::string tmp = cmake::GetCMakeFilesDirectoryPostSlash(); + tmp += "Makefile2"; commands.push_back(this->GetRecursiveMakeCall - ("CMakeFiles/Makefile2",localName.c_str())); + (tmp.c_str(),localName.c_str())); this->CreateCDCommand(commands, this->Makefile->GetHomeOutputDirectory(), this->Makefile->GetStartOutputDirectory()); @@ -291,6 +298,24 @@ void cmLocalUnixMakefileGenerator3 this->WriteMakeRule(ruleFileStream, "Convenience name for target.", t->second.GetName(), depends, commands, true); } + + // Add a fast rule to build the target + std::string makefileName = this->GetRelativeTargetDirectory(t->second); + makefileName += "/build.make"; + std::string makeTargetName = + this->GetRelativeTargetDirectory(t->second); + makeTargetName += "/build"; + localName = t->second.GetName(); + localName += "/fast"; + depends.clear(); + commands.clear(); + commands.push_back(this->GetRecursiveMakeCall + (makefileName.c_str(), makeTargetName.c_str())); + this->CreateCDCommand(commands, + this->Makefile->GetHomeOutputDirectory(), + this->Makefile->GetStartOutputDirectory()); + this->WriteMakeRule(ruleFileStream, "fast build rule for target.", + localName.c_str(), depends, commands, true); } } } @@ -299,7 +324,8 @@ void cmLocalUnixMakefileGenerator3 void cmLocalUnixMakefileGenerator3::WriteDirectoryInformationFile() { std::string infoFileName = this->Makefile->GetStartOutputDirectory(); - infoFileName += "/CMakeFiles/CMakeDirectoryInformation.cmake"; + infoFileName += cmake::GetCMakeFilesDirectory(); + infoFileName += "/CMakeDirectoryInformation.cmake"; // Open the output file. cmGeneratedFileStream infoFileStream(infoFileName.c_str()); @@ -630,7 +656,8 @@ void cmLocalUnixMakefileGenerator3 // the --check-build-system flag. { // Build command to run CMake to check if anything needs regenerating. - std::string cmakefileName = "CMakeFiles/Makefile.cmake"; + std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash(); + cmakefileName += "Makefile.cmake"; std::string runRule = "$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)"; runRule += " --check-build-system "; @@ -761,9 +788,16 @@ cmLocalUnixMakefileGenerator3 ::AppendCustomCommand(std::vector<std::string>& commands, const cmCustomCommand& cc) { - std::vector<std::string> commands1; + // if the command specified a working directory use it. + const char* dir = this->Makefile->GetStartOutputDirectory(); + const char* workingDir = cc.GetWorkingDirectory(); + if(workingDir) + { + dir = workingDir; + } // Add each command line to the set of commands. + std::vector<std::string> commands1; for(cmCustomCommandLines::const_iterator cl = cc.GetCommandLines().begin(); cl != cc.GetCommandLines().end(); ++cl) { @@ -773,7 +807,12 @@ cmLocalUnixMakefileGenerator3 if (cmd.size()) { cmSystemTools::ReplaceString(cmd, "/./", "/"); + // Convert the command to a relative path only if the current + // working directory will be the start-output directory. + if(!workingDir) + { cmd = this->Convert(cmd.c_str(),START_OUTPUT); + } if(cmd.find("/") == cmd.npos && commandLine[0].find("/") != cmd.npos) { @@ -799,16 +838,11 @@ cmLocalUnixMakefileGenerator3 } } - // push back the custom commands - const char* dir = this->Makefile->GetStartOutputDirectory(); - // if the command specified a working directory use it. - if(cc.GetWorkingDirectory()) - { - dir = cc.GetWorkingDirectory(); - } + // Setup the proper working directory for the commands. this->CreateCDCommand(commands1, dir, this->Makefile->GetHomeOutputDirectory()); + // push back the custom commands commands.insert(commands.end(), commands1.begin(), commands1.end()); } @@ -860,8 +894,7 @@ cmLocalUnixMakefileGenerator3::AppendEcho(std::vector<std::string>& commands, // Choose the color for the text. std::string color_name; #ifdef CMAKE_BUILD_WITH_CMAKE - if(this->GlobalGenerator->GetToolSupportsColor() && - this->Makefile->IsOn("CMAKE_COLOR_MAKEFILE")) + if(this->GlobalGenerator->GetToolSupportsColor() && this->ColorMakefile) { // See cmake::ExecuteEchoColor in cmake.cxx for these options. // This color set is readable on both black and white backgrounds. @@ -1127,31 +1160,17 @@ cmLocalUnixMakefileGenerator3 } //---------------------------------------------------------------------------- -bool -cmLocalUnixMakefileGenerator3 -::ScanDependencies(std::vector<std::string> const& args) +bool cmLocalUnixMakefileGenerator3::ScanDependencies(const char* tgtInfo) { - // Format of arguments is: $(CMAKE_COMMAND), cmake_depends, - // GeneratorName, home_output_dir, start_output_dir, info file The - // caller has ensured that all required arguments exist. - // The info file for this target - std::string const& infoFile = args[5]; + std::string const& infoFile = tgtInfo; // Read the directory information file. - cmake cm; - cmGlobalGenerator gg; - gg.SetCMakeInstance(&cm); - std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator()); - lg->SetGlobalGenerator(&gg); - cmMakefile* mf = lg->GetMakefile(); - mf->SetHomeOutputDirectory(args[3].c_str()); - mf->SetStartOutputDirectory(args[4].c_str()); - lg->SetupPathConversions(); - + cmMakefile* mf = this->Makefile; bool haveDirectoryInfo = false; - std::string dirInfoFile = args[4]; - dirInfoFile += "/CMakeFiles/CMakeDirectoryInformation.cmake"; + std::string dirInfoFile = this->Makefile->GetStartOutputDirectory(); + dirInfoFile += cmake::GetCMakeFilesDirectory(); + dirInfoFile += "/CMakeDirectoryInformation.cmake"; if(mf->ReadListFile(0, dirInfoFile.c_str()) && !cmSystemTools::GetErrorOccuredFlag()) { @@ -1283,7 +1302,7 @@ cmLocalUnixMakefileGenerator3 includeRegexScan.c_str(), includeRegexComplain.c_str(), generatedFiles, includeCacheFileName); - scanner->SetHomeOutputDirectory(mf->GetHomeOutputDirectory()); + scanner->SetLocalGenerator(this); } #ifdef CMAKE_BUILD_WITH_CMAKE else if(lang == "Fortran") @@ -1313,7 +1332,7 @@ cmLocalUnixMakefileGenerator3 ++si; // make sure the object file is relative to home output std::string obj = *si; - obj = lg->Convert(obj.c_str(),HOME_OUTPUT,MAKEFILE); + obj = this->Convert(obj.c_str(),HOME_OUTPUT,MAKEFILE); scanner->Write(src.c_str(),obj.c_str(), ruleFileStream, internalRuleFileStream); } @@ -1381,7 +1400,7 @@ void cmLocalUnixMakefileGenerator3 this->AppendEcho(commands, text, cmLocalUnixMakefileGenerator3::EchoGlobal); - // Utility targets store their rules in pre- and post-build commands. + // Global targets store their rules in pre- and post-build commands. this->AppendCustomDepends(depends, glIt->second.GetPreBuildCommands()); this->AppendCustomDepends(depends, @@ -1390,8 +1409,27 @@ void cmLocalUnixMakefileGenerator3 glIt->second.GetPreBuildCommands()); this->AppendCustomCommands(commands, glIt->second.GetPostBuildCommands()); + std::string targetName = glIt->second.GetName(); this->WriteMakeRule(ruleFileStream, targetString.c_str(), - glIt->first.c_str(), depends, commands, true); + targetName.c_str(), depends, commands, true); + + // Provide a "/fast" version of the target. + depends.clear(); + if(targetName == "install") + { + // Provide a fast install target that does not depend on all + // but has the same command. + depends.push_back("preinstall/fast"); + } + else + { + // Just forward to the real target so at least it will work. + depends.push_back(targetName); + commands.clear(); + } + targetName += "/fast"; + this->WriteMakeRule(ruleFileStream, targetString.c_str(), + targetName.c_str(), depends, commands, true); } } @@ -1408,11 +1446,45 @@ void cmLocalUnixMakefileGenerator3 depends.push_back("cmake_check_build_system"); - commands.push_back - (this->GetRecursiveMakeCall("CMakeFiles/Makefile2",dir.c_str())); + std::string progressDir = this->Makefile->GetHomeOutputDirectory(); + progressDir += cmake::GetCMakeFilesDirectory(); + { + cmOStringStream progCmd; + progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start "; // # src files + progCmd << this->Convert(progressDir.c_str(), + cmLocalGenerator::FULL, + cmLocalGenerator::SHELL); + cmGlobalUnixMakefileGenerator3 *gg = + static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator); + int n = gg->GetNumberOfSourceFiles(); + if(n > 100) + { + n = 100; + } + if (this->Parent) + { + n = 0; + } + progCmd << " " << n; + commands.push_back(progCmd.str()); + } + std::string mf2Dir = cmake::GetCMakeFilesDirectoryPostSlash(); + mf2Dir += "Makefile2"; + commands.push_back(this->GetRecursiveMakeCall(mf2Dir.c_str(), + dir.c_str())); this->CreateCDCommand(commands, this->Makefile->GetHomeOutputDirectory(), this->Makefile->GetStartOutputDirectory()); + if (!this->Parent) + { + cmOStringStream progCmd; + progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start "; // # 0 + progCmd << this->Convert(progressDir.c_str(), + cmLocalGenerator::FULL, + cmLocalGenerator::SHELL); + progCmd << " 0"; + commands.push_back(progCmd.str()); + } this->WriteMakeRule(ruleFileStream, "The main all target", "all", depends, commands, true); @@ -1422,8 +1494,8 @@ void cmLocalUnixMakefileGenerator3 dir = this->Convert(dir.c_str(),HOME_OUTPUT,MAKEFILE); commands.clear(); depends.clear(); - commands.push_back - (this->GetRecursiveMakeCall("CMakeFiles/Makefile2",dir.c_str())); + commands.push_back(this->GetRecursiveMakeCall(mf2Dir.c_str(), + dir.c_str())); this->CreateCDCommand(commands, this->Makefile->GetHomeOutputDirectory(), this->Makefile->GetStartOutputDirectory()); @@ -1454,21 +1526,21 @@ void cmLocalUnixMakefileGenerator3 depends.push_back("cmake_check_build_system"); } commands.push_back - (this->GetRecursiveMakeCall("CMakeFiles/Makefile2", dir.c_str())); + (this->GetRecursiveMakeCall(mf2Dir.c_str(), dir.c_str())); this->CreateCDCommand(commands, this->Makefile->GetHomeOutputDirectory(), this->Makefile->GetStartOutputDirectory()); this->WriteMakeRule(ruleFileStream, "Prepare targets for installation.", "preinstall", depends, commands, true); - commands.clear(); - depends.push_back("preinstall"); + depends.clear(); this->WriteMakeRule(ruleFileStream, "Prepare targets for installation.", "preinstall/fast", depends, commands, true); // write the depend rule, really a recompute depends rule depends.clear(); commands.clear(); - std::string cmakefileName = "CMakeFiles/Makefile.cmake"; + std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash(); + cmakefileName += "Makefile.cmake"; this->Convert(cmakefileName.c_str(),HOME_OUTPUT, cmLocalGenerator::MAKEFILE); std::string runRule = @@ -1816,7 +1888,7 @@ cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(const char* p) std::string cmLocalUnixMakefileGenerator3::GetTargetDirectory(cmTarget& target) { - std::string dir = "CMakeFiles/"; + std::string dir = cmake::GetCMakeFilesDirectoryPostSlash(); dir += target.GetName(); dir += ".dir"; return dir; diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index c96ca00..bb72d13 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -178,7 +178,7 @@ public: /** Called from command-line hook to scan dependencies. */ - virtual bool ScanDependencies(std::vector<std::string> const& args); + virtual bool ScanDependencies(const char* tgtInfo); /** Called from command-line hook to check dependencies. */ virtual void CheckDependencies(cmMakefile* mf, bool verbose, @@ -280,11 +280,14 @@ protected: cmTarget& target, const char* filename =0); bool ForceVerboseMakefiles; + std::map<cmStdString, std::vector<int> > ProgressFiles; + private: friend class cmMakefileTargetGenerator; friend class cmMakefileExecutableTargetGenerator; friend class cmMakefileLibraryTargetGenerator; friend class cmMakefileUtilityTargetGenerator; + friend class cmGlobalUnixMakefileGenerator3; std::map<cmStdString, IntegrityCheckSetMap> CheckDependFiles; @@ -306,6 +309,10 @@ private: std::string HomeRelativeOutputPath; + /* Copy the setting of CMAKE_COLOR_MAKEFILE from the makefile at the + beginning of generation to avoid many duplicate lookups. */ + bool ColorMakefile; + std::map<cmStdString,std::vector<cmTarget *> > LocalObjectFiles; /* does the work for each target */ diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 290adad..a5eb3cd 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -22,6 +22,8 @@ #include "cmCacheManager.h" #include "cmake.h" +#include <ctype.h> // for isspace + cmLocalVisualStudio7Generator::cmLocalVisualStudio7Generator() { this->Version = 7; @@ -687,7 +689,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, fout << "\t\t\t\tAdditionalOptions=\"" << libflags << "\"\n"; } fout << "\t\t\t\tOutputFile=\"" - << this->ConvertToXMLOutputPathSingle(libpath.c_str()) << ".\"/>\n"; + << this->ConvertToXMLOutputPathSingle(libpath.c_str()) << "\"/>\n"; break; } case cmTarget::SHARED_LIBRARY: @@ -958,6 +960,12 @@ void cmLocalVisualStudio7Generator::OutputDefineFlags(const char* flags, done = true; } + // Remove trailing whitespace from the definition. + while(!define.empty() && isspace(define[define.size()-1])) + { + define = define.substr(0, define.size()-1); + } + // Double-quotes in the value of the definition must be escaped // with a backslash. The entire definition should be quoted in // the generated xml attribute to avoid confusing the VS parser. @@ -1159,8 +1167,26 @@ void cmLocalVisualStudio7Generator << "\t\t\t\t\tName=\"" << aCompilerTool << "\"\n"; if(compileFlags.size()) { + std::string compileFlagsCopy = compileFlags; + std::map<cmStdString, cmStdString> fileFlagMap; + this->FillFlagMapFromCommandFlags + (fileFlagMap, + &cmLocalVisualStudio7GeneratorFlagTable[0], + compileFlagsCopy); + if(compileFlagsCopy.size() && + compileFlagsCopy.find_first_not_of(" ") + != compileFlagsCopy.npos) + { fout << "\t\t\t\t\tAdditionalOptions=\"" - << this->EscapeForXML(compileFlags.c_str()) << "\"\n"; + << this->EscapeForXML(compileFlagsCopy.c_str()) << "\"\n"; + } + for(std::map<cmStdString, + cmStdString>::iterator m = fileFlagMap.begin(); + m != fileFlagMap.end(); ++m) + { + fout << "\t\t\t\t\t" << m->first << "=\"" + << m->second << "\"\n"; + } } if(additionalDeps.length()) { diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index c8565a9..e4de342 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -252,11 +252,14 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) { // record commands until we hit the ENDMACRO // at the ENDMACRO call we shift gears and start looking for invocations - if(cmSystemTools::LowerCase(lff.Name) == "endmacro") + if(!cmSystemTools::Strucmp(lff.Name.c_str(),"macro")) { - std::vector<std::string> expandedArguments; - mf.ExpandArguments(lff.Arguments, expandedArguments); - if(!expandedArguments.empty() && (expandedArguments[0] == this->Args[0])) + this->Depth++; + } + else if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endmacro")) + { + // if this is the endmacro for this macro then execute + if (!this->Depth) { std::string name = this->Args[0]; std::vector<std::string>::size_type cc; @@ -280,6 +283,11 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) mf.RemoveFunctionBlocker(lff); return true; } + else + { + // decrement for each nested macro that ends + this->Depth--; + } } // if it wasn't an endmacro and we are not executing then we must be @@ -292,15 +300,18 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) bool cmMacroFunctionBlocker:: ShouldRemove(const cmListFileFunction& lff, cmMakefile &mf) { - if(cmSystemTools::LowerCase(lff.Name) == "endmacro") + if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endmacro")) { std::vector<std::string> expandedArguments; mf.ExpandArguments(lff.Arguments, expandedArguments); - if(!expandedArguments.empty() && (expandedArguments[0] == this->Args[0])) + if ((!expandedArguments.empty() && + (expandedArguments[0] == this->Args[0])) + || mf.IsOn("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS")) { return true; } } + return false; } diff --git a/Source/cmMacroCommand.h b/Source/cmMacroCommand.h index 99e8b91..d93bdd8 100644 --- a/Source/cmMacroCommand.h +++ b/Source/cmMacroCommand.h @@ -28,7 +28,7 @@ class cmMacroFunctionBlocker : public cmFunctionBlocker { public: - cmMacroFunctionBlocker() {} + cmMacroFunctionBlocker() {this->Depth=0;} virtual ~cmMacroFunctionBlocker() {} virtual bool IsFunctionBlocked(const cmListFileFunction&, cmMakefile &mf); virtual bool ShouldRemove(const cmListFileFunction&, cmMakefile &mf); @@ -36,6 +36,7 @@ public: std::vector<std::string> Args; std::vector<cmListFileFunction> Functions; + int Depth; }; /** \class cmMacroCommand diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 866911d..d4cc69d 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -36,6 +36,8 @@ #include <cmsys/RegularExpression.hxx> +#include <ctype.h> // for isspace + // default is not to be building executables cmMakefile::cmMakefile() { @@ -265,7 +267,8 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff) cmOStringStream error; error << "Error in cmake code at\n" << lff.FilePath << ":" << lff.Line << ":\n" - << rm->GetError(); + << rm->GetError() << std::endl + << "Current CMake stack: " << this->GetListFileStack().c_str(); cmSystemTools::Error(error.str().c_str()); return false; } @@ -281,7 +284,8 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff) cmOStringStream error; error << "Error in cmake code at\n" << lff.FilePath << ":" << lff.Line << ":\n" - << usedCommand->GetError(); + << usedCommand->GetError() << std::endl + << "Current CMake stack: " << this->GetListFileStack().c_str(); cmSystemTools::Error(error.str().c_str()); result = false; if ( this->GetCMakeInstance()->GetScriptMode() ) @@ -325,6 +329,7 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff) << "Unknown CMake command \"" << lff.Name.c_str() << "\"."; cmSystemTools::Error(error.str().c_str()); result = false; + cmSystemTools::SetFatalErrorOccured(); } } @@ -803,7 +808,29 @@ void cmMakefile::AddDefineFlag(const char* flag) void cmMakefile::RemoveDefineFlag(const char* flag) { - cmSystemTools::ReplaceString(this->DefineFlags, flag, " "); + // Check the length of the flag to remove. + std::string::size_type len = strlen(flag); + if(len < 1) + { + return; + } + + // Remove all instances of the flag that are surrounded by + // whitespace or the beginning/end of the string. + for(std::string::size_type lpos = this->DefineFlags.find(flag, 0); + lpos != std::string::npos; lpos = this->DefineFlags.find(flag, lpos)) + { + std::string::size_type rpos = lpos + len; + if((lpos <= 0 || isspace(this->DefineFlags[lpos-1])) && + (rpos >= this->DefineFlags.size() || isspace(this->DefineFlags[rpos]))) + { + this->DefineFlags.erase(lpos, len); + } + else + { + ++lpos; + } + } } void cmMakefile::AddLinkLibrary(const char* lib, @@ -824,18 +851,39 @@ void cmMakefile::AddLinkLibraryForTarget(const char *target, this->GetCMakeInstance()->GetGlobalGenerator()->FindTarget(0, lib); if(tgt) { + bool allowModules = true; + const char* versionValue + = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); + if (versionValue && (atof(versionValue) >= 2.4) ) + { + allowModules = false; + } // if it is not a static or shared library then you can not link to it if(!((tgt->GetType() == cmTarget::STATIC_LIBRARY) || (tgt->GetType() == cmTarget::SHARED_LIBRARY))) { cmOStringStream e; - e << "Attempt to add link library " << lib - << " which is not a library target to target " - << tgt->GetType() << " " << - target << "\n"; + e << "Attempt to add link target " << lib << " of type: " + << cmTarget::TargetTypeNames[(int)tgt->GetType()] + << "\nto target " << target + << ". You can only link to STATIC or SHARED libraries."; + // in older versions of cmake linking to modules was allowed + if( tgt->GetType() == cmTarget::MODULE_LIBRARY ) + { + e << + "\nTo allow linking of modules set " + "CMAKE_BACKWARDS_COMPATIBILITY to 2.2 or lower\n"; + } + // if no modules are allowed then this is always an error + if(!allowModules || + // if we allow modules but the type is not a module then it is + // still an error + (allowModules && tgt->GetType() != cmTarget::MODULE_LIBRARY)) + { cmSystemTools::Error(e.str().c_str()); } } + } i->second.AddLinkLibrary( *this, target, lib, llt ); } else @@ -1667,8 +1715,16 @@ const char *cmMakefile::ExpandVariablesInString(std::string& source, { markerPos += markerStartSize; // move past marker // find the end variable marker starting at the markerPos + // make sure it is a valid variable between std::string::size_type endVariablePos = - source.find(endVariableMarker, markerPos); + source.find_first_not_of( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", + markerPos); + if(endVariablePos != std::string::npos && + source[endVariablePos] != endVariableMarker) + { + endVariablePos = std::string::npos; + } if(endVariablePos == std::string::npos) { // no end marker found so add the bogus start @@ -1815,6 +1871,11 @@ void cmMakefile::AddDefaultDefinitions() this->AddDefinition("CMAKE_MINOR_VERSION", temp); sprintf(temp, "%d", cmMakefile::GetMajorVersion()); this->AddDefinition("CMAKE_MAJOR_VERSION", temp); + sprintf(temp, "%d", cmMakefile::GetPatchVersion()); + this->AddDefinition("CMAKE_PATCH_VERSION", temp); + + this->AddDefinition("CMAKE_FILES_DIRECTORY", + cmake::GetCMakeFilesDirectory()); } #if defined(CMAKE_BUILD_WITH_CMAKE) @@ -2001,10 +2062,21 @@ cmSourceFile* cmMakefile::GetSource(const char* sourceName) const { // if the source is provided with a full path use it, otherwise // by default it is in the current source dir - std::string path = cmSystemTools::GetFilenamePath(sourceName); - if (path.empty()) + std::string path; + if (cmSystemTools::FileIsFullPath(sourceName)) + { + path = cmSystemTools::GetFilenamePath(sourceName); + } + else { path = this->GetCurrentDirectory(); + // even though it is not a full path, it may still be relative + std::string subpath = cmSystemTools::GetFilenamePath(sourceName); + if (!subpath.empty()) + { + path += "/"; + path += cmSystemTools::GetFilenamePath(sourceName); + } } std::string sname = @@ -2684,3 +2756,17 @@ std::vector<cmTest*> *cmMakefile::GetTests() return &this->Tests; } +std::string cmMakefile::GetListFileStack() +{ + std::string tmp; + for (std::deque<cmStdString>::iterator i = this->ListFileStack.begin(); + i != this->ListFileStack.end(); ++i) + { + if (i != this->ListFileStack.begin()) + { + tmp += ";"; + } + tmp += *i; + } + return tmp; +} diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 172ab7a..7751a03 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -539,6 +539,11 @@ public: void AddCMakeDependFile(const char* file) { this->ListFiles.push_back(file);} + /** + * Get the list file stack as a string + */ + std::string GetListFileStack(); + /** * Get the vector of files created by this makefile */ diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index c999106..03b0cd2 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -22,6 +22,7 @@ #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmTarget.h" +#include "cmake.h" //---------------------------------------------------------------------------- void cmMakefileExecutableTargetGenerator::WriteRuleFiles() @@ -29,12 +30,15 @@ void cmMakefileExecutableTargetGenerator::WriteRuleFiles() // create the build.make file and directory, put in the common blocks this->CreateRuleFile(); - // Add in any rules for custom commands - this->WriteCustomCommandsForTarget(); - - // write in rules for object files + // write rules used to help build object files this->WriteCommonCodeRules(); + // write in rules for object files and custom commands + this->WriteTargetBuildRules(); + + // write the per-target per-language flags + this->WriteTargetLanguageFlags(); + // Write the dependency generation rule. this->WriteTargetDependRules(); @@ -165,7 +169,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) if(relink) { outpath = this->Makefile->GetStartOutputDirectory(); - outpath += "/CMakeFiles/CMakeRelink.dir"; + outpath += cmake::GetCMakeFilesDirectory(); + outpath += "/CMakeRelink.dir"; cmSystemTools::MakeDirectory(outpath.c_str()); outpath += "/"; } @@ -311,7 +316,6 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) symlink += targetOutPathReal; symlink += " "; symlink += targetOutPath; - commands.push_back(symlink); commands1.clear(); commands1.push_back(symlink); this->LocalGenerator->CreateCDCommand(commands1, @@ -380,19 +384,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) depends, commands, false); } - // Write convenience targets. - std::string dir = this->Makefile->GetStartOutputDirectory(); - dir += "/"; - dir += this->LocalGenerator->GetTargetDirectory(*this->Target); - std::string buildTargetRuleName = dir; - buildTargetRuleName += relink?"/preinstall":"/build"; - buildTargetRuleName = - this->Convert(buildTargetRuleName.c_str(), - cmLocalGenerator::HOME_OUTPUT, - cmLocalGenerator::MAKEFILE); - this->LocalGenerator->WriteConvenienceRule(*this->BuildFileStream, - targetFullPath.c_str(), - buildTargetRuleName.c_str()); + // Write the main driver rule to build everything in this target. + this->WriteTargetDriverRule(targetFullPath.c_str(), relink); // Clean all the possible executable names and symlinks and object files. this->CleanFiles.insert(this->CleanFiles.end(), diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 330465f..2c16755 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -22,6 +22,9 @@ #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmTarget.h" +#include "cmake.h" + +#include <memory> // auto_ptr //---------------------------------------------------------------------------- void cmMakefileLibraryTargetGenerator::WriteRuleFiles() @@ -29,12 +32,15 @@ void cmMakefileLibraryTargetGenerator::WriteRuleFiles() // create the build.make file and directory, put in the common blocks this->CreateRuleFile(); - // Add in any rules for custom commands - this->WriteCustomCommandsForTarget(); - - // write in rules for object files + // write rules used to help build object files this->WriteCommonCodeRules(); + // write in rules for object files and custom commands + this->WriteTargetBuildRules(); + + // write the per-target per-language flags + this->WriteTargetLanguageFlags(); + // Write the dependency generation rule. this->WriteTargetDependRules(); @@ -241,7 +247,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules if(relink) { outpath = this->Makefile->GetStartOutputDirectory(); - outpath += "/CMakeFiles/CMakeRelink.dir"; + outpath += cmake::GetCMakeFilesDirectory(); + outpath += "/CMakeRelink.dir"; cmSystemTools::MakeDirectory(outpath.c_str()); outpath += "/"; } @@ -371,9 +378,48 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules ->AppendCustomCommands(commands, this->Target->GetPreLinkCommands()); } + // Open the link script if it will be used. + bool useLinkScript = false; + std::string linkScriptName; + std::auto_ptr<cmGeneratedFileStream> linkScriptStream; + if(this->GlobalGenerator->GetUseLinkScript() && + (this->Target->GetType() == cmTarget::STATIC_LIBRARY || + this->Target->GetType() == cmTarget::SHARED_LIBRARY || + this->Target->GetType() == cmTarget::MODULE_LIBRARY)) + { + useLinkScript = true; + linkScriptName = this->TargetBuildDirectoryFull; + if(relink) + { + linkScriptName += "/relink.txt"; + } + else + { + linkScriptName += "/link.txt"; + } + std::auto_ptr<cmGeneratedFileStream> lss( + new cmGeneratedFileStream(linkScriptName.c_str())); + linkScriptStream = lss; + } + + std::vector<std::string> link_script_commands; + // Construct the main link rule. std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar); + if(useLinkScript) + { + cmSystemTools::ExpandListArgument(linkRule, link_script_commands); + std::string link_command = "$(CMAKE_COMMAND) -E cmake_link_script "; + link_command += this->Convert(linkScriptName.c_str(), + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + link_command += " --verbose=$(VERBOSE)"; + commands1.push_back(link_command); + } + else + { cmSystemTools::ExpandListArgument(linkRule, commands1); + } this->LocalGenerator->CreateCDCommand (commands1, this->Makefile->GetStartOutputDirectory(), @@ -413,11 +459,19 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules std::string variableName; std::string variableNameExternal; this->WriteObjectsVariable(variableName, variableNameExternal); - std::string buildObjs = "$("; + std::string buildObjs; + if(useLinkScript) + { + this->WriteObjectsString(buildObjs); + } + else + { + buildObjs = "$("; buildObjs += variableName; buildObjs += ") $("; buildObjs += variableNameExternal; buildObjs += ")"; + } std::string cleanObjs = "$("; cleanObjs += variableName; cleanObjs += ")"; @@ -425,7 +479,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules vars.TargetPDB = targetOutPathPDB.c_str(); vars.Language = linkLanguage; vars.Objects = buildObjs.c_str(); - std::string objdir = "CMakeFiles/"; + std::string objdir = cmake::GetCMakeFilesDirectoryPostSlash(); objdir += this->Target->GetName(); objdir += ".dir"; vars.ObjectDir = objdir.c_str(); @@ -488,13 +542,40 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules vars.LanguageCompileFlags = langFlags.c_str(); // Expand placeholders in the commands. this->LocalGenerator->TargetImplib = targetOutPathImport; + if(useLinkScript) + { + for(std::vector<std::string>::iterator i = link_script_commands.begin(); + i != link_script_commands.end(); ++i) + { + this->LocalGenerator->ExpandRuleVariables(*i, vars); + } + } + else + { for(std::vector<std::string>::iterator i = commands.begin(); i != commands.end(); ++i) { this->LocalGenerator->ExpandRuleVariables(*i, vars); } + } this->LocalGenerator->TargetImplib = ""; + // Optionally convert the build rule to use a script to avoid long + // command lines in the make shell. + if(useLinkScript) + { + for(std::vector<std::string>::iterator cmd = link_script_commands.begin(); + cmd != link_script_commands.end(); ++cmd) + { + // Do not write out empty commands or commands beginning in the + // shell no-op ":". + if(!cmd->empty() && (*cmd)[0] != ':') + { + (*linkScriptStream) << *cmd << "\n"; + } + } + } + // Write the build rule. this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, targetFullPathReal.c_str(), @@ -522,18 +603,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules depends, commands, false); } - // Write convenience targets. - std::string dir = this->Makefile->GetStartOutputDirectory(); - dir += "/"; - dir += this->LocalGenerator->GetTargetDirectory(*this->Target); - std::string buildTargetRuleName = dir; - buildTargetRuleName += relink?"/preinstall":"/build"; - buildTargetRuleName = - this->Convert(buildTargetRuleName.c_str(), - cmLocalGenerator::HOME_OUTPUT,cmLocalGenerator::MAKEFILE); - this->LocalGenerator->WriteConvenienceRule(*this->BuildFileStream, - targetFullPath.c_str(), - buildTargetRuleName.c_str()); + // Write the main driver rule to build everything in this target. + this->WriteTargetDriverRule(targetFullPath.c_str(), relink); // Clean all the possible library names and symlinks and object files. this->CleanFiles.insert(this->CleanFiles.end(), diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index f3434d0..a4f03c8 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -23,6 +23,7 @@ #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmTarget.h" +#include "cmake.h" #include "cmMakefileExecutableTargetGenerator.h" #include "cmMakefileLibraryTargetGenerator.h" @@ -99,7 +100,7 @@ void cmMakefileTargetGenerator::CreateRuleFile() } //---------------------------------------------------------------------------- -void cmMakefileTargetGenerator::WriteCustomCommandsForTarget() +void cmMakefileTargetGenerator::WriteTargetBuildRules() { // write the custom commands for this target // Look for files registered for cleaning in this directory. @@ -110,7 +111,56 @@ void cmMakefileTargetGenerator::WriteCustomCommandsForTarget() cmSystemTools::ExpandListArgument(additional_clean_files, this->CleanFiles); } - this->WriteCustomCommands(); + + // add custom commands to the clean rules? + const char* clean_no_custom = + this->Makefile->GetProperty("CLEAN_NO_CUSTOM"); + bool clean = cmSystemTools::IsOff(clean_no_custom); + + // First generate the object rule files. Save a list of all object + // files for this target. + const std::vector<cmSourceFile*>& sources = this->Target->GetSourceFiles(); + for(std::vector<cmSourceFile*>::const_iterator source = sources.begin(); + source != sources.end(); ++source) + { + if(cmCustomCommand* cc = (*source)->GetCustomCommand()) + { + this->GenerateCustomRuleFile(*cc); + if (clean) + { + 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)); + } + } + } + else if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY")) + { + if(!this->GlobalGenerator->IgnoreFile + ((*source)->GetSourceExtension().c_str())) + { + // Generate this object file's rule file. + this->WriteObjectRuleFiles(*(*source)); + } + else if((*source)->GetPropertyAsBool("EXTERNAL_OBJECT")) + { + // This is an external object file. Just add it. + this->ExternalObjects.push_back((*source)->GetFullPath()); + } + else + { + // We only get here if a source file is not an external object + // and has an extension that is listed as an ignored file type + // for this language. No message or diagnosis should be + // given. + } + } + } } @@ -159,37 +209,11 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules() cmLocalGenerator::HOME_OUTPUT, cmLocalGenerator::MAKEFILE) << "\n\n"; +} - // First generate the object rule files. Save a list of all object - // files for this target. - const std::vector<cmSourceFile*>& sources = this->Target->GetSourceFiles(); - for(std::vector<cmSourceFile*>::const_iterator source = sources.begin(); - source != sources.end(); ++source) - { - if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY") && - !(*source)->GetCustomCommand()) - { - if(!this->GlobalGenerator->IgnoreFile - ((*source)->GetSourceExtension().c_str())) - { - // Generate this object file's rule file. - this->WriteObjectRuleFiles(*(*source)); - } - else if((*source)->GetPropertyAsBool("EXTERNAL_OBJECT")) - { - // This is an external object file. Just add it. - this->ExternalObjects.push_back((*source)->GetFullPath()); - } - else - { - // We only get here if a source file is not an external object - // and has an extension that is listed as an ignored file type - // for this language. No message or diagnosis should be - // given. - } - } - } - +//---------------------------------------------------------------------------- +void cmMakefileTargetGenerator::WriteTargetLanguageFlags() +{ // write language flags for target std::map<cmStdString,cmLocalUnixMakefileGenerator3::IntegrityCheckSet>& checkSet = @@ -374,6 +398,27 @@ cmMakefileTargetGenerator // Construct the build message. std::vector<std::string> no_commands; std::vector<std::string> commands; + + // add in a progress call if needed + cmGlobalUnixMakefileGenerator3* gg = + static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator); + int prog = gg->ShouldAddProgressRule(); + + std::string progressDir = this->Makefile->GetHomeOutputDirectory(); + progressDir += cmake::GetCMakeFilesDirectory(); + cmOStringStream progCmd; + progCmd << "$(CMAKE_COMMAND) -E cmake_progress_report "; + progCmd << this->LocalGenerator->Convert(progressDir.c_str(), + cmLocalGenerator::FULL, + cmLocalGenerator::SHELL); + if (prog) + { + progCmd << " " << prog; + this->LocalGenerator->ProgressFiles[this->Target->GetName()]. + push_back(prog); + } + commands.push_back(progCmd.str()); + std::string buildEcho = "Building "; buildEcho += lang; buildEcho += " object "; @@ -581,20 +626,31 @@ void cmMakefileTargetGenerator::WriteTargetDependRules() cmLocalGenerator::FULL, cmLocalGenerator::SHELL)) << " && "; #endif - depCmd << "$(CMAKE_COMMAND) -E cmake_depends " - << " \"" + // Generate a call this signature: + // + // cmake -E cmake_depends <generator> + // <home-src-dir> <start-src-dir> + // <home-out-dir> <start-out-dir> + // <dep-info> + // + // This gives the dependency scanner enough information to recreate + // the state of our local generator sufficiently for its needs. + depCmd << "$(CMAKE_COMMAND) -E cmake_depends \"" << this->GlobalGenerator->GetName() << "\" " - << this->LocalGenerator->Convert - (this->Makefile->GetHomeOutputDirectory(), - cmLocalGenerator::FULL,cmLocalGenerator::SHELL) + << this->Convert(this->Makefile->GetHomeDirectory(), + cmLocalGenerator::FULL, cmLocalGenerator::SHELL) << " " - << this->LocalGenerator->Convert - (this->Makefile->GetStartOutputDirectory(), - cmLocalGenerator::FULL,cmLocalGenerator::SHELL) + << this->Convert(this->Makefile->GetStartDirectory(), + cmLocalGenerator::FULL, cmLocalGenerator::SHELL) + << " " + << this->Convert(this->Makefile->GetHomeOutputDirectory(), + cmLocalGenerator::FULL, cmLocalGenerator::SHELL) + << " " + << this->Convert(this->Makefile->GetStartOutputDirectory(), + cmLocalGenerator::FULL, cmLocalGenerator::SHELL) << " " << this->Convert(this->InfoFileNameFull.c_str(), - cmLocalGenerator::FULL, - cmLocalGenerator::SHELL); + cmLocalGenerator::FULL, cmLocalGenerator::SHELL); commands.push_back(depCmd.str()); // Write the rule. @@ -624,39 +680,6 @@ void cmMakefileTargetGenerator } //---------------------------------------------------------------------------- -void cmMakefileTargetGenerator::WriteCustomCommands() -{ - // add custom commands to the clean rules? - const char* clean_no_custom = - this->Makefile->GetProperty("CLEAN_NO_CUSTOM"); - bool clean = cmSystemTools::IsOff(clean_no_custom); - - // Generate the rule files for each custom command. - const std::vector<cmSourceFile*> &classes = - this->Makefile->GetSourceFiles(); - for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); - i != classes.end(); i++) - { - if(cmCustomCommand* cc = (*i)->GetCustomCommand()) - { - this->GenerateCustomRuleFile(*cc); - if (clean) - { - 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)); - } - } - } - } -} - -//---------------------------------------------------------------------------- void cmMakefileTargetGenerator ::GenerateCustomRuleFile(const cmCustomCommand& cc) { @@ -785,6 +808,112 @@ cmMakefileTargetGenerator *this->BuildFileStream << "\n" << "\n"; } +//---------------------------------------------------------------------------- +void +cmMakefileTargetGenerator +::WriteObjectsString(std::string& buildObjs) +{ + std::string object; + const char* no_quoted = + this->Makefile->GetDefinition("CMAKE_NO_QUOTED_OBJECTS"); + const char* space = ""; + for(std::vector<std::string>::const_iterator i = this->Objects.begin(); + i != this->Objects.end(); ++i) + { + if ( this->ExtraContent.find(i->c_str()) != this->ExtraContent.end() ) + { + continue; + } + buildObjs += space; + space = " "; + if(no_quoted) + { + buildObjs += + this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + } + else + { + buildObjs += + this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str()); + } + } + for(std::vector<std::string>::const_iterator i = + this->ExternalObjects.begin(); + i != this->ExternalObjects.end(); ++i) + { + buildObjs += space; + space = " "; + if(no_quoted) + { + buildObjs += + this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + } + else + { + buildObjs += + this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str()); + } + } +} + +//---------------------------------------------------------------------------- +void cmMakefileTargetGenerator::WriteTargetDriverRule(const char* main_output, + bool relink) +{ + // Compute the name of the driver target. + std::string dir = this->Makefile->GetStartOutputDirectory(); + dir += "/"; + dir += this->LocalGenerator->GetTargetDirectory(*this->Target); + std::string buildTargetRuleName = dir; + buildTargetRuleName += relink?"/preinstall":"/build"; + buildTargetRuleName = this->Convert(buildTargetRuleName.c_str(), + cmLocalGenerator::HOME_OUTPUT, + cmLocalGenerator::MAKEFILE); + + // Build the list of target outputs to drive. + std::vector<std::string> depends; + if(main_output) + { + depends.push_back(main_output); + } + + const char* comment = 0; + if(relink) + { + // Setup the comment for the preinstall driver. + comment = "Rule to relink during preinstall."; + } + else + { + // Setup the comment for the main build driver. + comment = "Rule to build all files generated by this target."; + + // Make sure all custom command outputs in this target are built. + const std::vector<cmSourceFile*>& sources = + this->Target->GetSourceFiles(); + for(std::vector<cmSourceFile*>::const_iterator source = sources.begin(); + source != sources.end(); ++source) + { + if(cmCustomCommand* cc = (*source)->GetCustomCommand()) + { + const std::vector<std::string>& outputs = cc->GetOutputs(); + for(std::vector<std::string>::const_iterator o = outputs.begin(); + o != outputs.end(); ++o) + { + depends.push_back(*o); + } + } + } + } + + // Write the driver rule. + std::vector<std::string> no_commands; + this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment, + buildTargetRuleName.c_str(), + depends, no_commands, true); +} //---------------------------------------------------------------------------- std::string cmMakefileTargetGenerator::GetFrameworkFlags() diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index c2422d7..a624305 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -55,11 +55,13 @@ protected: // create the file and directory etc void CreateRuleFile(); - // outputs the rules for any custom commands used by this target - void WriteCustomCommandsForTarget(); + // outputs the rules for object files and custom commands used by + // this target + void WriteTargetBuildRules(); // write some common code at the top of build.make void WriteCommonCodeRules(); + void WriteTargetLanguageFlags(); // write the provide require rules for this target void WriteTargetRequiresRules(); @@ -83,14 +85,16 @@ protected: void WriteObjectDependRules(cmSourceFile& source, std::vector<std::string>& depends); - // this is responsible for writing all of the rules for all this - // directories custom commands (but not utility targets) - void WriteCustomCommands(); + // write the build rule for a custom command void GenerateCustomRuleFile(const cmCustomCommand& cc); // write out the variable that lists the objects for this target void WriteObjectsVariable(std::string& variableName, std::string& variableNameExternal); + void WriteObjectsString(std::string& buildObjs); + + // write the driver rule to build target outputs + void WriteTargetDriverRule(const char* main_output, bool relink); // Return the a string with -F flags on apple std::string GetFrameworkFlags(); diff --git a/Source/cmMakefileUtilityTargetGenerator.cxx b/Source/cmMakefileUtilityTargetGenerator.cxx index 474f850..51a848f 100644 --- a/Source/cmMakefileUtilityTargetGenerator.cxx +++ b/Source/cmMakefileUtilityTargetGenerator.cxx @@ -33,7 +33,7 @@ void cmMakefileUtilityTargetGenerator::WriteRuleFiles() << "# Utility rule file for " << this->Target->GetName() << ".\n\n"; // write the custom commands for this target - this->WriteCustomCommandsForTarget(); + this->WriteTargetBuildRules(); // Collect the commands and dependencies. std::vector<std::string> commands; @@ -63,19 +63,8 @@ void cmMakefileUtilityTargetGenerator::WriteRuleFiles() this->Target->GetName(), depends, commands, true); - // Write convenience targets. - std::string dir = this->Makefile->GetStartOutputDirectory(); - dir += "/"; - dir += this->LocalGenerator->GetTargetDirectory(*this->Target); - std::string buildTargetRuleName = dir; - buildTargetRuleName += "/build"; - buildTargetRuleName = - this->LocalGenerator->Convert(buildTargetRuleName.c_str(), - cmLocalGenerator::HOME_OUTPUT, - cmLocalGenerator::MAKEFILE); - this->LocalGenerator->WriteConvenienceRule(*this->BuildFileStream, - this->Target->GetName(), - buildTargetRuleName.c_str()); + // Write the main driver rule to build everything in this target. + this->WriteTargetDriverRule(this->Target->GetName(), false); // Write clean target this->WriteTargetCleanRules(); diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index 2ba6fd7..5856b51 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -57,9 +57,10 @@ bool cmMessageCommand::InitialPass(std::vector<std::string> const& args) message += *i; } - if (send_error) + if (send_error || fatal_error) { - cmSystemTools::Error(message.c_str()); + //cmSystemTools::Error(message.c_str()); + this->SetError(message.c_str()); } else { @@ -76,6 +77,6 @@ bool cmMessageCommand::InitialPass(std::vector<std::string> const& args) { cmSystemTools::SetFatalErrorOccured(); } - return true; + return (!send_error && !fatal_error); } diff --git a/Source/cmSetTargetPropertiesCommand.h b/Source/cmSetTargetPropertiesCommand.h index 9bd3483..a605a77 100644 --- a/Source/cmSetTargetPropertiesCommand.h +++ b/Source/cmSetTargetPropertiesCommand.h @@ -112,6 +112,9 @@ public: "There are a few properties used to specify RPATH rules. " "INSTALL_RPATH is a semicolon-separated list specifying the rpath " "to use in installed targets (for platforms that support it). " + "INSTALL_RPATH_USE_LINK_PATH is a boolean that if set to true will " + "append directories in the linker search path and outside the " + "project to the INSTALL_RPATH. " "SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic " "generation of an rpath allowing the target to run from the " "build tree. " @@ -122,7 +125,8 @@ public: "directory portion of the \"install_name\" field of shared libraries " "on Mac OSX to use in the installed targets. " "When the target is created the values of " - "the variables CMAKE_INSTALL_RPATH, CMAKE_SKIP_BUILD_RPATH, " + "the variables CMAKE_INSTALL_RPATH, " + "CMAKE_INSTALL_RPATH_USE_LINK_PATH, CMAKE_SKIP_BUILD_RPATH, " "CMAKE_BUILD_WITH_INSTALL_RPATH, and CMAKE_INSTALL_NAME_DIR " "are used to initialize these properties.\n" "PROJECT_LABEL can be used to change the name of " diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 0f34a97..26bc47d 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -23,6 +23,11 @@ #include <set> #include <queue> #include <stdlib.h> // required for atof +const char* cmTarget::TargetTypeNames[] = { + "EXECUTABLE", "STATIC_LIBRARY", + "SHARED_LIBRARY", "MODULE_LIBRARY", "UTILITY", "GLOBAL_TARGET", + "INSTALL_FILES", "INSTALL_PROGRAMS", "INSTALL_DIRECTORY" +}; //---------------------------------------------------------------------------- cmTarget::cmTarget() @@ -58,6 +63,7 @@ void cmTarget::SetMakefile(cmMakefile* mf) // Setup default property values. this->SetPropertyDefault("INSTALL_NAME_DIR", ""); this->SetPropertyDefault("INSTALL_RPATH", ""); + this->SetPropertyDefault("INSTALL_RPATH_USE_LINK_PATH", "OFF"); this->SetPropertyDefault("SKIP_BUILD_RPATH", "OFF"); this->SetPropertyDefault("BUILD_WITH_INSTALL_RPATH", "OFF"); @@ -1316,19 +1322,36 @@ void cmTarget::GetLibraryNamesInternal(std::string& name, soversion = version; } + // Get the components of the library name. + std::string prefix; + std::string base; + std::string suffix; + this->GetFullNameInternal(type, config, false, prefix, base, suffix); + // The library name. - name = this->GetFullNameInternal(type, config, false); + name = prefix+base+suffix; // The library's soname. +#if defined(__APPLE__) + soName = prefix+base; +#else soName = name; +#endif if(soversion) { soName += "."; soName += soversion; } +#if defined(__APPLE__) + soName += suffix; +#endif // The library's real name on disk. +#if defined(__APPLE__) + realName = prefix+base; +#else realName = name; +#endif if(version) { realName += "."; @@ -1339,6 +1362,9 @@ void cmTarget::GetLibraryNamesInternal(std::string& name, realName += "."; realName += soversion; } +#if defined(__APPLE__) + realName += suffix; +#endif // The import library name. if(type == cmTarget::SHARED_LIBRARY) diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 7980218..557461f 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -36,7 +36,7 @@ public: enum TargetType { EXECUTABLE, STATIC_LIBRARY, SHARED_LIBRARY, MODULE_LIBRARY, UTILITY, GLOBAL_TARGET, INSTALL_FILES, INSTALL_PROGRAMS, INSTALL_DIRECTORY}; - + static const char* TargetTypeNames[]; enum CustomCommandType { PRE_BUILD, PRE_LINK, POST_BUILD }; /** diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx index c7405ca..f3fe893 100644 --- a/Source/cmTryCompileCommand.cxx +++ b/Source/cmTryCompileCommand.cxx @@ -110,7 +110,9 @@ int cmTryCompileCommand::CoreTryCompileCode( // signature if (srcFileSignature) { - tmpString = argv[1] + "/CMakeFiles/CMakeTmp"; + tmpString = argv[1]; + tmpString += cmake::GetCMakeFilesDirectory(); + tmpString += "/CMakeTmp"; binaryDirectory = tmpString.c_str(); } // make sure the binary directory exists diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index ba7b481..f8f2e86 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -68,7 +68,9 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv) tryCompile, false); // now try running the command if it compiled - std::string binaryDirectory = argv[2] + "/CMakeFiles/CMakeTmp"; + std::string binaryDirectory = argv[2]; + binaryDirectory += cmake::GetCMakeFilesDirectory(); + binaryDirectory += "/CMakeTmp"; if (!res) { int retVal = -1; diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx index 3c785d7..f88ed2b 100644 --- a/Source/cmWhileCommand.cxx +++ b/Source/cmWhileCommand.cxx @@ -28,7 +28,15 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) } // at end of for each execute recorded commands - if (cmSystemTools::LowerCase(lff.Name) == "endwhile") + if (!cmSystemTools::Strucmp(lff.Name.c_str(),"while")) + { + // record the number of while commands past this one + this->Depth++; + } + else if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endwhile")) + { + // if this is the endwhile for this while loop then execute + if (!this->Depth) { char* errorString = 0; @@ -53,6 +61,12 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) mf.RemoveFunctionBlocker(lff); return true; } + else + { + // decrement for each nested while that ends + this->Depth--; + } + } // record the command this->Functions.push_back(lff); @@ -62,11 +76,12 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) } bool cmWhileFunctionBlocker:: -ShouldRemove(const cmListFileFunction& lff, cmMakefile& ) +ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) { - if(cmSystemTools::LowerCase(lff.Name) == "endwhile") + if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endwhile")) { - if (lff.Arguments == this->Args) + if (lff.Arguments == this->Args + || mf.IsOn("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS")) { return true; } diff --git a/Source/cmWhileCommand.h b/Source/cmWhileCommand.h index 10c261c..e1f44eb 100644 --- a/Source/cmWhileCommand.h +++ b/Source/cmWhileCommand.h @@ -29,7 +29,7 @@ class cmWhileFunctionBlocker : public cmFunctionBlocker { public: - cmWhileFunctionBlocker() {Executing = false;} + cmWhileFunctionBlocker() {Executing = false; Depth=0;} virtual ~cmWhileFunctionBlocker() {} virtual bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf); @@ -39,6 +39,8 @@ public: std::vector<cmListFileArgument> Args; std::vector<cmListFileFunction> Functions; bool Executing; +private: + int Depth; }; /** \class cmWhileCommand diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 53b7526..2c64d2e 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -31,6 +31,9 @@ # include <cmsys/Terminal.h> #endif +# include <cmsys/Directory.hxx> +#include <cmsys/Process.h> + // only build kdevelop generator on non-windows platforms // when not bootstrapping cmake #if !defined(_WIN32) @@ -267,7 +270,16 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) std::string entry = arg.substr(2); if(entry.size() == 0) { - entry = args[++i]; + ++i; + if(i < args.size()) + { + entry = args[i]; + } + else + { + cmSystemTools::Error("-D must be followed with VAR=VALUE."); + return false; + } } std::string var, value; cmCacheManager::CacheEntryType type = cmCacheManager::UNINITIALIZED; @@ -291,7 +303,16 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) std::string path = arg.substr(2); if ( path.size() == 0 ) { - path = args[++i]; + ++i; + if(i < args.size()) + { + path = args[i]; + } + else + { + cmSystemTools::Error("-C must be followed by a file name."); + return false; + } } std::cerr << "loading initial cache file " << path.c_str() << "\n"; this->ReadListFile(path.c_str()); @@ -299,6 +320,11 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) else if(arg.find("-P",0) == 0) { i++; + if(i >= args.size()) + { + cmSystemTools::Error("-P must be followed by a file name."); + return false; + } std::string path = args[i]; if ( path.size() == 0 ) { @@ -425,7 +451,13 @@ void cmake::SetArgs(const std::vector<std::string>& args) std::string value = arg.substr(2); if(value.size() == 0) { - value = args[++i]; + ++i; + if(i >= args.size()) + { + cmSystemTools::Error("No generator specified for -G"); + return; + } + value = args[i]; } cmGlobalGenerator* gen = this->CreateGlobalGenerator(value.c_str()); @@ -837,6 +869,19 @@ int cmake::ExecuteCMakeCommand(std::vector<std::string>& args) return 0; } + // Echo string no new line + else if (args[1] == "echo_append" ) + { + unsigned int cc; + const char* space = ""; + for ( cc = 2; cc < args.size(); cc ++ ) + { + std::cout << space << args[cc]; + space = " "; + } + return 0; + } + #if defined(CMAKE_BUILD_WITH_CMAKE) // Command to create a symbolic link. Fails on platforms not // supporting them. @@ -855,15 +900,22 @@ int cmake::ExecuteCMakeCommand(std::vector<std::string>& args) // Remove file else if (args[1] == "remove" && args.size() > 2) { + bool force = false; for (std::string::size_type cc = 2; cc < args.size(); cc ++) { - if(args[cc] != "-f") + if(args[cc] == "\\-f" || args[cc] == "-f") { - if(args[cc] == "\\-f") + force = true; + } + else + { + // Complain if the file could not be removed, still exists, + // and the -f option was not given. + if(!cmSystemTools::RemoveFile(args[cc].c_str()) && !force && + cmSystemTools::FileExists(args[cc].c_str())) { - args[cc] = "-f"; + return 1; } - cmSystemTools::RemoveFile(args[cc].c_str()); } } return 0; @@ -931,6 +983,67 @@ int cmake::ExecuteCMakeCommand(std::vector<std::string>& args) return 1; } + // Command to start progress for a build + else if (args[1] == "cmake_progress_start" && args.size() == 4) + { + // bascially remove the directory + std::string dirName = args[2]; + dirName += "/Progress"; + cmSystemTools::RemoveADirectory(dirName.c_str()); + cmSystemTools::MakeDirectory(dirName.c_str()); + // write the count into the directory + std::string fName = dirName; + fName += "/count.txt"; + FILE *progFile = fopen(fName.c_str(),"w"); + if (progFile) + { + int count = atoi(args[3].c_str()); + fprintf(progFile,"%i\n",count); + fclose(progFile); + } + return 0; + } + + // Command to report progress for a build + else if (args[1] == "cmake_progress_report" && args.size() >= 3) + { + std::string dirName = args[2]; + dirName += "/Progress"; + std::string fName; + FILE *progFile; + unsigned int i; + for (i = 3; i < args.size(); ++i) + { + fName = dirName; + fName += "/"; + fName += args[i]; + progFile = fopen(fName.c_str(),"w"); + if (progFile) + { + fprintf(progFile,"empty"); + fclose(progFile); + } + } + int fileNum = static_cast<int> + (cmsys::Directory::GetNumberOfFilesInDirectory(dirName.c_str())); + // read the count + fName = dirName; + fName += "/count.txt"; + progFile = fopen(fName.c_str(),"r"); + if (progFile) + { + int count = 0; + fscanf(progFile,"%i",&count); + if (count > 0) + { + // print the progress + fprintf(stdout,"[%3i%%] ",((fileNum-3)*100)/count); + } + fclose(progFile); + } + return 0; + } + // Command to create a symbolic link. Fails on platforms not // supporting them. else if (args[1] == "create_symlink" && args.size() == 4) @@ -996,18 +1109,81 @@ int cmake::ExecuteCMakeCommand(std::vector<std::string>& args) // Internal CMake dependency scanning support. else if (args[1] == "cmake_depends" && args.size() >= 6) { + // Create a cmake object instance to process dependencies. cmake cm; - cmGlobalGenerator *ggd = cm.CreateGlobalGenerator(args[2].c_str()); - if (ggd) + std::string gen; + std::string homeDir; + std::string startDir; + std::string homeOutDir; + std::string startOutDir; + std::string depInfo; + if(args.size() >= 8) + { + // Full signature: + // + // -E cmake_depends <generator> + // <home-src-dir> <start-src-dir> + // <home-out-dir> <start-out-dir> + // <dep-info> + // + // All paths are provided. + gen = args[2]; + homeDir = args[3]; + startDir = args[4]; + homeOutDir = args[5]; + startOutDir = args[6]; + depInfo = args[7]; + } + else + { + // Support older signature for existing makefiles: + // + // -E cmake_depends <generator> + // <home-out-dir> <start-out-dir> + // <dep-info> + // + // Just pretend the source directories are the same as the + // binary directories so at least scanning will work. + gen = args[2]; + homeDir = args[3]; + startDir = args[4]; + homeOutDir = args[3]; + startOutDir = args[3]; + depInfo = args[5]; + } + + // Create a local generator configured for the directory in + // which dependencies will be scanned. + homeDir = cmSystemTools::CollapseFullPath(homeDir.c_str()); + startDir = cmSystemTools::CollapseFullPath(startDir.c_str()); + homeOutDir = cmSystemTools::CollapseFullPath(homeOutDir.c_str()); + startOutDir = cmSystemTools::CollapseFullPath(startOutDir.c_str()); + cm.SetHomeDirectory(homeDir.c_str()); + cm.SetStartDirectory(startDir.c_str()); + cm.SetHomeOutputDirectory(homeOutDir.c_str()); + cm.SetStartOutputDirectory(startOutDir.c_str()); + if(cmGlobalGenerator* ggd = cm.CreateGlobalGenerator(gen.c_str())) { - ggd->SetCMakeInstance(&cm); + cm.SetGlobalGenerator(ggd); std::auto_ptr<cmLocalGenerator> lgd(ggd->CreateLocalGenerator()); lgd->SetGlobalGenerator(ggd); - return lgd->ScanDependencies(args)? 0 : 2; + lgd->GetMakefile()->SetStartDirectory(startDir.c_str()); + lgd->GetMakefile()->SetStartOutputDirectory(startOutDir.c_str()); + lgd->GetMakefile()->MakeStartDirectoriesCurrent(); + lgd->SetupPathConversions(); + + // Actually scan dependencies. + return lgd->ScanDependencies(depInfo.c_str())? 0 : 2; } return 1; } + // Internal CMake link script support. + else if (args[1] == "cmake_link_script" && args.size() >= 3) + { + return cmake::ExecuteLinkScript(args); + } + #ifdef CMAKE_BUILD_WITH_CMAKE // Internal CMake color makefile support. else if (args[1] == "cmake_echo_color") @@ -1865,9 +2041,7 @@ int cmake::CheckBuildSystem() // This method will check the integrity of the build system if the // option was given on the command line. It reads the given file to - // determine whether CMake should rerun. If it does rerun then the - // generation step will check the integrity of dependencies. If it - // does not then we need to check the integrity here. + // determine whether CMake should rerun. // If no file is provided for the check, we have to rerun. if(this->CheckBuildSystemArgument.size() == 0) @@ -1916,6 +2090,25 @@ int cmake::CheckBuildSystem() return 1; } + // Now that we know the generator used to build the project, use it + // to check the dependency integrity. + const char* genName = mf->GetDefinition("CMAKE_DEPENDS_GENERATOR"); + if (!genName || genName[0] == '\0') + { + genName = "Unix Makefiles"; + } + 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); + } + // Get the set of dependencies and outputs. const char* dependsStr = mf->GetDefinition("CMAKE_MAKEFILE_DEPENDS"); const char* outputsStr = mf->GetDefinition("CMAKE_MAKEFILE_OUTPUTS"); @@ -1960,24 +2153,6 @@ int cmake::CheckBuildSystem() } } - // compute depends based on the generator specified - const char* genName = mf->GetDefinition("CMAKE_DEPENDS_GENERATOR"); - if (!genName || genName[0] == '\0') - { - genName = "Unix Makefiles"; - } - 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. return 0; } @@ -2193,7 +2368,7 @@ void cmake::GenerateGraphViz(const char* fileName) std::map<cmStdString, int> targetDeps; std::map<cmStdString, cmTarget*> targetPtrs; std::map<cmStdString, cmStdString> targetNamesNodes; - char tgtName[100]; + char tgtName[2048]; int cnt = 0; // First pass get the list of all cmake targets for ( lit = localGenerators.begin(); lit != localGenerators.end(); ++ lit ) @@ -2474,3 +2649,101 @@ int cmake::ExecuteEchoColor(std::vector<std::string>&) return 1; } #endif + +//---------------------------------------------------------------------------- +int cmake::ExecuteLinkScript(std::vector<std::string>& args) +{ + // The arguments are + // argv[0] == <cmake-executable> + // argv[1] == cmake_link_script + // argv[2] == <link-script-name> + // argv[3] == --verbose=? + bool verbose = false; + if(args.size() >= 4) + { + if(args[3].find("--verbose=") == 0) + { + if(!cmSystemTools::IsOff(args[3].substr(10).c_str())) + { + verbose = true; + } + } + } + + // Allocate a process instance. + cmsysProcess* cp = cmsysProcess_New(); + if(!cp) + { + std::cerr << "Error allocating process instance in link script." + << std::endl; + return 1; + } + + // Children should share stdout and stderr with this process. + cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); + cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); + + // Run the command lines verbatim. + cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1); + + // Read command lines from the script. + std::ifstream fin(args[2].c_str()); + if(!fin) + { + std::cerr << "Error opening link script \"" + << args[2] << "\"" << std::endl; + return 1; + } + + // Run one command at a time. + std::string command; + int result = 0; + while(result == 0 && cmSystemTools::GetLineFromStream(fin, command)) + { + // Setup this command line. + const char* cmd[2] = {command.c_str(), 0}; + cmsysProcess_SetCommand(cp, cmd); + + // Report the command if verbose output is enabled. + if(verbose) + { + std::cout << command << std::endl; + } + + // Run the command and wait for it to exit. + cmsysProcess_Execute(cp); + cmsysProcess_WaitForExit(cp, 0); + + // Report failure if any. + switch(cmsysProcess_GetState(cp)) + { + case cmsysProcess_State_Exited: + { + int value = cmsysProcess_GetExitValue(cp); + if(value != 0) + { + result = value; + } + } + break; + case cmsysProcess_State_Exception: + std::cerr << "Error running link command: " + << cmsysProcess_GetExceptionString(cp) << std::endl; + result = 1; + break; + case cmsysProcess_State_Error: + std::cerr << "Error running link command: " + << cmsysProcess_GetErrorString(cp) << std::endl; + result = 2; + break; + default: + break; + }; + } + + // Free the process instance. + cmsysProcess_Delete(cp); + + // Return the final resulting return value. + return result; +} diff --git a/Source/cmake.h b/Source/cmake.h index b2cea7b..d63cf7e 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -67,6 +67,11 @@ class cmake static unsigned int GetMinorVersion(); static const char *GetReleaseVersion(); + ///! construct an instance of cmake + static const char *GetCMakeFilesDirectory() {return "/CMakeFiles";}; + static const char *GetCMakeFilesDirectoryPostSlash() { + return "CMakeFiles/";}; + //@{ /** * Set/Get the home directory (or output directory) in the project. The @@ -318,6 +323,7 @@ protected: void GenerateGraphViz(const char* fileName); static int ExecuteEchoColor(std::vector<std::string>& args); + static int ExecuteLinkScript(std::vector<std::string>& args); cmVariableWatch* VariableWatch; diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index bb01116..c16cd1c 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -82,6 +82,9 @@ static const cmDocumentationEntry cmDocumentationOptions[] = {"--graphviz=[file]", "Generate graphviz of dependencies.", "Generate a graphviz input file that will contain all the library and " "executable dependencies in the project."}, + {"--debug-trycompile", "Do not delete the try compile directories..", + "Do not delete the files and directories created for try_compile calls. " + "This is useful in debugging failed try_compiles."}, {"--help-command cmd [file]", "Print help for a single command and exit.", "Full documentation specific to the given command is displayed."}, {"--help-command-list [file]", "List available listfile commands and exit.", @@ -236,10 +239,13 @@ int do_cmake(int ac, char** av) { cmSystemTools::Error("No script specified for argument -P"); } + else + { script_mode = true; args.push_back(av[i]); i++; args.push_back(av[i]); + } } else { diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx index b1ee992..cdf254f 100644 --- a/Source/kwsys/CommandLineArguments.cxx +++ b/Source/kwsys/CommandLineArguments.cxx @@ -364,7 +364,7 @@ void CommandLineArguments::DeleteRemainingArguments(int argc, char*** argv) int cc; for ( cc = 0; cc < argc; ++ cc ) { - delete [] *argv[cc]; + delete [] (*argv)[cc]; } delete [] *argv; } diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index 3f7e6ad..f7fb5e9 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -143,6 +143,47 @@ bool Directory::Load(const char* name) return _findclose(srchHandle) != -1; } +unsigned long Directory::GetNumberOfFilesInDirectory(const char* name) +{ +#if _MSC_VER < 1300 + long srchHandle; +#else + intptr_t srchHandle; +#endif + char* buf; + size_t n = strlen(name); + if ( name[n - 1] == '/' ) + { + buf = new char[n + 1 + 1]; + sprintf(buf, "%s*", name); + } + else + { + buf = new char[n + 2 + 1]; + sprintf(buf, "%s/*", name); + } + struct _finddata_t data; // data of current file + + // Now put them into the file array + srchHandle = _findfirst(buf, &data); + delete [] buf; + + if ( srchHandle == -1 ) + { + return 0; + } + + // Loop through names + unsigned long count = 0; + do + { + count++; + } + while ( _findnext(srchHandle, &data) != -1 ); + _findclose(srchHandle); + return count; +} + } // namespace KWSYS_NAMESPACE #else @@ -174,6 +215,24 @@ bool Directory::Load(const char* name) return 1; } +unsigned long Directory::GetNumberOfFilesInDirectory(const char* name) +{ + DIR* dir = opendir(name); + + if (!dir) + { + return 0; + } + + unsigned long count = 0; + for (dirent* d = readdir(dir); d; d = readdir(dir) ) + { + count++; + } + closedir(dir); + return count; +} + } // namespace KWSYS_NAMESPACE #endif diff --git a/Source/kwsys/Directory.hxx.in b/Source/kwsys/Directory.hxx.in index 22aafcc..ddb9104 100644 --- a/Source/kwsys/Directory.hxx.in +++ b/Source/kwsys/Directory.hxx.in @@ -48,6 +48,12 @@ public: unsigned long GetNumberOfFiles() const; /** + * Return the number of files in the specified directory. + * A higher performance static method. + */ + static unsigned long GetNumberOfFilesInDirectory(const char*); + + /** * Return the file at the given index, the indexing is 0 based */ const char* GetFile(unsigned long) const; diff --git a/Source/kwsys/Process.h.in b/Source/kwsys/Process.h.in index 96d3225..3d2db7b 100644 --- a/Source/kwsys/Process.h.in +++ b/Source/kwsys/Process.h.in @@ -36,6 +36,7 @@ #define kwsysProcess_SetPipeShared kwsys_ns(Process_SetPipeShared) #define kwsysProcess_Option_Detach kwsys_ns(Process_Option_Detach) #define kwsysProcess_Option_HideWindow kwsys_ns(Process_Option_HideWindow) +#define kwsysProcess_Option_Verbatim kwsys_ns(Process_Option_Verbatim) #define kwsysProcess_GetOption kwsys_ns(Process_GetOption) #define kwsysProcess_SetOption kwsys_ns(Process_SetOption) #define kwsysProcess_Option_e kwsys_ns(Process_Option_e) @@ -154,6 +155,13 @@ kwsysEXPORT void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, * kwsysProcess_Option_HideWindow = Whether to hide window on Windows. * 0 = No (default) * 1 = Yes + * + * kwsysProcess_Option_Verbatim = Whether SetCommand and AddCommand + * should treat the first argument + * as a verbatim command line + * and ignore the rest of the arguments. + * 0 = No (default) + * 1 = Yes */ kwsysEXPORT int kwsysProcess_GetOption(kwsysProcess* cp, int optionId); kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, @@ -161,7 +169,8 @@ kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, enum kwsysProcess_Option_e { kwsysProcess_Option_HideWindow, - kwsysProcess_Option_Detach + kwsysProcess_Option_Detach, + kwsysProcess_Option_Verbatim }; /** @@ -343,6 +352,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp); # undef kwsysProcess_SetPipeShared # undef kwsysProcess_Option_Detach # undef kwsysProcess_Option_HideWindow +# undef kwsysProcess_Option_Verbatim # undef kwsysProcess_GetOption # undef kwsysProcess_SetOption # undef kwsysProcess_Option_e diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index 0f4d1bc..ea257b0 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -24,17 +24,18 @@ Implementation for UNIX -On UNIX, a child process is forked to exec the program. Three -output pipes from the child are read by the parent process using a -select call to block until data are ready. Two of the pipes are -stdout and stderr for the child. The third is a special error pipe -that has two purposes. First, if the child cannot exec the program, -the error is reported through the error pipe. Second, the error -pipe is left open until the child exits. This is used in -conjunction with the timeout on the select call to implement a -timeout for program even when it closes stdout and stderr. +On UNIX, a child process is forked to exec the program. Three output +pipes are read by the parent process using a select call to block +until data are ready. Two of the pipes are stdout and stderr for the +child. The third is a special pipe populated by a signal handler to +indicate that a child has terminated. This is used in conjunction +with the timeout on the select call to implement a timeout for program +even when it closes stdout and stderr and at the same time avoiding +races. + */ + /* TODO: @@ -59,6 +60,7 @@ do. #include <time.h> /* gettimeofday */ #include <signal.h> /* sigaction */ #include <dirent.h> /* DIR, dirent */ +#include <ctype.h> /* isspace */ /* The number of pipes for the child's output. The standard stdout and stderr pipes are the first two. One more pipe is used to @@ -68,7 +70,7 @@ do. #define KWSYSPE_PIPE_COUNT 3 #define KWSYSPE_PIPE_STDOUT 0 #define KWSYSPE_PIPE_STDERR 1 -#define KWSYSPE_PIPE_TERM 2 +#define KWSYSPE_PIPE_SIGNAL 2 /* The maximum amount to read from a pipe at a time. */ #define KWSYSPE_PIPE_BUFFER_SIZE 1024 @@ -89,7 +91,6 @@ typedef struct kwsysProcessCreateInformation_s int StdIn; int StdOut; int StdErr; - int TermPipe; int ErrorPipe[2]; } kwsysProcessCreateInformation; @@ -99,6 +100,7 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error); static void kwsysProcessCleanupDescriptor(int* pfd); static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, kwsysProcessCreateInformation* si, int* readEnd); +static void kwsysProcessDestroy(kwsysProcess* cp); static int kwsysProcessSetupOutputPipeFile(int* p, const char* name); static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, kwsysProcessTime* timeoutTime); @@ -117,6 +119,11 @@ static void kwsysProcessRestoreDefaultSignalHandlers(void); static pid_t kwsysProcessFork(kwsysProcess* cp, kwsysProcessCreateInformation* si); static void kwsysProcessKill(pid_t process_id); +static int kwsysProcessesAdd(kwsysProcess* cp); +static void kwsysProcessesRemove(kwsysProcess* cp); +static void kwsysProcessesSignalHandler(int signum, siginfo_t* info, + void* ucontext); +static char** kwsysProcessParseVerbatimCommand(const char* command); /*--------------------------------------------------------------------------*/ /* Structure containing data used to implement the child's execution. */ @@ -126,9 +133,13 @@ struct kwsysProcess_s char*** Commands; int NumberOfCommands; - /* Descriptors for the read ends of the child's output pipes. */ + /* Descriptors for the read ends of the child's output pipes and + the signal pipe. */ int PipeReadEnds[KWSYSPE_PIPE_COUNT]; + /* Write descriptor for child termination signal pipe. */ + int SignalPipe; + /* Buffer for pipe data. */ char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE]; @@ -150,6 +161,9 @@ struct kwsysProcess_s /* Whether the child was created as a detached process. */ int Detached; + /* Whether to treat command lines as verbatim. */ + int Verbatim; + /* Time at which the child started. Negative for no timeout. */ kwsysProcessTime StartTime; @@ -159,15 +173,15 @@ struct kwsysProcess_s /* Flag for whether the timeout expired. */ int TimeoutExpired; - /* The old SIGCHLD handler. */ - struct sigaction OldSigChldAction; - /* The number of pipes left open during execution. */ int PipesLeft; /* File descriptor set for call to select. */ fd_set PipeSet; + /* The number of children still executing. */ + int CommandsLeft; + /* The current status of the child process. */ int State; @@ -300,7 +314,7 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) char*** newCommands; /* Make sure we have a command to add. */ - if(!cp || !command) + if(!cp || !command || !*command) { return 0; } @@ -323,7 +337,22 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) } /* Add the new command. */ + if(cp->Verbatim) { + /* In order to run the given command line verbatim we need to + parse it. */ + newCommands[cp->NumberOfCommands] = + kwsysProcessParseVerbatimCommand(*command); + if(!newCommands[cp->NumberOfCommands]) + { + /* Out of memory. */ + free(newCommands); + return 0; + } + } + else + { + /* Copy each argument string individually. */ char const* const* c = command; int n = 0; int i = 0; @@ -483,6 +512,7 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) switch(optionId) { case kwsysProcess_Option_Detach: return cp->OptionDetach; + case kwsysProcess_Option_Verbatim: return cp->Verbatim; default: return 0; } } @@ -498,6 +528,7 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) switch(optionId) { case kwsysProcess_Option_Detach: cp->OptionDetach = value; break; + case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break; default: break; } } @@ -558,8 +589,7 @@ const char* kwsysProcess_GetExceptionString(kwsysProcess* cp) void kwsysProcess_Execute(kwsysProcess* cp) { int i; - struct sigaction newSigChldAction; - kwsysProcessCreateInformation si = {-1, -1, -1, -1, {-1, -1}}; + kwsysProcessCreateInformation si = {-1, -1, -1, {-1, -1}}; /* Do not execute a second copy simultaneously. */ if(!cp || cp->State == kwsysProcess_State_Executing) @@ -597,15 +627,19 @@ void kwsysProcess_Execute(kwsysProcess* cp) } } - /* We want no special handling of SIGCHLD. Repeat call until it is - not interrupted. */ - memset(&newSigChldAction, 0, sizeof(struct sigaction)); - newSigChldAction.sa_handler = SIG_DFL; - while((sigaction(SIGCHLD, &newSigChldAction, &cp->OldSigChldAction) < 0) && - (errno == EINTR)); + /* If not running a detached child, add this object to the global + set of process objects that wish to be notified when a child + exits. */ + if(!cp->OptionDetach) + { + if(!kwsysProcessesAdd(cp)) + { + kwsysProcessCleanup(cp, 1); + return; + } + } - /* Setup the stderr and termination pipes to be shared by all processes. */ - for(i=KWSYSPE_PIPE_STDERR; i < KWSYSPE_PIPE_COUNT; ++i) + /* Setup the stderr pipe to be shared by all processes. */ { /* Create the pipe. */ int p[2]; @@ -616,15 +650,8 @@ void kwsysProcess_Execute(kwsysProcess* cp) } /* Store the pipe. */ - cp->PipeReadEnds[i] = p[0]; - if(i == KWSYSPE_PIPE_STDERR) - { + cp->PipeReadEnds[KWSYSPE_PIPE_STDERR] = p[0]; si.StdErr = p[1]; - } - else - { - si.TermPipe = p[1]; - } /* Set close-on-exec flag on the pipe's ends. */ if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || @@ -632,7 +659,6 @@ void kwsysProcess_Execute(kwsysProcess* cp) { kwsysProcessCleanup(cp, 1); kwsysProcessCleanupDescriptor(&si.StdErr); - kwsysProcessCleanupDescriptor(&si.TermPipe); return; } } @@ -645,7 +671,6 @@ void kwsysProcess_Execute(kwsysProcess* cp) { kwsysProcessCleanup(cp, 1); kwsysProcessCleanupDescriptor(&si.StdErr); - kwsysProcessCleanupDescriptor(&si.TermPipe); return; } } @@ -688,7 +713,6 @@ void kwsysProcess_Execute(kwsysProcess* cp) { kwsysProcessCleanupDescriptor(&si.StdErr); } - kwsysProcessCleanupDescriptor(&si.TermPipe); kwsysProcessCleanupDescriptor(&si.ErrorPipe[0]); kwsysProcessCleanupDescriptor(&si.ErrorPipe[1]); return; @@ -703,7 +727,6 @@ void kwsysProcess_Execute(kwsysProcess* cp) { kwsysProcessCleanupDescriptor(&si.StdErr); } - kwsysProcessCleanupDescriptor(&si.TermPipe); /* Restore the working directory. */ if(cp->RealWorkingDirectory) @@ -823,9 +846,10 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length, if(n > 0) { /* We have data on this pipe. */ - if(i == KWSYSPE_PIPE_TERM) + if(i == KWSYSPE_PIPE_SIGNAL) { - /* This is data on the special termination pipe. Ignore it. */ + /* A child process has terminated. */ + kwsysProcessDestroy(cp); } else if(data && length) { @@ -970,7 +994,6 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length, /*--------------------------------------------------------------------------*/ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout) { - int result = 0; int status = 0; int prPipe = 0; @@ -989,26 +1012,6 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout) } } - /* Wait for each child to terminate. The process should have - already exited because KWSYSPE_PIPE_TERM has been closed by this - point. Repeat the call until it is not interrupted. */ - if(!cp->Detached) - { - int i; - for(i=0; i < cp->NumberOfCommands; ++i) - { - while(((result = waitpid(cp->ForkPIDs[i], - &cp->CommandExitCodes[i], 0)) < 0) && - (errno == EINTR)); - if(result <= 0 && cp->State != kwsysProcess_State_Error) - { - /* Unexpected error. Report the first time this happens. */ - strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); - cp->State = kwsysProcess_State_Error; - } - } - } - /* Check if there was an error in one of the waitpid calls. */ if(cp->State == kwsysProcess_State_Error) { @@ -1080,22 +1083,35 @@ void kwsysProcess_Kill(kwsysProcess* cp) return; } + /* Close all the pipe read ends. Do this before killing the + children because Cygwin has problems killing processes that are + blocking to wait for writing to their output pipes. First close + the child exit report pipe write end to avoid causing a SIGPIPE + when the child terminates and our signal handler tries to report + it. */ + kwsysProcessCleanupDescriptor(&cp->SignalPipe); + for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) + { + kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); + } + cp->PipesLeft = 0; + /* Kill the children. */ cp->Killed = 1; for(i=0; i < cp->NumberOfCommands; ++i) { + int status; if(cp->ForkPIDs[i]) { + /* Kill the child. */ kwsysProcessKill(cp->ForkPIDs[i]); - } - } - /* Close all the pipe read ends. */ - for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) - { - kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); + /* Reap the child. Keep trying until the call is not + interrupted. */ + while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && (errno == EINTR)); } - cp->PipesLeft = 0; + } + cp->CommandsLeft = 0; } /*--------------------------------------------------------------------------*/ @@ -1107,6 +1123,7 @@ static int kwsysProcessInitialize(kwsysProcess* cp) { cp->PipeReadEnds[i] = -1; } + cp->SignalPipe = -1; cp->SelectError = 0; cp->StartTime.tv_sec = -1; cp->StartTime.tv_usec = -1; @@ -1114,6 +1131,7 @@ static int kwsysProcessInitialize(kwsysProcess* cp) cp->TimeoutTime.tv_usec = -1; cp->TimeoutExpired = 0; cp->PipesLeft = 0; + cp->CommandsLeft = 0; FD_ZERO(&cp->PipeSet); cp->State = kwsysProcess_State_Starting; cp->Killed = 0; @@ -1194,6 +1212,7 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error) { /* Kill the child. */ kwsysProcessKill(cp->ForkPIDs[i]); + /* Reap the child. Keep trying until the call is not interrupted. */ while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && @@ -1209,9 +1228,13 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error) } } - /* Restore the SIGCHLD handler. */ - while((sigaction(SIGCHLD, &cp->OldSigChldAction, 0) < 0) && - (errno == EINTR)); + /* If not creating a detached child, remove this object from the + global set of process objects that wish to be notified when a + child exits. */ + if(!cp->OptionDetach) + { + kwsysProcessesRemove(cp); + } /* Free memory. */ if(cp->ForkPIDs) @@ -1360,12 +1383,10 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, } /* Clear the close-on-exec flag for stdin, stdout, and stderr. - Also clear it for the termination pipe. All other pipe handles - will be closed when exec succeeds. */ + All other pipe handles will be closed when exec succeeds. */ fcntl(0, F_SETFD, 0); fcntl(1, F_SETFD, 0); fcntl(2, F_SETFD, 0); - fcntl(si->TermPipe, F_SETFD, 0); /* Restore all default signal handlers. */ kwsysProcessRestoreDefaultSignalHandlers(); @@ -1377,6 +1398,9 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, kwsysProcessChildErrorExit(si->ErrorPipe[1]); } + /* A child has been created. */ + ++cp->CommandsLeft; + /* We are done with the error reporting pipe write end. */ kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]); @@ -1425,6 +1449,47 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, } /*--------------------------------------------------------------------------*/ +static void kwsysProcessDestroy(kwsysProcess* cp) +{ + /* A child process has terminated. Reap it if it is one handled by + this object. */ + int i; + for(i=0; i < cp->NumberOfCommands; ++i) + { + if(cp->ForkPIDs[i]) + { + int result; + while(((result = waitpid(cp->ForkPIDs[i], + &cp->CommandExitCodes[i], WNOHANG)) < 0) && + (errno == EINTR)); + if(result > 0) + { + /* This child has termianted. */ + cp->ForkPIDs[i] = 0; + if(--cp->CommandsLeft == 0) + { + /* All children have terminated. Close the signal pipe + write end so that no more notifications are sent to this + object. */ + kwsysProcessCleanupDescriptor(&cp->SignalPipe); + + /* TODO: Once the children have terminated, switch + WaitForData to use a non-blocking read to get the + rest of the data from the pipe. This is needed when + grandchildren keep the output pipes open. */ + } + } + else if(result < 0 && cp->State != kwsysProcess_State_Error) + { + /* Unexpected error. Report the first time this happens. */ + strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); + cp->State = kwsysProcess_State_Error; + } + } + } +} + +/*--------------------------------------------------------------------------*/ static int kwsysProcessSetupOutputPipeFile(int* p, const char* name) { int fout; @@ -1909,12 +1974,15 @@ static pid_t kwsysProcessFork(kwsysProcess* cp, #elif defined(__hpux) || defined(__sparc) || defined(__sgi) || defined(_AIX) # define KWSYSPE_PS_COMMAND "ps -ef" # define KWSYSPE_PS_FORMAT "%*s %d %d %*[^\n]\n" +#elif defined(__CYGWIN__) +# define KWSYSPE_PS_COMMAND "ps aux" +# define KWSYSPE_PS_FORMAT "%d %d %*[^\n]\n" #endif /*--------------------------------------------------------------------------*/ static void kwsysProcessKill(pid_t process_id) { -#if defined(__linux__) +#if defined(__linux__) || defined(__CYGWIN__) DIR* procdir; #endif @@ -1922,7 +1990,7 @@ static void kwsysProcessKill(pid_t process_id) kill(process_id, SIGSTOP); /* Kill all children if we can find them. */ -#if defined(__linux__) +#if defined(__linux__) || defined(__CYGWIN__) /* First try using the /proc filesystem. */ if((procdir = opendir("/proc")) != NULL) { @@ -1980,8 +2048,8 @@ static void kwsysProcessKill(pid_t process_id) } else #endif -#if defined(KWSYSPE_PS_COMMAND) { +#if defined(KWSYSPE_PS_COMMAND) /* Try running "ps" to get the process information. */ FILE* ps = popen(KWSYSPE_PS_COMMAND, "r"); @@ -2005,9 +2073,461 @@ static void kwsysProcessKill(pid_t process_id) { pclose(ps); } - } #endif + } /* Kill the process. */ kill(process_id, SIGKILL); } + +/*--------------------------------------------------------------------------*/ +/* Global set of executing processes for use by the signal handler. + This global instance will be zero-initialized by the compiler. */ +typedef struct kwsysProcessInstances_s +{ + int Count; + int Size; + kwsysProcess** Processes; +} kwsysProcessInstances; +static kwsysProcessInstances kwsysProcesses; + +/* The old SIGCHLD handler. */ +static struct sigaction kwsysProcessesOldSigChldAction; + +/*--------------------------------------------------------------------------*/ +static void kwsysProcessesUpdate(kwsysProcessInstances* newProcesses) +{ + /* Block SIGCHLD while we update the set of pipes to check. + TODO: sigprocmask is undefined for threaded apps. See + pthread_sigmask. */ + sigset_t newset; + sigset_t oldset; + sigemptyset(&newset); + sigaddset(&newset, SIGCHLD); + sigprocmask(SIG_BLOCK, &newset, &oldset); + + /* Store the new set in that seen by the signal handler. */ + kwsysProcesses = *newProcesses; + + /* Restore the signal mask to the previous setting. */ + sigprocmask(SIG_SETMASK, &oldset, 0); +} + +/*--------------------------------------------------------------------------*/ +static int kwsysProcessesAdd(kwsysProcess* cp) +{ + /* Create a pipe through which the signal handler can notify the + given process object that a child has exited. */ + { + /* Create the pipe. */ + int oldfl[2]; + int p[2]; + if(pipe(p) < 0) + { + return 0; + } + + /* Store the pipes now to be sure they are cleaned up later. */ + cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL] = p[0]; + cp->SignalPipe = p[1]; + + /* Switch the pipe to non-blocking mode so that reading a byte can + be an atomic test-and-set. */ + if((oldfl[0] = fcntl(p[0], F_GETFL) < 0) || + (oldfl[1] = fcntl(p[1], F_GETFL) < 0) || + (fcntl(p[0], F_SETFL, oldfl[0] | O_NONBLOCK) < 0) || + (fcntl(p[1], F_SETFL, oldfl[1] | O_NONBLOCK) < 0)) + { + return 0; + } + + /* The children do not need this pipe. Set close-on-exec flag on + the pipe's ends. */ + if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) + { + return 0; + } + } + + /* Attempt to add the given signal pipe to the signal handler set. */ + { + + /* Make sure there is enough space for the new signal pipe. */ + kwsysProcessInstances oldProcesses = kwsysProcesses; + kwsysProcessInstances newProcesses = oldProcesses; + if(oldProcesses.Count == oldProcesses.Size) + { + /* Start with enough space for a small number of process instances + and double the size each time more is needed. */ + newProcesses.Size = oldProcesses.Size? oldProcesses.Size*2 : 4; + + /* Try allocating the new block of memory. */ + if((newProcesses.Processes = ((kwsysProcess**) + malloc(newProcesses.Size* + sizeof(kwsysProcess*))))) + { + /* Copy the old pipe set to the new memory. */ + if(oldProcesses.Count > 0) + { + memcpy(newProcesses.Processes, oldProcesses.Processes, + (oldProcesses.Count * sizeof(kwsysProcess*))); + } + } + else + { + /* Failed to allocate memory for the new signal pipe set. */ + return 0; + } + } + + /* Append the new signal pipe to the set. */ + newProcesses.Processes[newProcesses.Count++] = cp; + + /* Store the new set in that seen by the signal handler. */ + kwsysProcessesUpdate(&newProcesses); + + /* Free the original pipes if new ones were allocated. */ + if(newProcesses.Processes != oldProcesses.Processes) + { + free(oldProcesses.Processes); + } + + /* If this is the first process, enable the signal handler. */ + if(newProcesses.Count == 1) + { + /* Install our handler for SIGCHLD. Repeat call until it is not + interrupted. */ + struct sigaction newSigChldAction; + memset(&newSigChldAction, 0, sizeof(struct sigaction)); + newSigChldAction.sa_sigaction = kwsysProcessesSignalHandler; + newSigChldAction.sa_flags = SA_NOCLDSTOP | SA_RESTART | SA_SIGINFO; + while((sigaction(SIGCHLD, &newSigChldAction, + &kwsysProcessesOldSigChldAction) < 0) && + (errno == EINTR)); + } + } + + return 1; +} + +/*--------------------------------------------------------------------------*/ +static void kwsysProcessesRemove(kwsysProcess* cp) +{ + /* Attempt to remove the given signal pipe from the signal handler set. */ + { + /* Find the given process in the set. */ + kwsysProcessInstances newProcesses = kwsysProcesses; + int i; + for(i=0; i < newProcesses.Count; ++i) + { + if(newProcesses.Processes[i] == cp) + { + break; + } + } + if(i < newProcesses.Count) + { + /* Remove the process from the set. */ + --newProcesses.Count; + for(; i < newProcesses.Count; ++i) + { + newProcesses.Processes[i] = newProcesses.Processes[i+1]; + } + + /* If this was the last process, disable the signal handler. */ + if(newProcesses.Count == 0) + { + /* Restore the SIGCHLD handler. Repeat call until it is not + interrupted. */ + while((sigaction(SIGCHLD, &kwsysProcessesOldSigChldAction, 0) < 0) && + (errno == EINTR)); + + /* Free the table of process pointers since it is now empty. + This is safe because the signal handler has been removed. */ + newProcesses.Size = 0; + free(newProcesses.Processes); + newProcesses.Processes = 0; + } + + /* Store the new set in that seen by the signal handler. */ + kwsysProcessesUpdate(&newProcesses); + } + } + + /* Close the pipe through which the signal handler may have notified + the given process object that a child has exited. */ + kwsysProcessCleanupDescriptor(&cp->SignalPipe); +} + +/*--------------------------------------------------------------------------*/ +static void kwsysProcessesSignalHandler(int signum, siginfo_t* info, + void* ucontext) +{ + /* Signal all process objects that a child has terminated. */ + int i; + (void)signum; + (void)info; + (void)ucontext; + for(i=0; i < kwsysProcesses.Count; ++i) + { + /* Set the pipe in a signalled state. */ + char buf = 1; + kwsysProcess* cp = kwsysProcesses.Processes[i]; + read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1); + write(cp->SignalPipe, &buf, 1); + } +} + +/*--------------------------------------------------------------------------*/ +static int kwsysProcessAppendByte(char* local, + char** begin, char** end, + int* size, char c) +{ + /* Allocate space for the character. */ + if((*end - *begin) >= *size) + { + int length = *end - *begin; + char* newBuffer = (char*)malloc(*size*2); + if(!newBuffer) + { + return 0; + } + memcpy(newBuffer, *begin, length*sizeof(char)); + if(*begin != local) + { + free(*begin); + } + *begin = newBuffer; + *end = *begin + length; + *size *= 2; + } + + /* Store the character. */ + *(*end)++ = c; + return 1; +} + +/*--------------------------------------------------------------------------*/ +static int kwsysProcessAppendArgument(char** local, + char*** begin, char*** end, + int* size, + char* arg_local, + char** arg_begin, char** arg_end, + int* arg_size) +{ + /* Append a null-terminator to the argument string. */ + if(!kwsysProcessAppendByte(arg_local, arg_begin, arg_end, arg_size, '\0')) + { + return 0; + } + + /* Allocate space for the argument pointer. */ + if((*end - *begin) >= *size) + { + int length = *end - *begin; + char** newPointers = (char**)malloc(*size*2*sizeof(char*)); + if(!newPointers) + { + return 0; + } + memcpy(newPointers, *begin, length*sizeof(char*)); + if(*begin != local) + { + free(*begin); + } + *begin = newPointers; + *end = *begin + length; + *size *= 2; + } + + /* Allocate space for the argument string. */ + **end = (char*)malloc(*arg_end - *arg_begin); + if(!**end) + { + return 0; + } + + /* Store the argument in the command array. */ + memcpy(**end, *arg_begin, *arg_end - *arg_begin); + ++(*end); + + /* Reset the argument to be empty. */ + *arg_end = *arg_begin; + + return 1; +} + +/*--------------------------------------------------------------------------*/ +#define KWSYSPE_LOCAL_BYTE_COUNT 1024 +#define KWSYSPE_LOCAL_ARGS_COUNT 32 +static char** kwsysProcessParseVerbatimCommand(const char* command) +{ + /* Create a buffer for argument pointers during parsing. */ + char* local_pointers[KWSYSPE_LOCAL_ARGS_COUNT]; + int pointers_size = KWSYSPE_LOCAL_ARGS_COUNT; + char** pointer_begin = local_pointers; + char** pointer_end = pointer_begin; + + /* Create a buffer for argument strings during parsing. */ + char local_buffer[KWSYSPE_LOCAL_BYTE_COUNT]; + int buffer_size = KWSYSPE_LOCAL_BYTE_COUNT; + char* buffer_begin = local_buffer; + char* buffer_end = buffer_begin; + + /* Parse the command string. Try to behave like a UNIX shell. */ + char** newCommand = 0; + const char* c = command; + int in_argument = 0; + int in_escape = 0; + int in_single = 0; + int in_double = 0; + int failed = 0; + for(;*c; ++c) + { + if(in_escape) + { + /* This character is escaped so do no special handling. */ + if(!in_argument) + { + in_argument = 1; + } + if(!kwsysProcessAppendByte(local_buffer, &buffer_begin, + &buffer_end, &buffer_size, *c)) + { + failed = 1; + break; + } + in_escape = 0; + } + else if(*c == '\\' && !in_single) + { + /* The next character should be escaped. */ + in_escape = 1; + } + else if(*c == '\'' && !in_double) + { + /* Enter or exit single-quote state. */ + if(in_single) + { + in_single = 0; + } + else + { + in_single = 1; + if(!in_argument) + { + in_argument = 1; + } + } + } + else if(*c == '"' && !in_single) + { + /* Enter or exit double-quote state. */ + if(in_double) + { + in_double = 0; + } + else + { + in_double = 1; + if(!in_argument) + { + in_argument = 1; + } + } + } + else if(isspace(*c)) + { + if(in_argument) + { + if(in_single || in_double) + { + /* This space belongs to a quoted argument. */ + if(!kwsysProcessAppendByte(local_buffer, &buffer_begin, + &buffer_end, &buffer_size, *c)) + { + failed = 1; + break; + } + } + else + { + /* This argument has been terminated by whitespace. */ + if(!kwsysProcessAppendArgument(local_pointers, &pointer_begin, + &pointer_end, &pointers_size, + local_buffer, &buffer_begin, + &buffer_end, &buffer_size)) + { + failed = 1; + break; + } + in_argument = 0; + } + } + } + else + { + /* This character belong to an argument. */ + if(!in_argument) + { + in_argument = 1; + } + if(!kwsysProcessAppendByte(local_buffer, &buffer_begin, + &buffer_end, &buffer_size, *c)) + { + failed = 1; + break; + } + } + } + + /* Finish the last argument. */ + if(in_argument) + { + if(!kwsysProcessAppendArgument(local_pointers, &pointer_begin, + &pointer_end, &pointers_size, + local_buffer, &buffer_begin, + &buffer_end, &buffer_size)) + { + failed = 1; + } + } + + /* If we still have memory allocate space for the new command + buffer. */ + if(!failed) + { + int n = pointer_end - pointer_begin; + newCommand = (char**)malloc((n+1)*sizeof(char*)); + } + + if(newCommand) + { + /* Copy the arguments into the new command buffer. */ + int n = pointer_end - pointer_begin; + memcpy(newCommand, pointer_begin, sizeof(char*)*n); + newCommand[n] = 0; + } + else + { + /* Free arguments already allocated. */ + while(pointer_end != pointer_begin) + { + free(*(--pointer_end)); + } + } + + /* Free temporary buffers. */ + if(pointer_begin != local_pointers) + { + free(pointer_begin); + } + if(buffer_begin != local_buffer) + { + free(buffer_begin); + } + + /* Return the final command buffer. */ + return newCommand; +} diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c index 97e5706..6929c3e 100644 --- a/Source/kwsys/ProcessWin32.c +++ b/Source/kwsys/ProcessWin32.c @@ -104,8 +104,14 @@ static void kwsysProcessDestroy(kwsysProcess* cp, int event); static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name); static int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle); static void kwsysProcessCleanupHandle(PHANDLE h); +static void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle); static void kwsysProcessCleanup(kwsysProcess* cp, int error); static void kwsysProcessCleanErrorMessage(kwsysProcess* cp); +static int kwsysProcessComputeCommandLength(kwsysProcess* cp, + char const* const* command); +static void kwsysProcessComputeCommandLine(kwsysProcess* cp, + char const* const* command, + char* cmd); static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, kwsysProcessTime* timeoutTime); static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, @@ -205,6 +211,9 @@ struct kwsysProcess_s /* Whether to hide the child process's window. */ int HideWindow; + /* Whether to treat command lines as verbatim. */ + int Verbatim; + /* On Win9x platforms, the path to the forwarding executable. */ char* Win9x; @@ -645,7 +654,7 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) char** newCommands; /* Make sure we have a command to add. */ - if(!cp || !command) + if(!cp || !command || !*command) { return 0; } @@ -675,66 +684,8 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) because they come before the closing double-quote for the argument. */ { - char* cmd; - char const* const* arg; - int length = 0; /* First determine the length of the final string. */ - for(arg = command; *arg; ++arg) - { - /* Keep track of how many backslashes have been encountered in a - row in this argument. */ - int backslashes = 0; - int spaces = 0; - const char* c; - - /* Scan the string for spaces. If there are no spaces, we can - pass the argument verbatim. */ - for(c=*arg; *c; ++c) - { - if(*c == ' ' || *c == '\t') - { - spaces = 1; - break; - } - } - - /* Add the length of the argument, plus 1 for the space - separating the arguments. */ - length += (int)strlen(*arg) + 1; - - if(spaces) - { - /* Add 2 for double quotes since spaces are present. */ - length += 2; - - /* Scan the string to find characters that need escaping. */ - for(c=*arg; *c; ++c) - { - if(*c == '\\') - { - /* Found a backslash. It may need to be escaped later. */ - ++backslashes; - } - else if(*c == '"') - { - /* Found a double-quote. We need to escape it and all - immediately preceding backslashes. */ - length += backslashes + 1; - backslashes = 0; - } - else - { - /* Found another character. This eliminates the possibility - that any immediately preceding backslashes will be - escaped. */ - backslashes = 0; - } - } - - /* We need to escape all ending backslashes. */ - length += backslashes; - } - } + int length = kwsysProcessComputeCommandLength(cp, command); /* Allocate enough space for the command. We do not need an extra byte for the terminating null because we allocated a space for @@ -748,94 +699,8 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) } /* Construct the command line in the allocated buffer. */ - cmd = newCommands[cp->NumberOfCommands]; - for(arg = command; *arg; ++arg) - { - /* Keep track of how many backslashes have been encountered in a - row in an argument. */ - int backslashes = 0; - int spaces = 0; - const char* c; - - /* Scan the string for spaces. If there are no spaces, we can - pass the argument verbatim. */ - for(c=*arg; *c; ++c) - { - if(*c == ' ' || *c == '\t') - { - spaces = 1; - break; - } - } - - /* Add the separating space if this is not the first argument. */ - if(arg != command) - { - *cmd++ = ' '; - } - - if(spaces) - { - /* Add the opening double-quote for this argument. */ - *cmd++ = '"'; - - /* Add the characters of the argument, possibly escaping them. */ - for(c=*arg; *c; ++c) - { - if(*c == '\\') - { - /* Found a backslash. It may need to be escaped later. */ - ++backslashes; - *cmd++ = '\\'; - } - else if(*c == '"') - { - /* Add enough backslashes to escape any that preceded the - double-quote. */ - while(backslashes > 0) - { - --backslashes; - *cmd++ = '\\'; - } - - /* Add the backslash to escape the double-quote. */ - *cmd++ = '\\'; - - /* Add the double-quote itself. */ - *cmd++ = '"'; - } - else - { - /* We encountered a normal character. This eliminates any - escaping needed for preceding backslashes. Add the - character. */ - backslashes = 0; - *cmd++ = *c; - } - } - - /* Add enough backslashes to escape any trailing ones. */ - while(backslashes > 0) - { - --backslashes; - *cmd++ = '\\'; - } - - /* Add the closing double-quote for this argument. */ - *cmd++ = '"'; - } - else - { - /* No spaces. Add the argument verbatim. */ - for(c=*arg; *c; ++c) - { - *cmd++ = *c; - } - } - } - - /* Add the terminating null character to the command line. */ - *cmd = 0; + kwsysProcessComputeCommandLine(cp, command, + newCommands[cp->NumberOfCommands]); } /* Save the new array of commands. */ @@ -967,6 +832,7 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) { case kwsysProcess_Option_Detach: return cp->OptionDetach; case kwsysProcess_Option_HideWindow: return cp->HideWindow; + case kwsysProcess_Option_Verbatim: return cp->Verbatim; default: return 0; } } @@ -983,6 +849,7 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) { case kwsysProcess_Option_Detach: cp->OptionDetach = value; break; case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break; + case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break; default: break; } } @@ -1145,7 +1012,8 @@ void kwsysProcess_Execute(kwsysProcess* cp) &si.StartupInfo.hStdError)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, + STD_ERROR_HANDLE); return; } } @@ -1166,9 +1034,12 @@ void kwsysProcess_Execute(kwsysProcess* cp) /* Release resources that may have been allocated for this process before an error occurred. */ kwsysProcessCleanupHandle(&readEnd); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdInput); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdOutput); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdInput, + STD_INPUT_HANDLE); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdOutput, + STD_OUTPUT_HANDLE); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, + STD_ERROR_HANDLE); kwsysProcessCleanupHandle(&si.ErrorPipeRead); kwsysProcessCleanupHandle(&si.ErrorPipeWrite); return; @@ -1183,7 +1054,7 @@ void kwsysProcess_Execute(kwsysProcess* cp) processes in the pipeline. The stdout and stdin pipes are not shared among all children and are therefore closed by kwsysProcessCreate after each child is created. */ - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, STD_ERROR_HANDLE); /* Restore the working directory. */ if(cp->RealWorkingDirectory) @@ -1493,7 +1364,7 @@ void kwsysProcess_Kill(kwsysProcess* cp) /* Make sure we are executing a process. */ if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired || - cp->Killed || cp->Terminated) + cp->Killed) { return; } @@ -1501,6 +1372,12 @@ void kwsysProcess_Kill(kwsysProcess* cp) /* Disable the reading threads. */ kwsysProcessDisablePipeThreads(cp); + /* Skip actually killing the child if it has already terminated. */ + if(cp->Terminated) + { + return; + } + /* Kill the children. */ cp->Killed = 1; if(cp->Win9x) @@ -1897,8 +1774,10 @@ int kwsysProcessCreate(kwsysProcess* cp, int index, process's copies of the inherited stdout and stdin handles. The stderr handle is shared among all children and is closed by kwsysProcess_Execute after all children have been created. */ - kwsysProcessCleanupHandle(&si->StartupInfo.hStdInput); - kwsysProcessCleanupHandle(&si->StartupInfo.hStdOutput); + kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdInput, + STD_INPUT_HANDLE); + kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdOutput, + STD_OUTPUT_HANDLE); return 1; } @@ -1984,13 +1863,27 @@ int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name) /*--------------------------------------------------------------------------*/ int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle) { + /* Check whether the handle to be shared is already inherited. */ + DWORD flags; + int inherited = 0; + if(GetHandleInformation(GetStdHandle(nStdHandle), &flags) && + (flags & HANDLE_FLAG_INHERIT)) + { + inherited = 1; + } + /* Cleanup the previous handle. */ kwsysProcessCleanupHandle(handle); - /* Duplicate the standard handle to be sure it is inherited and can - be closed later. Do not close the original handle when + /* If the standard handle is not inherited then duplicate it to + create an inherited copy. Do not close the original handle when duplicating! */ - if(DuplicateHandle(GetCurrentProcess(), GetStdHandle(nStdHandle), + if(inherited) + { + *handle = GetStdHandle(nStdHandle); + return 1; + } + else if(DuplicateHandle(GetCurrentProcess(), GetStdHandle(nStdHandle), GetCurrentProcess(), handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { @@ -2046,6 +1939,19 @@ void kwsysProcessCleanupHandle(PHANDLE h) /*--------------------------------------------------------------------------*/ +/* Close the given handle if it is open and not a standard handle. + Reset its value to 0. */ +void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle) +{ + if(h && *h && (*h != GetStdHandle(nStdHandle))) + { + CloseHandle(*h); + *h = 0; + } +} + +/*--------------------------------------------------------------------------*/ + /* Close all handles created by kwsysProcess_Execute. */ void kwsysProcessCleanup(kwsysProcess* cp, int error) { @@ -2159,6 +2065,189 @@ void kwsysProcessCleanErrorMessage(kwsysProcess* cp) } /*--------------------------------------------------------------------------*/ +int kwsysProcessComputeCommandLength(kwsysProcess* cp, + char const* const* command) +{ + int length = 0; + if(cp->Verbatim) + { + /* Treat the first argument as a verbatim command line. Use its + length directly and add space for the null-terminator. */ + length = (int)strlen(*command)+1; + } + else + { + /* Compute the length of the command line when it is converted to + a single string. Space for the null-terminator is allocated by + the whitespace character allocated for the first argument that + will not be used. */ + char const* const* arg; + for(arg = command; *arg; ++arg) + { + /* Keep track of how many backslashes have been encountered in a + row in this argument. */ + int backslashes = 0; + int spaces = 0; + const char* c; + + /* Scan the string for spaces. If there are no spaces, we can + pass the argument verbatim. */ + for(c=*arg; *c; ++c) + { + if(*c == ' ' || *c == '\t') + { + spaces = 1; + break; + } + } + + /* Add the length of the argument, plus 1 for the space + separating the arguments. */ + length += (int)strlen(*arg) + 1; + + if(spaces) + { + /* Add 2 for double quotes since spaces are present. */ + length += 2; + + /* Scan the string to find characters that need escaping. */ + for(c=*arg; *c; ++c) + { + if(*c == '\\') + { + /* Found a backslash. It may need to be escaped later. */ + ++backslashes; + } + else if(*c == '"') + { + /* Found a double-quote. We need to escape it and all + immediately preceding backslashes. */ + length += backslashes + 1; + backslashes = 0; + } + else + { + /* Found another character. This eliminates the possibility + that any immediately preceding backslashes will be + escaped. */ + backslashes = 0; + } + } + + /* We need to escape all ending backslashes. */ + length += backslashes; + } + } + } + + return length; +} + +/*--------------------------------------------------------------------------*/ +void kwsysProcessComputeCommandLine(kwsysProcess* cp, + char const* const* command, + char* cmd) +{ + if(cp->Verbatim) + { + /* Copy the verbatim command line into the buffer. */ + strcpy(cmd, *command); + } + else + { + /* Construct the command line in the allocated buffer. */ + char const* const* arg; + for(arg = command; *arg; ++arg) + { + /* Keep track of how many backslashes have been encountered in a + row in an argument. */ + int backslashes = 0; + int spaces = 0; + const char* c; + + /* Scan the string for spaces. If there are no spaces, we can + pass the argument verbatim. */ + for(c=*arg; *c; ++c) + { + if(*c == ' ' || *c == '\t') + { + spaces = 1; + break; + } + } + + /* Add the separating space if this is not the first argument. */ + if(arg != command) + { + *cmd++ = ' '; + } + + if(spaces) + { + /* Add the opening double-quote for this argument. */ + *cmd++ = '"'; + + /* Add the characters of the argument, possibly escaping them. */ + for(c=*arg; *c; ++c) + { + if(*c == '\\') + { + /* Found a backslash. It may need to be escaped later. */ + ++backslashes; + *cmd++ = '\\'; + } + else if(*c == '"') + { + /* Add enough backslashes to escape any that preceded the + double-quote. */ + while(backslashes > 0) + { + --backslashes; + *cmd++ = '\\'; + } + + /* Add the backslash to escape the double-quote. */ + *cmd++ = '\\'; + + /* Add the double-quote itself. */ + *cmd++ = '"'; + } + else + { + /* We encountered a normal character. This eliminates any + escaping needed for preceding backslashes. Add the + character. */ + backslashes = 0; + *cmd++ = *c; + } + } + + /* Add enough backslashes to escape any trailing ones. */ + while(backslashes > 0) + { + --backslashes; + *cmd++ = '\\'; + } + + /* Add the closing double-quote for this argument. */ + *cmd++ = '"'; + } + else + { + /* No spaces. Add the argument verbatim. */ + for(c=*arg; *c; ++c) + { + *cmd++ = *c; + } + } + } + + /* Add the terminating null character to the command line. */ + *cmd = 0; + } +} + +/*--------------------------------------------------------------------------*/ /* Get the time at which either the process or user timeout will expire. Returns 1 if the user timeout is first, and 0 otherwise. */ int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, diff --git a/Source/kwsys/Terminal.c b/Source/kwsys/Terminal.c index 0837e10..e3265a1 100644 --- a/Source/kwsys/Terminal.c +++ b/Source/kwsys/Terminal.c @@ -142,10 +142,13 @@ static const char* kwsysTerminalVT100Names[] = "con80x50", "con80x60", "console", + "cygwin", "konsole", "linux", "msys", "rxvt", + "rxvt-unicode", + "screen", "vt100", "xterm", "xterm-color", diff --git a/Source/kwsys/kwsysPlatformCxxTests.cmake b/Source/kwsys/kwsysPlatformCxxTests.cmake index 6da82d6..7775457 100644 --- a/Source/kwsys/kwsysPlatformCxxTests.cmake +++ b/Source/kwsys/kwsysPlatformCxxTests.cmake @@ -7,10 +7,12 @@ MACRO(KWSYS_PLATFORM_CXX_TEST var description invert) COMPILE_DEFINITIONS -DTEST_${var} ${KWSYS_PLATFORM_CXX_TEST_DEFINES} OUTPUT_VARIABLE OUTPUT) IF(${var}_COMPILED) - FILE(APPEND ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeOutput.log + FILE(APPEND + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "${description} compiled with the following output:\n${OUTPUT}\n\n") ELSE(${var}_COMPILED) - FILE(APPEND ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeError.log + FILE(APPEND + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "${description} failed to compile with the following output:\n${OUTPUT}\n\n") ENDIF(${var}_COMPILED) IF(${invert} MATCHES INVERT) diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c index 36222b9..57802b2 100644 --- a/Source/kwsys/testProcess.c +++ b/Source/kwsys/testProcess.c @@ -32,7 +32,7 @@ int runChild(const char* cmd[], int state, int exception, int value, int share, int output, int delay, double timeout, int poll, - int repeat); + int repeat, int disown); int test1(int argc, const char* argv[]) { @@ -84,14 +84,6 @@ int test4(int argc, const char* argv[]) return 0; } -/* Quick hack to test grandchild killing. */ -/*#define TEST5_GRANDCHILD_KILL*/ -#ifdef TEST5_GRANDCHILD_KILL -# define TEST5_TIMEOUT 10 -#else -# define TEST5_TIMEOUT 30 -#endif - int test5(int argc, const char* argv[]) { int r; @@ -99,18 +91,14 @@ int test5(int argc, const char* argv[]) (void)argc; cmd[0] = argv[0]; cmd[1] = "run"; -#ifdef TEST5_GRANDCHILD_KILL - cmd[2] = "3"; -#else cmd[2] = "4"; -#endif cmd[3] = 0; fprintf(stdout, "Output on stdout before recursive test.\n"); fprintf(stderr, "Output on stderr before recursive test.\n"); fflush(stdout); fflush(stderr); r = runChild(cmd, kwsysProcess_State_Exception, - kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1); + kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0); fprintf(stdout, "Output on stdout after recursive test.\n"); fprintf(stderr, "Output on stderr after recursive test.\n"); fflush(stdout); @@ -163,10 +151,56 @@ int test7(int argc, const char* argv[]) return 0; } +int test8(int argc, const char* argv[]) +{ + /* Create a disowned grandchild to test handling of processes + that exit before their children. */ + int r; + const char* cmd[4]; + (void)argc; + cmd[0] = argv[0]; + cmd[1] = "run"; + cmd[2] = "108"; + cmd[3] = 0; + fprintf(stdout, "Output on stdout before grandchild test.\n"); + fprintf(stderr, "Output on stderr before grandchild test.\n"); + fflush(stdout); + fflush(stderr); + r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None, + 1, 1, 1, 0, 10, 0, 1, 1); + fprintf(stdout, "Output on stdout after grandchild test.\n"); + fprintf(stderr, "Output on stderr after grandchild test.\n"); + fflush(stdout); + fflush(stderr); + return r; +} + +int test8_grandchild(int argc, const char* argv[]) +{ + (void)argc; (void)argv; + fprintf(stdout, "Output on stdout from grandchild before sleep.\n"); + fprintf(stderr, "Output on stderr from grandchild before sleep.\n"); + fflush(stdout); + fflush(stderr); + /* TODO: Instead of closing pipes here leave them open to make sure + the grandparent can stop listening when the parent exits. This + part of the test cannot be enabled until the feature is + implemented. */ + fclose(stdout); + fclose(stderr); +#if defined(_WIN32) + Sleep(15000); +#else + sleep(15); +#endif + return 0; +} + + int runChild2(kwsysProcess* kp, const char* cmd[], int state, int exception, int value, int share, int output, int delay, double timeout, - int poll) + int poll, int disown) { int result = 0; char* data = 0; @@ -183,6 +217,10 @@ int runChild2(kwsysProcess* kp, kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDOUT, 1); kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDERR, 1); } + if(disown) + { + kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1); + } kwsysProcess_Execute(kp); if(poll) @@ -190,7 +228,7 @@ int runChild2(kwsysProcess* kp, pUserTimeout = &userTimeout; } - if(!share) + if(!share && !disown) { int p; while((p = kwsysProcess_WaitForData(kp, &data, &length, pUserTimeout))) @@ -236,7 +274,14 @@ int runChild2(kwsysProcess* kp, } } + if(disown) + { + kwsysProcess_Disown(kp); + } + else + { kwsysProcess_WaitForExit(kp, 0); + } switch (kwsysProcess_GetState(kp)) { @@ -258,6 +303,8 @@ int runChild2(kwsysProcess* kp, kwsysProcess_GetExceptionString(kp)); result = ((exception != kwsysProcess_GetExitException(kp)) || (value != kwsysProcess_GetExitValue(kp))); break; + case kwsysProcess_State_Disowned: + printf("Child was disowned.\n"); break; case kwsysProcess_State_Error: printf("Error in administrating child process: [%s]\n", kwsysProcess_GetErrorString(kp)); break; @@ -301,7 +348,7 @@ int runChild2(kwsysProcess* kp, int runChild(const char* cmd[], int state, int exception, int value, int share, int output, int delay, double timeout, - int poll, int repeat) + int poll, int repeat, int disown) { int result = 1; kwsysProcess* kp = kwsysProcess_New(); @@ -313,7 +360,7 @@ int runChild(const char* cmd[], int state, int exception, int value, while(repeat-- > 0) { result = runChild2(kp, cmd, state, exception, value, share, - output, delay, timeout, poll); + output, delay, timeout, poll, disown); } kwsysProcess_Delete(kp); return result; @@ -347,7 +394,7 @@ int main(int argc, const char* argv[]) n = atoi(argv[2]); } /* Check arguments. */ - if(n >= 1 && n <= 7 && argc == 3) + if(((n >= 1 && n <= 8) || n == 108) && argc == 3) { /* This is the child process for a requested test number. */ switch (n) @@ -359,14 +406,16 @@ int main(int argc, const char* argv[]) case 5: return test5(argc, argv); case 6: test6(argc, argv); return 0; case 7: return test7(argc, argv); + case 8: return test8(argc, argv); + case 108: return test8_grandchild(argc, argv); } fprintf(stderr, "Invalid test number %d.\n", n); return 1; } - else if(n >= 1 && n <= 7) + else if(n >= 1 && n <= 8) { /* This is the parent process for a requested test number. */ - int states[7] = + int states[8] = { kwsysProcess_State_Exited, kwsysProcess_State_Exited, @@ -374,9 +423,10 @@ int main(int argc, const char* argv[]) kwsysProcess_State_Exception, kwsysProcess_State_Exited, kwsysProcess_State_Expired, + kwsysProcess_State_Exited, kwsysProcess_State_Exited }; - int exceptions[7] = + int exceptions[8] = { kwsysProcess_Exception_None, kwsysProcess_Exception_None, @@ -384,14 +434,15 @@ int main(int argc, const char* argv[]) kwsysProcess_Exception_Fault, kwsysProcess_Exception_None, kwsysProcess_Exception_None, + kwsysProcess_Exception_None, kwsysProcess_Exception_None }; - int values[7] = {0, 123, 1, 1, 0, 0, 0}; - int outputs[7] = {1, 1, 1, 1, 1, 0, 1}; - int delays[7] = {0, 0, 0, 0, 0, 1, 0}; - double timeouts[7] = {10, 10, 10, 10, TEST5_TIMEOUT, 10, -1}; - int polls[7] = {0, 0, 0, 0, 0, 0, 1}; - int repeat[7] = {2, 1, 1, 1, 1, 1, 1}; + int values[8] = {0, 123, 1, 1, 0, 0, 0, 0}; + int outputs[8] = {1, 1, 1, 1, 1, 0, 1, 1}; + int delays[8] = {0, 0, 0, 0, 0, 1, 0, 0}; + double timeouts[8] = {10, 10, 10, 10, 30, 10, -1, 10}; + int polls[8] = {0, 0, 0, 0, 0, 0, 1, 0}; + int repeat[8] = {2, 1, 1, 1, 1, 1, 1, 1}; int r; const char* cmd[4]; #ifdef _WIN32 @@ -425,7 +476,7 @@ int main(int argc, const char* argv[]) fflush(stderr); r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], 0, outputs[n-1], delays[n-1], timeouts[n-1], - polls[n-1], repeat[n-1]); + polls[n-1], repeat[n-1], 0); fprintf(stdout, "Output on stdout after test %d.\n", n); fprintf(stderr, "Output on stderr after test %d.\n", n); fflush(stdout); @@ -444,7 +495,7 @@ int main(int argc, const char* argv[]) int exception = kwsysProcess_Exception_None; int value = 0; double timeout = 0; - int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout, 0, 1); + int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout, 0, 1, 0); return r; } else |