@@ -337,6 +337,89 @@ Error OnDiskOutputFile::initializeStream() {
337
337
return Error::success ();
338
338
}
339
339
340
+ namespace {
341
+ class OpenFileRAII {
342
+ static const int InvalidFd = -1 ;
343
+
344
+ public:
345
+ int Fd = InvalidFd;
346
+
347
+ ~OpenFileRAII () {
348
+ if (Fd != InvalidFd)
349
+ llvm::sys::Process::SafelyCloseFileDescriptor (Fd);
350
+ }
351
+ };
352
+
353
+ enum class FileDifference : uint8_t {
354
+ // / The source and destination paths refer to the exact same file.
355
+ IdenticalFile,
356
+ // / The source and destination paths refer to separate files with identical
357
+ // / contents.
358
+ SameContents,
359
+ // / The source and destination paths refer to separate files with different
360
+ // / contents.
361
+ DifferentContents
362
+ };
363
+ } // end anonymous namespace
364
+
365
+ static Expected<FileDifference>
366
+ areFilesDifferent (const llvm::Twine &Source, const llvm::Twine &Destination) {
367
+ if (sys::fs::equivalent (Source, Destination))
368
+ return FileDifference::IdenticalFile;
369
+
370
+ OpenFileRAII SourceFile;
371
+ sys::fs::file_status SourceStatus;
372
+ // If we can't open the source file, fail.
373
+ if (std::error_code EC = sys::fs::openFileForRead (Source, SourceFile.Fd ))
374
+ return convertToOutputError (Source, EC);
375
+
376
+ // If we can't stat the source file, fail.
377
+ if (std::error_code EC = sys::fs::status (SourceFile.Fd , SourceStatus))
378
+ return convertToOutputError (Source, EC);
379
+
380
+ OpenFileRAII DestFile;
381
+ sys::fs::file_status DestStatus;
382
+ // If we can't open the destination file, report different.
383
+ if (std::error_code Error =
384
+ sys::fs::openFileForRead (Destination, DestFile.Fd ))
385
+ return FileDifference::DifferentContents;
386
+
387
+ // If we can't open the destination file, report different.
388
+ if (std::error_code Error = sys::fs::status (DestFile.Fd , DestStatus))
389
+ return FileDifference::DifferentContents;
390
+
391
+ // If the files are different sizes, they must be different.
392
+ uint64_t Size = SourceStatus.getSize ();
393
+ if (Size != DestStatus.getSize ())
394
+ return FileDifference::DifferentContents;
395
+
396
+ // If both files are zero size, they must be the same.
397
+ if (Size == 0 )
398
+ return FileDifference::SameContents;
399
+
400
+ // The two files match in size, so we have to compare the bytes to determine
401
+ // if they're the same.
402
+ std::error_code SourceRegionErr;
403
+ sys::fs::mapped_file_region SourceRegion (
404
+ sys::fs::convertFDToNativeFile (SourceFile.Fd ),
405
+ sys::fs::mapped_file_region::readonly, Size, 0 , SourceRegionErr);
406
+ if (SourceRegionErr)
407
+ return convertToOutputError (Source, SourceRegionErr);
408
+
409
+ std::error_code DestRegionErr;
410
+ sys::fs::mapped_file_region DestRegion (
411
+ sys::fs::convertFDToNativeFile (DestFile.Fd ),
412
+ sys::fs::mapped_file_region::readonly, Size, 0 , DestRegionErr);
413
+
414
+ if (DestRegionErr)
415
+ return FileDifference::DifferentContents;
416
+
417
+ if (memcmp (SourceRegion.const_data (), DestRegion.const_data (), Size) != 0 )
418
+ return FileDifference::DifferentContents;
419
+
420
+ return FileDifference::SameContents;
421
+ }
422
+
340
423
Error OnDiskOutputFile::keep () {
341
424
// Destroy the streams to flush them.
342
425
BufferOS.reset ();
@@ -351,6 +434,25 @@ Error OnDiskOutputFile::keep() {
351
434
if (!TempPath)
352
435
return Error::success ();
353
436
437
+ if (Config.getOnlyIfDifferent ()) {
438
+ auto Result = areFilesDifferent (*TempPath, OutputPath);
439
+ if (!Result)
440
+ return Result.takeError ();
441
+ switch (*Result) {
442
+ case FileDifference::IdenticalFile:
443
+ // Do nothing for a self-move.
444
+ return Error::success ();
445
+
446
+ case FileDifference::SameContents:
447
+ // Files are identical; remove the source file.
448
+ (void ) sys::fs::remove (*TempPath);
449
+ return Error::success ();
450
+
451
+ case FileDifference::DifferentContents:
452
+ break ; // Rename the file.
453
+ }
454
+ }
455
+
354
456
// Move temporary to the final output path and remove it if that fails.
355
457
std::error_code RenameEC = sys::fs::rename (*TempPath, OutputPath);
356
458
if (!RenameEC)
0 commit comments