1 //===--- Distro.cpp - Linux distribution detection support ------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Driver/Distro.h"
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"
19 using namespace clang::driver
;
20 using namespace clang
;
22 static Distro::DistroType
DetectOsRelease(llvm::vfs::FileSystem
&VFS
) {
23 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>> File
=
24 VFS
.getBufferForFile("/etc/os-release");
26 File
= VFS
.getBufferForFile("/usr/lib/os-release");
28 return Distro::UnknownDistro
;
30 SmallVector
<StringRef
, 16> Lines
;
31 File
.get()->getBuffer().split(Lines
, "\n");
32 Distro::DistroType Version
= Distro::UnknownDistro
;
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
);
50 static Distro::DistroType
DetectLsbRelease(llvm::vfs::FileSystem
&VFS
) {
51 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>> File
=
52 VFS
.getBufferForFile("/etc/lsb-release");
54 return Distro::UnknownDistro
;
56 SmallVector
<StringRef
, 16> Lines
;
57 File
.get()->getBuffer().split(Lines
, "\n");
58 Distro::DistroType Version
= Distro::UnknownDistro
;
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("hardy", Distro::UbuntuHardy
)
65 .Case("intrepid", Distro::UbuntuIntrepid
)
66 .Case("jaunty", Distro::UbuntuJaunty
)
67 .Case("karmic", Distro::UbuntuKarmic
)
68 .Case("lucid", Distro::UbuntuLucid
)
69 .Case("maverick", Distro::UbuntuMaverick
)
70 .Case("natty", Distro::UbuntuNatty
)
71 .Case("oneiric", Distro::UbuntuOneiric
)
72 .Case("precise", Distro::UbuntuPrecise
)
73 .Case("quantal", Distro::UbuntuQuantal
)
74 .Case("raring", Distro::UbuntuRaring
)
75 .Case("saucy", Distro::UbuntuSaucy
)
76 .Case("trusty", Distro::UbuntuTrusty
)
77 .Case("utopic", Distro::UbuntuUtopic
)
78 .Case("vivid", Distro::UbuntuVivid
)
79 .Case("wily", Distro::UbuntuWily
)
80 .Case("xenial", Distro::UbuntuXenial
)
81 .Case("yakkety", Distro::UbuntuYakkety
)
82 .Case("zesty", Distro::UbuntuZesty
)
83 .Case("artful", Distro::UbuntuArtful
)
84 .Case("bionic", Distro::UbuntuBionic
)
85 .Case("cosmic", Distro::UbuntuCosmic
)
86 .Case("disco", Distro::UbuntuDisco
)
87 .Case("eoan", Distro::UbuntuEoan
)
88 .Case("focal", Distro::UbuntuFocal
)
89 .Case("groovy", Distro::UbuntuGroovy
)
90 .Case("hirsute", Distro::UbuntuHirsute
)
91 .Case("impish", Distro::UbuntuImpish
)
92 .Case("jammy", Distro::UbuntuJammy
)
93 .Case("kinetic", Distro::UbuntuKinetic
)
94 .Case("lunar", Distro::UbuntuLunar
)
95 .Case("mantic", Distro::UbuntuMantic
)
96 .Case("noble", Distro::UbuntuNoble
)
97 .Case("oracular", Distro::UbuntuOracular
)
98 .Case("plucky", Distro::UbuntuPlucky
)
99 .Default(Distro::UnknownDistro
);
103 static Distro::DistroType
DetectDistro(llvm::vfs::FileSystem
&VFS
) {
104 Distro::DistroType Version
= Distro::UnknownDistro
;
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
)
112 // Older systems might provide /etc/lsb-release.
113 Version
= DetectLsbRelease(VFS
);
114 if (Version
!= Distro::UnknownDistro
)
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");
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
;
134 return Distro::UnknownDistro
;
138 File
= VFS
.getBufferForFile("/etc/debian_version");
140 StringRef Data
= File
.get()->getBuffer();
141 // Contents: < major.minor > or < codename/sid >
143 if (!Data
.split('.').first
.getAsInteger(10, MajorVersion
)) {
144 switch (MajorVersion
) {
146 return Distro::DebianLenny
;
148 return Distro::DebianSqueeze
;
150 return Distro::DebianWheezy
;
152 return Distro::DebianJessie
;
154 return Distro::DebianStretch
;
156 return Distro::DebianBuster
;
158 return Distro::DebianBullseye
;
160 return Distro::DebianBookworm
;
162 return Distro::DebianTrixie
;
164 return Distro::UnknownDistro
;
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
);
180 File
= VFS
.getBufferForFile("/etc/SuSE-release");
182 StringRef Data
= File
.get()->getBuffer();
183 SmallVector
<StringRef
, 8> Lines
;
184 Data
.split(Lines
, "\n");
185 for (const StringRef
&Line
: Lines
) {
186 if (!Line
.trim().starts_with("VERSION"))
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('.');
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
;
201 return Distro::UnknownDistro
;
205 if (VFS
.exists("/etc/gentoo-release"))
206 return Distro::Gentoo
;
208 return Distro::UnknownDistro
;
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
215 if (!TargetOrHost
.isOSLinux())
216 return Distro::UnknownDistro
;
218 // True if we're backed by a real file system.
219 const bool onRealFS
= (llvm::vfs::getRealFileSystem() == &VFS
);
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
;
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
);
235 // This is mostly for passing tests which uses llvm::vfs::InMemoryFileSystem,
236 // which is not "real".
237 return DetectDistro(VFS
);
240 Distro::Distro(llvm::vfs::FileSystem
&VFS
, const llvm::Triple
&TargetOrHost
)
241 : DistroVal(GetDistro(VFS
, TargetOrHost
)) {}