| 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 | |