michaelplatings created this revision.
michaelplatings added a reviewer: peter.smith.
Herald added a subscriber: dmgreen.
Herald added a project: All.
michaelplatings requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay.
Herald added a project: clang.

This avoids a potential source of subtle errors if an older version of
Clang were to be used with a multilib.yaml that requires a newer Clang
to work correctly.

This feature is comparable to CMake's cmake_minimum_required.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D143763

Files:
  clang/lib/Driver/Multilib.cpp
  clang/unittests/Driver/MultilibTest.cpp

Index: clang/unittests/Driver/MultilibTest.cpp
===================================================================
--- clang/unittests/Driver/MultilibTest.cpp
+++ 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<std::string>({"-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<std::string>({"-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]
Index: clang/lib/Driver/Multilib.cpp
===================================================================
--- clang/lib/Driver/Multilib.cpp
+++ 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<MultilibSerialization> Multilibs;
   std::vector<MultilibSet::FlagMatcher> FlagMatchers;
 };
@@ -174,9 +176,38 @@
 
 template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
   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<int, 3> ClangMinimumVersion,
+        ClangVersion = {CLANG_VERSION_MAJOR, CLANG_VERSION_MINOR,
+                        CLANG_VERSION_PATCHLEVEL};
+
+    SmallVector<StringRef, 3> 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)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to