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