[arcmt] Introduce new '-ccc-arcmt-migrate <path>' ARC migration driver option.

This is a new mode of migration, where we avoid modifying the original files but
we emit temporary files instead.

<path> will be used to keep migration process metadata. Currently the temporary files
that are produced are put in the system's temp directory but we can put them
in the <path> if is necessary.

Also introduce new ARC migration functions in libclang whose only purpose,
currently, is to accept <path> and provide pairs of original file/transformed file
to map from the originals to the files after transformations are applied.

Finally introduce the c-arcmt-test utility that exercises the new libclang functions,
update arcmt-test, and add tests for the whole process.

rdar://9735086.

llvm-svn: 134844
diff --git a/clang/tools/arcmt-test/arcmt-test.cpp b/clang/tools/arcmt-test/arcmt-test.cpp
index 702e13a..eb0f569 100644
--- a/clang/tools/arcmt-test/arcmt-test.cpp
+++ b/clang/tools/arcmt-test/arcmt-test.cpp
@@ -14,7 +14,9 @@
 #include "clang/Frontend/Utils.h"
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Signals.h"
+#include "llvm/Support/system_error.h"
 
 using namespace clang;
 using namespace arcmt;
@@ -37,6 +39,20 @@
 static llvm::cl::opt<bool>
 VerboseOpt("v", llvm::cl::desc("Enable verbose output"));
 
+static llvm::cl::opt<bool>
+VerifyTransformedFiles("verify-transformed-files",
+llvm::cl::desc("Read pairs of file mappings (typically the output of "
+               "c-arcmt-test) and compare their contents with the filenames "
+               "provided in command-line"));
+
+static llvm::cl::opt<std::string>
+RemappingsFile("remappings-file",
+               llvm::cl::desc("Pairs of file mappings (typically the output of "
+               "c-arcmt-test)"));
+
+static llvm::cl::list<std::string>
+ResultFiles(llvm::cl::Positional, llvm::cl::desc("<filename>..."));
+
 static llvm::cl::extrahelp extraHelp(
   "\nusage with compiler args: arcmt-test [options] --args [compiler flags]\n");
 
@@ -183,6 +199,105 @@
   return false;
 }
 
+static bool filesCompareEqual(llvm::StringRef fname1, llvm::StringRef fname2) {
+  using namespace llvm;
+
+  OwningPtr<MemoryBuffer> file1;
+  MemoryBuffer::getFile(fname1, file1);
+  if (!file1)
+    return false;
+  
+  OwningPtr<MemoryBuffer> file2;
+  MemoryBuffer::getFile(fname2, file2);
+  if (!file2)
+    return false;
+
+  return file1->getBuffer() == file2->getBuffer();
+}
+
+static bool verifyTransformedFiles(llvm::ArrayRef<std::string> resultFiles) {
+  using namespace llvm;
+
+  assert(!resultFiles.empty());
+
+  std::map<StringRef, StringRef> resultMap;
+
+  for (ArrayRef<std::string>::iterator
+         I = resultFiles.begin(), E = resultFiles.end(); I != E; ++I) {
+    StringRef fname(*I);
+    if (!fname.endswith(".result")) {
+      errs() << "error: filename '" << fname
+                   << "' does not have '.result' extension\n";
+      return true;
+    }
+    resultMap[sys::path::stem(fname)] = fname;
+  }
+
+  OwningPtr<MemoryBuffer> inputBuf;
+  if (RemappingsFile.empty())
+    MemoryBuffer::getSTDIN(inputBuf);
+  else
+    MemoryBuffer::getFile(RemappingsFile, inputBuf);
+  if (!inputBuf) {
+    errs() << "error: could not read remappings input\n";
+    return true;
+  }
+
+  SmallVector<StringRef, 8> strs;
+  inputBuf->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+
+  if (strs.empty()) {
+    errs() << "error: no files to verify from stdin\n";
+    return true;
+  }
+  if (strs.size() % 2 != 0) {
+    errs() << "error: files to verify are not original/result pairs\n";
+    return true;
+  }
+
+  for (unsigned i = 0, e = strs.size(); i != e; i += 2) {
+    StringRef inputOrigFname = strs[i];
+    StringRef inputResultFname = strs[i+1];
+
+    std::map<StringRef, StringRef>::iterator It;
+    It = resultMap.find(sys::path::filename(inputOrigFname));
+    if (It == resultMap.end()) {
+      errs() << "error: '" << inputOrigFname << "' is not in the list of "
+             << "transformed files to verify\n";
+      return true;
+    }
+
+    bool exists = false;
+    sys::fs::exists(It->second, exists);
+    if (!exists) {
+      errs() << "error: '" << It->second << "' does not exist\n";
+      return true;
+    }
+    sys::fs::exists(inputResultFname, exists);
+    if (!exists) {
+      errs() << "error: '" << inputResultFname << "' does not exist\n";
+      return true;
+    }
+
+    if (!filesCompareEqual(It->second, inputResultFname)) {
+      errs() << "error: '" << It->second << "' is different than "
+             << "'" << inputResultFname << "'\n";
+      return true;
+    }
+
+    resultMap.erase(It);
+  }
+
+  if (!resultMap.empty()) {
+    for (std::map<StringRef, StringRef>::iterator
+           I = resultMap.begin(), E = resultMap.end(); I != E; ++I)
+      errs() << "error: '" << I->second << "' was not verified!\n";
+    return true;
+  }
+
+  return false; 
+}
+
 //===----------------------------------------------------------------------===//
 // Misc. functions.
 //===----------------------------------------------------------------------===//
@@ -236,7 +351,15 @@
       break;
   }
   llvm::cl::ParseCommandLineOptions(optargc, const_cast<char **>(argv), "arcmt-test");
-  
+
+  if (VerifyTransformedFiles) {
+    if (ResultFiles.empty()) {
+      llvm::cl::PrintHelpMessage();
+      return 1;
+    }
+    return verifyTransformedFiles(ResultFiles);
+  }
+
   if (optargc == argc) {
     llvm::cl::PrintHelpMessage();
     return 1;