diff options
31 files changed, 646 insertions, 31 deletions
diff --git a/include/clang-c/ARCMigrate.h b/include/clang-c/ARCMigrate.h new file mode 100644 index 0000000000..00085be131 --- /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 13f0778464..ad5cf4a2c1 100644 --- a/include/clang/ARCMigrate/ARCMT.h +++ b/include/clang/ARCMigrate/ARCMT.h @@ -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: diff --git a/include/clang/ARCMigrate/ARCMTActions.h b/include/clang/ARCMigrate/ARCMTActions.h index fd85a0836a..4c714f55b3 100644 --- a/include/clang/ARCMigrate/ARCMTActions.h +++ b/include/clang/ARCMigrate/ARCMTActions.h @@ -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); }; } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index fcd6f6466e..6befc8cd07 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -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">; diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 2cc71a90bd..d5482765e7 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -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]>; diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 3fedd6b94c..225a955b4a 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -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; diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp index 0678a255d7..73c8dbd6a4 100644 --- a/lib/ARCMigrate/ARCMT.cpp +++ b/lib/ARCMigrate/ARCMT.cpp @@ -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; diff --git a/lib/ARCMigrate/ARCMTActions.cpp b/lib/ARCMigrate/ARCMTActions.cpp index 7de62d289c..345c745242 100644 --- a/lib/ARCMigrate/ARCMTActions.cpp +++ b/lib/ARCMigrate/ARCMTActions.cpp @@ -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. +} diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp index ae5d3a3c93..c1dbe92ffb 100644 --- a/lib/ARCMigrate/FileRemapper.cpp +++ b/lib/ARCMigrate/FileRemapper.cpp @@ -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; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 084a3a0685..9b3e49480e 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -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; } } } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index ebc3340a3d..02a5088f93 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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)) { diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 45095997dc..f2db3ae741 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -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; } diff --git a/test/ARCMT/Inputs/test.h b/test/ARCMT/Inputs/test.h new file mode 100644 index 0000000000..756295f27e --- /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 0000000000..0638a3378c --- /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 0000000000..8416a88965 --- /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 0000000000..f351fe6c83 --- /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 0000000000..99f87b0721 --- /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 0000000000..f8e918ce25 --- /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 0000000000..108b2401fb --- /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 0000000000..51029c5204 --- /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 8f8aa097ce..aff437fd46 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 e0afc6a04a..bfd2a641ec 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 702e13a414..eb0f56943f 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 @@ 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; diff --git a/tools/c-arcmt-test/CMakeLists.txt b/tools/c-arcmt-test/CMakeLists.txt new file mode 100644 index 0000000000..bcb963dec3 --- /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 0000000000..6737a531ba --- /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 0000000000..86992da76b --- /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 0000000000..cd0d8bb64a --- /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 7a6270d176..9fd731d001 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( 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 ) diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile index e684652aa4..af93088410 100644 --- a/tools/libclang/Makefile +++ b/tools/libclang/Makefile @@ -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 diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports index df7cda2e7d..a8b466eff5 100644 --- a/tools/libclang/libclang.darwin.exports +++ b/tools/libclang/libclang.darwin.exports @@ -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 diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index f5e0a304e6..ac6fc3494f 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -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 |