@@ -1,5 +1,6 @@ | |||
#include <dds/build.hpp> | |||
#include <dds/logging.hpp> | |||
#include <dds/sdist.hpp> | |||
#include <dds/util.hpp> | |||
#include <libman/parse.hpp> | |||
@@ -20,28 +21,61 @@ struct cli_base { | |||
args::Group cmd_group{parser, "Available Commands"}; | |||
}; | |||
struct cli_build { | |||
cli_base& base; | |||
args::Command cmd{base.cmd_group, "build", "Build a library"}; | |||
struct common_flags { | |||
args::Command& cmd; | |||
args::HelpFlag _help{cmd, "help", "Print this hellp message and exit", {'h', "help"}}; | |||
}; | |||
args::HelpFlag _help{cmd, "help", "Display this help message and exit", {'h', "help"}}; | |||
struct common_project_flags { | |||
args::Command& cmd; | |||
path_flag lib_dir{cmd, | |||
"lib_dir", | |||
"The path to the directory containing the library", | |||
{"lib-dir"}, | |||
dds::fs::current_path()}; | |||
path_flag out_dir{cmd, | |||
"out_dir", | |||
"The directory in which to write the built files", | |||
{"out-dir"}, | |||
dds::fs::current_path() / "_build"}; | |||
path_flag root{cmd, | |||
"project_dir", | |||
"Path to the directory containing the project", | |||
{"project-dir"}, | |||
dds::fs::current_path()}; | |||
string_flag export_name{cmd, | |||
"export_name", | |||
"Set the name of the export", | |||
{"export-name", 'n'}, | |||
"Set the name of othe project", | |||
{'n', "export-name"}, | |||
dds::fs::current_path().filename().string()}; | |||
}; | |||
struct cli_sdist { | |||
cli_base& base; | |||
args::Command cmd{base.cmd_group, "sdist", "Create a source distribution of a project"}; | |||
common_flags _common{cmd}; | |||
common_project_flags project{cmd}; | |||
path_flag out{cmd, | |||
"out", | |||
"The full destination of the source distribution", | |||
{"out"}, | |||
dds::fs::current_path() / "project.dsd"}; | |||
args::Flag force{cmd, "force", "Forcibly replace an existing result", {"force"}}; | |||
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); | |||
return 0; | |||
} | |||
}; | |||
struct cli_build { | |||
cli_base& base; | |||
args::Command cmd{base.cmd_group, "build", "Build a project"}; | |||
common_flags _common{cmd}; | |||
common_project_flags project{cmd}; | |||
string_flag tc_filepath{cmd, | |||
"toolchain_file", | |||
@@ -75,6 +109,12 @@ struct cli_build { | |||
{"jobs", 'j'}, | |||
0}; | |||
path_flag out{cmd, | |||
"out", | |||
"The root build directory", | |||
{"out"}, | |||
dds::fs::current_path() / "_build"}; | |||
dds::toolchain _get_toolchain() { | |||
const auto tc_path = tc_filepath.Get(); | |||
if (tc_path.find(":") == 0) { | |||
@@ -92,9 +132,9 @@ struct cli_build { | |||
int run() { | |||
dds::build_params params; | |||
params.root = lib_dir.Get(); | |||
params.out_root = out_dir.Get(); | |||
params.export_name = export_name.Get(); | |||
params.root = project.root.Get(); | |||
params.out_root = out.Get(); | |||
params.export_name = project.export_name.Get(); | |||
params.toolchain = _get_toolchain(); | |||
params.do_export = export_.Get(); | |||
params.build_tests = build_tests.Get(); | |||
@@ -126,6 +166,7 @@ int main(int argc, char** argv) { | |||
cli_base cli{parser}; | |||
cli_build build{cli}; | |||
cli_sdist sdist{cli}; | |||
try { | |||
parser.ParseCLI(argc, argv); | |||
} catch (const args::Help&) { | |||
@@ -140,6 +181,8 @@ int main(int argc, char** argv) { | |||
try { | |||
if (build.cmd) { | |||
return build.run(); | |||
} else if (sdist.cmd) { | |||
return sdist.run(); | |||
} else { | |||
assert(false); | |||
std::terminate(); |
@@ -0,0 +1,67 @@ | |||
#include "./sdist.hpp" | |||
#include <dds/project.hpp> | |||
#include <dds/temp.hpp> | |||
#include <range/v3/algorithm/for_each.hpp> | |||
#include <range/v3/view/filter.hpp> | |||
#include <spdlog/spdlog.h> | |||
using namespace dds; | |||
namespace { | |||
void sdist_copy_library(path_ref out_root, const library& lib, const sdist_params& params) { | |||
auto sources_to_keep = // | |||
lib.sources() // | |||
| ranges::view::filter([&](const source_file& sf) { | |||
if (sf.kind == source_kind::source || sf.kind == source_kind::header) { | |||
return true; | |||
} | |||
if (sf.kind == source_kind::app && params.include_apps) { | |||
return true; | |||
} | |||
if (sf.kind == source_kind::test && params.include_tests) { | |||
return true; | |||
} | |||
return false; | |||
}); | |||
spdlog::info("Export library source from {}", lib.base_dir().string()); | |||
for (const auto& source : sources_to_keep) { | |||
auto relpath = fs::relative(source.path, lib.base_dir()); | |||
spdlog::info("Copy source file {}", relpath.string()); | |||
auto dest = out_root / relpath; | |||
fs::create_directories(dest.parent_path()); | |||
fs::copy(source.path, dest); | |||
} | |||
} | |||
} // namespace | |||
void dds::create_sdist(const sdist_params& params) { | |||
auto dest = params.dest_path; | |||
if (fs::exists(dest)) { | |||
if (!params.force) { | |||
throw std::runtime_error( | |||
fmt::format("Destination path '{}' already exists", dest.string())); | |||
} | |||
} | |||
auto tempdir = temporary_dir::create(); | |||
create_sdist_in_dir(tempdir.path(), params); | |||
if (fs::exists(dest) && params.force) { | |||
fs::remove_all(params.dest_path); | |||
} | |||
fs::create_directories(params.dest_path.parent_path()); | |||
safe_rename(tempdir.path(), params.dest_path); | |||
spdlog::info("Source distribution created in {}", params.dest_path.string()); | |||
} | |||
void dds::create_sdist_in_dir(path_ref out, const sdist_params& params) { | |||
auto project = project::from_directory(params.project_dir); | |||
if (project.main_library()) { | |||
sdist_copy_library(out, *project.main_library(), params); | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
#pragma once | |||
#include <dds/util.hpp> | |||
namespace dds { | |||
struct sdist_params { | |||
fs::path project_dir; | |||
fs::path dest_path; | |||
bool force = false; | |||
bool include_apps = false; | |||
bool include_tests = false; | |||
}; | |||
void create_sdist(const sdist_params&); | |||
void create_sdist_in_dir(path_ref, const sdist_params&); | |||
} // namespace dds |
@@ -0,0 +1,24 @@ | |||
#include "./temp.hpp" | |||
using namespace dds; | |||
temporary_dir temporary_dir::create() { | |||
auto base = fs::temp_directory_path(); | |||
auto file = (base / "dds-tmp-XXXXXX").string(); | |||
const char* tempdir_path = ::mktemp(file.data()); | |||
if (tempdir_path == nullptr) { | |||
throw std::system_error(std::error_code(errno, std::system_category()), | |||
"Failed to create a temporary directory"); | |||
} | |||
auto path = fs::path(tempdir_path); | |||
return std::make_shared<impl>(std::move(path)); | |||
} | |||
temporary_dir::impl::~impl() { | |||
std::error_code ec; | |||
if (fs::exists(path, ec)) { | |||
fs::remove_all(path, ec); | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
#pragma once | |||
#include <dds/util.hpp> | |||
#include <memory> | |||
namespace dds { | |||
class temporary_dir { | |||
struct impl { | |||
fs::path path; | |||
explicit impl(path_ref p) | |||
: path(p) {} | |||
impl(const impl&) = delete; | |||
~impl(); | |||
}; | |||
std::shared_ptr<impl> _ptr; | |||
temporary_dir(std::shared_ptr<impl> p) | |||
: _ptr(p) {} | |||
public: | |||
static temporary_dir create(); | |||
path_ref path() const noexcept { return _ptr->path; } | |||
}; | |||
} // namespace dds |