clang  19.0.0git
Distro.cpp
Go to the documentation of this file.
1 //===--- Distro.cpp - Linux distribution detection support ------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Driver/Distro.h"
10 #include "clang/Basic/LLVM.h"
11 #include "llvm/ADT/SmallVector.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/ADT/StringSwitch.h"
14 #include "llvm/Support/ErrorOr.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/Threading.h"
17 #include "llvm/TargetParser/Host.h"
18 #include "llvm/TargetParser/Triple.h"
19 
20 using namespace clang::driver;
21 using namespace clang;
22 
23 static Distro::DistroType DetectOsRelease(llvm::vfs::FileSystem &VFS) {
24  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
25  VFS.getBufferForFile("/etc/os-release");
26  if (!File)
27  File = VFS.getBufferForFile("/usr/lib/os-release");
28  if (!File)
29  return Distro::UnknownDistro;
30 
32  File.get()->getBuffer().split(Lines, "\n");
34 
35  // Obviously this can be improved a lot.
36  for (StringRef Line : Lines)
37  if (Version == Distro::UnknownDistro && Line.starts_with("ID="))
38  Version = llvm::StringSwitch<Distro::DistroType>(Line.substr(3))
39  .Case("alpine", Distro::AlpineLinux)
40  .Case("fedora", Distro::Fedora)
41  .Case("gentoo", Distro::Gentoo)
42  .Case("arch", Distro::ArchLinux)
43  // On SLES, /etc/os-release was introduced in SLES 11.
44  .Case("sles", Distro::OpenSUSE)
45  .Case("opensuse", Distro::OpenSUSE)
46  .Case("exherbo", Distro::Exherbo)
47  .Default(Distro::UnknownDistro);
48  return Version;
49 }
50 
51 static Distro::DistroType DetectLsbRelease(llvm::vfs::FileSystem &VFS) {
52  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
53  VFS.getBufferForFile("/etc/lsb-release");
54  if (!File)
55  return Distro::UnknownDistro;
56 
58  File.get()->getBuffer().split(Lines, "\n");
60 
61  for (StringRef Line : Lines)
62  if (Version == Distro::UnknownDistro &&
63  Line.starts_with("DISTRIB_CODENAME="))
64  Version = llvm::StringSwitch<Distro::DistroType>(Line.substr(17))
65  .Case("hardy", Distro::UbuntuHardy)
66  .Case("intrepid", Distro::UbuntuIntrepid)
67  .Case("jaunty", Distro::UbuntuJaunty)
68  .Case("karmic", Distro::UbuntuKarmic)
69  .Case("lucid", Distro::UbuntuLucid)
70  .Case("maverick", Distro::UbuntuMaverick)
71  .Case("natty", Distro::UbuntuNatty)
72  .Case("oneiric", Distro::UbuntuOneiric)
73  .Case("precise", Distro::UbuntuPrecise)
74  .Case("quantal", Distro::UbuntuQuantal)
75  .Case("raring", Distro::UbuntuRaring)
76  .Case("saucy", Distro::UbuntuSaucy)
77  .Case("trusty", Distro::UbuntuTrusty)
78  .Case("utopic", Distro::UbuntuUtopic)
79  .Case("vivid", Distro::UbuntuVivid)
80  .Case("wily", Distro::UbuntuWily)
81  .Case("xenial", Distro::UbuntuXenial)
82  .Case("yakkety", Distro::UbuntuYakkety)
83  .Case("zesty", Distro::UbuntuZesty)
84  .Case("artful", Distro::UbuntuArtful)
85  .Case("bionic", Distro::UbuntuBionic)
86  .Case("cosmic", Distro::UbuntuCosmic)
87  .Case("disco", Distro::UbuntuDisco)
88  .Case("eoan", Distro::UbuntuEoan)
89  .Case("focal", Distro::UbuntuFocal)
90  .Case("groovy", Distro::UbuntuGroovy)
91  .Case("hirsute", Distro::UbuntuHirsute)
92  .Case("impish", Distro::UbuntuImpish)
93  .Case("jammy", Distro::UbuntuJammy)
94  .Case("kinetic", Distro::UbuntuKinetic)
95  .Case("lunar", Distro::UbuntuLunar)
96  .Case("mantic", Distro::UbuntuMantic)
97  .Case("noble", Distro::UbuntuNoble)
98  .Case("oracular", Distro::UbuntuOracular)
99  .Default(Distro::UnknownDistro);
100  return Version;
101 }
102 
103 static Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS) {
105 
106  // Newer freedesktop.org's compilant systemd-based systems
107  // should provide /etc/os-release or /usr/lib/os-release.
108  Version = DetectOsRelease(VFS);
109  if (Version != Distro::UnknownDistro)
110  return Version;
111 
112  // Older systems might provide /etc/lsb-release.
113  Version = DetectLsbRelease(VFS);
114  if (Version != Distro::UnknownDistro)
115  return Version;
116 
117  // Otherwise try some distro-specific quirks for Red Hat...
118  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
119  VFS.getBufferForFile("/etc/redhat-release");
120 
121  if (File) {
122  StringRef Data = File.get()->getBuffer();
123  if (Data.starts_with("Fedora release"))
124  return Distro::Fedora;
125  if (Data.starts_with("Red Hat Enterprise Linux") ||
126  Data.starts_with("CentOS") || Data.starts_with("Scientific Linux")) {
127  if (Data.contains("release 7"))
128  return Distro::RHEL7;
129  else if (Data.contains("release 6"))
130  return Distro::RHEL6;
131  else if (Data.contains("release 5"))
132  return Distro::RHEL5;
133  }
134  return Distro::UnknownDistro;
135  }
136 
137  // ...for Debian
138  File = VFS.getBufferForFile("/etc/debian_version");
139  if (File) {
140  StringRef Data = File.get()->getBuffer();
141  // Contents: < major.minor > or < codename/sid >
142  int MajorVersion;
143  if (!Data.split('.').first.getAsInteger(10, MajorVersion)) {
144  switch (MajorVersion) {
145  case 5:
146  return Distro::DebianLenny;
147  case 6:
148  return Distro::DebianSqueeze;
149  case 7:
150  return Distro::DebianWheezy;
151  case 8:
152  return Distro::DebianJessie;
153  case 9:
154  return Distro::DebianStretch;
155  case 10:
156  return Distro::DebianBuster;
157  case 11:
158  return Distro::DebianBullseye;
159  case 12:
160  return Distro::DebianBookworm;
161  case 13:
162  return Distro::DebianTrixie;
163  default:
164  return Distro::UnknownDistro;
165  }
166  }
167  return llvm::StringSwitch<Distro::DistroType>(Data.split("\n").first)
168  .Case("squeeze/sid", Distro::DebianSqueeze)
169  .Case("wheezy/sid", Distro::DebianWheezy)
170  .Case("jessie/sid", Distro::DebianJessie)
171  .Case("stretch/sid", Distro::DebianStretch)
172  .Case("buster/sid", Distro::DebianBuster)
173  .Case("bullseye/sid", Distro::DebianBullseye)
174  .Case("bookworm/sid", Distro::DebianBookworm)
175  .Case("trixie/sid", Distro::DebianTrixie)
176  .Default(Distro::UnknownDistro);
177  }
178 
179  // ...for SUSE
180  File = VFS.getBufferForFile("/etc/SuSE-release");
181  if (File) {
182  StringRef Data = File.get()->getBuffer();
184  Data.split(Lines, "\n");
185  for (const StringRef &Line : Lines) {
186  if (!Line.trim().starts_with("VERSION"))
187  continue;
188  std::pair<StringRef, StringRef> SplitLine = Line.split('=');
189  // Old versions have split VERSION and PATCHLEVEL
190  // Newer versions use VERSION = x.y
191  std::pair<StringRef, StringRef> SplitVer =
192  SplitLine.second.trim().split('.');
193  int Version;
194 
195  // OpenSUSE/SLES 10 and older are not supported and not compatible
196  // with our rules, so just treat them as Distro::UnknownDistro.
197  if (!SplitVer.first.getAsInteger(10, Version) && Version > 10)
198  return Distro::OpenSUSE;
199  return Distro::UnknownDistro;
200  }
201  return Distro::UnknownDistro;
202  }
203 
204  // ...and others.
205  if (VFS.exists("/etc/gentoo-release"))
206  return Distro::Gentoo;
207 
208  return Distro::UnknownDistro;
209 }
210 
211 static Distro::DistroType GetDistro(llvm::vfs::FileSystem &VFS,
212  const llvm::Triple &TargetOrHost) {
213  // If we don't target Linux, no need to check the distro. This saves a few
214  // OS calls.
215  if (!TargetOrHost.isOSLinux())
216  return Distro::UnknownDistro;
217 
218  // True if we're backed by a real file system.
219  const bool onRealFS = (llvm::vfs::getRealFileSystem() == &VFS);
220 
221  // If the host is not running Linux, and we're backed by a real file
222  // system, no need to check the distro. This is the case where someone
223  // is cross-compiling from BSD or Windows to Linux, and it would be
224  // meaningless to try to figure out the "distro" of the non-Linux host.
225  llvm::Triple HostTriple(llvm::sys::getProcessTriple());
226  if (!HostTriple.isOSLinux() && onRealFS)
227  return Distro::UnknownDistro;
228 
229  if (onRealFS) {
230  // If we're backed by a real file system, perform
231  // the detection only once and save the result.
232  static Distro::DistroType LinuxDistro = DetectDistro(VFS);
233  return LinuxDistro;
234  }
235  // This is mostly for passing tests which uses llvm::vfs::InMemoryFileSystem,
236  // which is not "real".
237  return DetectDistro(VFS);
238 }
239 
240 Distro::Distro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost)
241  : DistroVal(GetDistro(VFS, TargetOrHost)) {}
static Distro::DistroType DetectOsRelease(llvm::vfs::FileSystem &VFS)
Definition: Distro.cpp:23
static Distro::DistroType DetectLsbRelease(llvm::vfs::FileSystem &VFS)
Definition: Distro.cpp:51
static Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS)
Definition: Distro.cpp:103
static Distro::DistroType GetDistro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost)
Definition: Distro.cpp:211
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
const char * Data
Distro()
Default constructor leaves the distribution unknown.
Definition: Distro.h:95
The JSON file list parser is used to communicate input to InstallAPI.