[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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@134844 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang-c/ARCMigrate.h b/include/clang-c/ARCMigrate.h
new file mode 100644
index 0000000..00085be
--- /dev/null
+++ b/include/clang-c/ARCMigrate.h
@@ -0,0 +1,79 @@
+/*===-- clang-c/ARCMigrate.h - ARC Migration Public C Interface ---*- C -*-===*\
+|*                                                                            *|
+|*                     The LLVM Compiler Infrastructure                       *|
+|*                                                                            *|
+|* This file is distributed under the University of Illinois Open Source      *|
+|* License. See LICENSE.TXT for details.                                      *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header provides a public interface to a Clang library for migrating   *|
+|* objective-c source files to ARC mode.                                      *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef CLANG_C_ARCMIGRATE_H
+#define CLANG_C_ARCMIGRATE_H
+
+#include "clang-c/Index.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \defgroup CARCMT libclang: C Interface to Clang ARC migration library
+ *
+ * The C Interface provides a small API that exposes facilities for translating
+ * objective-c source files of a project to Automatic Reference Counting mode.
+ *
+ * To avoid namespace pollution, data types are prefixed with "CMT" and
+ * functions are prefixed with "arcmt_".
+ *
+ * @{
+ */
+
+/**
+ * \brief A remapping of original source files and their translated files.
+ */
+typedef void *CMTRemap;
+
+/**
+ * \brief Retrieve a remapping.
+ *
+ * \param migrate_dir_path the path that clang used during the migration process.
+ *
+ * \returns the requested remapping. This remapping must be freed
+ * via a call to \c arcmt_remap_dispose(). Can return NULL if an error occurred.
+ */
+CINDEX_LINKAGE CMTRemap arcmt_getRemappings(const char *migrate_dir_path);
+
+/**
+ * \brief Determine the number of remappings.
+ */
+CINDEX_LINKAGE unsigned arcmt_remap_getNumFiles(CMTRemap);
+
+/**
+ * \brief Get the original filename.
+ */
+CINDEX_LINKAGE CXString arcmt_remap_getOriginalFile(CMTRemap, unsigned index);
+
+/**
+ * \brief Get the filename that the original file was translated into.
+ */
+CINDEX_LINKAGE
+CXString arcmt_remap_getTransformedFile(CMTRemap, unsigned index);
+
+/**
+ * \brief Dispose the remapping.
+ */
+CINDEX_LINKAGE void arcmt_remap_dispose(CMTRemap);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/include/clang/ARCMigrate/ARCMT.h b/include/clang/ARCMigrate/ARCMT.h
index 13f0778..ad5cf4a 100644
--- a/include/clang/ARCMigrate/ARCMT.h
+++ b/include/clang/ARCMigrate/ARCMT.h
@@ -41,6 +41,23 @@
                           llvm::StringRef Filename, InputKind Kind,
                           DiagnosticClient *DiagClient);
 
+/// \brief Applies automatic modifications and produces temporary files
+/// and metadata into the \arg outputDir path.
+///
+/// \returns false if no error is produced, true otherwise.
+bool migrateWithTemporaryFiles(CompilerInvocation &origCI,
+                               llvm::StringRef Filename, InputKind Kind,
+                               DiagnosticClient *DiagClient,
+                               llvm::StringRef outputDir);
+
+/// \brief Get the set of file remappings from the \arg outputDir path that
+/// migrateWithTemporaryFiles produced.
+///
+/// \returns false if no error is produced, true otherwise.
+bool getFileRemappings(std::vector<std::pair<std::string,std::string> > &remap,
+                       llvm::StringRef outputDir,
+                       DiagnosticClient *DiagClient);
+
 typedef void (*TransformFn)(MigrationPass &pass);
 
 std::vector<TransformFn> getAllTransformations();
@@ -51,8 +68,8 @@
   FileRemapper Remapper;
 
 public:
