From c841aaed836b3105fcce9487b34e541d6e092251 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Thu, 8 Sep 2022 16:56:00 +0200
Subject: [PATCH] ENH: use atomic move in wmkdepend

- avoids truncated files if dependency generation is interrupted
---
 wmake/src/wmkdepend.cc | 115 ++++++++++++++++++++++++-----------------
 wmake/src/wmkdepend.rl |  55 ++++++++++++++------
 2 files changed, 106 insertions(+), 64 deletions(-)

diff --git a/wmake/src/wmkdepend.cc b/wmake/src/wmkdepend.cc
index f8bf930a535..4d276d9b3aa 100644
--- a/wmake/src/wmkdepend.cc
+++ b/wmake/src/wmkdepend.cc
@@ -7,7 +7,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -112,6 +112,8 @@ bool optVerbose = false;
 //- The top-level source file being processed
 std::string sourceFile;
 
+//- The output stream
+FILE* output = stdout;
 
 //- All file opening and writing
 namespace Files
@@ -185,7 +187,7 @@ namespace Files
     }
 
 
-    //- Open a file for reading and emit its qualified name to stdout.
+    //- Open a file for reading and emit its qualified name to output.
     //
     //  Uses env substitutions at the beginning of the path
     //
@@ -215,13 +217,13 @@ namespace Files
                 )
                 {
                     fname += entry.len;  // Now positioned after the '/'
-                    fputs(entry.name.c_str(), stdout);
+                    fputs(entry.name.c_str(), output);
                     break;
                 }
             }
 
-            fputs(fname, stdout);
-            fputs(" \\\n", stdout);
+            fputs(fname, output);
+            fputs(" \\\n", output);
         }
         else if (errno == EMFILE)
         {
@@ -314,14 +316,14 @@ namespace Files
 // Can use 'variable p xxx;' etc to change these names
 
 
-#line 318 "wmkdepend.cc"
+#line 320 "wmkdepend.cc"
 static const int wmkdep_start = 21;
 static const int wmkdep_error = 0;
 
 static const int wmkdep_en_main = 21;
 
 
-#line 344 "wmkdepend.rl"
+#line 346 "wmkdepend.rl"
 
 
 
@@ -362,7 +364,7 @@ void processFile(std::string fileName)
 
     // Initialize FSM variables
     
-#line 366 "wmkdepend.cc"
+#line 368 "wmkdepend.cc"
 	{
 	cs = wmkdep_start;
 	ts = 0;
@@ -370,7 +372,7 @@ void processFile(std::string fileName)
 	act = 0;
 	}
 
-#line 383 "wmkdepend.rl"
+#line 385 "wmkdepend.rl"
    /* ^^^ FSM initialization here ^^^ */;
 
     // Local token start
@@ -415,7 +417,7 @@ void processFile(std::string fileName)
         }
 
         
-#line 419 "wmkdepend.cc"
+#line 421 "wmkdepend.cc"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -434,35 +436,35 @@ tr0:
 	}
 	goto st21;
 tr2:
-#line 342 "wmkdepend.rl"
+#line 344 "wmkdepend.rl"
 	{te = p+1;}
 	goto st21;
 tr17:
-#line 342 "wmkdepend.rl"
+#line 344 "wmkdepend.rl"
 	{{p = ((te))-1;}}
 	goto st21;
 tr21:
-#line 337 "wmkdepend.rl"
+#line 339 "wmkdepend.rl"
 	{te = p+1;}
 	goto st21;
 tr29:
-#line 340 "wmkdepend.rl"
+#line 342 "wmkdepend.rl"
 	{te = p+1;}
 	goto st21;
 tr31:
-#line 339 "wmkdepend.rl"
+#line 341 "wmkdepend.rl"
 	{te = p+1;}
 	goto st21;
 tr36:
-#line 334 "wmkdepend.rl"
+#line 336 "wmkdepend.rl"
 	{te = p;p--;}
 	goto st21;
 tr37:
-#line 342 "wmkdepend.rl"
+#line 344 "wmkdepend.rl"
 	{te = p;p--;}
 	goto st21;
 tr38:
-#line 340 "wmkdepend.rl"
+#line 342 "wmkdepend.rl"
 	{te = p;p--;}
 	goto st21;
 st21:
@@ -475,7 +477,7 @@ st21:
 case 21:
 #line 1 "NONE"
 	{ts = p;}
