diff --git a/include/cppship/cmd/index.h b/include/cppship/cmd/index.h new file mode 100644 index 0000000..ee1fefe --- /dev/null +++ b/include/cppship/cmd/index.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include "cppship/util/fs.h" + +namespace cppship::cmd { + +struct IndexOptions { + std::string operation; + std::optional name; + std::optional path; + std::optional git; +}; + +int run_index(const IndexOptions& options); + +} diff --git a/include/cppship/cmd/install.h b/include/cppship/cmd/install.h index 0faaf10..ce37d5f 100644 --- a/include/cppship/cmd/install.h +++ b/include/cppship/cmd/install.h @@ -1,13 +1,15 @@ #pragma once #include "cppship/core/profile.h" +#include namespace cppship::cmd { struct InstallOptions { Profile profile = Profile::debug; + std::string root; }; int run_install(const InstallOptions& options); -} \ No newline at end of file +} diff --git a/include/cppship/cmd/publish.h b/include/cppship/cmd/publish.h new file mode 100644 index 0000000..664c566 --- /dev/null +++ b/include/cppship/cmd/publish.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "cppship/util/fs.h" + +namespace cppship::cmd { + +struct PublishOptions { + fs::path index; +}; + +int run_publish(const PublishOptions& options); + +} diff --git a/include/cppship/cppship.h b/include/cppship/cppship.h index 441b80f..0cfd480 100644 --- a/include/cppship/cppship.h +++ b/include/cppship/cppship.h @@ -5,8 +5,10 @@ #include "cppship/cmd/clean.h" #include "cppship/cmd/cmake.h" #include "cppship/cmd/fmt.h" +#include "cppship/cmd/index.h" #include "cppship/cmd/init.h" #include "cppship/cmd/install.h" #include "cppship/cmd/lint.h" +#include "cppship/cmd/publish.h" #include "cppship/cmd/run.h" -#include "cppship/cmd/test.h" \ No newline at end of file +#include "cppship/cmd/test.h" diff --git a/include/cppship/util/fs.h b/include/cppship/util/fs.h index 8ee1f64..c6822b8 100644 --- a/include/cppship/util/fs.h +++ b/include/cppship/util/fs.h @@ -27,13 +27,9 @@ class ScopedCurrentDir { fs::path mPrevCwd; }; -inline void create_if_not_exist(const fs::path& path) -{ - if (fs::exists(path)) { - return; - } +void create_if_not_exist(const fs::path& path); - fs::create_directory(path); -} +inline constexpr std::string_view kCppShipDirName = ".cppship"; +fs::path get_cppship_dir(); -} \ No newline at end of file +} diff --git a/lib/cmd/index.cpp b/lib/cmd/index.cpp new file mode 100644 index 0000000..96366e9 --- /dev/null +++ b/lib/cmd/index.cpp @@ -0,0 +1,79 @@ +#include "cppship/cmd/index.h" +#include "cppship/core/manifest.h" +#include "cppship/exception.h" +#include "cppship/util/cmd.h" +#include "cppship/util/fs.h" +#include "cppship/util/log.h" +#include "cppship/util/string.h" +#include +#include +#include +#include +#include + +using namespace cppship; + +int cmd::run_index(const IndexOptions& options) +{ + const std::vector supported_operations { "add", "remove", "list" }; + if (!ranges::contains(supported_operations, options.operation)) { + throw Error { std::format("Unrecognized operation '{}'", options.operation) }; + } + const auto cppship_dir = get_cppship_dir(); + const auto index_store_dir = cppship_dir / "index_store"; + create_if_not_exist(index_store_dir); + + if (options.operation == "list") { + const auto out = check_output("conan remote list"); + const auto lines = util::split(out, boost::is_any_of("\n")); + fmt::print("local package indexes:\n"); + for (const auto& line : lines | ranges::views::filter([&index_store_dir](std::string_view line) { + return line.find(index_store_dir.string()) != std::string::npos; + ; + })) { + fmt::print(" {}\n", line); + } + return EXIT_SUCCESS; + } + + if (!options.name) { + throw Error { "--name option required" }; + } + const auto index_dir = index_store_dir / options.name.value(); + if (options.operation == "add") { + if (fs::exists(index_dir)) { + throw Error { fmt::format("index with name '{}' already exists", options.name.value()) }; + } + + if (!options.path && !options.git) { + throw Error { std::format("one of --path or --git must be given") }; + } + const auto url = options.path.value_or(options.git.value_or("")); + const auto git_cmd = fmt::format("git clone {} {}", url, index_dir.string()); + int res = run_cmd(git_cmd); + if (res != 0) { + throw Error { "git clone failed" }; + } + + const auto conan_cmd = fmt::format( + "conan remote add --type local-recipes-index {} {}", options.name.value(), index_dir.string()); + res = run_cmd(conan_cmd); + if (res != 0) { + throw Error { fmt::format("failed adding '{}' as local index", index_dir.string()) }; + } + } + if (options.operation == "remove") { + if (!fs::exists(index_dir)) { + throw Error { std::format("no index with name '{}' exists", options.name.value()) }; + } + fs::remove_all(index_dir); + + const auto conan_cmd = fmt::format("conan remote remove {}", options.name.value()); + int res = run_cmd(conan_cmd); + if (res != 0) { + throw Error { fmt::format("failed adding '{}' as local index", index_dir.string()) }; + } + } + + return EXIT_SUCCESS; +} diff --git a/lib/cmd/init.cpp b/lib/cmd/init.cpp index feed4c5..388e835 100644 --- a/lib/cmd/init.cpp +++ b/lib/cmd/init.cpp @@ -29,4 +29,4 @@ int cmd::run_init(const InitOptions& options) } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/lib/cmd/install.cpp b/lib/cmd/install.cpp index 975298f..a09c9c2 100644 --- a/lib/cmd/install.cpp +++ b/lib/cmd/install.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -32,9 +33,11 @@ int cmd::run_install([[maybe_unused]] const InstallOptions& options) return EXIT_SUCCESS; } - const auto dst = fmt::format("/usr/local/bin/{}", manifest.name()); - status("install", "{} to {}", bin_file.string(), dst); + const auto root = fs::path(options.root); + const auto dst = root / "bin" / manifest.name(); + cppship::create_if_not_exist(dst.parent_path()); + status("install", "{} to {}", bin_file.string(), dst.string()); fs::copy_file(bin_file, dst, fs::copy_options::overwrite_existing); return EXIT_SUCCESS; #endif -} \ No newline at end of file +} diff --git a/lib/cmd/publish.cpp b/lib/cmd/publish.cpp new file mode 100644 index 0000000..8ee49e1 --- /dev/null +++ b/lib/cmd/publish.cpp @@ -0,0 +1,9 @@ +#include "cppship/cmd/publish.h" +#include "cppship/core/manifest.h" +#include "cppship/exception.h" +#include "cppship/util/fs.h" +#include "cppship/util/log.h" + +using namespace cppship; + +int cmd::run_publish(const PublishOptions& options) { return EXIT_SUCCESS; } diff --git a/lib/util/fs.cpp b/lib/util/fs.cpp new file mode 100644 index 0000000..fb1c4c2 --- /dev/null +++ b/lib/util/fs.cpp @@ -0,0 +1,17 @@ +#include "cppship/util/fs.h" +#include + +void cppship::create_if_not_exist(const fs::path& path) +{ + if (fs::exists(path)) { + return; + } + + if (path.has_parent_path()) { + create_if_not_exist(path.parent_path()); + } + + fs::create_directory(path); +} + +fs::path cppship::get_cppship_dir() { return fs::path(std::getenv("HOME")) / kCppShipDirName; } diff --git a/src/main.cpp b/src/main.cpp index c6d83cd..1f4e99b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,7 @@ #include "cppship/cppship.h" #include "cppship/exception.h" +#include "cppship/util/fs.h" #include "cppship/util/log.h" #ifndef CPPSHIP_VERSION @@ -159,9 +160,7 @@ std::list build_commands(const ArgumentParser& common) // install auto& install = commands.emplace_back("install", common, [](const ArgumentParser& cmd) { - return cmd::run_install({ - .profile = parse_profile(cmd.get("--profile")), - }); + return cmd::run_install({ .profile = parse_profile(cmd.get("--profile")), .root = cmd.get("--root") }); }); install.parser.add_description("install binary if exists"); @@ -169,6 +168,10 @@ std::list build_commands(const ArgumentParser& common) .help("build with specific profile") .metavar("profile") .default_value(std::string { kProfileRelease }); + install.parser.add_argument("--root") + .help("specify the installation root") + .metavar("root") + .default_value(get_cppship_dir().string()); // run auto& run = commands.emplace_back("run", common, [](const ArgumentParser& cmd) { @@ -271,6 +274,22 @@ std::list build_commands(const ArgumentParser& common) cmake.parser.add_description("generate cmake CMakeFiles.txt"); + // index + auto& index = commands.emplace_back("index", common, [](const ArgumentParser& cmd) { + return run_index(cmd::IndexOptions { + .operation = cmd.get("operation"), + .name = cmd.present("name"), + .path = cmd.present("--path"), + .git = cmd.present("git"), + }); + }); + + index.parser.add_description("manage package indexes"); + index.parser.add_argument("operation").help("operation [add, remove, list]"); + index.parser.add_argument("--name").help("name of index"); + index.parser.add_argument("--path").help("filesystem path to local index to add"); + index.parser.add_argument("--git").help("git URL to index to add"); + return commands; } @@ -322,4 +341,4 @@ try { } catch (const std::exception& e) { error("unknown error {}", e.what()); return EXIT_FAILURE; -} \ No newline at end of file +} diff --git a/tests/util/fs.cpp b/tests/util/fs.cpp index 11b4d24..309f55c 100644 --- a/tests/util/fs.cpp +++ b/tests/util/fs.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include "cppship/util/fs.h" @@ -16,4 +18,39 @@ TEST(fs, ScopedCurrentDir) } EXPECT_EQ(fs::current_path(), cwd); -} \ No newline at end of file +} + +TEST(fs, CreateIfNotExist) +{ + const auto tmpdir = fs::temp_directory_path(); + const auto testdir = fs::path("cppship-unitests"); + + ScopedCurrentDir guard(tmpdir); + + if (fs::exists(testdir)) { + fs::remove_all(testdir); + } + fs::create_directory(testdir); + + { + ScopedCurrentDir guard2(testdir); + const auto newdir = fs::path("subdir1"); + EXPECT_FALSE(fs::exists(newdir)); + cppship::create_if_not_exist(newdir); + EXPECT_TRUE(fs::exists(newdir)); + } + { + ScopedCurrentDir guard2(testdir); + const auto newdir = fs::path("./subdir2/subsubdir1"); + EXPECT_FALSE(fs::exists(newdir)); + cppship::create_if_not_exist(newdir); + EXPECT_TRUE(fs::exists(newdir)); + } + { + ScopedCurrentDir guard2(testdir); + const auto newdir = fs::path("subdir3/subsubdir1/subsubsubdir1"); + EXPECT_FALSE(fs::exists(newdir)); + cppship::create_if_not_exist(newdir); + EXPECT_TRUE(fs::exists(newdir)); + } +}