From 1031d6676ed4478b95c4711c7d08f68149b97f01 Mon Sep 17 00:00:00 2001 From: CD Clark III Date: Thu, 20 Feb 2025 19:01:50 -0600 Subject: [PATCH 1/4] feat: added --root option to the install command. --root can be used to specify the install root. --- include/cppship/cmd/install.h | 4 +++- lib/cmd/install.cpp | 4 ++-- src/main.cpp | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) 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/lib/cmd/install.cpp b/lib/cmd/install.cpp index 975298f..17e1f29 100644 --- a/lib/cmd/install.cpp +++ b/lib/cmd/install.cpp @@ -32,9 +32,9 @@ int cmd::run_install([[maybe_unused]] const InstallOptions& options) return EXIT_SUCCESS; } - const auto dst = fmt::format("/usr/local/bin/{}", manifest.name()); + const auto dst = fmt::format("{}/bin/{}", options.root, manifest.name()); status("install", "{} to {}", bin_file.string(), dst); fs::copy_file(bin_file, dst, fs::copy_options::overwrite_existing); return EXIT_SUCCESS; #endif -} \ No newline at end of file +} diff --git a/src/main.cpp b/src/main.cpp index c6d83cd..e94369e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -159,9 +159,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 +167,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(std::string { "/usr/local" }); // run auto& run = commands.emplace_back("run", common, [](const ArgumentParser& cmd) { @@ -322,4 +324,4 @@ try { } catch (const std::exception& e) { error("unknown error {}", e.what()); return EXIT_FAILURE; -} \ No newline at end of file +} From fa44969b4cf0b782463d463918765d266d5ee1be Mon Sep 17 00:00:00 2001 From: CD Clark III Date: Sun, 23 Mar 2025 12:28:04 -0500 Subject: [PATCH 2/4] feat(install): create parent directories for exec install path The create_if_not_exist(...) function will now create parent directories if they do not exists. Added call to create_if_not_exists(...) when installing executable. --- include/cppship/util/fs.h | 9 +-------- lib/cmd/install.cpp | 7 +++++-- lib/util/fs.cpp | 14 ++++++++++++++ tests/util/fs.cpp | 39 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 lib/util/fs.cpp diff --git a/include/cppship/util/fs.h b/include/cppship/util/fs.h index 8ee1f64..70a4cb5 100644 --- a/include/cppship/util/fs.h +++ b/include/cppship/util/fs.h @@ -27,13 +27,6 @@ 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); } - -} \ No newline at end of file diff --git a/lib/cmd/install.cpp b/lib/cmd/install.cpp index 17e1f29..a09c9c2 100644 --- a/lib/cmd/install.cpp +++ b/lib/cmd/install.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -32,8 +33,10 @@ int cmd::run_install([[maybe_unused]] const InstallOptions& options) return EXIT_SUCCESS; } - const auto dst = fmt::format("{}/bin/{}", options.root, 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 diff --git a/lib/util/fs.cpp b/lib/util/fs.cpp new file mode 100644 index 0000000..2b3b895 --- /dev/null +++ b/lib/util/fs.cpp @@ -0,0 +1,14 @@ +#include "cppship/util/fs.h" + +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); +} 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)); + } +} From 9e3a61752814c4b48e593547fd76098bda3caa3d Mon Sep 17 00:00:00 2001 From: CD Clark III Date: Sun, 23 Mar 2025 12:53:16 -0500 Subject: [PATCH 3/4] feat(install): default installs to .cppship dir to squash --- include/cppship/util/fs.h | 3 +++ lib/util/fs.cpp | 3 +++ src/main.cpp | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/cppship/util/fs.h b/include/cppship/util/fs.h index 70a4cb5..c6822b8 100644 --- a/include/cppship/util/fs.h +++ b/include/cppship/util/fs.h @@ -29,4 +29,7 @@ class ScopedCurrentDir { void create_if_not_exist(const fs::path& path); +inline constexpr std::string_view kCppShipDirName = ".cppship"; +fs::path get_cppship_dir(); + } diff --git a/lib/util/fs.cpp b/lib/util/fs.cpp index 2b3b895..fb1c4c2 100644 --- a/lib/util/fs.cpp +++ b/lib/util/fs.cpp @@ -1,4 +1,5 @@ #include "cppship/util/fs.h" +#include void cppship::create_if_not_exist(const fs::path& path) { @@ -12,3 +13,5 @@ void cppship::create_if_not_exist(const fs::path& 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 e94369e..c1f60bf 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 @@ -170,7 +171,7 @@ std::list build_commands(const ArgumentParser& common) install.parser.add_argument("--root") .help("specify the installation root") .metavar("root") - .default_value(std::string { "/usr/local" }); + .default_value(get_cppship_dir().string()); // run auto& run = commands.emplace_back("run", common, [](const ArgumentParser& cmd) { From a755542be1f5d36e8634cc10521391ca705e6b48 Mon Sep 17 00:00:00 2001 From: CD Clark III Date: Sun, 23 Mar 2025 18:45:26 -0500 Subject: [PATCH 4/4] feat: added new `index` command for adding, removing, and listing local recipe index repos New `index` command can be used to add local recipe index repos to the .cppship directory. This is to make it easy add conan remotes from local directories or git repos. --- include/cppship/cmd/index.h | 19 +++++++++ include/cppship/cmd/publish.h | 15 +++++++ include/cppship/cppship.h | 4 +- lib/cmd/index.cpp | 79 +++++++++++++++++++++++++++++++++++ lib/cmd/init.cpp | 2 +- lib/cmd/publish.cpp | 9 ++++ src/main.cpp | 16 +++++++ 7 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 include/cppship/cmd/index.h create mode 100644 include/cppship/cmd/publish.h create mode 100644 lib/cmd/index.cpp create mode 100644 lib/cmd/publish.cpp 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/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/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/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/src/main.cpp b/src/main.cpp index c1f60bf..1f4e99b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -274,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; }