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