mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 00:45:18 +03:00
a477e9663f
Summary: This uses `clang-tidy` to comment out unused parameters (in functions, methods and lambdas) in fbcode. Cases that the tool failed to handle are fixed manually. Reviewed By: igorsugak Differential Revision: D5454343 fbshipit-source-id: 5dee339b4334e25e963891b519a5aa81fbf627b2
373 lines
11 KiB
C++
373 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
#include "HgCommand.h"
|
|
|
|
#include <folly/dynamic.h>
|
|
#include <folly/experimental/logging/xlog.h>
|
|
#include <gflags/gflags.h>
|
|
#include <thread>
|
|
|
|
namespace facebook {
|
|
namespace hgsparse {
|
|
|
|
using folly::Future;
|
|
using folly::Try;
|
|
|
|
namespace {
|
|
folly::Singleton<HgCommand> cmd_singleton;
|
|
}
|
|
|
|
DEFINE_int32(file_cache_size, 65536, "maximum number of file entries to cache");
|
|
|
|
static folly::StringPiece dirname(folly::StringPiece name) {
|
|
auto slash = name.rfind('/');
|
|
if (slash != std::string::npos) {
|
|
return name.subpiece(0, slash);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static folly::StringPiece basename(folly::StringPiece name) {
|
|
auto slash = name.rfind('/');
|
|
if (slash != std::string::npos) {
|
|
name.advance(slash + 1);
|
|
return name;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
// Generic function to insert an item in sorted order
|
|
template <typename T, typename COMP, typename CONT>
|
|
inline typename CONT::iterator sorted_insert(CONT &vec,
|
|
T&&val, COMP compare) {
|
|
auto find = std::lower_bound(vec.begin(), vec.end(), val, compare);
|
|
if (find != vec.end() && !compare(val, *find)) {
|
|
// Already exists
|
|
return find;
|
|
}
|
|
return vec.emplace(find, val);
|
|
}
|
|
|
|
struct compare_str {
|
|
inline bool operator()(const folly::fbstring &a, const folly::fbstring &b) {
|
|
// Bias dotfiles later so that we're more likely to match `ls` access
|
|
// patterns
|
|
int ascore = a[0] == '.' ? 0 : 1;
|
|
int bscore = b[0] == '.' ? 0 : 1;
|
|
if (ascore == bscore) {
|
|
return a < b;
|
|
}
|
|
return ascore > bscore;
|
|
}
|
|
};
|
|
|
|
HgDirInformation& HgTreeInformation::makeDir(folly::StringPiece name) {
|
|
auto key = name.str();
|
|
auto find = dirs_.find(key);
|
|
if (find == dirs_.end()) {
|
|
// Recursively build out parents if missing
|
|
auto parent_dir = dirname(name);
|
|
if (parent_dir != name) {
|
|
auto &parent = makeDir(dirname(name));
|
|
// Add ourselves to the parent
|
|
sorted_insert(parent.dirs, basename(name).str(), compare_str());
|
|
}
|
|
}
|
|
return dirs_[key];
|
|
}
|
|
|
|
void HgTreeInformation::loadManifest() {
|
|
std::thread thr([this] {
|
|
XLOG(INFO) << "Parsing manifest for " << repoDir_ << " @ " << rev_;
|
|
folly::Subprocess proc(
|
|
{"hg", "manifest", "-v", "-r", rev_},
|
|
folly::Subprocess::Options()
|
|
.pipeStdout()
|
|
.chdir(repoDir_)
|
|
.closeOtherFds()
|
|
.usePath());
|
|
auto read_cb = folly::Subprocess::readLinesCallback(
|
|
[this](int fd, folly::StringPiece line) {
|
|
if (fd == STDOUT_FILENO) {
|
|
if (line.empty()) {
|
|
return false;
|
|
}
|
|
line.removeSuffix("\n");
|
|
|
|
folly::StringPiece flags("");
|
|
if (line[4] == '@') {
|
|
flags = "l";
|
|
} else if (line[4] == '*') {
|
|
flags = "x";
|
|
}
|
|
auto filename = line.subpiece(6);
|
|
fileInfo_.set(filename.str(),
|
|
std::make_shared<HgFileInformation>(
|
|
flags, 0, basename(filename)));
|
|
} else {
|
|
XLOG(ERR) << "[" << repoDir_ << "] hg files -r " << rev_
|
|
<< " stderr: " << line;
|
|
}
|
|
return false; // Keep reading from the child
|
|
});
|
|
proc.communicate(std::ref(read_cb), [](int /*pfd*/, int /*cfd*/) {
|
|
// Don't write to the child
|
|
return true;
|
|
});
|
|
proc.wait();
|
|
XLOG(INFO) << "manifest loaded";
|
|
});
|
|
thr.detach();
|
|
}
|
|
|
|
void HgTreeInformation::buildTree() {
|
|
XLOG(INFO) << "Parsing file list for " << repoDir_ << " @ " << rev_;
|
|
size_t num_files = 0;
|
|
|
|
folly::Subprocess proc(
|
|
{"hg", "files", "-r", rev_},
|
|
folly::Subprocess::Options()
|
|
.pipeStdout()
|
|
.chdir(repoDir_)
|
|
.closeOtherFds()
|
|
.usePath());
|
|
|
|
auto read_cb = folly::Subprocess::readLinesCallback(
|
|
[this, &num_files](int fd, folly::StringPiece line) {
|
|
if (fd == STDOUT_FILENO) {
|
|
if (line.empty()) {
|
|
return false;
|
|
}
|
|
line.removeSuffix("\n");
|
|
folly::StringPiece dir = dirname(line);
|
|
folly::StringPiece filename = basename(line);
|
|
|
|
// This will create the dir node on demand
|
|
auto &d = makeDir(dir);
|
|
// and add this file to its list
|
|
sorted_insert(d.files, filename.str(), compare_str());
|
|
num_files++;
|
|
} else {
|
|
XLOG(ERR) << "[" << repoDir_ << "] hg files -r " << rev_
|
|
<< " stderr: " << line;
|
|
}
|
|
return false; // Keep reading from the child
|
|
});
|
|
proc.communicate(std::ref(read_cb), [](int /*pfd*/, int /*cfd*/) {
|
|
// Don't write to the child
|
|
return true;
|
|
});
|
|
proc.waitChecked();
|
|
XLOG(INFO) << "build tree with " << dirs_.size() << " dirs";
|
|
fileInfo_.setMaxSize(num_files * 1.2);
|
|
loadManifest();
|
|
}
|
|
|
|
const HgDirInformation& HgTreeInformation::readDir(folly::StringPiece name) {
|
|
return dirs_.at(name.str());
|
|
}
|
|
|
|
HgFileInformation::HgFileInformation(folly::StringPiece flags,
|
|
size_t fileSize,
|
|
folly::StringPiece filename)
|
|
: size(fileSize), name(filename.str()) {
|
|
if (flags.find('d') != std::string::npos) {
|
|
mode = S_IFDIR | 0755;
|
|
} else {
|
|
mode = S_IFREG;
|
|
|
|
if (flags.find('l') != std::string::npos) {
|
|
mode = S_IFLNK;
|
|
}
|
|
|
|
if (flags.find('x') != std::string::npos) {
|
|
mode |= 0755;
|
|
} else {
|
|
mode |= 0644;
|
|
}
|
|
}
|
|
}
|
|
|
|
folly::Future<std::shared_ptr<HgFileInformation>>
|
|
HgTreeInformation::rawStatFile(const std::string& filename) {
|
|
|
|
auto find_dir = dirs_.find(filename);
|
|
if (find_dir != dirs_.end()) {
|
|
auto dir = basename(filename);
|
|
return std::make_shared<HgFileInformation>("d", 0, dir);
|
|
}
|
|
|
|
folly::Promise<std::shared_ptr<HgFileInformation>> promise;
|
|
auto future = promise.getFuture();
|
|
|
|
std::thread thr([ this, promise = std::move(promise), filename ]() mutable {
|
|
promise.setWith([ this, filename = std::move(filename) ] {
|
|
std::vector<std::string> args = {"hg",
|
|
"files",
|
|
"-r",
|
|
rev_,
|
|
"-vT",
|
|
"{size}\\0{flags}\\0{abspath}\\n",
|
|
filename};
|
|
folly::Subprocess proc(
|
|
args,
|
|
folly::Subprocess::Options()
|
|
.pipeStdout()
|
|
.chdir(repoDir_)
|
|
.closeOtherFds()
|
|
.usePath());
|
|
auto output = proc.communicate();
|
|
proc.waitChecked();
|
|
|
|
folly::StringPiece line(output.first);
|
|
|
|
line.removeSuffix("\n");
|
|
folly::fbvector<folly::StringPiece> fields;
|
|
folly::split('\0', line, fields);
|
|
|
|
if (fields.size() != 3) {
|
|
throw std::runtime_error(
|
|
folly::to<std::string>("bad output from hg files: ", line));
|
|
}
|
|
|
|
auto fullname = fields[2];
|
|
|
|
return std::make_shared<HgFileInformation>(
|
|
fields[1], folly::to<size_t>(fields[0]), basename(fullname));
|
|
});
|
|
});
|
|
thr.detach();
|
|
return future;
|
|
}
|
|
|
|
folly::Future<std::vector<std::shared_ptr<HgFileInformation>>>
|
|
HgTreeInformation::statFiles(const std::vector<std::string>& files) {
|
|
std::vector<folly::Future<std::shared_ptr<HgFileInformation>>> futures;
|
|
|
|
return folly::collectAll(folly::window(files,
|
|
[this](std::string name) {
|
|
return fileInfo_.get(name);
|
|
},
|
|
sysconf(_SC_NPROCESSORS_ONLN) / 2))
|
|
.then([](
|
|
Try<std::vector<Try<std::shared_ptr<HgFileInformation>>>>&& items) {
|
|
std::vector<std::shared_ptr<HgFileInformation>> res;
|
|
for (auto &item : items.value()) {
|
|
res.push_back(item.value());
|
|
}
|
|
return res;
|
|
});
|
|
}
|
|
|
|
folly::Future<std::vector<std::shared_ptr<HgFileInformation>>>
|
|
HgTreeInformation::statDir(folly::StringPiece name) {
|
|
|
|
std::vector<std::string> names;
|
|
auto stat = dirs_.at(name.str());
|
|
|
|
for (auto& file_name : stat.dirs) {
|
|
names.emplace_back(
|
|
name.empty() ? file_name.toStdString()
|
|
: folly::to<std::string>(name, "/", file_name));
|
|
}
|
|
for (auto& file_name : stat.files) {
|
|
names.emplace_back(
|
|
name.empty() ? file_name.toStdString()
|
|
: folly::to<std::string>(name, "/", file_name));
|
|
}
|
|
|
|
return statFiles(names);
|
|
}
|
|
|
|
HgTreeInformation::HgTreeInformation(const std::string& repoDir,
|
|
const std::string& rev)
|
|
: repoDir_(repoDir),
|
|
rev_(rev),
|
|
fileInfo_(FLAGS_file_cache_size,
|
|
[=](const std::string name) { return rawStatFile(name); }) {
|
|
buildTree();
|
|
}
|
|
|
|
folly::Future<std::string> HgCommand::future_run(folly::Subprocess&& proc) {
|
|
struct proc_state {
|
|
folly::Subprocess proc;
|
|
folly::Promise<std::string> promise;
|
|
|
|
proc_state(folly::Subprocess &&p) : proc(std::move(p)) {}
|
|
};
|
|
|
|
auto state = std::make_shared<proc_state>(std::move(proc));
|
|
std::thread thr([state] {
|
|
state->promise.setWith([state] {
|
|
auto res = state->proc.communicate();
|
|
state->proc.waitChecked();
|
|
// Return stdout
|
|
return res.first;
|
|
});
|
|
});
|
|
thr.detach();
|
|
return state->promise.getFuture();
|
|
}
|
|
|
|
void HgCommand::setRepoDir(const std::string& repoDir) { repoDir_ = repoDir; }
|
|
void HgCommand::setRepoRev(const std::string& rev) { rev_ = rev; }
|
|
const std::string& HgCommand::getRepoRev() { return rev_; }
|
|
|
|
std::string HgCommand::run(const std::vector<std::string>& args) {
|
|
folly::Subprocess proc(
|
|
args,
|
|
folly::Subprocess::Options()
|
|
.pipeStdout()
|
|
.pipeStderr()
|
|
.closeOtherFds()
|
|
.usePath());
|
|
auto output = proc.communicate();
|
|
auto res = proc.returnCode();
|
|
if (!res.exited() || res.exitStatus() != 0) {
|
|
XLOG(ERR) << res.str() << ": " << output.second;
|
|
proc.pollChecked();
|
|
}
|
|
return output.first;
|
|
}
|
|
|
|
std::shared_ptr<HgTreeInformation> HgCommand::getTree(const std::string& rev) {
|
|
{
|
|
std::lock_guard<std::mutex> g(lock_);
|
|
auto find = treeInfo_.find(rev);
|
|
if (find != treeInfo_.end()) {
|
|
return find->second;
|
|
}
|
|
}
|
|
auto t = std::make_shared<HgTreeInformation>(repoDir_, rev);
|
|
{
|
|
std::lock_guard<std::mutex> g(lock_);
|
|
treeInfo_.set(rev, t);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
HgCommand::HgCommand() : treeInfo_(16) {}
|
|
|
|
std::string HgCommand::identifyRev() {
|
|
folly::Subprocess proc(
|
|
{"hg", "log", "-r", ".", "-T", "{node}"},
|
|
folly::Subprocess::Options().pipeStdout().closeOtherFds().usePath().chdir(
|
|
repoDir_));
|
|
auto output = proc.communicate();
|
|
proc.waitChecked();
|
|
folly::StringPiece hash(output.first);
|
|
hash.removeSuffix('+');
|
|
return hash.str();
|
|
}
|
|
|
|
|
|
}
|
|
}
|