-  MigrationProcess(const CompilerInvocation &CI, DiagnosticClient *diagClient)
-    : OrigCI(CI), DiagClient(diagClient) { }
+  MigrationProcess(const CompilerInvocation &CI, DiagnosticClient *diagClient,
+                   llvm::StringRef outputDir = llvm::StringRef());
 
   class RewriteListener {
   public:
diff --git a/include/clang/ARCMigrate/ARCMTActions.h b/include/clang/ARCMigrate/ARCMTActions.h
index fd85a08..4c714f5 100644
--- a/include/clang/ARCMigrate/ARCMTActions.h
+++ b/include/clang/ARCMigrate/ARCMTActions.h
@@ -24,12 +24,21 @@
   CheckAction(FrontendAction *WrappedAction);
 };
 
-class TransformationAction : public WrapperFrontendAction {
+class ModifyAction : public WrapperFrontendAction {
 protected:
   virtual bool BeginInvocation(CompilerInstance &CI);
 
 public:
-  TransformationAction(FrontendAction *WrappedAction);
+  ModifyAction(FrontendAction *WrappedAction);
+};
+
+class MigrateAction : public WrapperFrontendAction {
+  std::string MigrateDir;
+protected:
+  virtual bool BeginInvocation(CompilerInstance &CI);
+
+public:
+  MigrateAction(FrontendAction *WrappedAction, llvm::StringRef migrateDir);
 };
 
 }
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index fcd6f64..6befc8c 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -389,6 +389,10 @@
   HelpText<"Check for ARC migration issues that need manual handling">;
 def arcmt_modify : Flag<"-arcmt-modify">,
   HelpText<"Apply modifications to files to conform to ARC">;
+def arcmt_migrate : Flag<"-arcmt-migrate">,
+  HelpText<"Apply modifications and produces temporary files that conform to ARC">;
+def arcmt_migrate_directory : Separate<"-arcmt-migrate-directory">,
+  HelpText<"Directory for temporary files produced during ARC migration">;
 
 def import_module : Separate<"-import-module">,
   HelpText<"Import a module definition file">;
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 2cc71a9..d548276 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -118,6 +118,10 @@
   HelpText<"Apply modifications to files to conform to ARC">;
 def ccc_arrmt_check : Flag<"-ccc-arrmt-check">, Alias<ccc_arcmt_check>;
 def ccc_arrmt_modify : Flag<"-ccc-arrmt-modify">, Alias<ccc_arcmt_modify>;
+def ccc_arcmt_migrate : Separate<"-ccc-arcmt-migrate">, CCCDriverOpt,
+  HelpText<"Apply modifications and produces temporary files that conform to ARC">;
+def ccc_arcmt_migrate_EQ : Joined<"-ccc-arcmt-migrate=">, CCCDriverOpt,
+  Alias<ccc_arcmt_migrate>;
 
 // Make sure all other -ccc- options are rejected.
 def ccc_ : Joined<"-ccc-">, Group<ccc_Group>, Flags<[Unsupported]>;
diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h
index 3fedd6b..225a955 100644
--- a/include/clang/Frontend/FrontendOptions.h
+++ b/include/clang/Frontend/FrontendOptions.h
@@ -79,9 +79,12 @@
   enum {
     ARCMT_None,
     ARCMT_Check,
-    ARCMT_Modify
+    ARCMT_Modify,
+    ARCMT_Migrate
   } ARCMTAction;
 
+  std::string ARCMTMigrateDir;
+
   /// The input files and their types.
   std::vector<std::pair<InputKind, std::string> > Inputs;
 
diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp
index 0678a25..73c8dbd 100644
--- a/lib/ARCMigrate/ARCMT.cpp
+++ b/lib/ARCMigrate/ARCMT.cpp
@@ -269,9 +269,10 @@
 // applyTransformations.
 //===----------------------------------------------------------------------===//
 
