cpp11-migrate: Add -for-compilers command line switch.

This change add a new option command line option -for-compilers that allows the
user to enable multiple transforms automatically.

Another difference is that now all transforms are enabled by default.

llvm-svn: 187360
This commit is contained in:
Guillaume Papin 2013-07-29 15:58:47 +00:00
parent ee30546c00
commit a3eede2cc2
14 changed files with 397 additions and 21 deletions

View File

@ -61,6 +61,18 @@ bool AddOverrideTransform::handleBeginSource(clang::CompilerInstance &CI,
}
struct AddOverrideFactory : TransformFactory {
AddOverrideFactory() {
// if detecting macros is enabled, do not impose requirements on the
// compiler. It is assumed that the macros use is "C++11-aware", meaning it
// won't expand to override if the compiler doesn't support the specifier.
if (!DetectMacros) {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 7);
Since.Icc = Version(14);
Since.Msvc = Version(8);
}
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new AddOverrideTransform(Opts);
}

View File

@ -20,6 +20,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang;
@ -132,4 +133,36 @@ FrontendActionFactory *Transform::createActionFactory(MatchFinder &Finder) {
return new ActionFactory(Finder, /*Owner=*/ *this);
}
Version Version::getFromString(llvm::StringRef VersionStr) {
llvm::StringRef MajorStr, MinorStr;
Version V;
llvm::tie(MajorStr, MinorStr) = VersionStr.split('.');
if (!MinorStr.empty()) {
llvm::StringRef Ignore;
llvm::tie(MinorStr, Ignore) = MinorStr.split('.');
if (MinorStr.getAsInteger(10, V.Minor))
return Version();
}
if (MajorStr.getAsInteger(10, V.Major))
return Version();
return V;
}
TransformFactory::~TransformFactory() {}
namespace {
bool versionSupported(Version Required, Version AvailableSince) {
// null version, means no requirements, means supported
if (Required.isNull())
return true;
return Required >= AvailableSince;
}
} // end anonymous namespace
bool TransformFactory::supportsCompilers(CompilerVersions Required) const {
return versionSupported(Required.Clang, Since.Clang) &&
versionSupported(Required.Gcc, Since.Gcc) &&
versionSupported(Required.Icc, Since.Icc) &&
versionSupported(Required.Msvc, Since.Msvc);
}

View File

@ -222,16 +222,74 @@ private:
unsigned DeferredChanges;
};
/// \brief Describes a version number of the form major[.minor] (minor being
/// optional).
struct Version {
explicit Version(unsigned Major = 0, unsigned Minor = 0)
: Major(Major), Minor(Minor) {}
bool operator<(Version RHS) const {
if (Major < RHS.Major)
return true;
if (Major == RHS.Major)
return Minor < RHS.Minor;
return false;
}
bool operator==(Version RHS) const {
return Major == RHS.Major && Minor == RHS.Minor;
}
bool operator!=(Version RHS) const { return !(*this == RHS); }
bool operator>(Version RHS) const { return RHS < *this; }
bool operator<=(Version RHS) const { return !(*this > RHS); }
bool operator>=(Version RHS) const { return !(*this < RHS); }
bool isNull() const { return Minor == 0 && Major == 0; }
unsigned getMajor() const { return Major; }
unsigned getMinor() const { return Minor; }
/// \brief Creates a version from a string of the form \c major[.minor].
///
/// Note that any version component after \c minor is ignored.
///
/// \return A null version is returned on error.
static Version getFromString(llvm::StringRef VersionStr);
private:
unsigned Major;
unsigned Minor;
};
/// \brief Convenience structure to store the version of some compilers.
struct CompilerVersions {
Version Clang, Gcc, Icc, Msvc;
};
/// \brief A factory that can instantiate a specific transform.
///
/// Each transform should subclass it and implement the \c createTransform()
/// methods. Use \c TransformFactoryRegistry to register the transform globally.
/// Each transform should subclass this class and implement
/// \c createTransform().
///
/// In the sub-classed factory constructor, specify the earliest versions since
/// the compilers in \c CompilerVersions support the feature introduced by the
/// transform. See the example below.
///
/// Note that you should use \c TransformFactoryRegistry to register the
/// transform globally.
///
/// Example:
/// \code
/// class MyTransform : public Transform { ... };
///
/// struct MyFactory : TransformFactory {
/// MyFactory() {
/// Since.Clang = Version(3, 0);
/// Since.Gcc = Version(4, 7);
/// Since.Icc = Version(12);
/// Since.Msvc = Version(10);
/// }
///
/// Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
/// return new MyTransform(Opts);
/// }
@ -249,6 +307,17 @@ class TransformFactory {
public:
virtual ~TransformFactory();
virtual Transform *createTransform(const TransformOptions &) = 0;
/// \brief Whether the transform is supported by the required compilers or
/// not.
bool supportsCompilers(CompilerVersions Required) const;
protected:
/// \brief Since when the C++11 feature introduced by this transform has been
/// available.
///
/// Can be set by the sub-class in the constructor body.
CompilerVersions Since;
};
typedef llvm::Registry<TransformFactory> TransformFactoryRegistry;

