[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