From a2978d5b3d5397030ffd5b955efebeb72e08ff64 Mon Sep 17 00:00:00 2001 From: Joey Riches Date: Sat, 17 Sep 2022 15:24:00 +0100 Subject: [PATCH] builder: Optionally compress man & info pages. Resolves #16 Compress any man or info page files found in the manifest with zstd, if enabled by the packager. There are two considerations:- - It _will_ increase the size of the resulting .stone file(s), (compressing .stones with --long could resolve this in the future). - Updating symlinks is not yet tested. --- source/mason/build/analysers/compressman.d | 107 +++++++++++++++++++++ source/mason/build/analysers/package.d | 1 + source/mason/build/builder.d | 3 + source/mason/meson.build | 1 + 4 files changed, 112 insertions(+) create mode 100644 source/mason/build/analysers/compressman.d diff --git a/source/mason/build/analysers/compressman.d b/source/mason/build/analysers/compressman.d new file mode 100644 index 0000000000..242cec42ae --- /dev/null +++ b/source/mason/build/analysers/compressman.d @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: Copyright © 2020-2023 Serpent OS Developers + * + * SPDX-License-Identifier: Zlib + */ + +/** + * mason.build.analysers.compressman; + * + * Compress man pages with zstd + * + * Authors: Copyright © 2020-2023 Serpent OS Developers + * License: Zlib + */ + +module mason.build.analysers.compressman; + +import mason.build.builder : Builder; +import mason.build.context; +import moss.core.sizing : formattedSize; +import std.algorithm : canFind, endsWith; +import std.experimental.logger; +import std.file; +import std.stdio: File, toFile; +import std.string : format; + +public import moss.deps.analysis; + +/** + * Detect man or info pages + * + * Params: + * analyser = Scoped analyser for this run + * fileInfo = Current file to run analysis on + * Returns: AnalysisReturn.NextFunction when a page is found and compressman is enabled, + * otherwise AnalysisReturn.NextHandler. + */ +public AnalysisReturn acceptManInfoPages(scope Analyser analyser, ref FileInfo fileInfo) +{ + if (!buildContext.spec.options.compressman || fileInfo.type != FileType.Regular) + { + return AnalysisReturn.NextHandler; + } + + auto filename = fileInfo.path; + + /* Accept Man pages */ + if (filename.canFind("man") && filename.endsWith("1", "2", "3", "4", "5", "6", "7", "8", "9")) + { + return AnalysisReturn.NextFunction; + } + /* Accept Info pages */ + if (filename.canFind("info") && filename.endsWith(".info")) + { + return AnalysisReturn.NextFunction; + } + + return AnalysisReturn.NextHandler; +} + +/** + * Compress man or info pages with zstd + * + * Params: + * analyser = Scoped analyser for this run + * fileInfo = Current file to run analysis on + * Returns: AnalysisReturn.IgnoreFile always + */ +static AnalysisReturn compressPages(scope Analyser analyser, ref FileInfo fileInfo) +{ + import zstd.highlevel.context : CompressionContext; + + auto filename = fileInfo.path; + auto instance = analyser.userdata!Builder; + + auto compressor = new CompressionContext(); + + /* We have a symlink file, update it to point to the compressed file */ + // FIXME: Not tested + if ((fileInfo.type == FileType.Symlink) && (fileInfo.type != FileType.Directory)) + { + auto actualpath = std.file.readLink(filename); + std.file.symlink(format!"%s.zst"(actualpath), format!"%s.zst"(filename)); + /* Collect the updated symlink into the manifest */ + instance.collectPath(format!"%s.zst"(fileInfo.fullPath), instance.installRoot); + /* Remove the original file */ + return AnalysisReturn.IgnoreFile; + } + + /* Compress it in memory */ + auto page = std.file.read(fileInfo.fullPath); + auto compressedpage = compressor.compress(page, 19); + immutable double presize = page.length; + immutable double postsize = compressedpage.length; + info(format!"[Man] Compressed: %s. Original size: %s Compressed size: %s"(format!"%s.zst"(filename), + formattedSize(presize), formattedSize(postsize))); + + /* Write the compressed file to disk */ + File f = File(format!"%s.zst"(fileInfo.fullPath), "w"); + f.rawWrite(compressedpage); + + /* Collect the compressed file into the manifest */ + instance.collectPath(format!"%s.zst"(fileInfo.fullPath), instance.installRoot); + + /* Remove the original pre-compressed file */ + return AnalysisReturn.IgnoreFile; +} diff --git a/source/mason/build/analysers/package.d b/source/mason/build/analysers/package.d index 4ac8ae90ba..35bd606b76 100644 --- a/source/mason/build/analysers/package.d +++ b/source/mason/build/analysers/package.d @@ -17,6 +17,7 @@ module mason.build.analysers; public import mason.build.analysers.binary; public import mason.build.analysers.cmake; +public import mason.build.analysers.compressman; public import mason.build.analysers.elves; public import mason.build.analysers.rejects; public import mason.build.analysers.pkgconfig; diff --git a/source/mason/build/builder.d b/source/mason/build/builder.d index 49b2e4f52c..632c9c7e81 100644 --- a/source/mason/build/builder.d +++ b/source/mason/build/builder.d @@ -327,6 +327,9 @@ private: &acceptCmakeFiles, &handleCmakeFiles, &includeFile ], 50), + /* Compress man and info pages if enabled */ + AnalysisChain("compressman", [&acceptManInfoPages, &compressPages], 40), + /* Default inclusion policy */ AnalysisChain("default", [&includeFile], 0), ]; diff --git a/source/mason/meson.build b/source/mason/meson.build index 9b2d80da25..2363a2f109 100644 --- a/source/mason/meson.build +++ b/source/mason/meson.build @@ -4,6 +4,7 @@ libmason_sources = [ 'package.d', 'build/analysers/binary.d', 'build/analysers/cmake.d', + 'build/analysers/compressman.d', 'build/analysers/elves.d', 'build/analysers/package.d', 'build/analysers/pkgconfig.d',