-bool arcmt::applyTransformations(CompilerInvocation &origCI,
-                                 llvm::StringRef Filename, InputKind Kind,
-                                 DiagnosticClient *DiagClient) {
+static bool applyTransforms(CompilerInvocation &origCI,
+                            llvm::StringRef Filename, InputKind Kind,
+                            DiagnosticClient *DiagClient,
+                            llvm::StringRef outputDir) {
   if (!origCI.getLangOpts().ObjC1)
     return false;
 
@@ -284,7 +285,7 @@
   CInvok.getFrontendOpts().Inputs.clear();
   CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
   
-  MigrationProcess migration(CInvok, DiagClient);
+  MigrationProcess migration(CInvok, DiagClient, outputDir);
 
   std::vector<TransformFn> transforms = arcmt::getAllTransformations();
   assert(!transforms.empty());
@@ -294,12 +295,52 @@
     if (err) return true;
   }
 
-  origCI.getLangOpts().ObjCAutoRefCount = true;
+  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+  llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
+                 new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
+
+  if (outputDir.empty()) {
+    origCI.getLangOpts().ObjCAutoRefCount = true;
+    return migration.getRemapper().overwriteOriginal(*Diags);
+  } else
+    return migration.getRemapper().flushToDisk(outputDir, *Diags);
+}
+
+bool arcmt::applyTransformations(CompilerInvocation &origCI,
+                                 llvm::StringRef Filename, InputKind Kind,
+                                 DiagnosticClient *DiagClient) {
+  return applyTransforms(origCI, Filename, Kind, DiagClient, llvm::StringRef());
+}
+
+bool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
+                                      llvm::StringRef Filename, InputKind Kind,
+                                      DiagnosticClient *DiagClient,
+                                      llvm::StringRef outputDir) {
+  assert(!outputDir.empty() && "Expected output directory path");
+  return applyTransforms(origCI, Filename, Kind, DiagClient, outputDir);
+}
+
+bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
+                                  remap,
+                              llvm::StringRef outputDir,
+                              DiagnosticClient *DiagClient) {
+  assert(!outputDir.empty());
 
   llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
   llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
                  new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
-  return migration.getRemapper().overwriteOriginal(*Diags);
+
+  FileRemapper remapper;
+  bool err = remapper.initFromDisk(outputDir, *Diags,
+                                   /*ignoreIfFilesChanged=*/true);
+  if (err)
+    return true;
+
+  CompilerInvocation CI;
+  remapper.applyMappings(CI);
+  remap = CI.getPreprocessorOpts().RemappedFiles;
+
+  return false;
 }
 
 //===----------------------------------------------------------------------===//
@@ -382,6 +423,18 @@
 /// \brief Anchor for VTable.
 MigrationProcess::RewriteListener::~RewriteListener() { }
 
