Loading...
Searching...
No Matches
JitSearchPaths.hpp
1#pragma once
2#include <QDir>
3#include <QDirIterator>
4#include <QFileInfo>
5#include <QString>
6#include <QStringList>
7#include <QVersionNumber>
8
9#include <algorithm>
10#include <map>
11#include <string>
12#include <string_view>
13#include <vector>
14
15// The parts of the JIT's compiler invocation that only depend on what is on
16// disk and on what score was built with, so that they can be tested on a
17// fake tree.
18namespace Jit
19{
20
26inline QString selectClangResourceDir(
27 const QString& clangDir, int major, const QString& fullVersion)
28{
29 const QStringList entries
30 = QDir(clangDir).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
31 const QString majorName = QString::number(major);
32 if(entries.contains(majorName))
33 return majorName;
34 if(!fullVersion.isEmpty() && entries.contains(fullVersion))
35 return fullVersion;
36
37 // e.g. 15.0.7 for a JIT built against 15.0.6
38 QString best;
39 QVersionNumber bestVersion;
40 for(const QString& e : entries)
41 {
42 if(!e.startsWith(majorName + '.'))
43 continue;
44 const auto v = QVersionNumber::fromString(e);
45 if(best.isEmpty() || v > bestVersion)
46 {
47 best = e;
48 bestVersion = v;
49 }
50 }
51 return best;
52}
53
56inline QString newestClangResourceDir(const QString& clangDir)
57{
58 QString best;
59 QVersionNumber bestVersion;
60 for(const QString& e :
61 QDir(clangDir).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name))
62 {
63 const auto v = QVersionNumber::fromString(e);
64 if(v.isNull())
65 continue;
66 if(best.isEmpty() || v > bestVersion)
67 {
68 best = e;
69 bestVersion = v;
70 }
71 }
72 return best;
73}
74
80inline QString findClangRuntimeArchive(
81 const QStringList& roots, int major, const QString& fullVersion,
82 const QString& filter, const QStringList& preferred = {})
83{
84 for(const QString& root : roots)
85 {
86 const QString clangDir = root + "/lib/clang";
87 const QString version = selectClangResourceDir(clangDir, major, fullVersion);
88 if(version.isEmpty())
89 continue;
90
91 // Debian's /usr/lib/clang/<v>/lib links to /usr/lib/llvm-<v>/lib/clang/<v>/lib
92 QStringList found;
93 QDirIterator it(
94 clangDir + "/" + version, {filter}, QDir::Files,
95 QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
96 while(it.hasNext())
97 found.push_back(it.next());
98 if(found.empty())
99 continue;
100 found.sort();
101
102 for(const QString& p : preferred)
103 for(const QString& f : found)
104 if(QString(f).replace('\\', '/').contains(p))
105 return f;
106 return found.front();
107 }
108 return {};
109}
110
113inline QString withoutVendor(const QString& triple)
114{
115 const QStringList parts = triple.split('-');
116 if(parts.size() == 4)
117 return parts[0] + '-' + parts[2] + '-' + parts[3];
118 return triple;
119}
120
126inline QString selectMultiarchDir(
127 const QString& includeRoot, const QString& marker,
128 const std::vector<QString>& triples)
129{
130 QStringList found;
131 for(const QString& d :
132 QDir(includeRoot).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name))
133 if(QFileInfo::exists(includeRoot + "/" + d + "/" + marker))
134 found.push_back(d);
135
136 for(const QString& t : triples)
137 {
138 if(found.contains(t))
139 return t;
140 if(const QString v = withoutVendor(t); found.contains(v))
141 return v;
142 }
143 if(found.size() == 1)
144 return found.front();
145 return {};
146}
147
154inline std::vector<std::string> buildTreeIncludeArgs(
155 const std::vector<std::string_view>& dirs, std::string_view sourceTree,
156 std::string_view binaryTree, const std::string& sdkInclude)
157{
158 std::vector<std::string> args;
159 const QString cleanSdkInclude = QDir::cleanPath(QString::fromStdString(sdkInclude));
160 auto add = [&](std::string arg) {
161 if(std::find(args.begin(), args.end(), arg) == args.end())
162 args.push_back(std::move(arg));
163 };
164
165 for(std::string_view dir : dirs)
166 {
167 if(dir.empty())
168 continue;
169 const QString clean
170 = QDir::cleanPath(QString::fromUtf8(dir.data(), qsizetype(dir.size())));
171 if(clean == cleanSdkInclude)
172 continue;
173
174 if(clean.endsWith(".framework"))
175 {
176 add("-iframework" + QFileInfo(clean).path().toStdString());
177 continue;
178 }
179
180 std::string path(dir);
181 if(path.starts_with(sourceTree) || path.starts_with(binaryTree))
182 add("-I" + path);
183 else
184 add("-isystem" + path);
185 }
186 return args;
187}
188
193inline void removeRepeatedDefinitions(std::vector<std::string>& args)
194{
195 // Macro name -> value of the definition in effect
196 std::map<std::string, std::string, std::less<>> defined;
197 std::vector<std::string> out;
198 out.reserve(args.size());
199 for(auto& arg : args)
200 {
201 if(arg.size() > 2 && arg.starts_with("-D"))
202 {
203 std::string_view def = std::string_view(arg).substr(2);
204 const auto eq = def.find('=');
205 std::string name(def.substr(0, eq));
206 std::string value(eq == def.npos ? "1" : def.substr(eq + 1));
207 if(auto it = defined.find(name); it != defined.end() && it->second == value)
208 continue;
209 defined[std::move(name)] = std::move(value);
210 }
211 else if(arg.size() > 2 && arg.starts_with("-U"))
212 {
213 defined.erase(arg.substr(2));
214 }
215 out.push_back(std::move(arg));
216 }
217 args = std::move(out);
218}
219}