View File

@ -37,17 +37,35 @@ void Transforms::registerTransforms() {
I->getName(), cl::desc(I->getDesc()), cl::cat(TransformCategory));
}
bool Transforms::hasAnyExplicitOption() const {
for (OptionMap::const_iterator I = Options.begin(), E = Options.end(); I != E;
++I)
if (*I->second)
return true;
return false;
}
void
Transforms::createSelectedTransforms(const TransformOptions &GlobalOptions) {
Transforms::createSelectedTransforms(const TransformOptions &GlobalOptions,
const CompilerVersions &RequiredVersions) {
// if at least one transform is set explicitly on the command line, do not
// enable non-explicit ones
bool EnableAllTransformsByDefault = !hasAnyExplicitOption();
for (TransformFactoryRegistry::iterator I = TransformFactoryRegistry::begin(),
E = TransformFactoryRegistry::end();
I != E; ++I) {
bool OptionEnabled = *Options[I->getName()];
bool ExplicitlyEnabled = *Options[I->getName()];
bool OptionEnabled = EnableAllTransformsByDefault || ExplicitlyEnabled;
if (!OptionEnabled)
continue;
llvm::OwningPtr<TransformFactory> Factory(I->instantiate());
ChosenTransforms.push_back(Factory->createTransform(GlobalOptions));
if (Factory->supportsCompilers(RequiredVersions))
ChosenTransforms.push_back(Factory->createTransform(GlobalOptions));
else if (ExplicitlyEnabled)
llvm::errs() << "note: " << '-' << I->getName()
<< ": transform not available for specified compilers\n";
}
}

View File

@ -30,6 +30,7 @@ class Option;
} // namespace llvm
class Transform;
struct TransformOptions;
struct CompilerVersions;
typedef Transform *(*TransformCreator)(const TransformOptions &);
template <typename T>
@ -57,7 +58,8 @@ public:
/// \brief Instantiate all transforms that were selected on the command line.
///
/// Call *after* parsing options.
void createSelectedTransforms(const TransformOptions &Options);
void createSelectedTransforms(const TransformOptions &Options,
const CompilerVersions &RequiredVersions);
/// \brief Return an iterator to the start of a container of instantiated
/// transforms.
@ -68,6 +70,8 @@ public:
const_iterator end() const { return ChosenTransforms.end(); }
private:
bool hasAnyExplicitOption() const;
typedef llvm::StringMap<llvm::cl::opt<bool> *> OptionMap;
private:

View File

@ -68,6 +68,13 @@ int LoopConvertTransform::apply(FileOverrides &InputStates,
}
struct LoopConvertFactory : TransformFactory {
LoopConvertFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new LoopConvertTransform(Opts);
}

View File

