Manuel, I have tweaked in r157528. I wonder it kept semantics... ...Takumi
2012/5/23 Manuel Klimek <[email protected]>: > On Wed, May 23, 2012 at 12:57 AM, NAKAMURA Takumi <[email protected]> > wrote: >> >> Manuel, I don't think it is compatible to Win32. >> Manipulating opened files would be hard on Win32. > > > I cargo culted the general concept here from other parts of clang. Which > parts exactly don't work with Win32? > > Thanks, > /Manuel > >> >> 2012/05/23 2:05 "Manuel Klimek" <[email protected]>: >> >>> Author: klimek >>> Date: Tue May 22 12:01:35 2012 >>> New Revision: 157260 >>> >>> URL: http://llvm.org/viewvc/llvm-project?rev=157260&view=rev >>> Log: >>> Adds a method overwriteChangedFiles to the Rewriter. This is implemented >>> by >>> first writing the changed files to a temporary location and then >>> overwriting >>> the original files atomically. >>> >>> Also adds a RewriterTestContext to aid unit testing rewrting logic in >>> general. >>> >>> >>> Added: >>> cfe/trunk/unittests/Tooling/RewriterTest.cpp (with props) >>> cfe/trunk/unittests/Tooling/RewriterTestContext.h (with props) >>> Modified: >>> cfe/trunk/include/clang/Rewrite/Rewriter.h >>> cfe/trunk/lib/Rewrite/Rewriter.cpp >>> cfe/trunk/unittests/CMakeLists.txt >>> >>> Modified: cfe/trunk/include/clang/Rewrite/Rewriter.h >>> URL: >>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Rewrite/Rewriter.h?rev=157260&r1=157259&r2=157260&view=diff >>> >>> ============================================================================== >>> --- cfe/trunk/include/clang/Rewrite/Rewriter.h (original) >>> +++ cfe/trunk/include/clang/Rewrite/Rewriter.h Tue May 22 12:01:35 2012 >>> @@ -279,6 +279,13 @@ >>> buffer_iterator buffer_begin() { return RewriteBuffers.begin(); } >>> buffer_iterator buffer_end() { return RewriteBuffers.end(); } >>> >>> + /// SaveFiles - Save all changed files to disk. >>> + /// >>> + /// Returns whether not all changes were saved successfully. >>> + /// Outputs diagnostics via the source manager's diagnostic engine >>> + /// in case of an error. >>> + bool overwriteChangedFiles(); >>> + >>> private: >>> unsigned getLocationOffsetAndFileID(SourceLocation Loc, FileID &FID) >>> const; >>> }; >>> >>> Modified: cfe/trunk/lib/Rewrite/Rewriter.cpp >>> URL: >>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Rewrite/Rewriter.cpp?rev=157260&r1=157259&r2=157260&view=diff >>> >>> ============================================================================== >>> --- cfe/trunk/lib/Rewrite/Rewriter.cpp (original) >>> +++ cfe/trunk/lib/Rewrite/Rewriter.cpp Tue May 22 12:01:35 2012 >>> @@ -15,9 +15,12 @@ >>> #include "clang/Rewrite/Rewriter.h" >>> #include "clang/AST/Stmt.h" >>> #include "clang/AST/Decl.h" >>> -#include "clang/Lex/Lexer.h" >>> +#include "clang/Basic/DiagnosticIDs.h" >>> +#include "clang/Basic/FileManager.h" >>> #include "clang/Basic/SourceManager.h" >>> +#include "clang/Lex/Lexer.h" >>> #include "llvm/ADT/SmallString.h" >>> +#include "llvm/Support/FileSystem.h" >>> using namespace clang; >>> >>> raw_ostream &RewriteBuffer::write(raw_ostream &os) const { >>> @@ -412,3 +415,68 @@ >>> >>> return false; >>> } >>> + >>> +// A wrapper for a file stream that atomically overwrites the target. >>> +// >>> +// Creates a file output stream for a temporary file in the constructor, >>> +// which is later accessible via getStream() if ok() return true. >>> +// Flushes the stream and moves the temporary file to the target >>> location >>> +// in the destructor. >>> +class AtomicallyMovedFile { >>> +public: >>> + AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef >>> Filename, >>> + bool &AllWritten) >>> + : Diagnostics(Diagnostics), Filename(Filename), >>> AllWritten(AllWritten) { >>> + TempFilename = Filename; >>> + TempFilename += "-%%%%%%%%"; >>> + int FD; >>> + if (llvm::sys::fs::unique_file(TempFilename.str(), FD, TempFilename, >>> + /*makeAbsolute=*/true, 0664)) { >>> + AllWritten = false; >>> + Diagnostics.Report(clang::diag::err_unable_to_make_temp) >>> + << TempFilename; >>> + } else { >>> + FileStream.reset(new llvm::raw_fd_ostream(FD, >>> /*shouldClose=*/true)); >>> + } >>> + } >>> + >>> + ~AtomicallyMovedFile() { >>> + if (!ok()) return; >>> + >>> + FileStream->flush(); >>> + if (llvm::error_code ec = >>> + llvm::sys::fs::rename(TempFilename.str(), Filename)) { >>> + AllWritten = false; >>> + Diagnostics.Report(clang::diag::err_unable_to_rename_temp) >>> + << TempFilename << Filename << ec.message(); >>> + bool existed; >>> + // If the remove fails, there's not a lot we can do - this is >>> already an >>> + // error. >>> + llvm::sys::fs::remove(TempFilename.str(), existed); >>> + } >>> + } >>> + >>> + bool ok() { return FileStream; } >>> + llvm::raw_ostream &getStream() { return *FileStream; } >>> + >>> +private: >>> + DiagnosticsEngine &Diagnostics; >>> + StringRef Filename; >>> + SmallString<128> TempFilename; >>> + OwningPtr<llvm::raw_fd_ostream> FileStream; >>> + bool &AllWritten; >>> +}; >>> + >>> +bool Rewriter::overwriteChangedFiles() { >>> + bool AllWritten = true; >>> + for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; >>> ++I) { >>> + const FileEntry *Entry = >>> + getSourceMgr().getFileEntryForID(I->first); >>> + AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), >>> Entry->getName(), >>> + AllWritten); >>> + if (File.ok()) { >>> + I->second.write(File.getStream()); >>> + } >>> + } >>> + return !AllWritten; >>> +} >>> >>> Modified: cfe/trunk/unittests/CMakeLists.txt >>> URL: >>> http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/CMakeLists.txt?rev=157260&r1=157259&r2=157260&view=diff >>> >>> ============================================================================== >>> --- cfe/trunk/unittests/CMakeLists.txt (original) >>> +++ cfe/trunk/unittests/CMakeLists.txt Tue May 22 12:01:35 2012 >>> @@ -70,5 +70,6 @@ >>> Tooling/CompilationDatabaseTest.cpp >>> Tooling/ToolingTest.cpp >>> Tooling/RecursiveASTVisitorTest.cpp >>> - USED_LIBS gtest gtest_main clangAST clangTooling >>> + Tooling/RewriterTest.cpp >>> + USED_LIBS gtest gtest_main clangAST clangTooling clangRewrite >>> ) >>> >>> Added: cfe/trunk/unittests/Tooling/RewriterTest.cpp >>> URL: >>> http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RewriterTest.cpp?rev=157260&view=auto >>> >>> ============================================================================== >>> --- cfe/trunk/unittests/Tooling/RewriterTest.cpp (added) >>> +++ cfe/trunk/unittests/Tooling/RewriterTest.cpp Tue May 22 12:01:35 2012 >>> @@ -0,0 +1,37 @@ >>> +//===- unittest/Tooling/RewriterTest.cpp >>> ----------------------------------===// >>> +// >>> +// The LLVM Compiler Infrastructure >>> +// >>> +// This file is distributed under the University of Illinois Open Source >>> +// License. See LICENSE.TXT for details. >>> +// >>> >>> +//===----------------------------------------------------------------------===// >>> + >>> +#include "RewriterTestContext.h" >>> +#include "gtest/gtest.h" >>> + >>> +namespace clang { >>> + >>> +TEST(Rewriter, OverwritesChangedFiles) { >>> + RewriterTestContext Context; >>> + FileID ID = Context.createOnDiskFile("t.cpp", >>> "line1\nline2\nline3\nline4"); >>> + Context.Rewrite.ReplaceText(Context.getLocation(ID, 2, 1), 5, >>> "replaced"); >>> + EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles()); >>> + EXPECT_EQ("line1\nreplaced\nline3\nline4", >>> + Context.getFileContentFromDisk("t.cpp")); >>> +} >>> + >>> +TEST(Rewriter, ContinuesOverwritingFilesOnError) { >>> + RewriterTestContext Context; >>> + FileID FailingID = Context.createInMemoryFile("invalid/failing.cpp", >>> "test"); >>> + Context.Rewrite.ReplaceText(Context.getLocation(FailingID, 1, 2), 1, >>> "other"); >>> + FileID WorkingID = Context.createOnDiskFile( >>> + "working.cpp", "line1\nline2\nline3\nline4"); >>> + Context.Rewrite.ReplaceText(Context.getLocation(WorkingID, 2, 1), 5, >>> + "replaced"); >>> + EXPECT_TRUE(Context.Rewrite.overwriteChangedFiles()); >>> + EXPECT_EQ("line1\nreplaced\nline3\nline4", >>> + Context.getFileContentFromDisk("working.cpp")); >>> +} >>> + >>> +} // end namespace clang >>> >>> Propchange: cfe/trunk/unittests/Tooling/RewriterTest.cpp >>> >>> ------------------------------------------------------------------------------ >>> svn:eol-style = LF >>> >>> Added: cfe/trunk/unittests/Tooling/RewriterTestContext.h >>> URL: >>> http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RewriterTestContext.h?rev=157260&view=auto >>> >>> ============================================================================== >>> --- cfe/trunk/unittests/Tooling/RewriterTestContext.h (added) >>> +++ cfe/trunk/unittests/Tooling/RewriterTestContext.h Tue May 22 12:01:35 >>> 2012 >>> @@ -0,0 +1,120 @@ >>> +//===--- RewriterTestContext.h ----------------------------------*- C++ >>> -*-===// >>> +// >>> +// The LLVM Compiler Infrastructure >>> +// >>> +// This file is distributed under the University of Illinois Open Source >>> +// License. See LICENSE.TXT for details. >>> +// >>> >>> +//===----------------------------------------------------------------------===// >>> +// >>> +// This file defines a utility class for Rewriter related tests. >>> +// >>> >>> +//===----------------------------------------------------------------------===// >>> + >>> +#ifndef LLVM_CLANG_REWRITER_TEST_CONTEXT_H >>> +#define LLVM_CLANG_REWRITER_TEST_CONTEXT_H >>> + >>> +#include "clang/Basic/Diagnostic.h" >>> +#include "clang/Basic/FileManager.h" >>> +#include "clang/Basic/LangOptions.h" >>> +#include "clang/Basic/SourceManager.h" >>> +#include "clang/Frontend/DiagnosticOptions.h" >>> +#include "clang/Frontend/TextDiagnosticPrinter.h" >>> +#include "clang/Rewrite/Rewriter.h" >>> +#include "llvm/Support/Path.h" >>> +#include "llvm/Support/raw_ostream.h" >>> + >>> +namespace clang { >>> + >>> +/// \brief A class that sets up a ready to use Rewriter. >>> +/// >>> +/// Useful in unit tests that need a Rewriter. Creates all dependencies >>> +/// of a Rewriter with default values for testing and provides >>> convenience >>> +/// methods, which help with writing tests that change files. >>> +class RewriterTestContext { >>> + public: >>> + RewriterTestContext() >>> + : Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>()), >>> + DiagnosticPrinter(llvm::outs(), DiagnosticOptions()), >>> + Files((FileSystemOptions())), >>> + Sources(Diagnostics, Files), >>> + Rewrite(Sources, Options) { >>> + Diagnostics.setClient(&DiagnosticPrinter, false); >>> + } >>> + >>> + ~RewriterTestContext() { >>> + if (TemporaryDirectory.isValid()) { >>> + std::string ErrorInfo; >>> + TemporaryDirectory.eraseFromDisk(true, &ErrorInfo); >>> + assert(ErrorInfo.empty()); >>> + } >>> + } >>> + >>> + FileID createInMemoryFile(StringRef Name, StringRef Content) { >>> + const llvm::MemoryBuffer *Source = >>> + llvm::MemoryBuffer::getMemBuffer(Content); >>> + const FileEntry *Entry = >>> + Files.getVirtualFile(Name, Source->getBufferSize(), 0); >>> + Sources.overrideFileContents(Entry, Source, true); >>> + assert(Entry != NULL); >>> + return Sources.createFileID(Entry, SourceLocation(), >>> SrcMgr::C_User); >>> + } >>> + >>> + FileID createOnDiskFile(StringRef Name, StringRef Content) { >>> + if (!TemporaryDirectory.isValid()) { >>> + std::string ErrorInfo; >>> + TemporaryDirectory = >>> llvm::sys::Path::GetTemporaryDirectory(&ErrorInfo); >>> + assert(ErrorInfo.empty()); >>> + } >>> + llvm::SmallString<1024> Path(TemporaryDirectory.str()); >>> + llvm::sys::path::append(Path, Name); >>> + std::string ErrorInfo; >>> + llvm::raw_fd_ostream OutStream(Path.c_str(), >>> + ErrorInfo, >>> llvm::raw_fd_ostream::F_Binary); >>> + assert(ErrorInfo.empty()); >>> + OutStream << Content; >>> + OutStream.close(); >>> + const FileEntry *File = Files.getFile(Path); >>> + assert(File != NULL); >>> + return Sources.createFileID(File, SourceLocation(), SrcMgr::C_User); >>> + } >>> + >>> + SourceLocation getLocation(FileID ID, unsigned Line, unsigned Column) >>> { >>> + SourceLocation Result = Sources.translateFileLineCol( >>> + Sources.getFileEntryForID(ID), Line, Column); >>> + assert(Result.isValid()); >>> + return Result; >>> + } >>> + >>> + std::string getRewrittenText(FileID ID) { >>> + std::string Result; >>> + llvm::raw_string_ostream OS(Result); >>> + Rewrite.getEditBuffer(ID).write(OS); >>> + return Result; >>> + } >>> + >>> + std::string getFileContentFromDisk(StringRef Name) { >>> + llvm::SmallString<1024> Path(TemporaryDirectory.str()); >>> + llvm::sys::path::append(Path, Name); >>> + // We need to read directly from the FileManager without relaying >>> through >>> + // a FileEntry, as otherwise we'd read through an already opened >>> file >>> + // descriptor, which might not see the changes made. >>> + // FIXME: Figure out whether there is a way to get the SourceManger >>> to >>> + // reopen the file. >>> + return Files.getBufferForFile(Path, NULL)->getBuffer(); >>> + } >>> + >>> + DiagnosticsEngine Diagnostics; >>> + TextDiagnosticPrinter DiagnosticPrinter; >>> + FileManager Files; >>> + SourceManager Sources; >>> + LangOptions Options; >>> + Rewriter Rewrite; >>> + >>> + // Will be set once on disk files are generated. >>> + llvm::sys::Path TemporaryDirectory; >>> +}; >>> + >>> +} // end namespace clang >>> + >>> +#endif >>> >>> Propchange: cfe/trunk/unittests/Tooling/RewriterTestContext.h >>> >>> ------------------------------------------------------------------------------ >>> svn:eol-style = LF >>> >>> >>> _______________________________________________ >>> cfe-commits mailing list >>> [email protected] >>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits > > _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
