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
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 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
58ProjectInfo* 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 = &it;
70 match_length = source_path.size();
71 }
72 }
73 return result;
74}
75
76bool 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
88std::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