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#include "projectmanager.h"
23#include "filesystem.h"
24#include "stringbuilder.h"
25
26#include <clang/Basic/Version.h>
27#include <llvm/ADT/SmallString.h>
28#include <llvm/Support/FileSystem.h>
29#include <llvm/Support/Path.h>
30
31ProjectManager::ProjectManager(std::string outputPrefix, std::string _dataPath)
32 : outputPrefix(std::move(outputPrefix))
33 , dataPath(std::move(_dataPath))
34{
35 if (dataPath.empty())
36 dataPath = "../data";
37
38 for (auto &&info : systemProjects()) {
39 addProject(info);
40 }
41}
42
43bool ProjectManager::addProject(ProjectInfo info)
44{
45 if (info.source_path.empty())
46 return false;
47 llvm::SmallString<256> filename;
48 canonicalize(path: info.source_path, result&: filename);
49 if (filename.empty())
50 return false;
51 if (filename[filename.size() - 1] != '/')
52 filename += '/';
53 info.source_path = filename.c_str();
54
55 projects.push_back(x: std::move(info));
56 return true;
57}
58
59ProjectInfo *ProjectManager::projectForFile(llvm::StringRef filename)
60{
61 unsigned int match_length = 0;
62 ProjectInfo *result = nullptr;
63
64 for (auto &it : projects) {
65 const std::string &source_path = it.source_path;
66 if (source_path.size() < match_length) {
67 continue;
68 }
69 if (filename.startswith(Prefix: source_path)) {
70 result = &it;
71 match_length = source_path.size();
72 }
73 }
74 return result;
75}
76
77bool ProjectManager::shouldProcess(llvm::StringRef filename, ProjectInfo *project)
78{
79 if (!project)
80 return false;
81 if (project->type == ProjectInfo::External)
82 return false;
83
84 std::string fn = outputPrefix % "/" % project->name % "/"
85 % filename.substr(Start: project->source_path.size()) % ".html";
86 return !llvm::sys::fs::exists(Path: fn);
87 // || boost::filesystem::last_write_time(p) < entry->getModificationTime();
88}
89
90std::string ProjectManager::includeRecovery(llvm::StringRef includeName, llvm::StringRef from)
91{
92#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR >= 5
93 if (includeRecoveryCache.empty()) {
94 for (const auto &proj : projects) {
95 // skip sub project
96 llvm::StringRef sourcePath(proj.source_path);
97 auto parentPath = sourcePath.substr(Start: 0, N: sourcePath.rfind(C: '/'));
98 if (projectForFile(filename: parentPath))
99 continue;
100
101 std::error_code EC;
102 for (llvm::sys::fs::recursive_directory_iterator it(sourcePath, EC), DirEnd;
103 it != DirEnd && !EC; it.increment(ec&: EC)) {
104 auto fileName = llvm::sys::path::filename(path: it->path());
105 if (fileName.startswith(Prefix: ".")) {
106 it.no_push();
107 continue;
108 }
109 includeRecoveryCache.insert(x: { std::string(fileName), it->path() });
110 }
111 }
112 }
113 llvm::StringRef includeFileName = llvm::sys::path::filename(path: includeName);
114 std::string resolved;
115 int weight = -1000;
116 auto range = includeRecoveryCache.equal_range(x: std::string(includeFileName));
117 for (auto it = range.first; it != range.second; ++it) {
118 llvm::StringRef candidate(it->second);
119 unsigned int suf_len = 0;
120 while (suf_len < std::min(a: candidate.size(), b: includeName.size())) {
121 if (candidate[candidate.size() - suf_len - 1]
122 != includeName[includeName.size() - suf_len - 1])
123 break;
124 suf_len++;
125 }
126 // Each paths part that are similar from the expected name are weighted 1000 points f
127 int w = includeName.substr(Start: includeName.size() - suf_len).count(C: '/') * 1000;
128 if (w + 1000 < weight)
129 continue;
130
131 // after that, order by similarity with the from url
132 unsigned int pref_len = 0;
133 while (pref_len < std::min(a: candidate.size(), b: from.size())) {
134 if (candidate[pref_len] != from[pref_len])
135 break;
136 pref_len++;
137 }
138 w += candidate.substr(Start: 0, N: pref_len).count(C: '/') * 10;
139
140 // and the smaller the path, the better
141 w -= candidate.count(C: '/');
142
143 if (w < weight)
144 continue;
145
146 weight = w;
147 resolved = std::string(candidate);
148 }
149 return resolved;
150#else
151 return {}; // Not supported with clang < 3.4
152#endif
153}
154

source code of codebrowser/generator/projectmanager.cpp