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 | |
31 | ProjectManager::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 | |
43 | bool 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 | |
59 | ProjectInfo *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 = ⁢ |
71 | match_length = source_path.size(); |
72 | } |
73 | } |
74 | return result; |
75 | } |
76 | |
77 | bool 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 | |
90 | std::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 | |