forked from OSchip/llvm-project
[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
This commit is contained in:
parent
25659e93c7
commit
7fbd97f641
clang
include
clang-c
clang
lib
ARCMigrate
Driver
Frontend
FrontendTool
test/ARCMT
tools
|
@ -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
|
||||
|
|
@ -41,6 +41,23 @@ bool applyTransformations(CompilerInvocation &origCI,
|
|||
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 @@ class MigrationProcess {
|
|||
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:
|
||||
|
|
|
@ -24,12 +24,21 @@ public:
|
|||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -389,6 +389,10 @@ def arcmt_check : Flag<"-arcmt-check">,
|
|||
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">;
|
||||
|
|
|
@ -118,6 +118,10 @@ def ccc_arcmt_modify : Flag<"-ccc-arcmt-modify">, CCCDriverOpt,
|
|||
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]>;
|
||||
|
|
|
@ -79,9 +79,12 @@ public:
|
|||
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;
|
||||
|
||||
|
|
|
@ -269,9 +269,10 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
|
|||
// 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 @@ bool arcmt::applyTransformations(CompilerInvocation &origCI,
|
|||
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 @@ bool arcmt::applyTransformations(CompilerInvocation &origCI,
|
|||
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 @@ public:
|
|||
/// \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;
|
||||
|
|
|
@ -28,11 +28,26 @@ bool CheckAction::BeginInvocation(CompilerInstance &CI) {
|
|||
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.
|
||||
}
|
||||
|
|
|
@ -71,11 +71,8 @@ bool FileRemapper::initFromDisk(llvm::StringRef outputDir, Diagnostic &Diag,
|
|||
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 @@ bool FileRemapper::flushToDisk(llvm::StringRef outputDir, Diagnostic &Diag) {
|
|||
|
||||
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 @@ bool FileRemapper::flushToDisk(llvm::StringRef outputDir, Diagnostic &Diag) {
|
|||
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;
|
||||
|
|
|
@ -1423,7 +1423,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
|
||||
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 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -431,6 +431,13 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts,
|
|||
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 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
|||
|
||||
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 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
|||
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)) {
|
||||
|
|
|
@ -97,7 +97,10 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
#define PART1
|
||||
#include "test.h"
|
||||
|
||||
void test1(id p) {
|
||||
[p release];
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#define PART1
|
||||
#include "test.h"
|
||||
|
||||
void test1(id p) {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#define PART2
|
||||
#include "test.h"
|
||||
|
||||
void test2(id p) {
|
||||
[p release];
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#define PART2
|
||||
#include "test.h"
|
||||
|
||||
void test2(id p) {
|
||||
}
|
|
@ -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"
|
|
@ -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
|
|
@ -1,4 +1,5 @@
|
|||
add_subdirectory(libclang)
|
||||
add_subdirectory(c-index-test)
|
||||
add_subdirectory(arcmt-test)
|
||||
add_subdirectory(c-arcmt-test)
|
||||
add_subdirectory(driver)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 @@ VerifyDiags("verify",llvm::cl::desc("Verify emitted diagnostics and warnings"));
|
|||
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 @@ static bool performTransformations(llvm::StringRef resourcesPath,
|
|||
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 @@ int main(int argc, const char **argv) {
|
|||
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;
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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"
|
|
@ -1,4 +1,5 @@
|
|||
set(LLVM_USED_LIBS
|
||||
clangARCMigrate
|
||||
clangFrontend
|
||||
clangDriver
|
||||
clangSerialization
|
||||
|
@ -14,6 +15,7 @@ set( LLVM_LINK_COMPONENTS
|
|||
)
|
||||
|
||||
set(SOURCES
|
||||
ARCMigrate.cpp
|
||||
CIndex.cpp
|
||||
CIndexCXX.cpp
|
||||
CIndexCodeCompletion.cpp
|
||||
|
@ -25,6 +27,7 @@ set(SOURCES
|
|||
CXString.cpp
|
||||
CXType.cpp
|
||||
../../include/clang-c/Index.h
|
||||
../../include/clang-c/ARCMigrate.h
|
||||
)
|
||||
|
||||
if( LLVM_ENABLE_PIC )
|
||||
|
|
|
@ -16,8 +16,8 @@ LINK_LIBS_IN_SHARED = 1
|
|||
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
|
||||
|
||||
|
|
|
@ -137,3 +137,8 @@ _clang_toggleCrashRecovery
|
|||
_clang_tokenize
|
||||
_clang_visitChildren
|
||||
_clang_visitChildrenWithBlock
|
||||
_arcmt_getRemappings
|
||||
_arcmt_remap_getNumFiles
|
||||
_arcmt_remap_getOriginalFile
|
||||
_arcmt_remap_getTransformedFile
|
||||
_arcmt_remap_dispose
|
||||
|
|
|
@ -137,3 +137,8 @@ clang_toggleCrashRecovery
|
|||
clang_tokenize
|
||||
clang_visitChildren
|
||||
clang_visitChildrenWithBlock
|
||||
arcmt_getRemappings
|
||||
arcmt_remap_getNumFiles
|
||||
arcmt_remap_getOriginalFile
|
||||
arcmt_remap_getTransformedFile
|
||||
arcmt_remap_dispose
|
||||
|
|
Loading…
Reference in New Issue