-#line 479 "wmkdepend.cc"
+#line 481 "wmkdepend.cc"
 	switch( (*p) ) {
 		case 10: goto st23;
 		case 11: goto tr34;
@@ -496,14 +498,14 @@ case 1:
 tr32:
 #line 1 "NONE"
 	{te = p+1;}
-#line 334 "wmkdepend.rl"
+#line 336 "wmkdepend.rl"
 	{act = 1;}
 	goto st22;
 st22:
 	if ( ++p == pe )
 		goto _test_eof22;
 case 22:
-#line 507 "wmkdepend.cc"
+#line 509 "wmkdepend.cc"
 	switch( (*p) ) {
 		case 10: goto st23;
 		case 11: goto tr34;
@@ -525,14 +527,14 @@ case 23:
 tr34:
 #line 1 "NONE"
 	{te = p+1;}
-#line 334 "wmkdepend.rl"
+#line 336 "wmkdepend.rl"
 	{act = 1;}
 	goto st24;
 st24:
 	if ( ++p == pe )
 		goto _test_eof24;
 case 24:
-#line 536 "wmkdepend.cc"
+#line 538 "wmkdepend.cc"
 	switch( (*p) ) {
 		case 10: goto st23;
 		case 32: goto tr34;
@@ -630,14 +632,14 @@ case 10:
 	}
 	goto tr12;
 tr12:
-#line 318 "wmkdepend.rl"
+#line 320 "wmkdepend.rl"
 	{ tok = p;  /* Local token start */ }
 	goto st11;
 st11:
 	if ( ++p == pe )
 		goto _test_eof11;
 case 11:
-#line 641 "wmkdepend.cc"
+#line 643 "wmkdepend.cc"
 	switch( (*p) ) {
 		case 10: goto tr15;
 		case 34: goto tr16;
@@ -646,7 +648,7 @@ case 11:
 tr13:
 #line 1 "NONE"
 	{te = p+1;}
-#line 318 "wmkdepend.rl"
+#line 320 "wmkdepend.rl"
 	{ tok = p;  /* Local token start */ }
 	goto st25;
 tr15:
@@ -657,7 +659,7 @@ st25:
 	if ( ++p == pe )
 		goto _test_eof25;
 case 25:
-#line 661 "wmkdepend.cc"
+#line 663 "wmkdepend.cc"
 	if ( (*p) == 34 )
 		goto tr19;
 	goto st12;
@@ -669,7 +671,7 @@ case 12:
 		goto tr19;
 	goto st12;
 tr19:
-#line 320 "wmkdepend.rl"
+#line 322 "wmkdepend.rl"
 	{
         processFile(tok, p);
         tok = nullptr;          /* Done with buffer */
@@ -679,12 +681,12 @@ st13:
 	if ( ++p == pe )
 		goto _test_eof13;
 case 13:
-#line 683 "wmkdepend.cc"
+#line 685 "wmkdepend.cc"
 	if ( (*p) == 10 )
 		goto tr21;
 	goto st13;
 tr16:
-#line 320 "wmkdepend.rl"
+#line 322 "wmkdepend.rl"
 	{
         processFile(tok, p);
         tok = nullptr;          /* Done with buffer */
@@ -694,7 +696,7 @@ st14:
 	if ( ++p == pe )
 		goto _test_eof14;
 case 14:
-#line 698 "wmkdepend.cc"
+#line 700 "wmkdepend.cc"
 	if ( (*p) == 10 )
 		goto tr21;
 	goto st14;
@@ -725,7 +727,7 @@ st26:
 	if ( ++p == pe )
 		goto _test_eof26;
 case 26:
-#line 729 "wmkdepend.cc"
+#line 731 "wmkdepend.cc"
 	if ( (*p) == 42 )
 		goto st18;
 	goto st17;
@@ -758,14 +760,14 @@ case 19:
 tr30:
 #line 1 "NONE"
 	{te = p+1;}
-#line 340 "wmkdepend.rl"
+#line 342 "wmkdepend.rl"
 	{act = 4;}
 	goto st27;
 st27:
 	if ( ++p == pe )
 		goto _test_eof27;
 case 27:
-#line 769 "wmkdepend.cc"
+#line 771 "wmkdepend.cc"
 	if ( (*p) == 10 )
 		goto tr2;
 	goto st1;
@@ -840,7 +842,7 @@ cs = 0;
 	_out: {}
 	}
 
-#line 426 "wmkdepend.rl"
+#line 428 "wmkdepend.rl"
        /* ^^^ FSM execution here ^^^ */;
 
         if (0 == cs)
@@ -877,10 +879,12 @@ cs = 0;
             pending = 0;
         }
     }
-    fclose(infile);
+    ::fclose(infile);
 }
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
 int main(int argc, char* argv[])
 {
     if (argc < 2)
@@ -927,7 +931,7 @@ int main(int argc, char* argv[])
 
     // Verify that input file has an extension
     {
-        auto dot = sourceFile.find_last_of("./");
+        const auto dot = sourceFile.find_last_of("./");
         if (dot == std::string::npos || sourceFile[dot] != '.')
         {
             std::cerr
@@ -979,22 +983,39 @@ int main(int argc, char* argv[])
         }
     }
 
-    if (outputFile.size() && !freopen(outputFile.c_str(), "w", stdout))
+
+    // Output to an intermediate file
+    std::string outputTmp;
+    if (outputFile.length())
     {
-        std::cerr
-            << EXENAME ": could not open file '"
-            << outputFile << "' for output: " << strerror(errno) << "\n";
-        return 1;
+        outputTmp = outputFile + ".part";
+
+        output = ::fopen(outputTmp.c_str(), "w");
+
+        if (!output)
+        {
+            std::cerr
+                << EXENAME ": could not open file '"
+                << outputTmp << "' for output: " << strerror(errno) << '\n';
+            return 1;
+        }
     }
 
-    fputs("$(OBJECTS_DIR)/", stdout);
-    fputs(sourceFile.c_str(), stdout);
-    fputs(".dep: \\\n", stdout);
+    fputs("$(OBJECTS_DIR)/", output);
+    fputs(sourceFile.c_str(), output);
+    fputs(".dep: \\\n", output);
 
     processFile(sourceFile);
 
-    fputs("\n#END\n", stdout);
-    fflush(stdout);
+    fputs("\n#END\n", output);
+    fflush(output);
+
+    // Atomic move of intermediate file to final output file
+    if (outputFile.length())
+    {
+        ::fclose(output);
+        ::rename(outputTmp.c_str(), outputFile.c_str());
+    }
 
     return 0;
 }
diff --git a/wmake/src/wmkdepend.rl b/wmake/src/wmkdepend.rl
index 04d7ece23d2..85c109f63d7 100644
--- a/wmake/src/wmkdepend.rl
+++ b/wmake/src/wmkdepend.rl
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -110,6 +110,8 @@ bool optVerbose = false;
 //- The top-level source file being processed
 std::string sourceFile;
 
+//- The output stream
+FILE* output = stdout;
 
 //- All file opening and writing
 namespace Files
@@ -183,7 +185,7 @@ namespace Files
     }
 
 
