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