+MigrationProcess::MigrationProcess(const CompilerInvocation &CI,
+                                   DiagnosticClient *diagClient,
+                                   llvm::StringRef outputDir)
+  : OrigCI(CI), DiagClient(diagClient) {
+  if (!outputDir.empty()) {
+    llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+    llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
+                 new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
+    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
+  }
+}
+
 bool MigrationProcess::applyTransform(TransformFn trans,
                                       RewriteListener *listener) {
   llvm::OwningPtr<CompilerInvocation> CInvok;
diff --git a/lib/ARCMigrate/ARCMTActions.cpp b/lib/ARCMigrate/ARCMTActions.cpp
index 7de62d2..345c745 100644
--- a/lib/ARCMigrate/ARCMTActions.cpp
+++ b/lib/ARCMigrate/ARCMTActions.cpp
@@ -28,11 +28,26 @@
 CheckAction::CheckAction(FrontendAction *WrappedAction)
   : WrapperFrontendAction(WrappedAction) {}
 
-bool TransformationAction::BeginInvocation(CompilerInstance &CI) {
-  return !arcmt::applyTransformations(CI.getInvocation(), getCurrentFile(),
-                                  getCurrentFileKind(),
-                                  CI.getDiagnostics().getClient());
+bool ModifyAction::BeginInvocation(CompilerInstance &CI) {
+  return !arcmt::applyTransformations(CI.getInvocation(),
+                                      getCurrentFile(), getCurrentFileKind(),
+                                      CI.getDiagnostics().getClient());
 }
 
-TransformationAction::TransformationAction(FrontendAction *WrappedAction)
+ModifyAction::ModifyAction(FrontendAction *WrappedAction)
   : WrapperFrontendAction(WrappedAction) {}
+
+bool MigrateAction::BeginInvocation(CompilerInstance &CI) {
+  return !arcmt::migrateWithTemporaryFiles(CI.getInvocation(),
+                                           getCurrentFile(),
+                                           getCurrentFileKind(),
+                                           CI.getDiagnostics().getClient(),
+                                           MigrateDir);
+}
+
+MigrateAction::MigrateAction(FrontendAction *WrappedAction,
+                             llvm::StringRef migrateDir)
+  : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir) {
+  if (MigrateDir.empty())
+    MigrateDir = "."; // user current directory if none is given.
+}
diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp
index ae5d3a3..c1dbe92 100644
--- a/lib/ARCMigrate/FileRemapper.cpp
+++ b/lib/ARCMigrate/FileRemapper.cpp
@@ -71,11 +71,8 @@
     fin >> fromFilename >> timeModified >> toFilename;
     if (fin.eof())
       break;
-    if (!fin.good()) {
-      if (ignoreIfFilesChanged)
-        return false;
+    if (!fin.good())
       return report(std::string("Error in format of file: ") + infoFile, Diag);
-    }
 
     const FileEntry *origFE = FileMgr->getFile(fromFilename);
     if (!origFE) {
@@ -115,8 +112,7 @@
 
   std::string errMsg;
   std::string infoFile = getRemapInfoFile(outputDir);
-  llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
-                               llvm::raw_fd_ostream::F_Binary);
+  llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg);
   if (!errMsg.empty() || infoOut.has_error())
     return report(errMsg, Diag);
 
@@ -124,11 +120,15 @@
          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
 
     const FileEntry *origFE = I->first;
-    infoOut << origFE->getName() << '\n';
+    llvm::SmallString<200> origPath = llvm::StringRef(origFE->getName());
+    fs::make_absolute(origPath);
+    infoOut << origPath << '\n';
     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
 
     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
-      infoOut << FE->getName() << '\n';
+      llvm::SmallString<200> newPath = llvm::StringRef(FE->getName());
+      fs::make_absolute(newPath);
+      infoOut << newPath << '\n';
     } else {
 
       llvm::SmallString<64> tempPath;
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 084a3a0..9b3e494 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1423,7 +1423,8 @@
 
   if (!Args.hasArg(options::OPT_fno_objc_arc)) {
     if (const Arg *A = Args.getLastArg(options::OPT_ccc_arcmt_check,
-                                       options::OPT_ccc_arcmt_modify)) {
+                                       options::OPT_ccc_arcmt_modify,
+                                       options::OPT_ccc_arcmt_migrate)) {
       switch (A->getOption().getID()) {
       default:
         llvm_unreachable("missed a case");
@@ -1433,6 +1434,11 @@
       case options::OPT_ccc_arcmt_modify:
         CmdArgs.push_back("-arcmt-modify");
         break;
+      case options::OPT_ccc_arcmt_migrate:
+        CmdArgs.push_back("-arcmt-migrate");
+        CmdArgs.push_back("-arcmt-migrate-directory");
+        CmdArgs.push_back(A->getValue(Args));
+        break;
       }
     }
   }
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index ebc3340..02a5088 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -431,6 +431,13 @@
   case FrontendOptions::ARCMT_Modify:
     Res.push_back("-arcmt-modify");
     break;
