1 //===--- ConfigProvider.cpp - Loading of user configuration ---------------===//
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 "ConfigProvider.h"
11 #include "ConfigFragment.h"
12 #include "support/FileCache.h"
13 #include "support/Path.h"
14 #include "support/ThreadsafeFS.h"
15 #include "support/Trace.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/StringMap.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Path.h"
29 // Threadsafe cache around reading a YAML config file from disk.
30 class FileConfigCache
: public FileCache
{
31 mutable llvm::SmallVector
<CompiledFragment
, 1> CachedValue
;
32 std::string Directory
;
35 FileConfigCache(llvm::StringRef Path
, llvm::StringRef Directory
)
36 : FileCache(Path
), Directory(Directory
) {}
38 void get(const ThreadsafeFS
&TFS
, DiagnosticCallback DC
,
39 std::chrono::steady_clock::time_point FreshTime
, bool Trusted
,
40 std::vector
<CompiledFragment
> &Out
) const {
43 [&](std::optional
<llvm::StringRef
> Data
) {
46 for (auto &Fragment
: Fragment::parseYAML(*Data
, path(), DC
)) {
47 Fragment
.Source
.Directory
= Directory
;
48 Fragment
.Source
.Trusted
= Trusted
;
49 CachedValue
.push_back(std::move(Fragment
).compile(DC
));
52 [&]() { llvm::copy(CachedValue
, std::back_inserter(Out
)); });
56 std::unique_ptr
<Provider
> Provider::fromYAMLFile(llvm::StringRef AbsPath
,
57 llvm::StringRef Directory
,
58 const ThreadsafeFS
&FS
,
60 class AbsFileProvider
: public Provider
{
61 mutable FileConfigCache Cache
; // threadsafe
62 const ThreadsafeFS
&FS
;
65 std::vector
<CompiledFragment
>
66 getFragments(const Params
&P
, DiagnosticCallback DC
) const override
{
67 std::vector
<CompiledFragment
> Result
;
68 Cache
.get(FS
, DC
, P
.FreshTime
, Trusted
, Result
);
73 AbsFileProvider(llvm::StringRef Path
, llvm::StringRef Directory
,
74 const ThreadsafeFS
&FS
, bool Trusted
)
75 : Cache(Path
, Directory
), FS(FS
), Trusted(Trusted
) {
76 assert(llvm::sys::path::is_absolute(Path
));
80 return std::make_unique
<AbsFileProvider
>(AbsPath
, Directory
, FS
, Trusted
);
83 std::unique_ptr
<Provider
>
84 Provider::fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath
,
85 const ThreadsafeFS
&FS
, bool Trusted
) {
86 class RelFileProvider
: public Provider
{
88 const ThreadsafeFS
&FS
;
91 mutable std::mutex Mu
;
92 // Keys are the (posix-style) ancestor directory, not the config within it.
93 // We only insert into this map, so pointers to values are stable forever.
94 // Mutex guards the map itself, not the values (which are threadsafe).
95 mutable llvm::StringMap
<FileConfigCache
> Cache
;
97 std::vector
<CompiledFragment
>
98 getFragments(const Params
&P
, DiagnosticCallback DC
) const override
{
99 namespace path
= llvm::sys::path
;
104 // Compute absolute paths to all ancestors (substrings of P.Path).
105 llvm::SmallVector
<llvm::StringRef
, 8> Ancestors
;
106 for (auto Ancestor
= absoluteParent(P
.Path
); !Ancestor
.empty();
107 Ancestor
= absoluteParent(Ancestor
)) {
108 Ancestors
.emplace_back(Ancestor
);
110 // Ensure corresponding cache entries exist in the map.
111 llvm::SmallVector
<FileConfigCache
*, 8> Caches
;
113 std::lock_guard
<std::mutex
> Lock(Mu
);
114 for (llvm::StringRef Ancestor
: Ancestors
) {
115 auto It
= Cache
.find(Ancestor
);
116 // Assemble the actual config file path only once.
117 if (It
== Cache
.end()) {
118 llvm::SmallString
<256> ConfigPath
= Ancestor
;
119 path::append(ConfigPath
, RelPath
);
120 // Use native slashes for reading the file, affects diagnostics.
121 llvm::sys::path::native(ConfigPath
);
122 It
= Cache
.try_emplace(Ancestor
, ConfigPath
.str(), Ancestor
).first
;
124 Caches
.push_back(&It
->second
);
127 // Finally query each individual file.
128 // This will take a (per-file) lock for each file that actually exists.
129 std::vector
<CompiledFragment
> Result
;
130 for (FileConfigCache
*Cache
: llvm::reverse(Caches
))
131 Cache
->get(FS
, DC
, P
.FreshTime
, Trusted
, Result
);
136 RelFileProvider(llvm::StringRef RelPath
, const ThreadsafeFS
&FS
,
138 : RelPath(RelPath
), FS(FS
), Trusted(Trusted
) {
139 assert(llvm::sys::path::is_relative(RelPath
));
143 return std::make_unique
<RelFileProvider
>(RelPath
, FS
, Trusted
);
146 std::unique_ptr
<Provider
>
147 Provider::combine(std::vector
<const Provider
*> Providers
) {
148 class CombinedProvider
: public Provider
{
149 std::vector
<const Provider
*> Providers
;
151 std::vector
<CompiledFragment
>
152 getFragments(const Params
&P
, DiagnosticCallback DC
) const override
{
153 std::vector
<CompiledFragment
> Result
;
154 for (const auto &Provider
: Providers
) {
155 for (auto &Fragment
: Provider
->getFragments(P
, DC
))
156 Result
.push_back(std::move(Fragment
));
162 CombinedProvider(std::vector
<const Provider
*> Providers
)
163 : Providers(std::move(Providers
)) {}
166 return std::make_unique
<CombinedProvider
>(std::move(Providers
));
169 Config
Provider::getConfig(const Params
&P
, DiagnosticCallback DC
) const {
170 trace::Span
Tracer("getConfig");
172 SPAN_ATTACH(Tracer
, "path", P
.Path
);
174 for (const auto &Fragment
: getFragments(P
, DC
))
179 } // namespace config
180 } // namespace clangd