clang: lib/Tooling/JSONCompilationDatabase.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

18#include "llvm/ADT/STLExtras.h"

19#include "llvm/ADT/SmallString.h"

20#include "llvm/ADT/SmallVector.h"

21#include "llvm/ADT/StringRef.h"

22#include "llvm/Support/Allocator.h"

23#include "llvm/Support/Casting.h"

24#include "llvm/Support/CommandLine.h"

25#include "llvm/Support/ErrorOr.h"

26#include "llvm/Support/MemoryBuffer.h"

27#include "llvm/Support/Path.h"

28#include "llvm/Support/StringSaver.h"

29#include "llvm/Support/VirtualFileSystem.h"

30#include "llvm/Support/YAMLParser.h"

31#include "llvm/Support/raw_ostream.h"

32#include "llvm/TargetParser/Host.h"

33#include "llvm/TargetParser/Triple.h"

34#include

35#include

36#include

37#include

38#include <system_error>

39#include

40#include

41#include

42

43using namespace clang;

44using namespace tooling;

45

46namespace {

47

48

49

50

51

52class CommandLineArgumentParser {

53 public:

54 CommandLineArgumentParser(StringRef CommandLine)

55 : Input(CommandLine), Position(Input.begin()-1) {}

56

57 std::vectorstd::string parse() {

58 bool HasMoreInput = true;

59 while (HasMoreInput && nextNonWhitespace()) {

60 std::string Argument;

61 HasMoreInput = parseStringInto(Argument);

62 CommandLine.push_back(Argument);

63 }

64 return CommandLine;

65 }

66

67 private:

68

69

70 bool parseStringInto(std::string &String) {

71 do {

72 if (*Position == '"') {

73 if (!parseDoubleQuotedStringInto(String)) return false;

74 } else if (*Position == '\'') {

75 if (!parseSingleQuotedStringInto(String)) return false;

76 } else {

77 if (!parseFreeStringInto(String)) return false;

78 }

79 } while (*Position != ' ');

80 return true;

81 }

82

83 bool parseDoubleQuotedStringInto(std::string &String) {

84 if (!next()) return false;

85 while (*Position != '"') {

86 if (!skipEscapeCharacter()) return false;

87 String.push_back(*Position);

88 if (!next()) return false;

89 }

90 return next();

91 }

92

93 bool parseSingleQuotedStringInto(std::string &String) {

94 if (!next()) return false;

95 while (*Position != '\'') {

96 String.push_back(*Position);

97 if (!next()) return false;

98 }

99 return next();

100 }

101

102 bool parseFreeStringInto(std::string &String) {

103 do {

104 if (!skipEscapeCharacter()) return false;

105 String.push_back(*Position);

106 if (!next()) return false;

107 } while (*Position != ' ' && *Position != '"' && *Position != '\'');

108 return true;

109 }

110

111 bool skipEscapeCharacter() {

112 if (*Position == '\\') {

113 return next();

114 }

115 return true;

116 }

117

118 bool nextNonWhitespace() {

119 do {

120 if (!next()) return false;

121 } while (*Position == ' ');

122 return true;

123 }

124

125 bool next() {

126 ++Position;

127 return Position != Input.end();

128 }

129

130 const StringRef Input;

131 StringRef::iterator Position;

132 std::vectorstd::string CommandLine;

133};

134

136 StringRef EscapedCommandLine) {

137 if (Syntax == JSONCommandLineSyntax::AutoDetect) {

138#ifdef _WIN32

139

140 Syntax = JSONCommandLineSyntax::Windows;

141#else

142 Syntax = JSONCommandLineSyntax::Gnu;

143#endif

144 }

145

146 if (Syntax == JSONCommandLineSyntax::Windows) {

147 llvm::BumpPtrAllocator Alloc;

148 llvm::StringSaver Saver(Alloc);

150 llvm:🆑:TokenizeWindowsCommandLine(EscapedCommandLine, Saver, T);

151 std::vectorstd::string Result(T.begin(), T.end());

152 return Result;

153 }

154 assert(Syntax == JSONCommandLineSyntax::Gnu);

155 CommandLineArgumentParser parser(EscapedCommandLine);

156 return parser.parse();

157}

