ソースを参照

Getting started on a local sdist repo

default_compile_flags
vector-of-bool 5年前
コミット
2921882962
5個のファイルの変更304行の追加2行の削除
  1. +72
    -2
      src/dds/ddslim.main.cpp
  2. +47
    -0
      src/dds/repo/repo.cpp
  3. +67
    -0
      src/dds/repo/repo.hpp
  4. +28
    -0
      src/dds/util/flock.hpp
  5. +90
    -0
      src/dds/util/flock.nix.cpp

+ 72
- 2
src/dds/ddslim.main.cpp ファイルの表示

@@ -1,7 +1,10 @@
#include <dds/build.hpp>
#include <dds/logging.hpp>
#include <dds/repo/repo.hpp>
#include <dds/sdist.hpp>
#include <dds/util/fs.hpp>
#include <dds/util/paths.hpp>
#include <dds/util/signal.hpp>

#include <libman/parse.hpp>

@@ -25,7 +28,7 @@ struct cli_base {
struct common_flags {
args::Command& cmd;

args::HelpFlag _help{cmd, "help", "Print this hellp message and exit", {'h', "help"}};
args::HelpFlag _help{cmd, "help", "Print this help message and exit", {'h', "help"}};
};

struct common_project_flags {
@@ -38,6 +41,55 @@ struct common_project_flags {
dds::fs::current_path()};
};

struct cli_repo {
cli_base& base;
args::Command cmd{base.cmd_group, "repo", "Manage the package repository"};
common_flags _common{cmd};

path_flag where{cmd, "dir", "Directory in which to initialize the repository", {'d', "dir"}};

args::Group repo_group{cmd, "Repo subcommands"};

struct {
cli_repo& parent;
args::Command cmd{parent.repo_group, "ls", "List repository contents"};
common_flags _common{cmd};

int run() {
return dds::repository::with_repository(dds::repository::default_local_path(),
dds::repo_flags::none,
[&](auto) { return 0; });
}
} ls{*this};

struct {
cli_repo& parent;
args::Command cmd{parent.repo_group, "init", "Initialize a directory as a repository"};
common_flags _common{cmd};

int run() {
if (parent.where.Get().empty()) {
throw args::ParseError("The --dir flag is required");
}
auto repo_dir = dds::fs::absolute(parent.where.Get());
dds::repository::with_repository(repo_dir, dds::repo_flags::create_if_absent, [](auto) {
});
return 0;
}
} init{*this};

int run() {
if (ls.cmd) {
return ls.run();
} else if (init.cmd) {
return init.run();
} else {
assert(false);
std::terminate();
}
}
};

struct cli_sdist {
cli_base& base;
args::Command cmd{base.cmd_group, "sdist", "Create a source distribution of a project"};
@@ -53,13 +105,25 @@ struct cli_sdist {
dds::fs::current_path() / "project.dsd"};

args::Flag force{cmd, "force", "Forcibly replace an existing result", {"force"}};
args::Flag export_{cmd,
"export",
"Export the result into the local repository",
{'E', "export"}};

int run() {
dds::sdist_params params;
params.project_dir = project.root.Get();
params.dest_path = out.Get();
params.force = force.Get();
dds::create_sdist(params);
auto sdist = dds::create_sdist(params);
if (export_.Get()) {
dds::repository::with_repository( //
dds::repository::default_local_path(),
dds::repo_flags::create_if_absent | dds::repo_flags::write_lock,
[&](dds::repository repo) { //
repo.add_sdist(sdist);
});
}
return 0;
}
};
@@ -161,6 +225,7 @@ int main(int argc, char** argv) {
cli_base cli{parser};
cli_build build{cli};
cli_sdist sdist{cli};
cli_repo repo{cli};
try {
parser.ParseCLI(argc, argv);
} catch (const args::Help&) {
@@ -179,10 +244,15 @@ int main(int argc, char** argv) {
return build.run();
} else if (sdist.cmd) {
return sdist.run();
} else if (repo.cmd) {
return repo.run();
} else {
assert(false);
std::terminate();
}
} catch (const dds::user_cancelled&) {
spdlog::critical("Operation cancelled by user");
return 2;
} catch (const std::exception& e) {
spdlog::critical(e.what());
return 2;

+ 47
- 0
src/dds/repo/repo.cpp ファイルの表示

@@ -0,0 +1,47 @@
#include "./repo.hpp"

#include <dds/sdist.hpp>
#include <dds/util/paths.hpp>

#include <spdlog/spdlog.h>

#include <range/v3/range/conversion.hpp>

using namespace dds;

using namespace ranges;

void repository::_log_blocking(path_ref dirpath) noexcept {
spdlog::warn("Another process has the repository directory locked [{}]", dirpath.string());
spdlog::warn("Waiting for repository to be released...");
}

void repository::_init_repo_dir(path_ref dirpath) noexcept {
fs::create_directories(dirpath / "dist");
}

fs::path repository::default_local_path() noexcept { return dds_data_dir() / "repo"; }

repository repository::open_for_directory(path_ref dirpath) {
auto dist_dir = dirpath / "dist";
auto entries = fs::directory_iterator(dist_dir) | to_vector;
return {dirpath};
}

void repository::add_sdist(const sdist& sd) {
auto sd_dest = _root / "dist" / sd.name() / sd.version() / sd.hash();
if (fs::exists(sd_dest)) {
spdlog::info("Source distribution '{}' is already available in the local repo",
sd.path().string());
return;
}
auto tmp_copy = sd_dest;
tmp_copy.replace_filename(".tmp-import");
if (fs::exists(tmp_copy)) {
fs::remove_all(tmp_copy);
}
fs::create_directories(tmp_copy.parent_path());
fs::copy(sd.path(), tmp_copy, fs::copy_options::recursive);
fs::rename(tmp_copy, sd_dest);
spdlog::info("Source distribution '{}' successfully exported", sd.ident());
}

+ 67
- 0
src/dds/repo/repo.hpp ファイルの表示

@@ -0,0 +1,67 @@
#pragma once

#include <dds/util/flock.hpp>
#include <dds/util/fs.hpp>

#include <cassert>
#include <functional>
#include <shared_mutex>

namespace dds {

class sdist;

enum repo_flags {
none = 0b00,
create_if_absent = 0b01,
write_lock = 0b10,
};

inline repo_flags operator|(repo_flags a, repo_flags b) {
return static_cast<repo_flags>(int(a) | int(b));
}

class repository {
fs::path _root;
repository(path_ref p)
: _root(p) {}

static void _log_blocking(path_ref dir) noexcept;
static void _init_repo_dir(path_ref dir) noexcept;

public:
template <typename Func>
static decltype(auto) with_repository(path_ref dirpath, repo_flags flags, Func&& fn) {
if (!fs::exists(dirpath)) {
if (flags & repo_flags::create_if_absent) {
_init_repo_dir(dirpath);
}
}

shared_file_mutex mut{dirpath / ".lock"};
std::shared_lock shared_lk{mut, std::defer_lock};
std::unique_lock excl_lk{mut, std::defer_lock};

if (flags & repo_flags::write_lock) {
if (!excl_lk.try_lock()) {
_log_blocking(dirpath);
excl_lk.lock();
}
} else {
if (!shared_lk.try_lock()) {
_log_blocking(dirpath);
shared_lk.lock();
}
}

return std::invoke((Func &&) fn, open_for_directory(dirpath));
}

static repository open_for_directory(path_ref);

static fs::path default_local_path() noexcept;

void add_sdist(const sdist&);
};

} // namespace dds

+ 28
- 0
src/dds/util/flock.hpp ファイルの表示

@@ -0,0 +1,28 @@
#pragma once

#include <dds/util/fs.hpp>

namespace dds {

class shared_file_mutex {
fs::path _path;
void* _lock_data = nullptr;

public:
shared_file_mutex(path_ref p);

shared_file_mutex(const shared_file_mutex&) = delete;

~shared_file_mutex();

path_ref path() const noexcept { return _path; }

bool try_lock() noexcept;
bool try_lock_shared() noexcept;
void lock();
void lock_shared();
void unlock();
void unlock_shared();
};

} // namespace dds

+ 90
- 0
src/dds/util/flock.nix.cpp ファイルの表示

@@ -0,0 +1,90 @@
#ifndef _WIN32

#include "./flock.hpp"

#include <dds/util/signal.hpp>

#include <spdlog/fmt/fmt.h>

#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>

using namespace dds;

namespace {

struct lock_data {
int fd;

bool do_lock(int fcntl_kind, short int lock_kind, path_ref p) {
struct ::flock lk = {};
lk.l_type = lock_kind;
lk.l_len = 0;
lk.l_whence = SEEK_SET;
lk.l_start = 0;
auto rc = ::fcntl(fd, fcntl_kind, &lk);
if (rc == -1) {
cancellation_point();
if (errno == EAGAIN || errno == EACCES) {
return false;
}
throw std::system_error(std::error_code(errno, std::system_category()),
fmt::format("Failed to modify file lock [{}]", p.string()));
}
return true;
}
};

} // namespace

#define THIS_DATA static_cast<lock_data*>(_lock_data)

shared_file_mutex::shared_file_mutex(path_ref filepath)
: _path{filepath} {
int fd = ::open(_path.string().c_str(), O_CREAT | O_CLOEXEC | O_RDWR, 0b110'100'100);
if (fd < 0) {
throw std::system_error(std::error_code(errno, std::system_category()),
fmt::format("Failed to open file for locking [{}]",
_path.string()));
}
_lock_data = new lock_data(lock_data{fd});
}

#define MY_LOCK_DATA (*static_cast<lock_data*>(_lock_data))

shared_file_mutex::~shared_file_mutex() {
assert(_lock_data);
::close(MY_LOCK_DATA.fd);
delete &MY_LOCK_DATA;
_lock_data = nullptr;
}

bool shared_file_mutex::try_lock() noexcept {
// Attempt to take an exclusive lock
return MY_LOCK_DATA.do_lock(F_SETLK, F_WRLCK, _path);
}

bool shared_file_mutex::try_lock_shared() noexcept {
// Take a non-exclusive lock
return MY_LOCK_DATA.do_lock(F_SETLK, F_RDLCK, _path);
}

void shared_file_mutex::lock() {
// Blocking exclusive lock
MY_LOCK_DATA.do_lock(F_SETLKW, F_WRLCK, _path);
}

void shared_file_mutex::lock_shared() {
// Blocking shared lock
MY_LOCK_DATA.do_lock(F_SETLKW, F_RDLCK, _path);
}

void shared_file_mutex::unlock() {
// Unlock
MY_LOCK_DATA.do_lock(F_SETLK, F_UNLCK, _path);
}

void shared_file_mutex::unlock_shared() { unlock(); }

#endif

読み込み中…
キャンセル
保存