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"
28 // Threadsafe cache around reading a YAML config file from disk.
29 class FileConfigCache
: public FileCache
{
30 mutable llvm::SmallVector
<CompiledFragment
, 1> CachedValue
;
31 std::string Directory
;
34 FileConfigCache(llvm::StringRef Path
, llvm::StringRef Directory
)
35 : FileCache(Path
), Directory(Directory
) {}
37 void get(const ThreadsafeFS
&TFS
, DiagnosticCallback DC
,
38 std::chrono::steady_clock::time_point FreshTime
, bool Trusted
,
39 std::vector
<CompiledFragment
> &Out
) const {
42 [&](llvm::Optional
<llvm::StringRef
> Data
) {
45 for (auto &Fragment
: Fragment::parseYAML(*Data
, path(), DC
)) {
46 Fragment
.Source
.Directory
= Directory
;
47 Fragment
.Source
.Trusted
= Trusted
;
48 CachedValue
.push_back(std::move(Fragment
).compile(DC
));
51 [&]() { llvm::copy(CachedValue
, std::back_inserter(Out
)); });
55 std::unique_ptr
<Provider
> Provider::fromYAMLFile(llvm::StringRef AbsPath
,
56 llvm::StringRef Directory
,
57 const ThreadsafeFS
&FS
,
59 class AbsFileProvider
: public Provider
{
60 mutable FileConfigCache Cache
; // threadsafe
61 const ThreadsafeFS
&FS
;
64 std::vector
<CompiledFragment
>
65 getFragments(const Params
&P
, DiagnosticCallback DC
) const override
{
66 std::vector
<CompiledFragment
> Result
;
67 Cache
.get(FS
, DC
, P
.FreshTime
, Trusted
, Result
);
72 AbsFileProvider(llvm::StringRef Path
, llvm::StringRef Directory
,
73 const ThreadsafeFS
&FS
, bool Trusted
)
74 : Cache(Path
, Directory
), FS(FS
), Trusted(Trusted
) {
75 assert(llvm::sys::path::is_absolute(Path
));
79 return std::make_unique
<AbsFileProvider
>(AbsPath
, Directory
, FS
, Trusted
);
82 std::unique_ptr
<Provider
>
83 Provider::fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath
,
84 const ThreadsafeFS
&FS
, bool Trusted
) {
85 class RelFileProvider
: public Provider
{
87 const ThreadsafeFS
&FS
;
90 mutable std::mutex Mu
;
91 // Keys are the (posix-style) ancestor directory, not the config within it.
92 // We only insert into this map, so pointers to values are stable forever.
93 // Mutex guards the map itself, not the values (which are threadsafe).
94 mutable llvm::StringMap
<FileConfigCache
> Cache
;
96 std::vector
<CompiledFragment
>
97 getFragments(const Params
&P
, DiagnosticCallback DC
) const override
{
98 namespace path
= llvm::sys::path
;
103 // Compute absolute paths to all ancestors (substrings of P.Path).
104 llvm::SmallVector
<llvm::StringRef
, 8> Ancestors
;
105 for (auto Ancestor
= absoluteParent(P
.Path
); !Ancestor
.empty();
106 Ancestor
= absoluteParent(Ancestor
)) {
107 Ancestors
.emplace_back(Ancestor
);
109 // Ensure corresponding cache entries exist in the map.
110 llvm::SmallVector
<FileConfigCache
*, 8> Caches
;
112 std::lock_guard
<std::mutex
> Lock(Mu
);
113 for (llvm::StringRef Ancestor
: Ancestors
) {
114 auto It
= Cache
.find(Ancestor
);
115 // Assemble the actual config file path only once.
116 if (It
== Cache
.end()) {
117 llvm::SmallString
<256> ConfigPath
= Ancestor
;
118 path::append(ConfigPath
, RelPath
);
119 // Use native slashes for reading the file, affects diagnostics.
120 llvm::sys::path::native(ConfigPath
);
121 It
= Cache
.try_emplace(Ancestor
, ConfigPath
.str(), Ancestor
).first
;
123 Caches
.push_back(&It
->second
);
126 // Finally query each individual file.
127 // This will take a (per-file) lock for each file that actually exists.
128 std::vector
<CompiledFragment
> Result
;
129 for (FileConfigCache
*Cache
: llvm::reverse(Caches
))
130 Cache
->get(FS
, DC
, P
.FreshTime
, Trusted
, Result
);
135 RelFileProvider(llvm::StringRef RelPath
, const ThreadsafeFS
&FS
,
137 : RelPath(RelPath
), FS(FS
), Trusted(Trusted
) {
138 assert(llvm::sys::path::is_relative(RelPath
));
142 return std::make_unique
<RelFileProvider
>(RelPath
, FS
, Trusted
);
145 std::unique_ptr
<Provider
>
146 Provider::combine(std::vector
<const Provider
*> Providers
) {
147 class CombinedProvider
: public Provider
{
148 std::vector
<const Provider
*> Providers
;
150 std::vector
<CompiledFragment
>
151 getFragments(const Params
&P
, DiagnosticCallback DC
) const override
{
152 std::vector
<CompiledFragment
> Result
;
153 for (const auto &Provider
: Providers
) {
154 for (auto &Fragment
: Provider
->getFragments(P
, DC
))
155 Result
.push_back(std::move(Fragment
));
161 CombinedProvider(std::vector
<const Provider
*> Providers
)
162 : Providers(std::move(Providers
)) {}
165 return std::make_unique
<CombinedProvider
>(std::move(Providers
));
168 Config
Provider::getConfig(const Params
&P
, DiagnosticCallback DC
) const {
169 trace::Span
Tracer("getConfig");
171 SPAN_ATTACH(Tracer
, "path", P
.Path
);
173 for (const auto &Fragment
: getFragments(P
, DC
))
178 } // namespace config
179 } // namespace clangd