diff --git a/clang/lib/Driver/Multilib.cpp b/clang/lib/Driver/Multilib.cpp --- a/clang/lib/Driver/Multilib.cpp +++ b/clang/lib/Driver/Multilib.cpp @@ -8,6 +8,7 @@ #include "clang/Driver/Multilib.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/Version.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -137,6 +138,7 @@ }; struct MultilibSetSerialization { + std::string ClangMinimumVersion; std::vector Multilibs; std::vector FlagMatchers; }; @@ -174,9 +176,38 @@ template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) { + io.mapRequired("clangMinimumVersion", M.ClangMinimumVersion); io.mapRequired("variants", M.Multilibs); io.mapOptional("flagMap", M.FlagMatchers); } + static std::string validate(IO &io, MultilibSetSerialization &M) { + if (M.ClangMinimumVersion.empty()) + return "missing required key 'clangMinimumVersion'"; + + SmallVector ClangMinimumVersion, + ClangVersion = {CLANG_VERSION_MAJOR, CLANG_VERSION_MINOR, + CLANG_VERSION_PATCHLEVEL}; + + SmallVector MinVerStrings; + StringRef(M.ClangMinimumVersion).split(MinVerStrings, '.'); + + if (MinVerStrings.size() != 3) + return "not a valid version string \"" + M.ClangMinimumVersion + "\""; + + for (StringRef S : MinVerStrings) { + int V; + if (S.getAsInteger(10, V)) + return "not a valid version string \"" + M.ClangMinimumVersion + "\""; + ClangMinimumVersion.push_back(V); + } + + if (ClangVersion < ClangMinimumVersion) { + return "clang version " CLANG_VERSION_STRING + " is less than clangMinimumVersion: " + + M.ClangMinimumVersion; + } + return std::string{}; + } }; LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization) diff --git a/clang/unittests/Driver/MultilibTest.cpp b/clang/unittests/Driver/MultilibTest.cpp --- a/clang/unittests/Driver/MultilibTest.cpp +++ b/clang/unittests/Driver/MultilibTest.cpp @@ -13,6 +13,7 @@ #include "clang/Driver/Multilib.h" #include "../../lib/Driver/ToolChains/CommonArgs.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/Version.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" @@ -197,20 +198,80 @@ &Diagnostic); } +static bool parse(MultilibSet &MS, std::string &Diagnostic, + const std::string &Data) { + return MS.parse(llvm::MemoryBufferRef(Data, "TEST"), diagnosticCallback, + &Diagnostic); +} + static bool parse(MultilibSet &MS, const char *Data) { return MS.parse(llvm::MemoryBufferRef(Data, "TEST")); } +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) +// Avoid using MULTILIB_CLANG_VERSION in case it has extra non-numeric parts. +#define MULTILIB_CLANG_VERSION \ + STRINGIFY(CLANG_VERSION_MAJOR) \ + "." STRINGIFY(CLANG_VERSION_MINOR) "." STRINGIFY(CLANG_VERSION_PATCHLEVEL) +#define YAML_PREAMBLE "clangMinimumVersion: " MULTILIB_CLANG_VERSION "\n" + TEST(MultilibTest, ParseInvalid) { std::string Diagnostic; MultilibSet MS; - EXPECT_FALSE(parse(MS, Diagnostic, "{}")); - EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'variants'")) + EXPECT_FALSE(parse(MS, Diagnostic, R"( +variants: [] +)")); + EXPECT_TRUE(StringRef(Diagnostic) + .contains("missing required key 'clangMinimumVersion'")) << Diagnostic; + // Require all 3 major.minor.patch version components EXPECT_FALSE(parse(MS, Diagnostic, R"( +clangMinimumVersion: )" STRINGIFY(CLANG_VERSION_MAJOR) R"(.0 +variants: [] +)")); + EXPECT_TRUE(StringRef(Diagnostic) + .contains("not a valid version string \"" STRINGIFY( + CLANG_VERSION_MAJOR) ".0\"")) + << Diagnostic; + + EXPECT_FALSE(parse(MS, Diagnostic, R"( +clangMinimumVersion: )" MULTILIB_CLANG_VERSION R"(a +variants: [] +)")); + EXPECT_TRUE( + StringRef(Diagnostic) + .contains("not a valid version string \"" MULTILIB_CLANG_VERSION + "a\"")) + << Diagnostic; + + // Reject configurations that require a later clang version + EXPECT_FALSE(parse(MS, Diagnostic, + R"( +clangMinimumVersion: )" + std::to_string(CLANG_VERSION_MAJOR + 1) + + R"(.0.0 +variants: [] +)")); + EXPECT_TRUE(StringRef(Diagnostic) + .contains("clang version " MULTILIB_CLANG_VERSION + " is less than clangMinimumVersion: " + + std::to_string(CLANG_VERSION_MAJOR + 1) + ".0.0")) + << Diagnostic; + + // but accept configurations that only need an earlier clang version + EXPECT_TRUE(parse(MS, Diagnostic, R"( +clangMinimumVersion: 16.0.0 +variants: [] +)")) << Diagnostic; + + EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE)); + EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'variants'")) + << Diagnostic; + + EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"( variants: - dir: /abc flags: [] @@ -219,7 +280,7 @@ EXPECT_TRUE(StringRef(Diagnostic).contains("paths must be relative")) << Diagnostic; - EXPECT_FALSE(parse(MS, Diagnostic, R"( + EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"( variants: - flags: [] printArgs: [] @@ -227,7 +288,7 @@ EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'dir'")) << Diagnostic; - EXPECT_FALSE(parse(MS, Diagnostic, R"( + EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"( variants: - dir: . printArgs: [] @@ -235,7 +296,7 @@ EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'flags'")) << Diagnostic; - EXPECT_FALSE(parse(MS, Diagnostic, R"( + EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"( variants: - dir: . flags: [] @@ -244,7 +305,7 @@ StringRef(Diagnostic).contains("missing required key 'printArgs'")) << Diagnostic; - EXPECT_FALSE(parse(MS, Diagnostic, R"( + EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"( variants: [] flagMap: - regex: abc @@ -254,7 +315,7 @@ .contains("value required for 'matchFlags' or 'noMatchFlags'")) << Diagnostic; - EXPECT_FALSE(parse(MS, Diagnostic, R"( + EXPECT_FALSE(parse(MS, Diagnostic, YAML_PREAMBLE R"( variants: [] flagMap: - dir: . @@ -268,7 +329,7 @@ TEST(MultilibTest, Parse) { MultilibSet MS; - EXPECT_TRUE(parse(MS, R"( + EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: . flags: [] @@ -277,7 +338,7 @@ EXPECT_EQ(1U, MS.size()); EXPECT_EQ("", MS.begin()->gccSuffix()); - EXPECT_TRUE(parse(MS, R"( + EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: abc flags: [] @@ -286,7 +347,7 @@ EXPECT_EQ(1U, MS.size()); EXPECT_EQ("/abc", MS.begin()->gccSuffix()); - EXPECT_TRUE(parse(MS, R"( + EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: pqr flags: [] @@ -297,7 +358,7 @@ EXPECT_EQ(std::vector({"-mfloat-abi=soft"}), MS.begin()->getPrintArgs()); - EXPECT_TRUE(parse(MS, R"( + EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: pqr flags: [] @@ -307,7 +368,7 @@ EXPECT_EQ(std::vector({"-mfloat-abi=soft", "-fno-exceptions"}), MS.begin()->getPrintArgs()); - EXPECT_TRUE(parse(MS, R"( + EXPECT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: a flags: [] @@ -322,7 +383,7 @@ TEST(MultilibTest, SelectSoft) { MultilibSet MS; Multilib Selected; - ASSERT_TRUE(parse(MS, R"( + ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: s flags: [softabi] @@ -341,7 +402,7 @@ TEST(MultilibTest, SelectSoftFP) { MultilibSet MS; Multilib Selected; - ASSERT_TRUE(parse(MS, R"( + ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: f flags: [mfloat-abi=softfp] @@ -357,7 +418,7 @@ // with hard float. MultilibSet MS; Multilib Selected; - ASSERT_TRUE(parse(MS, R"( + ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: h flags: [mfloat-abi=hard] @@ -371,7 +432,7 @@ TEST(MultilibTest, SelectFloatABI) { MultilibSet MS; Multilib Selected; - ASSERT_TRUE(parse(MS, R"( + ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: s flags: [softabi] @@ -403,7 +464,7 @@ // selected because soft is compatible with softfp and last wins. MultilibSet MS; Multilib Selected; - ASSERT_TRUE(parse(MS, R"( + ASSERT_TRUE(parse(MS, YAML_PREAMBLE R"( variants: - dir: h flags: [hardabi, hasfp] @@ -431,7 +492,7 @@ } TEST(MultilibTest, SelectMClass) { - const char *MultilibSpec = R"( + const char *MultilibSpec = YAML_PREAMBLE R"( variants: - dir: thumb/v6-m/nofp flags: [target=thumbv6m-none-eabi]