+  case FrontendOptions::ARCMT_Migrate:
+    Res.push_back("-arcmt-migrate");
+    break;
+  }
+  if (!Opts.ARCMTMigrateDir.empty()) {
+    Res.push_back("-arcmt-migrate-directory");
+    Res.push_back(Opts.ARCMTMigrateDir);
   }
 
   bool NeedLang = false;
@@ -1251,7 +1258,8 @@
 
   Opts.ARCMTAction = FrontendOptions::ARCMT_None;
   if (const Arg *A = Args.getLastArg(OPT_arcmt_check,
-                                     OPT_arcmt_modify)) {
+                                     OPT_arcmt_modify,
+                                     OPT_arcmt_migrate)) {
     switch (A->getOption().getID()) {
     default:
       llvm_unreachable("missed a case");
@@ -1261,8 +1269,12 @@
     case OPT_arcmt_modify:
       Opts.ARCMTAction = FrontendOptions::ARCMT_Modify;
       break;
+    case OPT_arcmt_migrate:
+      Opts.ARCMTAction = FrontendOptions::ARCMT_Migrate;
+      break;
     }
   }
+  Opts.ARCMTMigrateDir = Args.getLastArgValue(OPT_arcmt_migrate_directory);
 
   InputKind DashX = IK_None;
   if (const Arg *A = Args.getLastArg(OPT_x)) {
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 4509599..f2db3ae 100644
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -97,7 +97,10 @@
     Act = new arcmt::CheckAction(Act);
     break;
   case FrontendOptions::ARCMT_Modify:
-    Act = new arcmt::TransformationAction(Act);
+    Act = new arcmt::ModifyAction(Act);
+    break;
+  case FrontendOptions::ARCMT_Migrate:
+    Act = new arcmt::MigrateAction(Act, CI.getFrontendOpts().ARCMTMigrateDir);
     break;
   }
 
diff --git a/test/ARCMT/Inputs/test.h b/test/ARCMT/Inputs/test.h
new file mode 100644
index 0000000..756295f
--- /dev/null
+++ b/test/ARCMT/Inputs/test.h
@@ -0,0 +1,15 @@
+@protocol NSObject
+- (oneway void)release;
+@end
+
+#ifdef PART1
+static inline void part1(id p) {
+  [p release];
+}
+#endif
+
+#ifdef PART2
+static inline void part2(id p) {
+  [p release];
+}
+#endif
diff --git a/test/ARCMT/Inputs/test.h.result b/test/ARCMT/Inputs/test.h.result
new file mode 100644
index 0000000..0638a33
--- /dev/null
+++ b/test/ARCMT/Inputs/test.h.result
@@ -0,0 +1,13 @@
+@protocol NSObject
+- (oneway void)release;
+@end
+
+#ifdef PART1
+static inline void part1(id p) {
+}
+#endif
+
+#ifdef PART2
+static inline void part2(id p) {
+}
+#endif
diff --git a/test/ARCMT/Inputs/test1.m.in b/test/ARCMT/Inputs/test1.m.in
new file mode 100644
index 0000000..8416a88
--- /dev/null
+++ b/test/ARCMT/Inputs/test1.m.in
@@ -0,0 +1,6 @@
+#define PART1
+#include "test.h"
+
+void test1(id p) {
+  [p release];
+}
diff --git a/test/ARCMT/Inputs/test1.m.in.result b/test/ARCMT/Inputs/test1.m.in.result
new file mode 100644
index 0000000..f351fe6
--- /dev/null
+++ b/test/ARCMT/Inputs/test1.m.in.result
@@ -0,0 +1,5 @@
+#define PART1
+#include "test.h"
+
+void test1(id p) {
+}
diff --git a/test/ARCMT/Inputs/test2.m.in b/test/ARCMT/Inputs/test2.m.in
new file mode 100644
index 0000000..99f87b0
--- /dev/null
+++ b/test/ARCMT/Inputs/test2.m.in
@@ -0,0 +1,6 @@
+#define PART2
+#include "test.h"
+
+void test2(id p) {
+  [p release];
+}
diff --git a/test/ARCMT/Inputs/test2.m.in.result b/test/ARCMT/Inputs/test2.m.in.result
new file mode 100644
index 0000000..f8e918c
--- /dev/null
+++ b/test/ARCMT/Inputs/test2.m.in.result
@@ -0,0 +1,5 @@
+#define PART2
+#include "test.h"
+
+void test2(id p) {
+}
diff --git a/test/ARCMT/driver-migrate.m b/test/ARCMT/driver-migrate.m
new file mode 100644
index 0000000..108b240
--- /dev/null
+++ b/test/ARCMT/driver-migrate.m
@@ -0,0 +1,3 @@
+// RUN: %clang -### -ccc-arcmt-migrate /foo/bar -fsyntax-only %s 2>&1 | FileCheck %s
+
+// CHECK: "-arcmt-migrate" "-arcmt-migrate-directory" "/foo/bar"
diff --git a/test/ARCMT/migrate.m b/test/ARCMT/migrate.m
new file mode 100644
index 0000000..51029c5
--- /dev/null
+++ b/test/ARCMT/migrate.m
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t %S/Inputs/test1.m.in -x objective-c -fobjc-nonfragile-abi
+// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t %S/Inputs/test2.m.in -x objective-c -fobjc-nonfragile-abi
+// RUN: c-arcmt-test -arcmt-migrate-directory %t | arcmt-test -verify-transformed-files %S/Inputs/test1.m.in.result %S/Inputs/test2.m.in.result %S/Inputs/test.h.result
+// RUN: rm -rf %t
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 8f8aa09..aff437f 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_subdirectory(libclang)
 add_subdirectory(c-index-test)
 add_subdirectory(arcmt-test)
