Przeglądaj źródła

ENH: Add check for infinite loops. Make sure that files written using WRITE_FILE and FILE WRITE are not used as input files. Fixes Bug #678 - WRITE_FILE and FILE(WRITE...) lead to infinite loops

Andy Cedilnik 21 lat temu
rodzic
commit
55a71ba572

+ 1 - 0
Source/cmFileCommand.cxx

@@ -100,6 +100,7 @@ bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args,
     }
   file << message;
   file.close();
+  m_Makefile->AddWrittenFile(fileName.c_str());
   return true;
 }
 

+ 4 - 0
Source/cmFileCommand.h

@@ -81,6 +81,10 @@ public:
       "if it does not exists.\n"
       "APPEND will write a message into a file same as WRITE, except "
       "it will append it to the end of the file\n"
+      "NOTE: When using FILE WRITE and FILE APPEND, the produced file "
+      "cannot be used as an input to CMake (CONFIGURE_FILE, source file ...) "
+      "because it will lead to infinite loop. Use CONFIGURE_FILE if you "
+      "want to generate input files to CMake.\n"
       "READ will read the content of a file and store it into the "
       "variable.\n"
       "GLOB will generate a list of all files that match the globbing "

+ 1 - 0
Source/cmGlobalGenerator.cxx

@@ -326,6 +326,7 @@ void cmGlobalGenerator::Configure()
         }
       m_CMakeInstance->UpdateProgress("Configuring", 
                                     0.9f+0.1f*(i+1.0f)/m_LocalGenerators.size());
+      m_LocalGenerators[i]->GetMakefile()->CheckInfiniteLoops();
       }
     }
 

+ 21 - 0
Source/cmMakefile.cxx

@@ -2357,5 +2357,26 @@ int cmMakefile::ConfigureFile(const char* infile, const char* outfile,
   return 1;
 }
 
+void cmMakefile::AddWrittenFile(const char* file)
+{ this->GetCMakeInstance()->AddWrittenFile(file); }
 
+bool cmMakefile::HasWrittenFile(const char* file)
+{ return this->GetCMakeInstance()->HasWrittenFile(file); }
 
+bool cmMakefile::CheckInfiniteLoops()
+{
+  std::vector<std::string>::iterator it;
+  for ( it = m_ListFiles.begin();
+    it != m_ListFiles.end();
+    ++ it )
+    {
+    if ( this->HasWrittenFile(it->c_str()) )
+      {
+      cmOStringStream str;
+      str << "File " << it->c_str() << " is written by WRITE_FILE (or FILE WRITE) command and should not be used as input to CMake. Please use CONFIGURE_FILE to be safe. Refer to the note next to FILE WRITE command.";
+      cmSystemTools::Error(str.str().c_str());
+      return false;
+      }
+    }
+  return true;
+}

+ 12 - 0
Source/cmMakefile.h

@@ -82,6 +82,18 @@ public:
   void RemoveFunctionBlocker(cmFunctionBlocker *fb)
     { m_FunctionBlockers.remove(fb);}
   void RemoveFunctionBlocker(const cmListFileFunction& lff);
+
+  /**
+   * Add file to the written file list. These file should not be in the list
+   * of dependencies because they cause infinite loops.
+   */
+  void AddWrittenFile(const char* file);
+  bool HasWrittenFile(const char* file);
+
+  /**
+   * Check if there are any infinite loops
+   */
+  bool CheckInfiniteLoops();
   
   /**
    * Try running cmake and building a file. This is used for dynalically

+ 1 - 0
Source/cmWriteFileCommand.cxx

@@ -55,6 +55,7 @@ bool cmWriteFileCommand::InitialPass(std::vector<std::string> const& args)
     }
   file << message << std::endl;
   file.close();
+  m_Makefile->AddWrittenFile(fileName.c_str());
 
   return true;
 }

+ 7 - 1
Source/cmWriteFileCommand.h

@@ -67,7 +67,13 @@ public:
       "  WRITE_FILE(filename \"message to write\"... [APPEND])\n"
       "The first argument is the file name, the rest of the arguments are "
       "messages to write. If the argument APPEND is specified, then "
-      "the message will be appended.";
+      "the message will be appended.\n"
+      "NOTE 1: FILE WRITE and FILE APPEND do exactly the same as this one "
+      "but add some more functionality.\n"
+      "NOTE 2: When using WRITE_FILE the produced file cannot be used as an "
+      "input to CMake (CONFIGURE_FILE, source file ...) because it will "
+      "lead to infinite loop. Use CONFIGURE_FILE if you want to generate "
+      "input files to CMake.";
     }
   
   cmTypeMacro(cmWriteFileCommand, cmCommand);

+ 10 - 0
Source/cmake.cxx

@@ -1416,3 +1416,13 @@ void cmake::GetGeneratorDocumentation(std::vector<cmDocumentationEntry>& v)
   cmDocumentationEntry empty = {0,0,0};
   v.push_back(empty);
 }
+
+void cmake::AddWrittenFile(const char* file)
+{
+  m_WrittenFiles.insert(file);
+}
+
+bool cmake::HasWrittenFile(const char* file)
+{
+  return m_WrittenFiles.find(file) != m_WrittenFiles.end();
+}

+ 7 - 1
Source/cmake.h

@@ -257,7 +257,11 @@ class cmake
   ///! Debug the try compile stuff by not delelting the files
   bool GetDebugTryCompile(){return m_DebugTryCompile;}
   void DebugTryCompileOn(){m_DebugTryCompile = true;}
-  
+
+  ///! Get the list of files written by CMake using FILE(WRITE / WRITE_FILE
+  void AddWrittenFile(const char* file);
+  bool HasWrittenFile(const char* file);
+
 protected:
   typedef cmGlobalGenerator* (*CreateGeneratorFunctionType)();
   typedef std::map<cmStdString, CreateGeneratorFunctionType> RegisteredGeneratorsMap;
@@ -273,6 +277,8 @@ protected:
   std::string m_cmStartDirectory; 
   std::string m_StartOutputDirectory;
 
+  std::set<cmStdString> m_WrittenFiles;
+
   ///! return true if the same cmake was used to make the cache.
   bool CacheVersionMatches();
   ///! read in a cmake list file to initialize the cache