clang 18.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
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
20using namespace clang::driver;
21using namespace clang;
22
23static 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)
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.startswith("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
51static 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)
56
58 File.get()->getBuffer().split(Lines, "\n");
60
61 for (StringRef Line : Lines)
62 if (Version == Distro::UnknownDistro &&
63 Line.startswith("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 .Default(Distro::UnknownDistro);
98 return Version;
99}
100
101static Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS) {
103
104 // Newer freedesktop.org's compilant systemd-based systems
105 // should provide /etc/os-release or /usr/lib/os-release.
106 Version = DetectOsRelease(VFS);
107 if (Version != Distro::UnknownDistro)
108 return Version;
109
110 // Older systems might provide /etc/lsb-release.
111 Version = DetectLsbRelease(VFS);
112 if (Version != Distro::UnknownDistro)
113 return Version;
114
115 // Otherwise try some distro-specific quirks for Red Hat...
116 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
117 VFS.getBufferForFile("/etc/redhat-release");
118
119 if (File) {
120 StringRef Data = File.get()->getBuffer();
121 if (Data.startswith("Fedora release"))
122 return Distro::Fedora;
123 if (Data.startswith("Red Hat Enterprise Linux") ||
124 Data.startswith("CentOS") || Data.startswith("Scientific Linux")) {
125 if (Data.contains("release 7"))
126 return Distro::RHEL7;
127 else if (Data.contains("release 6"))
128 return Distro::RHEL6;
129 else if (Data.contains("release 5"))
130 return Distro::RHEL5;
131 }
133 }
134
135 // ...for Debian
136 File = VFS.getBufferForFile("/etc/debian_version");
137 if (File) {
138 StringRef Data = File.get()->getBuffer();
139 // Contents: < major.minor > or < codename/sid >
140 int MajorVersion;
141 if (!Data.split('.').first.getAsInteger(10, MajorVersion)) {
142 switch (MajorVersion) {
143 case 5:
144 return Distro::DebianLenny;
145 case 6:
147 case 7:
149 case 8:
151 case 9:
153 case 10:
155 case 11:
157 case 12:
159 case 13:
161 default:
163 }
164 }
165 return llvm::StringSwitch<Distro::DistroType>(Data.split("\n").first)
166 .Case("squeeze/sid", Distro::DebianSqueeze)
167 .Case("wheezy/sid", Distro::DebianWheezy)
168 .Case("jessie/sid", Distro::DebianJessie)
169 .Case("stretch/sid", Distro::DebianStretch)
170 .Case("buster/sid", Distro::DebianBuster)
171 .Case("bullseye/sid", Distro::DebianBullseye)
172 .Case("bookworm/sid", Distro::DebianBookworm)
173 .Case("trixie/sid", Distro::DebianTrixie)
174 .Default(Distro::UnknownDistro);
175 }
176
177 // ...for SUSE
178 File = VFS.getBufferForFile("/etc/SuSE-release");
179 if (File) {
180 StringRef Data = File.get()->getBuffer();
182 Data.split(Lines, "\n");
183 for (const StringRef &Line : Lines) {
184 if (!Line.trim().startswith("VERSION"))
185 continue;
186 std::pair<StringRef, StringRef> SplitLine = Line.split('=');
187 // Old versions have split VERSION and PATCHLEVEL
188 // Newer versions use VERSION = x.y
189 std::pair<StringRef, StringRef> SplitVer =
190 SplitLine.second.trim().split('.');
191 int Version;
192
193 // OpenSUSE/SLES 10 and older are not supported and not compatible
194 // with our rules, so just treat them as Distro::UnknownDistro.
195 if (!SplitVer.first.getAsInteger(10, Version) && Version > 10)
196 return Distro::OpenSUSE;
198 }
200 }
201
202 // ...and others.
203 if (VFS.exists("/etc/gentoo-release"))
204 return Distro::Gentoo;
205
207}
208
209static Distro::DistroType GetDistro(llvm::vfs::FileSystem &VFS,
210 const llvm::Triple &TargetOrHost) {
211 // If we don't target Linux, no need to check the distro. This saves a few
212 // OS calls.
213 if (!TargetOrHost.isOSLinux())
215
216 // True if we're backed by a real file system.
217 const bool onRealFS = (llvm::vfs::getRealFileSystem() == &VFS);
218
219 // If the host is not running Linux, and we're backed by a real file
220 // system, no need to check the distro. This is the case where someone
221 // is cross-compiling from BSD or Windows to Linux, and it would be
222 // meaningless to try to figure out the "distro" of the non-Linux host.
223 llvm::Triple HostTriple(llvm::sys::getProcessTriple());
224 if (!HostTriple.isOSLinux() && onRealFS)
226
227 if (onRealFS) {
228 // If we're backed by a real file system, perform
229 // the detection only once and save the result.
230 static Distro::DistroType LinuxDistro = DetectDistro(VFS);
231 return LinuxDistro;
232 }
233 // This is mostly for passing tests which uses llvm::vfs::InMemoryFileSystem,
234 // which is not "real".
235 return DetectDistro(VFS);
236}
237
238Distro::Distro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost)
239 : 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:101
static Distro::DistroType GetDistro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost)
Definition: Distro.cpp:209
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:93