@ -50,6 +50,13 @@ ReplaceAutoPtrTransform::apply(FileOverrides &InputStates,
}
struct ReplaceAutoPtrFactory : TransformFactory {
ReplaceAutoPtrFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new ReplaceAutoPtrTransform(Opts);
}

View File

@ -49,6 +49,13 @@ int UseAutoTransform::apply(FileOverrides &InputStates,
}
struct UseAutoFactory : TransformFactory {
UseAutoFactory() {
Since.Clang = Version(2, 9);
Since.Gcc = Version(4, 4);
Since.Icc = Version(12);
Since.Msvc = Version(10);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new UseAutoTransform(Opts);
}

View File

@ -50,6 +50,13 @@ int UseNullptrTransform::apply(FileOverrides &InputStates,
}
struct UseNullptrFactory : TransformFactory {
UseNullptrFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(12, 1);
Since.Msvc = Version(10);
}
Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE {
return new UseNullptrTransform(Opts);
}

View File

@ -24,6 +24,8 @@
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
@ -36,8 +38,8 @@ TransformOptions GlobalOptions;
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::extrahelp MoreHelp(
"EXAMPLES:\n\n"
"Use 'auto' type specifier, no compilation database:\n\n"
" cpp11-migrate -use-auto path/to/file.cpp -- -Ipath/to/include/\n"
"Apply all transforms on a given file, no compilation database:\n\n"
" cpp11-migrate path/to/file.cpp -- -Ipath/to/include/\n"
"\n"
"Convert for loops to the new ranged-based for loops on all files in a "
"subtree\nand reformat the code automatically using the LLVM style:\n\n"
@ -47,7 +49,10 @@ static cl::extrahelp MoreHelp(
"Make use of both nullptr and the override specifier, using git ls-files:\n"
"\n"
" git ls-files '*.cpp' | xargs -I{} cpp11-migrate -p build/path \\\n"
" -use-nullptr -add-override -override-macros {}\n");
" -use-nullptr -add-override -override-macros {}\n"
"\n"
"Apply all transforms supported by both clang >= 3.0 and gcc >= 4.7:\n\n"
" cpp11-migrate -for-compilers=clang-3.0,gcc-4.7 foo.cpp -- -Ibar\n");
static cl::opt<RiskLevel, /*ExternalStorage=*/true> MaxRiskLevel(
"risk", cl::desc("Select a maximum risk level:"),
@ -113,6 +118,68 @@ static cl::opt<bool, /*ExternalStorage=*/true> EnableHeaderModifications(
cl::location(GlobalOptions.EnableHeaderModifications),
cl::init(false));
cl::opt<std::string> SupportedCompilers(
"for-compilers", cl::value_desc("string"),
cl::desc("Select transforms targeting the intersection of\n"
"language features supported by the given compilers.\n"
"Takes a comma-seperated list of <compiler>-<version>.\n"
"\t<compiler> can be any of: clang, gcc, icc, msvc\n"
"\t<version> is <major>[.<minor>]\n"));
/// \brief Extract the minimum compiler versions as requested on the command
/// line by the switch \c -for-compilers.
///
/// \param ProgName The name of the program, \c argv[0], used to print errors.
/// \param Error If an error occur while parsing the versions this parameter is
/// set to \c true, otherwise it will be left untouched.
static CompilerVersions handleSupportedCompilers(const char *ProgName,
bool &Error) {
if (SupportedCompilers.getNumOccurrences() == 0)
return CompilerVersions();
CompilerVersions RequiredVersions;
llvm::SmallVector<llvm::StringRef, 4> Compilers;
llvm::StringRef(SupportedCompilers).split(Compilers, ",");
for (llvm::SmallVectorImpl<llvm::StringRef>::iterator I = Compilers.begin(),
E = Compilers.end();
I != E; ++I) {
llvm::StringRef Compiler, VersionStr;
llvm::tie(Compiler, VersionStr) = I->split('-');
Version *V = llvm::StringSwitch<Version *>(Compiler)
.Case("clang", &RequiredVersions.Clang)
.Case("gcc", &RequiredVersions.Gcc).Case("icc", &RequiredVersions.Icc)
.Case("msvc", &RequiredVersions.Msvc).Default(NULL);
if (V == NULL) {
llvm::errs() << ProgName << ": " << Compiler
<< ": unsupported platform\n";
Error = true;
continue;
}
if (VersionStr.empty()) {
llvm::errs() << ProgName << ": " << *I
<< ": missing version number in platform\n";
Error = true;
continue;
}
Version Version = Version::getFromString(VersionStr);
if (Version.isNull()) {
llvm::errs()
<< ProgName << ": " << *I
<< ": invalid version, please use \"<major>[.<minor>]\" instead of \""
<< VersionStr << "\"\n";
Error = true;
continue;
}
// support the lowest version given
if (V->isNull() || Version < *V)
*V = Version;
}
return RequiredVersions;
}
/// \brief Creates the Reformatter if the format style option is provided,
/// return a null pointer otherwise.
///
@ -161,10 +228,13 @@ int main(int argc, const char **argv) {
GlobalOptions.EnableTiming = (TimingDirectoryName != NoTiming);
// Check the reformatting style option
bool BadStyle = false;
bool CmdSwitchError = false;
llvm::OwningPtr<Reformatter> ChangesReformatter(
handleFormatStyle(argv[0], BadStyle));
if (BadStyle)
handleFormatStyle(argv[0], CmdSwitchError));
CompilerVersions RequiredVersions =
handleSupportedCompilers(argv[0], CmdSwitchError);
if (CmdSwitchError)
return 1;
// Populate the ModifiableHeaders structure if header modifications are
@ -176,10 +246,14 @@ int main(int argc, const char **argv) {
.readListFromFile(IncludeFromFile, ExcludeFromFile);
}
TransformManager.createSelectedTransforms(GlobalOptions);
TransformManager.createSelectedTransforms(GlobalOptions, RequiredVersions);
if (TransformManager.begin() == TransformManager.end()) {
llvm::errs() << argv[0] << ": No selected transforms\n";
if (SupportedCompilers.empty())
llvm::errs() << argv[0] << ": no selected transforms\n";
else
llvm::errs() << argv[0]
<< ": no transforms available for specified compilers\n";
return 1;
}

View File

@ -7,7 +7,16 @@ cpp11-migrate Usage
``<source#>`` specifies the path to the source to migrate. This path may be
relative to the current directory.
At least one transform must be enabled.
By default all transformations are applied. There are two ways to enable a
subset of the transformations:
1. Explicitly, by referring to the transform options directly, see
:ref:`transform-specific-command-line-options`.
2. Implicitly, based on the compilers to support, see
:ref:`-for-compilers=\<string\> <for-compilers-option>`.
If both ways of specifying transforms are used only explicitly specified
transformations that are supported by the given compilers will be applied.
General Command Line Options
============================
@ -113,6 +122,54 @@ General Command Line Options
with other accepted changes. Re-applying the transform will resolve deferred
changes.
.. _for-compilers-option:
.. option:: -for-compilers=<string>
Select transforms targeting the intersection of language features supported by
the given compilers.
Four compilers are supported. The transforms are enabled according to this
table:
=============== ===== === ==== ====
Transforms clang gcc icc mscv
=============== ===== === ==== ====
AddOverride (1) 3.0 4.7 14 8
LoopConvert 3.0 4.6 13 11
ReplaceAutoPtr 3.0 4.6 13 11
UseAuto 2.9 4.4 12 10
UseNullptr 3.0 4.6 12.1 10
=============== ===== === ==== ====
(1): if *-override-macros* is provided it's assumed that the macros are C++11
aware and the transform is enabled without regard to the supported compilers.
The structure of the argument to the `-for-compilers` option is
**<compiler>-<major ver>[.<minor ver>]** where **<compiler>** is one of the
compilers from the above table.
Some examples:
1. To support `Clang >= 3.0`, `gcc >= 4.6` and `MSVC >= 11`:
``cpp11-migrate -for-compilers=clang-3.0,gcc-4.6,msvc-11 <args..>``
Enables LoopConvert, ReplaceAutoPtr, UseAuto, UseNullptr.
2. To support `icc >= 12` while using a C++11-aware macro for the `override`
virtual specifier:
``cpp11-migrate -for-compilers=icc-12 -override-macros <args..>``
Enables AddOverride and UseAuto.
.. warning::
If your version of Clang depends on the GCC headers (e.g: when `libc++` is
not used), then you probably want to add the GCC version to the targeted
platforms as well.
.. option:: -perf[=<directory>]
Turns on performance measurement and output functionality. The time it takes to
@ -124,6 +181,8 @@ General Command Line Options
The time recorded for a transform includes parsing and creating source code
replacements.
.. _transform-specific-command-line-options:
Transform-Specific Command Line Options
=======================================

View File

@ -0,0 +1,63 @@
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=clang-2.9 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=CLANG-29 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=clang-2.9 -override-macros %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=CLANG-29-OV-MACROS -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=clang-3.0 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=CLANG-30 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=gcc-4.6 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=GCC-46 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=gcc-4.7 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=GCC-47 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=icc-13 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=ICC-13 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=icc-14 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=ICC-14 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=msvc-8 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=MSVC-8 -input-file=%t.cpp %s
//
// Test multiple compilers
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: cpp11-migrate -for-compilers=clang-3.0,gcc-4.6,gcc-4.7 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=MULTIPLE -input-file=%t.cpp %s
//
// Test unknown platform
// RUN: not cpp11-migrate -for-compilers=foo-10 %t.cpp -- -std=c++11
//
// Test when no transforms can be selected because the compiler lacks support of
// the needed C++11 features
// RUN: not cpp11-migrate -for-compilers=clang-2.0 %t.cpp -- -std=c++11
// Test add overrides
struct A {
virtual A *clone() = 0;
};
#define LLVM_OVERRIDE override
struct B : A {
virtual B *clone();
// CLANG-29-OV-MACROS: virtual B *clone() LLVM_OVERRIDE;
// CLANG-29: virtual B *clone();
// CLANG-30: virtual B *clone() override;
// GCC-46: virtual B *clone();
// GCC-47: virtual B *clone() override;
// ICC-13: virtual B *clone();
// ICC-14: virtual B *clone() override;
// MSVC-8: virtual B *clone() override;
// MULTIPLE: virtual B *clone();
};

View File

@ -1,6 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: not cpp11-migrate %t.cpp --
int main(int argc, char** argv) {
return 0;
}

View File

@ -302,3 +302,25 @@ TEST(Transform, isFileModifiable) {
Tool.run(tooling::newFrontendActionFactory(&Finder));
}
}
TEST(VersionTest, Interface) {
Version V;
ASSERT_TRUE(V.isNull());
ASSERT_TRUE(Version(1) < Version(1, 1));
ASSERT_TRUE(Version(1) < Version(2));
ASSERT_TRUE(Version(1, 1) < Version(2));
ASSERT_TRUE(Version(1, 1) == Version(1, 1));
ASSERT_EQ(Version(1).getMajor(), unsigned(1));
ASSERT_EQ(Version(1).getMinor(), unsigned(0));
ASSERT_EQ(Version(1, 2).getMinor(), unsigned(2));
}
TEST(VersionTest, getFromString) {
ASSERT_EQ(Version(1), Version::getFromString("1"));
ASSERT_EQ(Version(1, 2), Version::getFromString("1.2"));
ASSERT_TRUE(Version::getFromString("foo").isNull());
ASSERT_TRUE(Version::getFromString("1bar").isNull());
// elements after major.minor are ignored
ASSERT_EQ(Version(1, 2), Version::getFromString("1.2.3"));
}