+add_subdirectory(c-arcmt-test)
 add_subdirectory(driver)
diff --git a/tools/Makefile b/tools/Makefile
index e0afc6a..bfd2a64 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -8,7 +8,7 @@
 ##===----------------------------------------------------------------------===##
 
 CLANG_LEVEL := ..
-DIRS := driver libclang c-index-test arcmt-test
+DIRS := driver libclang c-index-test arcmt-test c-arcmt-test
 
 include $(CLANG_LEVEL)/../../Makefile.config
 
diff --git a/tools/arcmt-test/arcmt-test.cpp b/tools/arcmt-test/arcmt-test.cpp
index 702e13a..eb0f569 100644
--- a/tools/arcmt-test/arcmt-test.cpp
+++ b/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;
diff --git a/tools/c-arcmt-test/CMakeLists.txt b/tools/c-arcmt-test/CMakeLists.txt
new file mode 100644
index 0000000..bcb963d
--- /dev/null
+++ b/tools/c-arcmt-test/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_USED_LIBS libclang)
+
+set( LLVM_LINK_COMPONENTS
+  support
+  mc
+  )
+
+add_clang_executable(c-arcmt-test
+  c-arcmt-test.cpp
+  )
+
+set_target_properties(c-arcmt-test
+  PROPERTIES
+  LINKER_LANGUAGE CXX)
diff --git a/tools/c-arcmt-test/Makefile b/tools/c-arcmt-test/Makefile
new file mode 100644
index 0000000..6737a53
--- /dev/null
+++ b/tools/c-arcmt-test/Makefile
@@ -0,0 +1,24 @@
+##===- tools/c-arcmt-test/Makefile -------------------------*- Makefile -*-===##
+# 
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+# 
+##===----------------------------------------------------------------------===##
+CLANG_LEVEL := ../..
+
+TOOLNAME = c-arcmt-test
+
+# No plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+# Don't install this. It is used for tests.
+NO_INSTALL = 1
+
+LINK_COMPONENTS := support mc
+USEDLIBS = clang.a clangIndex.a clangFrontend.a clangDriver.a \
+	   clangSerialization.a clangParse.a clangSema.a \
+	   clangRewrite.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile
diff --git a/tools/c-arcmt-test/c-arcmt-test.c b/tools/c-arcmt-test/c-arcmt-test.c
new file mode 100644
index 0000000..86992da
--- /dev/null
+++ b/tools/c-arcmt-test/c-arcmt-test.c
@@ -0,0 +1,82 @@
+/* c-arcmt-test.c */
+
+#include "clang-c/ARCMigrate.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static int print_remappings(const char *path) {
+  CMTRemap remap;
+  unsigned i, N;
+  CXString origFname;
+  CXString transFname;
+
+  remap = arcmt_getRemappings(path);
+  if (!remap)
+    return 1;
+
+  N = arcmt_remap_getNumFiles(remap);
+  for (i = 0; i != N; ++i) {
+    origFname = arcmt_remap_getOriginalFile(remap, i);
+    transFname = arcmt_remap_getTransformedFile(remap, i);
+
+    fprintf(stdout, "%s\n", clang_getCString(origFname));
+    fprintf(stdout, "%s\n", clang_getCString(transFname));
+
+    clang_disposeString(origFname);
+    clang_disposeString(transFname);
+  }
+
+  arcmt_remap_dispose(remap);
+  return 0;
+}
+
+/******************************************************************************/
+/* Command line processing.                                                   */
+/******************************************************************************/
+
+static void print_usage(void) {
+  fprintf(stderr,
+    "usage: c-arcmt-test -arcmt-migrate-directory <path>\n\n\n");
+}
+
+/***/
+
+int carcmttest_main(int argc, const char **argv) {
+  clang_enableStackTraces();
+  if (argc == 3 && strncmp(argv[1], "-arcmt-migrate-directory", 24) == 0)
+    return print_remappings(argv[2]);
+
+  print_usage();
+  return 1;
+}
+
+/***/
+
+/* We intentionally run in a separate thread to ensure we at least minimal
+ * testing of a multithreaded environment (for example, having a reduced stack
+ * size). */
+
+typedef struct thread_info {
+  int argc;
+  const char **argv;
+  int result;
+} thread_info;
+void thread_runner(void *client_data_v) {
+  thread_info *client_data = client_data_v;
+  client_data->result = carcmttest_main(client_data->argc, client_data->argv);
+}
+
+int main(int argc, const char **argv) {
+  thread_info client_data;
+
+  setenv("LIBCLANG_LOGGING", "1", /*overwrite=*/0);
+
+  if (getenv("CINDEXTEST_NOTHREADS"))
+    return carcmttest_main(argc, argv);
+
+  client_data.argc = argc;
+  client_data.argv = argv;
+  clang_executeOnThread(thread_runner, &client_data, 0);
+  return client_data.result;
+}
diff --git a/tools/libclang/ARCMigrate.cpp b/tools/libclang/ARCMigrate.cpp
new file mode 100644
index 0000000..cd0d8bb
--- /dev/null
+++ b/tools/libclang/ARCMigrate.cpp
@@ -0,0 +1,96 @@
+//===- ARCMigrate.cpp - Clang-C ARC Migration Library ---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the main API hooks in the Clang-C ARC Migration library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-c/ARCMigrate.h"
+
+#include "CXString.h"
+#include "clang/ARCMigrate/ARCMT.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace clang;
+using namespace arcmt;
+
+namespace {
+
+struct Remap {
+  std::vector<std::pair<std::string, std::string> > Vec;
+};
+
+} // anonymous namespace.
+
+//===----------------------------------------------------------------------===//
+// libClang public APIs.
+//===----------------------------------------------------------------------===//
+
+extern "C" {
+
+CMTRemap arcmt_getRemappings(const char *migrate_dir_path) {
+  bool Logging = ::getenv("LIBCLANG_LOGGING");
+
+  if (!migrate_dir_path) {
+    if (Logging)
+      llvm::errs() << "arcmt_getRemappings was called with NULL parameter\n";
+    return 0;
+  }
+
+  bool exists = false;
+  llvm::sys::fs::exists(migrate_dir_path, exists);
+  if (!exists) {
+    if (Logging) {
+      llvm::errs() << "Error by arcmt_getRemappings(\"" << migrate_dir_path
+                   << "\")\n";
+      llvm::errs() << "\"" << migrate_dir_path << "\" does not exist\n";
+    }
+    return 0;
+  }
+
+  TextDiagnosticBuffer diagBuffer;
+  llvm::OwningPtr<Remap> remap(new Remap());
+
+  bool err = arcmt::getFileRemappings(remap->Vec, migrate_dir_path,&diagBuffer);
+
+  if (err) {
+    if (Logging) {
+      llvm::errs() << "Error by arcmt_getRemappings(\"" << migrate_dir_path
+                   << "\")\n";
+      for (TextDiagnosticBuffer::const_iterator
+             I = diagBuffer.err_begin(), E = diagBuffer.err_end(); I != E; ++I)
+        llvm::errs() << I->second << '\n';
+    }
+    return 0;
+  }
+
+  return remap.take();
+}
+
+unsigned arcmt_remap_getNumFiles(CMTRemap map) {
+  return static_cast<Remap *>(map)->Vec.size();
+  
+}
+
+CXString arcmt_remap_getOriginalFile(CMTRemap map, unsigned index) {
+  return cxstring::createCXString(static_cast<Remap *>(map)->Vec[index].first,
+                                  /*DupString =*/ true);
+}
+
+CXString arcmt_remap_getTransformedFile(CMTRemap map, unsigned index) {
+  return cxstring::createCXString(static_cast<Remap *>(map)->Vec[index].second,
+                                  /*DupString =*/ true);
+}
+
+void arcmt_remap_dispose(CMTRemap map) {
+  delete static_cast<Remap *>(map);
+}
+
+} // end: extern "C"
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index 7a6270d..9fd731d 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -1,4 +1,5 @@
 set(LLVM_USED_LIBS
+  clangARCMigrate
   clangFrontend
   clangDriver
   clangSerialization
@@ -14,6 +15,7 @@
   )
 
 set(SOURCES
+  ARCMigrate.cpp
   CIndex.cpp
   CIndexCXX.cpp
   CIndexCodeCompletion.cpp
@@ -25,6 +27,7 @@
   CXString.cpp
   CXType.cpp
   ../../include/clang-c/Index.h
+  ../../include/clang-c/ARCMigrate.h
   )
 
 if( LLVM_ENABLE_PIC )
diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile
index e684652..af93088 100644
--- a/tools/libclang/Makefile
+++ b/tools/libclang/Makefile
@@ -16,8 +16,8 @@
 SHARED_LIBRARY = 1
 
 LINK_COMPONENTS := support mc
-USEDLIBS = clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \
-	   clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a
+USEDLIBS = clangARCMigrate.a clangFrontend.a clangDriver.a clangSerialization.a \
+		 clangParse.a clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
 
diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports
index df7cda2..a8b466e 100644
--- a/tools/libclang/libclang.darwin.exports
+++ b/tools/libclang/libclang.darwin.exports
@@ -137,3 +137,8 @@
 _clang_tokenize
 _clang_visitChildren
 _clang_visitChildrenWithBlock
+_arcmt_getRemappings
+_arcmt_remap_getNumFiles
+_arcmt_remap_getOriginalFile
+_arcmt_remap_getTransformedFile
+_arcmt_remap_dispose
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index f5e0a30..ac6fc34 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -137,3 +137,8 @@
 clang_tokenize
 clang_visitChildren
 clang_visitChildrenWithBlock
+arcmt_getRemappings
+arcmt_remap_getNumFiles
+arcmt_remap_getOriginalFile
+arcmt_remap_getTransformedFile
+arcmt_remap_dispose