Retrieve updatable packages
This commit is contained in:
parent
dbf14caee2
commit
34b10f41aa
43
CMakeLists.txt
Normal file
43
CMakeLists.txt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(katja
|
||||||
|
VERSION 1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
include(FindBoost)
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
|
add_executable(katja
|
||||||
|
src/component.h
|
||||||
|
src/command.h
|
||||||
|
src/package.h
|
||||||
|
src/sbo.h
|
||||||
|
src/component.cpp
|
||||||
|
src/command.cpp
|
||||||
|
src/package.cpp
|
||||||
|
src/sbo.cpp
|
||||||
|
src/main.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(katja PRIVATE src)
|
||||||
|
|
||||||
|
find_package(Boost 1.78.0 REQUIRED COMPONENTS filesystem)
|
||||||
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
FetchContent_Declare(ftxui
|
||||||
|
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
|
||||||
|
GIT_TAG v4.0.0
|
||||||
|
)
|
||||||
|
FetchContent_GetProperties(ftxui)
|
||||||
|
if(NOT ftxui_POPULATED)
|
||||||
|
FetchContent_Populate(ftxui)
|
||||||
|
add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(katja
|
||||||
|
PRIVATE Boost::filesystem
|
||||||
|
PRIVATE ftxui::screen
|
||||||
|
PRIVATE ftxui::dom
|
||||||
|
PRIVATE ftxui::component
|
||||||
|
)
|
16
build.ninja
16
build.ninja
@ -1,16 +0,0 @@
|
|||||||
rule cxx
|
|
||||||
command = g++ -c -o $out $in
|
|
||||||
description = CXX $out
|
|
||||||
|
|
||||||
rule link
|
|
||||||
command = g++ -o $out $in -lboost_filesystem -lboost_system
|
|
||||||
description = LINK $out
|
|
||||||
|
|
||||||
build build/package.o: cxx src/package.cpp | src/package.h
|
|
||||||
build build/command.o: cxx src/command.cpp | src/command.h build/package.o src/config.h
|
|
||||||
|
|
||||||
build build/main.o: cxx src/main.cpp
|
|
||||||
|
|
||||||
build build/slackbuilder: link build/main.o build/command.o build/package.o
|
|
||||||
|
|
||||||
default build/slackbuilder
|
|
@ -1,18 +1,52 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "sbo.h"
|
||||||
|
#include <ftxui/dom/elements.hpp>
|
||||||
|
#include <ftxui/component/screen_interactive.hpp>
|
||||||
|
#include "component.h"
|
||||||
|
|
||||||
namespace katja
|
namespace katja
|
||||||
{
|
{
|
||||||
void list::execute() const
|
void list::execute() const
|
||||||
{
|
{
|
||||||
for (const auto& package : katja::read_package_database())
|
sbo sbo_repository;
|
||||||
|
std::unordered_map<std::string, package> packages = katja::read_package_database();
|
||||||
|
std::vector<std::vector<std::string>> table_data;
|
||||||
|
std::set<std::string> installed_packages;
|
||||||
|
std::unordered_map<std::string, package> package_database = read_package_database();
|
||||||
|
|
||||||
|
for (const auto& package : package_database)
|
||||||
{
|
{
|
||||||
std::cout << package.second.name()
|
std::string tag = package.second.tag();
|
||||||
<< " " << package.second.version()
|
if (tag.find("_SBo") == std::string::npos)
|
||||||
<< " (" << package.second.tag() << ")"
|
{
|
||||||
<< std::endl;
|
continue;
|
||||||
|
}
|
||||||
|
installed_packages.insert(package.first);
|
||||||
}
|
}
|
||||||
|
for (const auto& package : sbo_repository.list(installed_packages))
|
||||||
|
{
|
||||||
|
if (package_database.find(package.first)->second.version() == package.second.version())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
table_data.push_back({
|
||||||
|
package.second.name(),
|
||||||
|
package.second.version(),
|
||||||
|
package.second.tag(),
|
||||||
|
package.second.architecture(),
|
||||||
|
"SBo"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto screen = ftxui::ScreenInteractive::Fullscreen();
|
||||||
|
PackageList list_component{
|
||||||
|
{ "Package name", "Version", "Tag", "Architecture", "Repository" },
|
||||||
|
table_data
|
||||||
|
};
|
||||||
|
|
||||||
|
screen.Loop(std::make_shared<PackageList>(list_component));
|
||||||
}
|
}
|
||||||
|
|
||||||
void help::execute() const
|
void help::execute() const
|
||||||
@ -21,30 +55,9 @@ namespace katja
|
|||||||
"\tkatja {list|update|help} [OPTIONS]\n\n";
|
"\tkatja {list|update|help} [OPTIONS]\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
update::update()
|
|
||||||
: git_binary(boost::process::search_path("git"))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void update::execute() const
|
void update::execute() const
|
||||||
{
|
{
|
||||||
std::filesystem::path workdir{ WORKDIR };
|
sbo().refresh();
|
||||||
std::filesystem::path repository = workdir / "sbo/repository";
|
|
||||||
std::filesystem::file_status repository_status = std::filesystem::status(repository);
|
|
||||||
|
|
||||||
if (std::filesystem::exists(repository_status)
|
|
||||||
&& !std::filesystem::is_directory(repository_status))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("The working directory path \""
|
|
||||||
+ repository.string() + "\" exists, but it isn't a directory.");
|
|
||||||
}
|
|
||||||
else if (!std::filesystem::exists(repository_status))
|
|
||||||
{
|
|
||||||
git("clone", std::filesystem::path(),
|
|
||||||
"git://git.slackbuilds.org/slackbuilds.git", repository.native());
|
|
||||||
}
|
|
||||||
git("remote", repository.native(), "update", "--prune");
|
|
||||||
git("reset", repository.native(), "--hard", "origin/master");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command_exception::command_exception(const command_exception_t exception_type,
|
command_exception::command_exception(const command_exception_t exception_type,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "package.h"
|
#include "package.h"
|
||||||
#include <boost/process.hpp>
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
namespace katja
|
namespace katja
|
||||||
{
|
{
|
||||||
@ -28,24 +28,7 @@ namespace katja
|
|||||||
|
|
||||||
class update final : public command
|
class update final : public command
|
||||||
{
|
{
|
||||||
boost::filesystem::path git_binary;
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void git(const std::string& command, const std::filesystem::path& cwd, const Args&... args) const
|
|
||||||
{
|
|
||||||
if (cwd.empty())
|
|
||||||
{
|
|
||||||
boost::process::system(git_binary, command, args...);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boost::process::system(git_binary, "-C", cwd.native(), command, args...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit update();
|
|
||||||
|
|
||||||
void execute() const override;
|
void execute() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
76
src/component.cpp
Normal file
76
src/component.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include <ftxui/component/screen_interactive.hpp>
|
||||||
|
#include "component.h"
|
||||||
|
|
||||||
|
namespace katja
|
||||||
|
{
|
||||||
|
PackageList::PackageList(const std::vector<std::string>& header, const std::vector<std::vector<std::string>>& data)
|
||||||
|
: m_data(data), m_header(header)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ftxui::Element PackageList::Render()
|
||||||
|
{
|
||||||
|
std::vector<std::vector<std::string>> data{ m_header };
|
||||||
|
|
||||||
|
std::size_t dimy = ftxui::ScreenInteractive::Active()->dimy();
|
||||||
|
if (dimy == 0)
|
||||||
|
{
|
||||||
|
dimy = ftxui::Terminal::Size().dimy;
|
||||||
|
}
|
||||||
|
if (dimy > 4) // 4 = headers and borders.
|
||||||
|
{
|
||||||
|
std::size_t row_count = std::min(m_data.size() - top_line, dimy - 4 + top_line);
|
||||||
|
|
||||||
|
std::copy(m_data.cbegin() + top_line, m_data.cbegin() + row_count, std::back_inserter(data));
|
||||||
|
}
|
||||||
|
ftxui::Table table{ data };
|
||||||
|
table.SelectAll().Border(ftxui::LIGHT);
|
||||||
|
|
||||||
|
// Add border around the first column.
|
||||||
|
table.SelectColumn(0).Border(ftxui::LIGHT);
|
||||||
|
|
||||||
|
// Make first row bold with a double border.
|
||||||
|
table.SelectRow(0).Decorate(ftxui::bold);
|
||||||
|
table.SelectRow(0).SeparatorVertical(ftxui::LIGHT);
|
||||||
|
table.SelectRow(0).Border(ftxui::DOUBLE);
|
||||||
|
|
||||||
|
// Align right the "Release date" column.
|
||||||
|
table.SelectColumn(2).DecorateCells(ftxui::align_right);
|
||||||
|
|
||||||
|
// Select row from the second to the last.
|
||||||
|
auto content = table.SelectRows(1, -1);
|
||||||
|
// Alternate in between 3 colors.
|
||||||
|
content.DecorateCellsAlternateRow(color(ftxui::Color::Blue), 3, 0);
|
||||||
|
content.DecorateCellsAlternateRow(color(ftxui::Color::Cyan), 3, 1);
|
||||||
|
content.DecorateCellsAlternateRow(color(ftxui::Color::White), 3, 2);
|
||||||
|
|
||||||
|
return table.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageList::OnEvent(ftxui::Event event)
|
||||||
|
{
|
||||||
|
if (event == ftxui::Event::Character('q') || event == ftxui::Event::Escape)
|
||||||
|
{
|
||||||
|
event.screen_->ExitLoopClosure()();
|
||||||
|
}
|
||||||
|
else if (event == ftxui::Event::Character('j') || event == ftxui::Event::ArrowDown)
|
||||||
|
{
|
||||||
|
if (m_data.size() > 0 && top_line < (m_data.size() - 1))
|
||||||
|
{
|
||||||
|
++top_line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event == ftxui::Event::Character('k') || event == ftxui::Event::ArrowUp)
|
||||||
|
{
|
||||||
|
if (top_line > 0)
|
||||||
|
{
|
||||||
|
--top_line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
20
src/component.h
Normal file
20
src/component.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ftxui/component/component.hpp>
|
||||||
|
#include <ftxui/dom/table.hpp>
|
||||||
|
|
||||||
|
namespace katja
|
||||||
|
{
|
||||||
|
class PackageList final : public ftxui::ComponentBase
|
||||||
|
{
|
||||||
|
std::vector<std::vector<std::string>> m_data;
|
||||||
|
std::vector<std::string> m_header;
|
||||||
|
std::size_t top_line{ 0 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
PackageList(const std::vector<std::string>& header, const std::vector<std::vector<std::string>>& data);
|
||||||
|
|
||||||
|
virtual ftxui::Element Render() override;
|
||||||
|
virtual bool OnEvent(ftxui::Event event) override;
|
||||||
|
};
|
||||||
|
}
|
@ -1 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#define WORKDIR "./var"
|
#define WORKDIR "./var"
|
||||||
|
@ -73,4 +73,34 @@ namespace katja
|
|||||||
}
|
}
|
||||||
return packages;
|
return packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package_exception::package_exception(const std::string& package_name, const std::string& reason) noexcept
|
||||||
|
: m_message(package_name)
|
||||||
|
{
|
||||||
|
m_message.reserve(m_message.size() + 2 + reason.size());
|
||||||
|
m_message += ": " + reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string_view package_exception::package_name() const noexcept
|
||||||
|
{
|
||||||
|
std::size_t colon_position = m_message.find(':');
|
||||||
|
|
||||||
|
if (colon_position == std::string::npos)
|
||||||
|
{
|
||||||
|
return std::string_view(m_message);
|
||||||
|
}
|
||||||
|
else if (colon_position == 0)
|
||||||
|
{
|
||||||
|
return std::string_view("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::string_view(m_message.c_str(), colon_position - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *package_exception::what() const noexcept
|
||||||
|
{
|
||||||
|
return m_message.c_str();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <set>
|
||||||
|
#include <forward_list>
|
||||||
|
|
||||||
namespace katja
|
namespace katja
|
||||||
{
|
{
|
||||||
@ -23,5 +27,24 @@ namespace katja
|
|||||||
static std::optional<package> parse(const std::string& full_name) noexcept;
|
static std::optional<package> parse(const std::string& full_name) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class repository
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual std::unordered_map<std::string, package> list(const std::set<std::string>& package_names) = 0;
|
||||||
|
virtual void refresh() = 0;
|
||||||
|
virtual std::forward_list<std::string> check_dependencies(const std::string& package_name) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
std::unordered_map<std::string, package> read_package_database();
|
std::unordered_map<std::string, package> read_package_database();
|
||||||
|
|
||||||
|
class package_exception : public std::exception
|
||||||
|
{
|
||||||
|
std::string m_message;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit package_exception(const std::string& package_name, const std::string& reason) noexcept;
|
||||||
|
|
||||||
|
const std::string_view package_name() const noexcept;
|
||||||
|
const char *what() const noexcept override;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
232
src/sbo.cpp
Normal file
232
src/sbo.cpp
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
#include "config.h"
|
||||||
|
#include "sbo.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace katja
|
||||||
|
{
|
||||||
|
sbo::sbo()
|
||||||
|
: git_binary(boost::process::search_path("git"))
|
||||||
|
, cache_path(std::filesystem::path{ WORKDIR } / "sbo/repository")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
info sbo::parse_info_file(
|
||||||
|
const std::filesystem::path& package_directory) const
|
||||||
|
{
|
||||||
|
std::map<std::string, std::vector<std::string>> results;
|
||||||
|
std::filesystem::path info_path = cache_path
|
||||||
|
/ package_directory
|
||||||
|
/ package_directory.filename().replace_extension(".info");
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(info_path))
|
||||||
|
{
|
||||||
|
throw package_exception(package_directory.filename().string(), "Package doesn't exist.");
|
||||||
|
}
|
||||||
|
// Parse the info file.
|
||||||
|
std::ifstream info_file{ info_path, std::ios::in };
|
||||||
|
if (!info_file.is_open())
|
||||||
|
{
|
||||||
|
throw package_exception(package_directory.filename().string(), "Unable to open the .info file.");
|
||||||
|
}
|
||||||
|
std::string line;
|
||||||
|
std::string variable_name;
|
||||||
|
std::vector<std::string> variable_values;
|
||||||
|
|
||||||
|
while (std::getline(info_file, line))
|
||||||
|
{
|
||||||
|
std::string::const_iterator current_char = line.cbegin();
|
||||||
|
|
||||||
|
if (variable_name.empty())
|
||||||
|
{
|
||||||
|
current_char = std::find(current_char, line.cend(), '=');
|
||||||
|
if (current_char == line.cbegin() || current_char == line.cend())
|
||||||
|
{
|
||||||
|
break; // Expected 'variable name' or '='.
|
||||||
|
}
|
||||||
|
++current_char;
|
||||||
|
if (current_char == line.cend() || *current_char != '"')
|
||||||
|
{
|
||||||
|
break; // Expected '"'.
|
||||||
|
}
|
||||||
|
variable_name = std::string(line.cbegin(), current_char - 1);
|
||||||
|
|
||||||
|
variable_values.push_back("");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (current_char += 1; current_char != line.end(); ++current_char)
|
||||||
|
{
|
||||||
|
if (*current_char == ' ')
|
||||||
|
{
|
||||||
|
if (!variable_values.back().empty())
|
||||||
|
{
|
||||||
|
variable_values.push_back("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*current_char == '"')
|
||||||
|
{
|
||||||
|
if (variable_values.back().empty())
|
||||||
|
{
|
||||||
|
variable_values.pop_back();
|
||||||
|
}
|
||||||
|
results.insert_or_assign(variable_name, variable_values);
|
||||||
|
variable_name = "";
|
||||||
|
variable_values.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (*current_char == '\\')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
variable_values.back().push_back(*current_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_char == line.cend())
|
||||||
|
{
|
||||||
|
break; // Expected '\' or '"'.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info _info;
|
||||||
|
|
||||||
|
_info.prgnam = results["PRGNAM"][0];
|
||||||
|
_info.version = results["VERSION"][0];
|
||||||
|
_info.homepage = results["HOMEPAGE"].size() == 0 ? "" : results["HOMEPAGE"][0];
|
||||||
|
_info.download = results["DOWNLOAD"];
|
||||||
|
_info.md5sum = results["DOWNLOAD"];
|
||||||
|
_info.download_x86_64 = results["DOWNLOAD_X86_64"];
|
||||||
|
_info.md5sum_x86_64 = results["MD5SUM_X86_64"];
|
||||||
|
_info.requires = results["REQUIRES"];
|
||||||
|
_info.maintainer = results["MAINTAINER"][0];
|
||||||
|
_info.email = results["EMAIL"][0];
|
||||||
|
|
||||||
|
return _info;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::filesystem::path> sbo::collect_packages() const
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::filesystem::path> results;
|
||||||
|
|
||||||
|
for (const auto& category_directory : std::filesystem::directory_iterator(cache_path))
|
||||||
|
{
|
||||||
|
if (!category_directory.is_directory())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const auto& package_directory : std::filesystem::directory_iterator(category_directory.path()))
|
||||||
|
{
|
||||||
|
results.insert({ package_directory.path().filename().string(),
|
||||||
|
category_directory.path().filename() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, package> sbo::list(const std::set<std::string>& package_names)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
std::unordered_map<std::string, package> packages;
|
||||||
|
boost::process::environment slackbuild_environment;
|
||||||
|
slackbuild_environment["PRINT_PACKAGE_NAME"] = "y";
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::filesystem::path> package_categories = collect_packages();
|
||||||
|
|
||||||
|
for (const std::string& package_name : package_names)
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::filesystem::path>::iterator package_category =
|
||||||
|
package_categories.find(package_name);
|
||||||
|
|
||||||
|
if (package_category == package_categories.cend())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::filesystem::path package_directory_name{ package_category->first };
|
||||||
|
std::filesystem::path slackbuild_cwd = cache_path
|
||||||
|
/ package_category->second / package_directory_name;
|
||||||
|
|
||||||
|
// Execute the SlackBuild to get package information.
|
||||||
|
std::filesystem::path slackbuild_file_name =
|
||||||
|
package_directory_name.replace_extension(".SlackBuild");
|
||||||
|
boost::process::ipstream slackbuild_output;
|
||||||
|
|
||||||
|
boost::process::child slackbuild_process("/bin/bash", slackbuild_file_name.native(),
|
||||||
|
slackbuild_environment,
|
||||||
|
boost::process::start_dir(slackbuild_cwd.native()),
|
||||||
|
boost::process::std_out > slackbuild_output,
|
||||||
|
boost::process::std_in.close(),
|
||||||
|
boost::process::std_err > boost::process::null);
|
||||||
|
|
||||||
|
std::optional<package> maybe_package;
|
||||||
|
if (slackbuild_process.running() && std::getline(slackbuild_output, line) && !line.empty())
|
||||||
|
{
|
||||||
|
maybe_package = package::parse(std::filesystem::path(line).replace_extension(""));
|
||||||
|
}
|
||||||
|
slackbuild_process.terminate();
|
||||||
|
|
||||||
|
if (maybe_package.has_value())
|
||||||
|
{
|
||||||
|
packages.insert({ maybe_package.value().name(), maybe_package.value() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packages;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbo::refresh()
|
||||||
|
{
|
||||||
|
std::filesystem::file_status repository_status = std::filesystem::status(cache_path);
|
||||||
|
|
||||||
|
if (std::filesystem::exists(repository_status)
|
||||||
|
&& !std::filesystem::is_directory(repository_status))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("The working directory path \""
|
||||||
|
+ cache_path.string() + "\" exists, but it isn't a directory.");
|
||||||
|
}
|
||||||
|
else if (!std::filesystem::exists(repository_status))
|
||||||
|
{
|
||||||
|
git("clone", std::filesystem::path(),
|
||||||
|
"git://git.slackbuilds.org/slackbuilds.git", cache_path.native());
|
||||||
|
}
|
||||||
|
git("remote", cache_path.native(), "update", "--prune");
|
||||||
|
git("reset", cache_path.native(), "--hard", "origin/master");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::forward_list<std::string> sbo::check_dependencies(const std::string& package_name)
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::filesystem::path> package_categories = collect_packages();
|
||||||
|
ordered_set resolved;
|
||||||
|
|
||||||
|
resolve_dependencies(package_name, package_categories, resolved);
|
||||||
|
std::forward_list<std::string> results;
|
||||||
|
|
||||||
|
for (const auto& dependency : resolved.get<1>())
|
||||||
|
{
|
||||||
|
results.push_front(dependency);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbo::resolve_dependencies(const std::string& package_name,
|
||||||
|
const std::unordered_map<std::string, std::filesystem::path>& package_categories,
|
||||||
|
ordered_set& resolved) const
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::filesystem::path>::const_iterator package_category =
|
||||||
|
package_categories.find(package_name);
|
||||||
|
|
||||||
|
if (package_category == package_categories.cend())
|
||||||
|
{
|
||||||
|
throw package_exception(package_name, "Package not found.");
|
||||||
|
}
|
||||||
|
info info_file = parse_info_file(package_category->second / package_category->first);
|
||||||
|
|
||||||
|
for (const auto& package_dependency : info_file.requires)
|
||||||
|
{
|
||||||
|
if (resolved.find(package_dependency) == resolved.cend())
|
||||||
|
{
|
||||||
|
resolve_dependencies(package_dependency, package_categories, resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolved.insert(package_name);
|
||||||
|
}
|
||||||
|
}
|
73
src/sbo.h
Normal file
73
src/sbo.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "package.h"
|
||||||
|
#include <boost/process.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <map>
|
||||||
|
#include <boost/multi_index_container.hpp>
|
||||||
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
|
#include <boost/multi_index/sequenced_index.hpp>
|
||||||
|
|
||||||
|
namespace katja
|
||||||
|
{
|
||||||
|
struct info
|
||||||
|
{
|
||||||
|
std::string prgnam;
|
||||||
|
std::string version;
|
||||||
|
std::string homepage;
|
||||||
|
std::vector<std::string> download;
|
||||||
|
std::vector<std::string> md5sum;
|
||||||
|
std::vector<std::string> download_x86_64;
|
||||||
|
std::vector<std::string> md5sum_x86_64;
|
||||||
|
std::vector<std::string> requires;
|
||||||
|
std::string maintainer;
|
||||||
|
std::string email;
|
||||||
|
};
|
||||||
|
|
||||||
|
class sbo : public repository
|
||||||
|
{
|
||||||
|
using ordered_set = boost::multi_index_container<
|
||||||
|
std::string,
|
||||||
|
boost::multi_index::indexed_by<
|
||||||
|
boost::multi_index::ordered_unique<boost::multi_index::identity<std::string>>,
|
||||||
|
boost::multi_index::sequenced<>
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
boost::filesystem::path git_binary;
|
||||||
|
std::filesystem::path cache_path;
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void git(const std::string& command, const std::filesystem::path& cwd, const Args&... args) const
|
||||||
|
{
|
||||||
|
if (cwd.empty())
|
||||||
|
{
|
||||||
|
boost::process::system(git_binary, command, args...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boost::process::system(git_binary, "-C", cwd.native(), command, args...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info parse_info_file(const std::filesystem::path& package_directory) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of packages in the repository.
|
||||||
|
*
|
||||||
|
* @return A map containing package names as key, and its category
|
||||||
|
* directory name as value.
|
||||||
|
*/
|
||||||
|
std::unordered_map<std::string, std::filesystem::path> collect_packages() const;
|
||||||
|
|
||||||
|
void resolve_dependencies(const std::string& package_name,
|
||||||
|
const std::unordered_map<std::string, std::filesystem::path>& package_categories,
|
||||||
|
ordered_set& resolved) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit sbo();
|
||||||
|
|
||||||
|
virtual std::unordered_map<std::string, package> list(const std::set<std::string>& package_names) override;
|
||||||
|
virtual void refresh() override;
|
||||||
|
virtual std::forward_list<std::string> check_dependencies(const std::string& package_name) override;
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user