mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Equivalent to llvm::sys::fs::rename, except that if the destination file exists and has the same contents as the source file, the source file is simply deleted and the destination file is not touched. Used in next commit. Swift SVN r28041
91 lines
2.9 KiB
C++
91 lines
2.9 KiB
C++
//===--- FileSystem.cpp - Extra helpers for manipulating files ------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/FileSystem.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Process.h"
|
|
|
|
using namespace swift;
|
|
|
|
namespace {
|
|
class OpenFileRAII {
|
|
static const int INVALID_FD = -1;
|
|
public:
|
|
int fd = INVALID_FD;
|
|
|
|
~OpenFileRAII() {
|
|
if (fd != INVALID_FD)
|
|
llvm::sys::Process::SafelyCloseFileDescriptor(fd);
|
|
}
|
|
};
|
|
}
|
|
|
|
std::error_code swift::moveFileIfDifferent(const llvm::Twine &source,
|
|
const llvm::Twine &destination) {
|
|
namespace fs = llvm::sys::fs;
|
|
|
|
// First check for a self-move.
|
|
if (fs::equivalent(source, destination))
|
|
return std::error_code();
|
|
|
|
OpenFileRAII sourceFile;
|
|
fs::file_status sourceStatus;
|
|
if (std::error_code error = fs::openFileForRead(source, sourceFile.fd)) {
|
|
// If we can't open the source file, fail.
|
|
return error;
|
|
}
|
|
if (std::error_code error = fs::status(sourceFile.fd, sourceStatus)) {
|
|
// If we can't stat the source file, fail.
|
|
return error;
|
|
}
|
|
|
|
OpenFileRAII destFile;
|
|
fs::file_status destStatus;
|
|
bool couldReadDest = !fs::openFileForRead(destination, destFile.fd);
|
|
if (couldReadDest)
|
|
couldReadDest = !fs::status(destFile.fd, destStatus);
|
|
|
|
// If we could read the destination file, and it matches the source file in
|
|
// size, they may be the same. Do an actual comparison of the contents.
|
|
if (couldReadDest && sourceStatus.getSize() == destStatus.getSize()) {
|
|
uint64_t size = sourceStatus.getSize();
|
|
bool same = false;
|
|
if (size == 0) {
|
|
same = true;
|
|
} else {
|
|
std::error_code sourceRegionErr;
|
|
fs::mapped_file_region sourceRegion(sourceFile.fd,
|
|
fs::mapped_file_region::readonly,
|
|
size, 0, sourceRegionErr);
|
|
if (sourceRegionErr)
|
|
return sourceRegionErr;
|
|
|
|
std::error_code destRegionErr;
|
|
fs::mapped_file_region destRegion(destFile.fd,
|
|
fs::mapped_file_region::readonly,
|
|
size, 0, destRegionErr);
|
|
|
|
if (!destRegionErr) {
|
|
same = (0 == memcmp(sourceRegion.const_data(), destRegion.const_data(),
|
|
size));
|
|
}
|
|
}
|
|
|
|
// If the file contents are the same, we are done. Just delete the source.
|
|
if (same)
|
|
return fs::remove(source);
|
|
}
|
|
|
|
// If we get here, we weren't able to prove that the files are the same.
|
|
return fs::rename(source, destination);
|
|
}
|