-    //- Open a file for reading and emit its qualified name to stdout.
+    //- Open a file for reading and emit its qualified name to output.
     //
     //  Uses env substitutions at the beginning of the path
     //
@@ -213,13 +215,13 @@ namespace Files
                 )
                 {
                     fname += entry.len;  // Now positioned after the '/'
-                    fputs(entry.name.c_str(), stdout);
+                    fputs(entry.name.c_str(), output);
                     break;
                 }
             }
 
-            fputs(fname, stdout);
-            fputs(" \\\n", stdout);
+            fputs(fname, output);
+            fputs(" \\\n", output);
         }
         else if (errno == EMFILE)
         {
@@ -459,10 +461,12 @@ void processFile(std::string fileName)
             pending = 0;
         }
     }
-    fclose(infile);
+    ::fclose(infile);
 }
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
 int main(int argc, char* argv[])
 {
     if (argc < 2)
@@ -509,7 +513,7 @@ int main(int argc, char* argv[])
 
     // Verify that input file has an extension
     {
-        auto dot = sourceFile.find_last_of("./");
+        const auto dot = sourceFile.find_last_of("./");
         if (dot == std::string::npos || sourceFile[dot] != '.')
         {
             std::cerr
@@ -561,22 +565,39 @@ int main(int argc, char* argv[])
         }
     }
 
-    if (outputFile.size() && !freopen(outputFile.c_str(), "w", stdout))
+
+    // Output to an intermediate file
+    std::string outputTmp;
+    if (outputFile.length())
     {
-        std::cerr
-            << EXENAME ": could not open file '"
-            << outputFile << "' for output: " << strerror(errno) << "\n";
-        return 1;
+        outputTmp = outputFile + ".part";
+
+        output = ::fopen(outputTmp.c_str(), "w");
+
+        if (!output)
+        {
+            std::cerr
+                << EXENAME ": could not open file '"
+                << outputTmp << "' for output: " << strerror(errno) << '\n';
+            return 1;
+        }
     }
 
-    fputs("$(OBJECTS_DIR)/", stdout);
-    fputs(sourceFile.c_str(), stdout);
-    fputs(".dep: \\\n", stdout);
+    fputs("$(OBJECTS_DIR)/", output);
+    fputs(sourceFile.c_str(), output);
+    fputs(".dep: \\\n", output);
 
     processFile(sourceFile);
 
-    fputs("\n#END\n", stdout);
-    fflush(stdout);
+    fputs("\n#END\n", output);
+    fflush(output);
+
+    // Atomic move of intermediate file to final output file
+    if (outputFile.length())
+    {
+        ::fclose(output);
+        ::rename(outputTmp.c_str(), outputFile.c_str());
+    }
 
     return 0;
 }
-- 
GitLab