From 146c39fe703c9d170c00a815ea1f89636ece4b23 Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Mon, 1 Apr 2024 15:58:02 +0200 Subject: [PATCH] feat: support for displaying/serializing library dependencies (gh #207) --- CMakeLists.txt | 1 + include/dwarfs/block_compressor.h | 2 + include/dwarfs/library_dependencies.h | 54 ++++++++++++ src/dwarfs/compression/brotli.cpp | 7 ++ src/dwarfs/compression/flac.cpp | 4 + src/dwarfs/compression/lz4.cpp | 8 ++ src/dwarfs/compression/lzma.cpp | 4 + src/dwarfs/compression/null.cpp | 2 + src/dwarfs/compression/ricepp.cpp | 2 + src/dwarfs/compression/zstd.cpp | 4 + src/dwarfs/history.cpp | 12 +++ src/dwarfs/library_dependencies.cpp | 119 ++++++++++++++++++++++++++ src/dwarfs_main.cpp | 2 + src/dwarfsbench_main.cpp | 5 +- src/dwarfsck_main.cpp | 6 +- src/dwarfsextract_main.cpp | 8 +- src/mkdwarfs_main.cpp | 10 ++- thrift/history.thrift | 1 + 18 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 include/dwarfs/library_dependencies.h create mode 100644 src/dwarfs/library_dependencies.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e387401..77c4e1d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -469,6 +469,7 @@ list(APPEND LIBDWARFS_SRC src/dwarfs/inode_manager.cpp src/dwarfs/inode_ordering.cpp src/dwarfs/inode_reader_v2.cpp + src/dwarfs/library_dependencies.cpp src/dwarfs/logger.cpp src/dwarfs/metadata_types.cpp src/dwarfs/metadata_v2.cpp diff --git a/include/dwarfs/block_compressor.h b/include/dwarfs/block_compressor.h index 5bd28f8c..89c27345 100644 --- a/include/dwarfs/block_compressor.h +++ b/include/dwarfs/block_compressor.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -161,6 +162,7 @@ class compression_info { virtual std::string_view name() const = 0; virtual std::string_view description() const = 0; virtual std::vector const& options() const = 0; + virtual std::set library_dependencies() const = 0; }; class compression_factory : public compression_info { diff --git a/include/dwarfs/library_dependencies.h b/include/dwarfs/library_dependencies.h new file mode 100644 index 00000000..3e2d79ff --- /dev/null +++ b/include/dwarfs/library_dependencies.h @@ -0,0 +1,54 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of dwarfs. + * + * dwarfs is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * dwarfs is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with dwarfs. If not, see . + */ + +#pragma once + +#include +#include + +namespace dwarfs { + +enum class version_format { + maj_min_patch_dec_100, // 1.2.3 <-> 10203 +}; + +class library_dependencies { + public: + static std::string common_as_string(); + + void add_library(std::string const& name_version_string); + void add_library(std::string const& library_name, + std::string const& version_string); + void add_library(std::string const& library_name, uint64_t version, + version_format fmt); + void add_library(std::string const& library_name, unsigned major, + unsigned minor, unsigned patch); + + void add_common_libraries(); + + std::string as_string() const; + std::set const& as_set() const { return deps_; } + + private: + std::set deps_; +}; + +} // namespace dwarfs diff --git a/src/dwarfs/compression/brotli.cpp b/src/dwarfs/compression/brotli.cpp index 83d9fa45..c0149953 100644 --- a/src/dwarfs/compression/brotli.cpp +++ b/src/dwarfs/compression/brotli.cpp @@ -193,6 +193,13 @@ class brotli_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { + return {fmt::format("libbrotlienc-{}", + version_string(::BrotliEncoderVersion())), + fmt::format("libbrotlidec-{}", + version_string(::BrotliDecoderVersion()))}; + } + std::unique_ptr make_compressor(option_map& om) const override { return std::make_unique( diff --git a/src/dwarfs/compression/flac.cpp b/src/dwarfs/compression/flac.cpp index 533245d1..b5f254c6 100644 --- a/src/dwarfs/compression/flac.cpp +++ b/src/dwarfs/compression/flac.cpp @@ -505,6 +505,10 @@ class flac_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { + return {fmt::format("libFLAC++-{}", ::FLAC__VERSION_STRING)}; + } + std::unique_ptr make_compressor(option_map& om) const override { return std::make_unique( diff --git a/src/dwarfs/compression/lz4.cpp b/src/dwarfs/compression/lz4.cpp index e131029f..9e90398d 100644 --- a/src/dwarfs/compression/lz4.cpp +++ b/src/dwarfs/compression/lz4.cpp @@ -175,6 +175,10 @@ class lz4_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { + return {fmt::format("liblz4-{}", ::LZ4_versionString())}; + } + std::unique_ptr make_compressor(option_map&) const override { return std::make_unique>(); @@ -206,6 +210,10 @@ class lz4hc_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { + return {fmt::format("liblz4-{}", ::LZ4_versionString())}; + } + std::unique_ptr make_compressor(option_map& om) const override { return std::make_unique>( diff --git a/src/dwarfs/compression/lzma.cpp b/src/dwarfs/compression/lzma.cpp index a3260ef2..7b41d522 100644 --- a/src/dwarfs/compression/lzma.cpp +++ b/src/dwarfs/compression/lzma.cpp @@ -347,6 +347,10 @@ class lzma_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { + return {fmt::format("liblzma-{}", ::lzma_version_string())}; + } + std::unique_ptr make_compressor(option_map& om) const override { return std::make_unique( diff --git a/src/dwarfs/compression/null.cpp b/src/dwarfs/compression/null.cpp index d9bed9b7..22de7bd0 100644 --- a/src/dwarfs/compression/null.cpp +++ b/src/dwarfs/compression/null.cpp @@ -117,6 +117,8 @@ class null_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { return {}; } + std::unique_ptr make_compressor(option_map&) const override { return std::make_unique(); diff --git a/src/dwarfs/compression/ricepp.cpp b/src/dwarfs/compression/ricepp.cpp index b80ef430..c8c4668d 100644 --- a/src/dwarfs/compression/ricepp.cpp +++ b/src/dwarfs/compression/ricepp.cpp @@ -279,6 +279,8 @@ class ricepp_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { return {}; } + std::unique_ptr make_compressor(option_map& om) const override { return std::make_unique( diff --git a/src/dwarfs/compression/zstd.cpp b/src/dwarfs/compression/zstd.cpp index f9dd3560..fb725e94 100644 --- a/src/dwarfs/compression/zstd.cpp +++ b/src/dwarfs/compression/zstd.cpp @@ -191,6 +191,10 @@ class zstd_compression_factory : public compression_factory { std::vector const& options() const override { return options_; } + std::set library_dependencies() const override { + return {fmt::format("libzstd-{}", ::ZSTD_versionString())}; + } + std::unique_ptr make_compressor(option_map& om) const override { return std::make_unique( diff --git a/src/dwarfs/history.cpp b/src/dwarfs/history.cpp index 0bf873d0..b6dbf1c8 100644 --- a/src/dwarfs/history.cpp +++ b/src/dwarfs/history.cpp @@ -27,6 +27,7 @@ #include #include "dwarfs/history.h" +#include "dwarfs/library_dependencies.h" #include "dwarfs/version.h" namespace dwarfs { @@ -66,6 +67,9 @@ void history::append(std::optional> args) { if (cfg_.with_timestamps) { histent.timestamp() = std::time(nullptr); } + library_dependencies deps; + deps.add_common_libraries(); + histent.library_versions() = deps.as_set(); } std::vector history::serialize() const { @@ -163,6 +167,14 @@ folly::dynamic history::as_dynamic() const { ; } + if (histent.library_versions().has_value()) { + folly::dynamic libs = folly::dynamic::array; + for (auto const& lib : histent.library_versions().value()) { + libs.push_back(lib); + } + entry["library_versions"] = std::move(libs); + } + dyn.push_back(std::move(entry)); } diff --git a/src/dwarfs/library_dependencies.cpp b/src/dwarfs/library_dependencies.cpp new file mode 100644 index 00000000..5fcbeacb --- /dev/null +++ b/src/dwarfs/library_dependencies.cpp @@ -0,0 +1,119 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of dwarfs. + * + * dwarfs is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * dwarfs is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with dwarfs. If not, see . + */ + +#include + +#include + +#include +#include + +#include "dwarfs/block_compressor.h" +#include "dwarfs/library_dependencies.h" + +namespace dwarfs { + +namespace { + +std::string version_to_string(uint64_t version, version_format fmt) { + switch (fmt) { + case version_format::maj_min_patch_dec_100: + return fmt::format("{}.{}.{}", version / 10000, (version / 100) % 100, + version % 100); + } + + throw std::invalid_argument("unsupported version format"); +} + +} // namespace + +std::string library_dependencies::common_as_string() { + library_dependencies deps; + deps.add_common_libraries(); + return deps.as_string(); +} + +void library_dependencies::add_library(std::string const& name_version_string) { + auto tmp = name_version_string; + if (tmp.starts_with("lib")) { + tmp.erase(0, 3); + } + std::replace(tmp.begin(), tmp.end(), ' ', '-'); + deps_.insert(tmp); +} + +void library_dependencies::add_library(std::string const& library_name, + std::string const& version_string) { + add_library(fmt::format("{}-{}", library_name, version_string)); +} + +void library_dependencies::add_library(std::string const& library_name, + uint64_t version, version_format fmt) { + add_library(library_name, version_to_string(version, fmt)); +} + +void library_dependencies::add_library(std::string const& library_name, + unsigned major, unsigned minor, + unsigned patch) { + add_library(library_name, fmt::format("{}.{}.{}", major, minor, patch)); +} + +void library_dependencies::add_common_libraries() { + add_library("libxxhash", ::XXH_versionNumber(), + version_format::maj_min_patch_dec_100); + add_library("libfmt", FMT_VERSION, version_format::maj_min_patch_dec_100); + add_library("libcrypto", OPENSSL_version_major(), OPENSSL_version_minor(), + OPENSSL_version_patch()); + + compression_registry::instance().for_each_algorithm( + [this](compression_type, compression_info const& info) { + for (auto const& lib : info.library_dependencies()) { + add_library(lib); + } + }); +} + +std::string library_dependencies::as_string() const { + static constexpr size_t width{80}; + static constexpr std::string_view prefix{"using: "}; + std::string rv{prefix}; + size_t col = prefix.size(); + bool first{true}; + + for (auto const& dep : deps_) { + if (col + dep.size() + 2 > width) { + rv += ",\n"; + rv.append(prefix.size(), ' '); + col = prefix.size(); + } else if (!first) { + rv += ", "; + col += 2; + } + + rv += dep; + col += dep.size(); + first = false; + } + + return rv; +} + +} // namespace dwarfs diff --git a/src/dwarfs_main.cpp b/src/dwarfs_main.cpp index 80bf703a..505e46d5 100644 --- a/src/dwarfs_main.cpp +++ b/src/dwarfs_main.cpp @@ -86,6 +86,7 @@ #include "dwarfs/fstypes.h" #include "dwarfs/iolayer.h" #include "dwarfs/iovec_read_buf.h" +#include "dwarfs/library_dependencies.h" #include "dwarfs/logger.h" #include "dwarfs/metadata_v2.h" #include "dwarfs/mmap.h" @@ -1011,6 +1012,7 @@ int op_rename(char const* from, char const* to, unsigned int flags) { void usage(std::ostream& os, std::filesystem::path const& progname) { os << tool_header("dwarfs", fmt::format(", fuse version {}", FUSE_USE_VERSION)) + << library_dependencies::common_as_string() << "\n\n" #if !DWARFS_FUSE_LOWLEVEL << "USING HIGH-LEVEL FUSE API\n\n" #endif diff --git a/src/dwarfsbench_main.cpp b/src/dwarfsbench_main.cpp index c704ae25..7add3312 100644 --- a/src/dwarfsbench_main.cpp +++ b/src/dwarfsbench_main.cpp @@ -30,6 +30,7 @@ #include "dwarfs/filesystem_v2.h" #include "dwarfs/fstypes.h" #include "dwarfs/iolayer.h" +#include "dwarfs/library_dependencies.h" #include "dwarfs/logger.h" #include "dwarfs/mmap.h" #include "dwarfs/options.h" @@ -85,7 +86,9 @@ int dwarfsbench_main(int argc, sys_char** argv, iolayer const& iol) { } if (vm.count("help") or !vm.count("filesystem")) { - iol.out << tool_header("dwarfsbench") << opts << "\n"; + iol.out << tool_header("dwarfsbench") + << library_dependencies::common_as_string() << "\n\n" + << opts << "\n"; return 0; } diff --git a/src/dwarfsck_main.cpp b/src/dwarfsck_main.cpp index 0e8c8767..a8d1e728 100644 --- a/src/dwarfsck_main.cpp +++ b/src/dwarfsck_main.cpp @@ -42,6 +42,7 @@ #include "dwarfs/file_access.h" #include "dwarfs/filesystem_v2.h" #include "dwarfs/iolayer.h" +#include "dwarfs/library_dependencies.h" #include "dwarfs/logger.h" #include "dwarfs/mmap.h" #include "dwarfs/options.h" @@ -250,7 +251,10 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) { auto constexpr usage = "Usage: dwarfsck [OPTIONS...]\n"; if (vm.count("help") or !vm.count("input")) { - iol.out << tool_header("dwarfsck") << usage << "\n" << opts << "\n"; + iol.out << tool_header("dwarfsck") + << library_dependencies::common_as_string() << "\n\n" + << usage << "\n" + << opts << "\n"; return 0; } diff --git a/src/dwarfsextract_main.cpp b/src/dwarfsextract_main.cpp index ff29e347..cdda7c8e 100644 --- a/src/dwarfsextract_main.cpp +++ b/src/dwarfsextract_main.cpp @@ -33,6 +33,7 @@ #include "dwarfs/filesystem_extractor.h" #include "dwarfs/filesystem_v2.h" #include "dwarfs/iolayer.h" +#include "dwarfs/library_dependencies.h" #include "dwarfs/logger.h" #include "dwarfs/mmap.h" #include "dwarfs/options.h" @@ -128,8 +129,11 @@ int dwarfsextract_main(int argc, sys_char** argv, iolayer const& iol) { auto constexpr usage = "Usage: dwarfsextract [OPTIONS...]\n"; if (vm.count("help") or !vm.count("input")) { - iol.out << tool_header("dwarfsextract") << "using " - << ::archive_version_string() << "\n\n" + library_dependencies deps; + deps.add_common_libraries(); + deps.add_library(::archive_version_string()); + + iol.out << tool_header("dwarfsextract") << deps.as_string() << "\n\n" << usage << "\n" << opts << "\n"; return 0; diff --git a/src/mkdwarfs_main.cpp b/src/mkdwarfs_main.cpp index 20d9d275..4f1653c2 100644 --- a/src/mkdwarfs_main.cpp +++ b/src/mkdwarfs_main.cpp @@ -72,6 +72,7 @@ #include "dwarfs/fragment_order_parser.h" #include "dwarfs/integral_value_parser.h" #include "dwarfs/iolayer.h" +#include "dwarfs/library_dependencies.h" #include "dwarfs/logger.h" #include "dwarfs/match.h" #include "dwarfs/mmap.h" @@ -699,7 +700,9 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) { std::string sep(30 + l_dc + l_sc + l_mc + l_or, '-'); - iol.out << tool_header("mkdwarfs") << usage << opts << "\n" + iol.out << tool_header("mkdwarfs") + << library_dependencies::common_as_string() << "\n\n" + << usage << opts << "\n" << "Compression level defaults:\n" << " " << sep << "\n" << fmt::format(" Level Block {:{}s} {:s} Inode\n", @@ -752,7 +755,10 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) { if (vm.count("help") or !(vm.count("input") or vm.count("input-list")) or (!vm.count("output") and !vm.count("debug-filter"))) { - iol.out << tool_header("mkdwarfs") << usage << "\n" << basic_opts << "\n"; + iol.out << tool_header("mkdwarfs") + << library_dependencies::common_as_string() << "\n\n" + << usage << "\n" + << basic_opts << "\n"; return 0; } diff --git a/thrift/history.thrift b/thrift/history.thrift index 230669ca..c389df56 100644 --- a/thrift/history.thrift +++ b/thrift/history.thrift @@ -48,6 +48,7 @@ struct history_entry { 3: string compiler_id 4: optional list arguments 5: optional UInt64 timestamp + 6: optional set library_versions } struct history {