From 69325d5b7cfecf1b3128745efc33612aedf1b8b4 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 9 Jul 2011 20:00:58 +0000 Subject: [PATCH] [arcmt] Introduce new '-ccc-arcmt-migrate ' ARC migration driver option. This is a new mode of migration, where we avoid modifying the original files but we emit temporary files instead. 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 if is necessary. Also introduce new ARC migration functions in libclang whose only purpose, currently, is to accept 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 --- include/clang-c/ARCMigrate.h | 79 +++++++++++ include/clang/ARCMigrate/ARCMT.h | 21 ++- include/clang/ARCMigrate/ARCMTActions.h | 13 +- include/clang/Driver/CC1Options.td | 4 + include/clang/Driver/Options.td | 4 + include/clang/Frontend/FrontendOptions.h | 5 +- lib/ARCMigrate/ARCMT.cpp | 65 ++++++++- lib/ARCMigrate/ARCMTActions.cpp | 25 +++- lib/ARCMigrate/FileRemapper.cpp | 16 +-- lib/Driver/Tools.cpp | 8 +- lib/Frontend/CompilerInvocation.cpp | 14 +- .../ExecuteCompilerInvocation.cpp | 5 +- test/ARCMT/Inputs/test.h | 15 +++ test/ARCMT/Inputs/test.h.result | 13 ++ test/ARCMT/Inputs/test1.m.in | 6 + test/ARCMT/Inputs/test1.m.in.result | 5 + test/ARCMT/Inputs/test2.m.in | 6 + test/ARCMT/Inputs/test2.m.in.result | 5 + test/ARCMT/driver-migrate.m | 3 + test/ARCMT/migrate.m | 4 + tools/CMakeLists.txt | 1 + tools/Makefile | 2 +- tools/arcmt-test/arcmt-test.cpp | 125 +++++++++++++++++- tools/c-arcmt-test/CMakeLists.txt | 14 ++ tools/c-arcmt-test/Makefile | 24 ++++ tools/c-arcmt-test/c-arcmt-test.c | 82 ++++++++++++ tools/libclang/ARCMigrate.cpp | 96 ++++++++++++++ tools/libclang/CMakeLists.txt | 3 + tools/libclang/Makefile | 4 +- tools/libclang/libclang.darwin.exports | 5 + tools/libclang/libclang.exports | 5 + 31 files changed, 646 insertions(+), 31 deletions(-) create mode 100644 include/clang-c/ARCMigrate.h create mode 100644 test/ARCMT/Inputs/test.h create mode 100644 test/ARCMT/Inputs/test.h.result create mode 100644 test/ARCMT/Inputs/test1.m.in create mode 100644 test/ARCMT/Inputs/test1.m.in.result create mode 100644 test/ARCMT/Inputs/test2.m.in create mode 100644 test/ARCMT/Inputs/test2.m.in.result create mode 100644 test/ARCMT/driver-migrate.m create mode 100644 test/ARCMT/migrate.m create mode 100644 tools/c-arcmt-test/CMakeLists.txt create mode 100644 tools/c-arcmt-test/Makefile create mode 100644 tools/c-arcmt-test/c-arcmt-test.c create mode 100644 tools/libclang/ARCMigrate.cpp 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 > &remap, + llvm::StringRef outputDir, + DiagnosticClient *DiagClient); + typedef void (*TransformFn)(MigrationPass &pass); std::vector 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; def ccc_arrmt_modify : Flag<"-ccc-arrmt-modify">, Alias; +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; // Make sure all other -ccc- options are rejected. def ccc_ : Joined<"-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 > 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 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 DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr 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 > & + remap, + llvm::StringRef outputDir, + DiagnosticClient *DiagClient) { + assert(!outputDir.empty()); llvm::IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); llvm::IntrusiveRefCntPtr 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 DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr Diags( + new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true); + } +} + bool MigrationProcess::applyTransform(TransformFn trans, RewriteListener *listener) { llvm::OwningPtr 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()) { - 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 VerboseOpt("v", llvm::cl::desc("Enable verbose output")); +static llvm::cl::opt +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 +RemappingsFile("remappings-file", + llvm::cl::desc("Pairs of file mappings (typically the output of " + "c-arcmt-test)")); + +static llvm::cl::list +ResultFiles(llvm::cl::Positional, llvm::cl::desc("...")); + 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 file1; + MemoryBuffer::getFile(fname1, file1); + if (!file1) + return false; + + OwningPtr file2; + MemoryBuffer::getFile(fname2, file2); + if (!file2) + return false; + + return file1->getBuffer() == file2->getBuffer(); +} + +static bool verifyTransformedFiles(llvm::ArrayRef resultFiles) { + using namespace llvm; + + assert(!resultFiles.empty()); + + std::map resultMap; + + for (ArrayRef::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 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 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::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::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(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 +#include +#include + +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 \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 > 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(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(map)->Vec.size(); + +} + +CXString arcmt_remap_getOriginalFile(CMTRemap map, unsigned index) { + return cxstring::createCXString(static_cast(map)->Vec[index].first, + /*DupString =*/ true); +} + +CXString arcmt_remap_getTransformedFile(CMTRemap map, unsigned index) { + return cxstring::createCXString(static_cast(map)->Vec[index].second, + /*DupString =*/ true); +} + +void arcmt_remap_dispose(CMTRemap map) { + delete static_cast(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 -- 2.40.0