158

159

160

162 std::unique_ptr

163 loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {

165 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");

167 JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect);

170 std::move(Base), llvm::vfs::getRealFileSystem())))

171 : nullptr;

172 }

173};

174

175}

176

177

178

179static CompilationDatabasePluginRegistry::Add

180X("json-compilation-database", "Reads JSON formatted compilation databases");

181

183namespace tooling {

184

185

186

188

189}

190}

191

192std::unique_ptr

194 std::string &ErrorMessage,

196

197 llvm::ErrorOr<std::unique_ptrllvm::MemoryBuffer> DatabaseBuffer =

198 llvm::MemoryBuffer::getFile(FilePath, false,

199 true,

200 true);

201 if (std::error_code Result = DatabaseBuffer.getError()) {

202 ErrorMessage = "Error while opening JSON database: " + Result.message();

203 return nullptr;

204 }

205 std::unique_ptr Database(

207 if (!Database->parse(ErrorMessage))

208 return nullptr;

209 return Database;

210}

211

212std::unique_ptr

214 std::string &ErrorMessage,

216 std::unique_ptrllvm::MemoryBuffer DatabaseBuffer(

217 llvm::MemoryBuffer::getMemBufferCopy(DatabaseString));

218 std::unique_ptr Database(

220 if (!Database->parse(ErrorMessage))

221 return nullptr;

222 return Database;

223}

224

225std::vector

228 llvm::sys::path::native(FilePath, NativeFilePath);

229

230 std::string Error;

231 llvm::raw_string_ostream ES(Error);

232 StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES);

233 if (Match.empty())

234 return {};

235 const auto CommandsRefI = IndexByFile.find(Match);

236 if (CommandsRefI == IndexByFile.end())

237 return {};

238 std::vector Commands;

239 getCommands(CommandsRefI->getValue(), Commands);

240 return Commands;

241}

242

243std::vectorstd::string

245 std::vectorstd::string Result;

246 for (const auto &CommandRef : IndexByFile)

247 Result.push_back(CommandRef.first().str());

249}

250

251std::vector

253 std::vector Commands;

254 getCommands(AllCommands, Commands);

255 return Commands;

256}

257

259 Name.consume_back(".exe");

260 return Name;

261}

262

263

264

265

266

268 if (Args.size() < 2)

269 return false;

270 StringRef Wrapper =

272 if (Wrapper == "distcc" || Wrapper == "ccache" || Wrapper == "sccache") {

273

274

275

276

277

278

279

280

281

282

283

284 bool HasCompiler =

285 (Args[1][0] != '-') &&

287 if (HasCompiler) {

288 Args.erase(Args.begin());

289 return true;

290 }

291

292 }

293 return false;

294}

295

296static std::vectorstd::string

298 const std::vector<llvm::yaml::ScalarNode *> &Nodes) {

300 std::vectorstd::string Arguments;

301 if (Nodes.size() == 1)

302 Arguments = unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage));

303 else

305 Arguments.push_back(std::string(Node->getValue(Storage)));

306

308 ;

309 return Arguments;

310}

311

312void JSONCompilationDatabase::getCommands(

314 std::vector &Commands) const {

315 for (const auto &CommandRef : CommandsRef) {

319 auto Output = std::get<3>(CommandRef);

320 Commands.emplace_back(

321 std::get<0>(CommandRef)->getValue(DirectoryStorage),

322 std::get<1>(CommandRef)->getValue(FilenameStorage),

324 Output ? Output->getValue(OutputStorage) : "");

325 }

326}

327

328bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {

329 llvm::yaml::document_iterator I = YAMLStream.begin();

330 if (I == YAMLStream.end()) {

331 ErrorMessage = "Error while parsing YAML.";

332 return false;

333 }

334 llvm::yaml::Node *Root = I->getRoot();

335 if (!Root) {

336 ErrorMessage = "Error while parsing YAML.";

337 return false;

338 }

339 auto *Array = dyn_castllvm::yaml::SequenceNode(Root);

340 if (!Array) {

341 ErrorMessage = "Expected array.";

342 return false;

343 }

344 for (auto &NextObject : *Array) {

345 auto *Object = dyn_castllvm::yaml::MappingNode(&NextObject);

346 if (!Object) {

347 ErrorMessage = "Expected object.";

348 return false;

349 }

350 llvm::yaml::ScalarNode *Directory = nullptr;

351 std::optional<std::vector<llvm::yaml::ScalarNode *>> Command;

352 llvm::yaml::ScalarNode *File = nullptr;

353 llvm::yaml::ScalarNode *Output = nullptr;

354 for (auto& NextKeyValue : *Object) {

355 auto *KeyString = dyn_castllvm::yaml::ScalarNode(NextKeyValue.getKey());

356 if (!KeyString) {

357 ErrorMessage = "Expected strings as key.";

358 return false;

359 }

361 StringRef KeyValue = KeyString->getValue(KeyStorage);

362 llvm::yaml::Node *Value = NextKeyValue.getValue();

364 ErrorMessage = "Expected value.";

365 return false;

366 }

367 auto *ValueString = dyn_castllvm::yaml::ScalarNode(Value);

368 auto *SequenceString = dyn_castllvm::yaml::SequenceNode(Value);

369 if (KeyValue == "arguments") {

370 if (!SequenceString) {

371 ErrorMessage = "Expected sequence as value.";

372 return false;

373 }

374 Command = std::vector<llvm::yaml::ScalarNode *>();

375 for (auto &Argument : *SequenceString) {

376 auto *Scalar = dyn_castllvm::yaml::ScalarNode(&Argument);

377 if (!Scalar) {

378 ErrorMessage = "Only strings are allowed in 'arguments'.";

379 return false;

380 }

381 Command->push_back(Scalar);

382 }

383 } else {

384 if (!ValueString) {

385 ErrorMessage = "Expected string as value.";

386 return false;

387 }

388 if (KeyValue == "directory") {

389 Directory = ValueString;

390 } else if (KeyValue == "command") {

391 if (!Command)

392 Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);

393 } else if (KeyValue == "file") {

394 File = ValueString;

395 } else if (KeyValue == "output") {

396 Output = ValueString;

397 } else {

398 ErrorMessage =

399 ("Unknown key: \"" + KeyString->getRawValue() + "\"").str();

400 return false;

401 }

402 }

403 }

405 ErrorMessage = "Missing key: \"file\".";

406 return false;

407 }

408 if (!Command) {

409 ErrorMessage = "Missing key: \"command\" or \"arguments\".";

410 return false;

411 }

412 if (!Directory) {

413 ErrorMessage = "Missing key: \"directory\".";

414 return false;

415 }

417 StringRef FileName = File->getValue(FileStorage);

419 if (llvm::sys::path::is_relative(FileName)) {

421 SmallString<128> AbsolutePath(Directory->getValue(DirectoryStorage));

422 llvm::sys::path::append(AbsolutePath, FileName);

423 llvm::sys::path::native(AbsolutePath, NativeFilePath);

424 } else {

425 llvm::sys::path::native(FileName, NativeFilePath);

426 }

427 llvm::sys::path::remove_dots(NativeFilePath, true);

428 auto Cmd = CompileCommandRef(Directory, File, *Command, Output);

429 IndexByFile[NativeFilePath].push_back(Cmd);

430 AllCommands.push_back(Cmd);

431 MatchTrie.insert(NativeFilePath);

432 }

433 return true;

434}

BoundNodesTreeBuilder Nodes

static std::vector< std::string > nodeToCommandLine(JSONCommandLineSyntax Syntax, const std::vector< llvm::yaml::ScalarNode * > &Nodes)

static bool unwrapCommand(std::vector< std::string > &Args)

static llvm::StringRef stripExecutableExtension(llvm::StringRef Name)

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)

The JSON file list parser is used to communicate input to InstallAPI.

@ Result

The result type of a method or function.

const FunctionProtoType * T