1/****************************************************************************
2 * Copyright (C) 2012-2016 Woboq GmbH
3 * Olivier Goffart <contact at woboq.com>
4 * https://woboq.com/codebrowser.html
5 *
6 * This file is part of the Woboq Code Browser.
7 *
8 * Commercial License Usage:
9 * Licensees holding valid commercial licenses provided by Woboq may use
10 * this file in accordance with the terms contained in a written agreement
11 * between the licensee and Woboq.
12 * For further information see https://woboq.com/codebrowser.html
13 *
14 * Alternatively, this work may be used under a Creative Commons
15 * Attribution-NonCommercial-ShareAlike 3.0 (CC-BY-NC-SA 3.0) License.
16 * http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US
17 * This license does not allow you to use the code browser to assist the
18 * development of your commercial software. If you intent to do so, consider
19 * purchasing a commercial licence.
20 ****************************************************************************/
21
22
23#include "clang/AST/ASTContext.h"
24#include "clang/Frontend/FrontendActions.h"
25#include "clang/Tooling/JSONCompilationDatabase.h"
26#include "clang/Tooling/Tooling.h"
27#include "llvm/Support/CommandLine.h"
28
29#include <clang/Frontend/CompilerInstance.h>
30#include <llvm/ADT/StringSwitch.h>
31#include <llvm/Support/Path.h>
32
33#include "annotator.h"
34#include "browserastvisitor.h"
35#include "compat.h"
36#include "filesystem.h"
37#include "preprocessorcallback.h"
38#include "projectmanager.h"
39#include "stringbuilder.h"
40#include <ctime>
41#include <fstream>
42#include <iostream>
43#include <limits>
44#include <stdexcept>
45
46#include "embedded_includes.h"
47
48namespace cl = llvm::cl;
49
50cl::opt<std::string> BuildPath(
51 "b", cl::value_desc("build_path"),
52 cl::desc(
53 "Build path containing compilation database (compile_commands.json) If this argument is "
54 "not passed, the compilation arguments can be passed on the command line after '--'"),
55 cl::Optional);
56
57cl::list<std::string> SourcePaths(cl::Positional, cl::desc("<sources>* [-- <compile command>]"),
58 cl::ZeroOrMore);
59
60cl::opt<std::string> OutputPath("o", cl::value_desc("output path"),
61 cl::desc("Output directory where the generated files will be put"),
62 cl::Required);
63
64cl::list<std::string> ProjectPaths(
65 "p", cl::value_desc("<project>:<path>[:<revision>]"),
66 cl::desc(
67 "Project specification: The name of the project, the absolute path of the source code, and "
68 "the revision separated by colons. Example: -p projectname:/path/to/source/code:0.3beta"),
69 cl::ZeroOrMore);
70
71
72cl::list<std::string> ExternalProjectPaths(
73 "e", cl::value_desc("<project>:<path>:<url>"),
74 cl::desc("Reference to an external project. Example: -e "
75 "clang/include/clang:/opt/llvm/include/clang/:https://code.woboq.org/llvm"),
76 cl::ZeroOrMore);
77
78cl::opt<std::string>
79 DataPath("d", cl::value_desc("data path"),
80 cl::desc("Data url where all the javascript and css files are found. Can be absolute, "
81 "or relative to the output directory. Defaults to ../data"),
82 cl::Optional);
83
84cl::opt<bool>
85 ProcessAllSources("a",
86 cl::desc("Process all files from the compile_commands.json. If this argument "
87 "is passed, the list of sources does not need to be passed"));
88
89cl::extrahelp extra(
90
91 R"(
92
93EXAMPLES:
94
95Simple generation without compile command or project (compile command specified inline)
96 codebrowser_generator -o ~/public_html/code -d https://code.woboq.org/data $PWD -- -std=c++14 -I/opt/llvm/include
97
98With a project
99 codebrowser_generator -b $PWD/build -a -p codebrowser:$PWD -o ~/public_html/code
100)");
101
102#if 1
103std::string locationToString(clang::SourceLocation loc, clang::SourceManager &sm)
104{
105 clang::PresumedLoc fixed = sm.getPresumedLoc(Loc: loc);
106 if (!fixed.isValid())
107 return "???";
108 return (llvm::Twine(fixed.getFilename()) + ":" + llvm::Twine(fixed.getLine())).str();
109}
110#endif
111
112enum class DatabaseType {
113 InDatabase,
114 NotInDatabase,
115 ProcessFullDirectory
116};
117
118struct BrowserDiagnosticClient : clang::DiagnosticConsumer
119{
120 Annotator &annotator;
121 BrowserDiagnosticClient(Annotator &fm)
122 : annotator(fm)
123 {
124 }
125
126 static bool isImmintrinDotH(const clang::PresumedLoc &loc)
127 {
128 return llvm::StringRef(loc.getFilename()).contains(Other: "immintrin.h");
129 }
130
131 virtual void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel,
132 const clang::Diagnostic &Info) override
133 {
134 std::string clas;
135 llvm::SmallString<1000> diag;
136 Info.FormatDiagnostic(OutStr&: diag);
137
138 switch (DiagLevel) {
139 case clang::DiagnosticsEngine::Fatal:
140 // ignore tons of errors in immintrin.h
141 if (isImmintrinDotH(loc: annotator.getSourceMgr().getPresumedLoc(Loc: Info.getLocation())))
142 return;
143 std::cerr << "FATAL ";
144 LLVM_FALLTHROUGH;
145 case clang::DiagnosticsEngine::Error:
146 std::cerr << "Error: " << locationToString(loc: Info.getLocation(), sm&: annotator.getSourceMgr())
147 << ": " << diag.c_str() << std::endl;
148 clas = "error";
149 break;
150 case clang::DiagnosticsEngine::Warning:
151 clas = "warning";
152 break;
153 default:
154 return;
155 }
156 clang::SourceRange Range = Info.getLocation();
157 annotator.reportDiagnostic(range: Range, msg: diag.c_str(), clas);
158 }
159};
160
161class BrowserASTConsumer : public clang::ASTConsumer
162{
163 clang::CompilerInstance &ci;
164 Annotator annotator;
165 DatabaseType WasInDatabase;
166
167public:
168 BrowserASTConsumer(clang::CompilerInstance &ci, ProjectManager &projectManager,
169 DatabaseType WasInDatabase)
170 : clang::ASTConsumer()
171 , ci(ci)
172 , annotator(projectManager)
173 , WasInDatabase(WasInDatabase)
174 {
175 // ci.getLangOpts().DelayedTemplateParsing = (true);
176#if CLANG_VERSION_MAJOR < 16
177 // the meaning of this function has changed which causes
178 // a lot of issues in clang 16
179 ci.getPreprocessor().enableIncrementalProcessing();
180#endif
181 }
182 virtual ~BrowserASTConsumer()
183 {
184 ci.getDiagnostics().setClient(client: new clang::IgnoringDiagConsumer, ShouldOwnClient: true);
185 }
186
187 virtual void Initialize(clang::ASTContext &Ctx) override
188 {
189 annotator.setSourceMgr(sm&: Ctx.getSourceManager(), lo: Ctx.getLangOpts());
190 annotator.setMangleContext(Ctx.createMangleContext());
191 ci.getPreprocessor().addPPCallbacks(C: maybe_unique(val: new PreprocessorCallback(
192 annotator, ci.getPreprocessor(), WasInDatabase == DatabaseType::ProcessFullDirectory)));
193 ci.getDiagnostics().setClient(client: new BrowserDiagnosticClient(annotator), ShouldOwnClient: true);
194 ci.getDiagnostics().setErrorLimit(0);
195 }
196
197 virtual bool HandleTopLevelDecl(clang::DeclGroupRef D) override
198 {
199 if (ci.getDiagnostics().hasFatalErrorOccurred()) {
200 // Reset errors: (Hack to ignore the fatal errors.)
201 ci.getDiagnostics().Reset();
202 // When there was fatal error, processing the warnings may cause crashes
203 ci.getDiagnostics().setIgnoreAllWarnings(true);
204 }
205 return true;
206 }
207
208 virtual void HandleTranslationUnit(clang::ASTContext &Ctx) override
209 {
210
211 /* if (PP.getDiagnostics().hasErrorOccurred())
212 return;*/
213 ci.getPreprocessor().getDiagnostics().getClient();
214
215
216 BrowserASTVisitor v(annotator);
217 v.TraverseDecl(d: Ctx.getTranslationUnitDecl());
218
219
220 annotator.generate(ci.getSema(), WasInDatabase: WasInDatabase != DatabaseType::NotInDatabase);
221 }
222
223 virtual bool shouldSkipFunctionBody(clang::Decl *D) override
224 {
225 return !annotator.shouldProcess(
226 clang::FullSourceLoc(D->getLocation(), annotator.getSourceMgr())
227 .getExpansionLoc()
228 .getFileID());
229 }
230};
231
232class BrowserAction : public clang::ASTFrontendAction
233{
234 static std::set<std::string> processed;
235 DatabaseType WasInDatabase;
236
237protected:
238#if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR <= 5
239 virtual clang::ASTConsumer *
240#else
241 virtual std::unique_ptr<clang::ASTConsumer>
242#endif
243 CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef InFile) override
244 {
245 if (processed.count(x: InFile.str())) {
246 std::cerr << "Skipping already processed " << InFile.str() << std::endl;
247 return nullptr;
248 }
249 processed.insert(x: InFile.str());
250
251 CI.getFrontendOpts().SkipFunctionBodies = true;
252
253 return maybe_unique(val: new BrowserASTConsumer(CI, *projectManager, WasInDatabase));
254 }
255
256public:
257 BrowserAction(DatabaseType WasInDatabase = DatabaseType::InDatabase)
258 : WasInDatabase(WasInDatabase)
259 {
260 }
261 virtual bool hasCodeCompletionSupport() const override
262 {
263 return true;
264 }
265 static ProjectManager *projectManager;
266};
267
268
269std::set<std::string> BrowserAction::processed;
270ProjectManager *BrowserAction::projectManager = nullptr;
271
272static bool proceedCommand(std::vector<std::string> command, llvm::StringRef Directory,
273 llvm::StringRef file, clang::FileManager *FM, DatabaseType WasInDatabase)
274{
275 // This code change all the paths to be absolute paths
276 // FIXME: it is a bit fragile.
277 bool previousIsDashI = false;
278 bool previousNeedsMacro = false;
279 bool hasNoStdInc = false;
280 for (std::string &A : command) {
281 if (previousIsDashI && !A.empty() && A[0] != '/') {
282 A = Directory % "/" % A;
283 previousIsDashI = false;
284 continue;
285 } else if (A == "-I") {
286 previousIsDashI = true;
287 continue;
288 } else if (A == "-nostdinc" || A == "-nostdinc++") {
289 hasNoStdInc = true;
290 continue;
291 } else if (A == "-U" || A == "-D") {
292 previousNeedsMacro = true;
293 continue;
294 }
295 if (previousNeedsMacro) {
296 previousNeedsMacro = false;
297 continue;
298 }
299 previousIsDashI = false;
300 if (A.empty())
301 continue;
302 if (llvm::StringRef(A).startswith(Prefix: "-I") && A[2] != '/') {
303 A = "-I" % Directory % "/" % llvm::StringRef(A).substr(Start: 2);
304 continue;
305 }
306 if (A[0] == '-' || A[0] == '/')
307 continue;
308 std::string PossiblePath = Directory % "/" % A;
309 if (llvm::sys::fs::exists(Path: PossiblePath))
310 A = PossiblePath;
311 }
312
313#if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR < 6
314 auto Ajust = [&](clang::tooling::ArgumentsAdjuster &&aj) { command = aj.Adjust(command); };
315 Ajust(clang::tooling::ClangSyntaxOnlyAdjuster());
316 Ajust(clang::tooling::ClangStripOutputAdjuster());
317#elif CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR < 8
318 command = clang::tooling::getClangSyntaxOnlyAdjuster()(command);
319 command = clang::tooling::getClangStripOutputAdjuster()(command);
320#else
321 command = clang::tooling::getClangSyntaxOnlyAdjuster()(command, file);
322 command = clang::tooling::getClangStripOutputAdjuster()(command, file);
323#endif
324
325 if (!hasNoStdInc) {
326#ifndef _WIN32
327 command.push_back(x: "-isystem");
328#else
329 command.push_back("-I");
330#endif
331
332 command.push_back(x: "/builtins");
333 }
334
335 command.push_back(x: "-Qunused-arguments");
336 command.push_back(x: "-Wno-unknown-warning-option");
337 clang::tooling::ToolInvocation Inv(command, maybe_unique(val: new BrowserAction(WasInDatabase)), FM);
338
339#if CLANG_VERSION_MAJOR <= 10
340 if (!hasNoStdInc) {
341 // Map the builtins includes
342 const EmbeddedFile *f = EmbeddedFiles;
343 while (f->filename) {
344 Inv.mapVirtualFile(f->filename, { f->content, f->size });
345 f++;
346 }
347 }
348#endif
349
350 bool result = Inv.run();
351 if (!result) {
352 std::cerr << "Error: The file was not recognized as source code: " << file.str()
353 << std::endl;
354 }
355 return result;
356}
357
358int main(int argc, const char **argv)
359{
360 std::string ErrorMessage;
361 std::unique_ptr<clang::tooling::CompilationDatabase> Compilations(
362 clang::tooling::FixedCompilationDatabase::loadFromCommandLine(Argc&: argc, Argv: argv
363#if CLANG_VERSION_MAJOR >= 5
364 ,
365 ErrorMsg&: ErrorMessage
366#endif
367 ));
368 if (!ErrorMessage.empty()) {
369 std::cerr << ErrorMessage << std::endl;
370 ErrorMessage = {};
371 }
372
373 llvm::cl::ParseCommandLineOptions(argc, argv);
374
375#ifdef _WIN32
376 make_forward_slashes(OutputPath._Get_data()._Myptr());
377#endif
378
379 ProjectManager projectManager(OutputPath, DataPath);
380 for (std::string &s : ProjectPaths) {
381 auto colonPos = s.find(c: ':');
382 if (colonPos >= s.size()) {
383 std::cerr << "fail to parse project option : " << s << std::endl;
384 continue;
385 }
386 auto secondColonPos = s.find(c: ':', pos: colonPos + 1);
387 ProjectInfo info { s.substr(pos: 0, n: colonPos),
388 s.substr(pos: colonPos + 1, n: secondColonPos - colonPos - 1),
389 secondColonPos < s.size() ? s.substr(pos: secondColonPos + 1)
390 : std::string() };
391 if (!projectManager.addProject(info: std::move(info))) {
392 std::cerr << "invalid project directory for : " << s << std::endl;
393 }
394 }
395 for (std::string &s : ExternalProjectPaths) {
396 auto colonPos = s.find(c: ':');
397 if (colonPos >= s.size()) {
398 std::cerr << "fail to parse project option : " << s << std::endl;
399 continue;
400 }
401 auto secondColonPos = s.find(c: ':', pos: colonPos + 1);
402 if (secondColonPos >= s.size()) {
403 std::cerr << "fail to parse project option : " << s << std::endl;
404 continue;
405 }
406 ProjectInfo info { s.substr(pos: 0, n: colonPos),
407 s.substr(pos: colonPos + 1, n: secondColonPos - colonPos - 1),
408 ProjectInfo::External };
409 info.external_root_url = s.substr(pos: secondColonPos + 1);
410 if (!projectManager.addProject(info: std::move(info))) {
411 std::cerr << "invalid project directory for : " << s << std::endl;
412 }
413 }
414 BrowserAction::projectManager = &projectManager;
415
416
417 if (!Compilations && llvm::sys::fs::exists(Path: BuildPath)) {
418 if (llvm::sys::fs::is_directory(Path: BuildPath)) {
419 Compilations = std::unique_ptr<clang::tooling::CompilationDatabase>(
420 clang::tooling::CompilationDatabase::loadFromDirectory(BuildDirectory: BuildPath, ErrorMessage));
421 } else {
422 Compilations = std::unique_ptr<clang::tooling::CompilationDatabase>(
423 clang::tooling::JSONCompilationDatabase::loadFromFile(
424 FilePath: BuildPath, ErrorMessage
425#if CLANG_VERSION_MAJOR >= 4
426 ,
427 Syntax: clang::tooling::JSONCommandLineSyntax::AutoDetect
428#endif
429 ));
430 }
431 if (!Compilations && !ErrorMessage.empty()) {
432 std::cerr << ErrorMessage << std::endl;
433 }
434 }
435
436 if (!Compilations) {
437 std::cerr
438 << "Could not load compilationdatabase. "
439 "Please use the -b option to a path containing a compile_commands.json, or use "
440 "'--' followed by the compilation commands."
441 << std::endl;
442 return EXIT_FAILURE;
443 }
444
445 bool IsProcessingAllDirectory = false;
446 std::vector<std::string> DirContents;
447 std::vector<std::string> AllFiles = Compilations->getAllFiles();
448 std::sort(first: AllFiles.begin(), last: AllFiles.end());
449 llvm::ArrayRef<std::string> Sources = SourcePaths;
450 if (Sources.empty() && ProcessAllSources) {
451 // Because else the order is too random
452 Sources = AllFiles;
453 } else if (ProcessAllSources) {
454 std::cerr << "Cannot use both sources and '-a'" << std::endl;
455 return EXIT_FAILURE;
456 } else if (Sources.size() == 1 && llvm::sys::fs::is_directory(Path: Sources.front())) {
457#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR >= 5
458 // A directory was passed, process all the files in that directory
459 llvm::SmallString<128> DirName;
460 llvm::sys::path::native(path: Sources.front(), result&: DirName);
461 while (DirName.endswith(Suffix: "/"))
462 DirName.pop_back();
463 std::error_code EC;
464 for (llvm::sys::fs::recursive_directory_iterator it(DirName.str(), EC), DirEnd;
465 it != DirEnd && !EC; it.increment(ec&: EC)) {
466 if (llvm::sys::path::filename(path: it->path()).startswith(Prefix: ".")) {
467 it.no_push();
468 continue;
469 }
470 DirContents.push_back(x: it->path());
471 }
472 Sources = DirContents;
473 IsProcessingAllDirectory = true;
474 if (EC) {
475 std::cerr << "Error reading the directory: " << EC.message() << std::endl;
476 return EXIT_FAILURE;
477 }
478
479 if (ProjectPaths.empty()) {
480 ProjectInfo info { std::string(llvm::sys::path::filename(path: DirName)),
481 std::string(DirName.str()) };
482 projectManager.addProject(info: std::move(info));
483 }
484#else
485 std::cerr << "Passing directory is only implemented with llvm >= 3.5" << std::endl;
486 return EXIT_FAILURE;
487#endif
488 }
489
490 if (Sources.empty()) {
491 std::cerr << "No source files. Please pass source files as argument, or use '-a'"
492 << std::endl;
493 return EXIT_FAILURE;
494 }
495 if (ProjectPaths.empty() && !IsProcessingAllDirectory) {
496 std::cerr << "You must specify a project name and directory with '-p name:directory'"
497 << std::endl;
498 return EXIT_FAILURE;
499 }
500
501#if CLANG_VERSION_MAJOR >= 12
502 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> VFS(
503 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
504 clang::FileManager FM({ .WorkingDir: "." }, VFS);
505#else
506 clang::FileManager FM({ "." });
507#endif
508 FM.Retain();
509
510#if CLANG_VERSION_MAJOR >= 12
511 // Map virtual files
512 {
513 auto InMemoryFS = llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>(
514 new llvm::vfs::InMemoryFileSystem);
515 VFS->pushOverlay(FS: InMemoryFS);
516 // Map the builtins includes
517 const EmbeddedFile *f = EmbeddedFiles;
518 while (f->filename) {
519 InMemoryFS->addFile(Path: f->filename, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: f->content));
520 f++;
521 }
522 }
523#endif
524
525 int Progress = 0;
526
527 std::vector<std::string> NotInDB;
528
529 for (const auto &it : Sources) {
530 std::string file = clang::tooling::getAbsolutePath(File: it);
531 Progress++;
532
533 if (it.empty() || it == "-")
534 continue;
535
536 llvm::SmallString<256> filename;
537 canonicalize(path: file, result&: filename);
538
539 if (auto project = projectManager.projectForFile(filename)) {
540 if (!projectManager.shouldProcess(filename, project)) {
541 std::cerr << "Sources: Skipping already processed " << filename.c_str()
542 << std::endl;
543 continue;
544 }
545 } else {
546 std::cerr << "Sources: Skipping file not included by any project " << filename.c_str()
547 << std::endl;
548 continue;
549 }
550
551 bool isHeader = llvm::StringSwitch<bool>(llvm::sys::path::extension(path: filename))
552 .Cases(S0: ".h", S1: ".H", S2: ".hh", S3: ".hpp", Value: true)
553 .Default(Value: false);
554
555 auto compileCommandsForFile = Compilations->getCompileCommands(FilePath: file);
556 if (!compileCommandsForFile.empty() && !isHeader) {
557 std::cerr << '[' << (100 * Progress / Sources.size()) << "%] Processing " << file
558 << "\n";
559 proceedCommand(command: compileCommandsForFile.front().CommandLine,
560 Directory: compileCommandsForFile.front().Directory, file, FM: &FM,
561 WasInDatabase: IsProcessingAllDirectory ? DatabaseType::ProcessFullDirectory
562 : DatabaseType::InDatabase);
563 } else {
564 // TODO: Try to find a command line for a file in the same path
565 std::cerr << "Delayed " << file << "\n";
566 Progress--;
567 NotInDB.push_back(x: std::string(filename.str()));
568 continue;
569 }
570 }
571
572 for (const auto &it : NotInDB) {
573 std::string file = clang::tooling::getAbsolutePath(File: it);
574 Progress++;
575
576 if (auto project = projectManager.projectForFile(filename: file)) {
577 if (!projectManager.shouldProcess(filename: file, project)) {
578 std::cerr << "NotInDB: Skipping already processed " << file.c_str() << std::endl;
579 continue;
580 }
581 } else {
582 std::cerr << "NotInDB: Skipping file not included by any project " << file.c_str()
583 << std::endl;
584 continue;
585 }
586
587 llvm::StringRef similar;
588
589 auto compileCommandsForFile = Compilations->getCompileCommands(FilePath: file);
590 std::string fileForCommands = file;
591 if (compileCommandsForFile.empty()) {
592 // Find the element with the bigger prefix
593 auto lower = std::lower_bound(first: AllFiles.cbegin(), last: AllFiles.cend(), val: file);
594 if (lower == AllFiles.cend())
595 lower = AllFiles.cbegin();
596 compileCommandsForFile = Compilations->getCompileCommands(FilePath: *lower);
597 fileForCommands = *lower;
598 }
599
600 bool success = false;
601 if (!compileCommandsForFile.empty()) {
602 std::cerr << '[' << (100 * Progress / Sources.size()) << "%] Processing " << file
603 << "\n";
604 auto command = compileCommandsForFile.front().CommandLine;
605 std::replace(first: command.begin(), last: command.end(), old_value: fileForCommands, new_value: it);
606 if (llvm::StringRef(file).endswith(Suffix: ".qdoc")) {
607 command.insert(position: command.begin() + 1, x: "-xc++");
608 // include the header for this .qdoc file
609 command.push_back(x: "-include");
610 command.push_back(x: llvm::StringRef(file).substr(Start: 0, N: file.size() - 5) % ".h");
611 }
612 success = proceedCommand(command: std::move(command), Directory: compileCommandsForFile.front().Directory,
613 file, FM: &FM,
614 WasInDatabase: IsProcessingAllDirectory ? DatabaseType::ProcessFullDirectory
615 : DatabaseType::NotInDatabase);
616 } else {
617 std::cerr << "Could not find commands for " << file << "\n";
618 }
619
620 if (!success && !IsProcessingAllDirectory) {
621 ProjectInfo *projectinfo = projectManager.projectForFile(filename: file);
622 if (!projectinfo)
623 continue;
624 if (!projectManager.shouldProcess(filename: file, project: projectinfo))
625 continue;
626
627 auto now = std::time(timer: 0);
628 auto tm = localtime(timer: &now);
629 char buf[80];
630 std::strftime(s: buf, maxsize: sizeof(buf), format: "%Y-%b-%d", tp: tm);
631
632 std::string footer = "Generated on <em>" % std::string(buf) % "</em>" % " from project "
633 % projectinfo->name % "</a>";
634 if (!projectinfo->revision.empty())
635 footer %= " revision <em>" % projectinfo->revision % "</em>";
636
637#if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR <= 4
638 llvm::OwningPtr<llvm::MemoryBuffer> Buf;
639 if (!llvm::MemoryBuffer::getFile(file, Buf))
640 continue;
641#else
642 auto B = llvm::MemoryBuffer::getFile(Filename: file);
643 if (!B)
644 continue;
645 std::unique_ptr<llvm::MemoryBuffer> Buf = std::move(B.get());
646#endif
647
648 std::string fn = projectinfo->name % "/"
649 % llvm::StringRef(file).substr(Start: projectinfo->source_path.size());
650
651 Generator g;
652 g.generate(outputPrefix: projectManager.outputPrefix, dataPath: projectManager.dataPath, filename: fn,
653 begin: Buf->getBufferStart(), end: Buf->getBufferEnd(), footer,
654 warningMessage: "Warning: This file is not a C or C++ file. It does not have highlighting.",
655 interestingDefitions: std::set<std::string>());
656
657 std::ofstream fileIndex;
658 fileIndex.open(s: projectManager.outputPrefix + "/otherIndex", mode: std::ios::app);
659 if (!fileIndex)
660 continue;
661 fileIndex << fn << '\n';
662 }
663 }
664}
665

source code of codebrowser/generator/main.cpp