This commit is contained in:
parent
6a2aa756ca
commit
9cb65ce856
15 changed files with 171 additions and 43 deletions
|
@ -6,8 +6,14 @@ steps:
|
|||
- name: build
|
||||
image: arminfriedl/xwim-build
|
||||
commands:
|
||||
- meson wrap install gtest
|
||||
- meson build
|
||||
- ninja -C build
|
||||
- ninja -C build test && ninja -C build coverage
|
||||
- echo "******** TEST LOGS ***********"
|
||||
- cat build/meson-logs/testlog.txt
|
||||
- echo "****** COVERAGE LOGS *********"
|
||||
- cat build/meson-logs/coverage.txt
|
||||
|
||||
trigger:
|
||||
event:
|
||||
|
@ -23,6 +29,7 @@ steps:
|
|||
- name: build
|
||||
image: arminfriedl/xwim-build
|
||||
commands:
|
||||
- meson wrap install gtest
|
||||
- meson --buildtype=release build
|
||||
- ninja -C build
|
||||
- mkdir xwim-${DRONE_TAG}-x86_64-glibc-linux
|
||||
|
|
|
@ -790,8 +790,7 @@ WARN_LOGFILE =
|
|||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = "@PROJECT_SRCDIR@" \
|
||||
"@PROJECT_BINDIR@"
|
||||
INPUT = "@PROJECT_SRCDIR@"
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
@ -864,7 +863,7 @@ EXCLUDE_SYMBOLS =
|
|||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH = "@PROJECT_EXAMPLESDIR@"
|
||||
EXAMPLE_PATH =
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
|
@ -2020,8 +2019,7 @@ SEARCH_INCLUDES = YES
|
|||
# preprocessor.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH = "@PROJECT_SRCDIR@" \
|
||||
"@PROJECT_BINDIR@"
|
||||
INCLUDE_PATH = "@PROJECT_SRCDIR@"
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
|
|
|
@ -14,7 +14,7 @@ else
|
|||
endif
|
||||
|
||||
cdata.set('PROJECT_NAME', meson.project_name())
|
||||
cdata.set('PROJECT_SRCDIR', join_paths(meson.current_source_dir(),'..','src',meson.project_name()))
|
||||
cdata.set('PROJECT_SRCDIR', join_paths(meson.current_source_dir(),'..','src'))
|
||||
cdata.set('PROJECT_DOCDIR', meson.current_source_dir())
|
||||
cdata.set('PROJECT_TESTDIR', join_paths(meson.current_source_dir(),'..','test'))
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
project('xwim', 'cpp',
|
||||
version: '0.2',
|
||||
default_options: ['cpp_std=c++17'])
|
||||
default_options: ['cpp_std=c++17',
|
||||
'warning_level=3',
|
||||
'b_coverage=true'])
|
||||
|
||||
subdir('src')
|
||||
subdir('doc')
|
||||
subdir('test')
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace xwim {
|
||||
|
||||
/** Class for interacting with archives */
|
||||
class Archive {
|
||||
private:
|
||||
std::filesystem::path path;
|
||||
|
@ -19,7 +20,13 @@ class Archive {
|
|||
public:
|
||||
explicit Archive(std::filesystem::path path);
|
||||
|
||||
/** Generate an ArchiveSpec by analysing the archive at `path`
|
||||
*
|
||||
* @returns ArchiveSpec for the archive
|
||||
*/
|
||||
ArchiveSpec check();
|
||||
|
||||
/** Extract the archive at `path` according to given ExtractSpec */
|
||||
void extract(ExtractSpec extract_spec);
|
||||
};
|
||||
|
||||
|
|
|
@ -4,9 +4,16 @@
|
|||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace xwim {
|
||||
|
||||
/** A view into an archive entry
|
||||
*
|
||||
* The view is non-owning and the caller must guarantee
|
||||
* that the parent archive entry is valid when the view
|
||||
* is accessed.
|
||||
*/
|
||||
class ArchiveEntryView {
|
||||
private:
|
||||
archive_entry* ae;
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
/** @file fileformats.hpp
|
||||
* @brief Handle archive extensions
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
namespace logger = spdlog;
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace xwim {
|
||||
|
||||
/** Common archive formats understood by xwim
|
||||
*
|
||||
* The underlying libarchive backend retrieves format information by a process
|
||||
|
@ -11,22 +20,21 @@ namespace logger = spdlog;
|
|||
* Stripping extensions via `std::filesystem::path` does not work reliably since
|
||||
* it gets easily confused by dots in the regular file name.
|
||||
*/
|
||||
const std::set<std::string> fileformats{".7z", ".7zip", ".jar", ".tgz",
|
||||
".bz2", ".bzip2", ".gz", ".gzip",
|
||||
".rar", ".tar", "xz", ".zip"};
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace xwim {
|
||||
|
||||
const std::set<std::string> fileformats{
|
||||
".7z", ".7zip", ".jar", ".tgz", ".bz2", ".bzip2", ".gz",
|
||||
".gzip", ".rar", ".tar", ".tar.gz", ".tar.bz2", ".tar.xz", ".zip"};
|
||||
|
||||
inline std::filesystem::path stem(std::filesystem::path& path) {
|
||||
std::filesystem::path p_stem {path};
|
||||
/** Strip archive extensions from a path
|
||||
*
|
||||
* @returns Base filename without archive extensions
|
||||
*/
|
||||
inline std::filesystem::path stem(const std::filesystem::path& path) {
|
||||
std::filesystem::path p_stem{path};
|
||||
logger::trace("Stemming {}", p_stem.string());
|
||||
|
||||
while( fileformats.find(p_stem.extension().string()) != fileformats.end() ) {
|
||||
p_stem = p_stem.filename();
|
||||
|
||||
while (fileformats.find(p_stem.extension().string()) != fileformats.end()) {
|
||||
p_stem = p_stem.stem();
|
||||
logger::trace("Stemmed to {}", p_stem.string());
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
src = ['main.cpp',
|
||||
xwim_src = ['main.cpp',
|
||||
'archive.cpp',
|
||||
'archive_sys.cpp']
|
||||
|
||||
inc = ['archive.hpp',
|
||||
xwim_inc = ['archive.hpp',
|
||||
'spec.hpp',
|
||||
'archive_sys.hpp',
|
||||
'fileformats.hpp']
|
||||
|
||||
libs = [dependency('libarchive', required: true),
|
||||
dependency('fmt', required: true, static: true),
|
||||
dependency('spdlog', required: true, static: true)]
|
||||
xwim_libs = [dependency('libarchive', required: true),
|
||||
dependency('fmt', required: true),
|
||||
dependency('spdlog', required: true)]
|
||||
|
||||
executable('xwim', src, inc, dependencies: libs)
|
||||
executable('xwim', xwim_src, xwim_inc, dependencies: xwim_libs)
|
||||
|
|
48
src/spec.hpp
48
src/spec.hpp
|
@ -1,35 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <archive.h>
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
namespace xwim {
|
||||
|
||||
/** Properties of an archive
|
||||
*
|
||||
* These properties can be retrieved by analyzing the
|
||||
* archive. There is no outside-knowledge. All information
|
||||
* is in the archive.
|
||||
*/
|
||||
struct ArchiveSpec {
|
||||
bool has_single_root = false;
|
||||
bool is_root_filename = false;
|
||||
bool is_root_dir = false;
|
||||
bool has_subarchive = false;
|
||||
bool has_single_root = false; /** There is only a single file xor a single
|
||||
folder at the archive's root */
|
||||
bool is_root_filename = false; /** the name of the (single) root is the same
|
||||
as the stemmed archive file name. Cannot be
|
||||
true if `has_single_root` is false */
|
||||
bool is_root_dir = false; /** The (single) root is a folder. Cnnot be true if
|
||||
`has_single_root` is false */
|
||||
bool has_subarchive = false; /** Whether the archive contains sub-archives */
|
||||
};
|
||||
|
||||
/** Properties influencing the extraction process
|
||||
*
|
||||
* These properties can be set to influence the extraction
|
||||
* process accordingly.
|
||||
*/
|
||||
struct ExtractSpec {
|
||||
bool make_dir = false;
|
||||
std::filesystem::path dirname{};
|
||||
bool extract_subarchive = false;
|
||||
bool make_dir = false; /** Create a new directory for extraction at `dirname` */
|
||||
std::filesystem::path dirname{}; /** The path to a directory for extraction */
|
||||
bool extract_subarchive = false; /** Recursively extract sub-archives */
|
||||
};
|
||||
|
||||
} // namespace xwim
|
||||
|
||||
#if FMT_VERSION < 50300
|
||||
typedef fmt::basic_parse_context<char> format_parse_context;
|
||||
#endif
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<xwim::ArchiveSpec> {
|
||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||
constexpr auto parse(format_parse_context & ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const xwim::ArchiveSpec& spec, FormatContext& ctx) {
|
||||
return format_to(ctx.out(),"Archive["
|
||||
return format_to(ctx.out(),
|
||||
"Archive["
|
||||
" .has_single_root={},"
|
||||
" .is_root_filename={}"
|
||||
" .is_root_dir={}"
|
||||
|
@ -46,11 +68,13 @@ struct fmt::formatter<xwim::ExtractSpec> {
|
|||
|
||||
template <typename FormatContext>
|
||||
auto format(const xwim::ExtractSpec& spec, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), "Extract["
|
||||
return format_to(ctx.out(),
|
||||
"Extract["
|
||||
" .make_dir={},"
|
||||
" .dirname={}"
|
||||
" .extract_subarchive={}"
|
||||
" ]",
|
||||
spec.make_dir, spec.dirname.string(), spec.extract_subarchive);
|
||||
spec.make_dir, spec.dirname.string(),
|
||||
spec.extract_subarchive);
|
||||
}
|
||||
};
|
||||
|
|
4
subprojects/.gitignore
vendored
Normal file
4
subprojects/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
11
test/archive_test.cpp
Normal file
11
test/archive_test.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <archive.hpp>
|
||||
#include <spec.hpp>
|
||||
|
||||
TEST(ArchiveTest, ArchiveSpecDetectsSingleRoot) {
|
||||
xwim::Archive archive("test/archives/root.tar.gz");
|
||||
|
||||
xwim::ArchiveSpec spec = archive.check();
|
||||
ASSERT_TRUE(spec.has_single_root);
|
||||
}
|
1
test/archives/meson.build
Normal file
1
test/archives/meson.build
Normal file
|
@ -0,0 +1 @@
|
|||
configure_file(input: 'root.tar.gz', output: 'root.tar.gz', copy: true)
|
BIN
test/archives/root.tar.gz
Normal file
BIN
test/archives/root.tar.gz
Normal file
Binary file not shown.
34
test/fileformats_test.cpp
Normal file
34
test/fileformats_test.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fileformats.hpp>
|
||||
#include <string>
|
||||
|
||||
TEST(FileformatsTest, StemStripsSingleKnownExtension) {
|
||||
std::filesystem::path archive_path {"/some/path/to/file.rar"};
|
||||
|
||||
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file"});
|
||||
}
|
||||
|
||||
TEST(FileformatsTest, StemStripsMultipleKnownExtensions) {
|
||||
std::filesystem::path archive_path{"/some/path/to/file.tar.rar.gz.7z.rar"};
|
||||
|
||||
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file"});
|
||||
}
|
||||
|
||||
TEST(FileformatsTest, StemStripsOnlyKnownExtension) {
|
||||
std::filesystem::path archive_path{"/some/path/to/file.ukn.rar"};
|
||||
|
||||
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file.ukn"});
|
||||
}
|
||||
|
||||
TEST(FileformatsTest, StemStripsNothingWithoutKnownExtension) {
|
||||
std::filesystem::path archive_path{"/some/path/to/file.ukn"};
|
||||
|
||||
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file.ukn"});
|
||||
}
|
||||
|
||||
TEST(FileformatsTest, StemStripsNothingWithoutExtension) {
|
||||
std::filesystem::path archive_path{"/some/path/to/filerar"};
|
||||
|
||||
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"filerar"});
|
||||
}
|
23
test/meson.build
Normal file
23
test/meson.build
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Run tests+coverage with `ninja clean && ninja test && ninja coverage`
|
||||
gtest_proj = subproject('gtest')
|
||||
gtest_dep = gtest_proj.get_variable('gtest_main_dep')
|
||||
|
||||
xwim_src = ['../src/archive.cpp',
|
||||
'../src/archive_sys.cpp']
|
||||
|
||||
subdir('archives')
|
||||
|
||||
archive_test_exe = executable('archive_test_exe',
|
||||
sources: ['archive_test.cpp', xwim_src],
|
||||
include_directories: ['../src'],
|
||||
dependencies: [gtest_dep, xwim_libs])
|
||||
|
||||
|
||||
test('archive test', archive_test_exe)
|
||||
|
||||
fileformats_test_exe = executable('fileformats_test_exe',
|
||||
sources: ['fileformats_test.cpp', xwim_src],
|
||||
include_directories: ['../src'],
|
||||
dependencies: [gtest_dep, xwim_libs])
|
||||
|
||||
test('fileformats test', fileformats_test_exe)
|
Loading…
Reference in a new issue